fix-bug-15613.patch
text/x-diff
Filename: fix-bug-15613.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| contrib/file_fdw/file_fdw.c | 0 | 0 |
| contrib/postgres_fdw/postgres_fdw.c | 0 | 0 |
| doc/src/sgml/fdwhandler.sgml | 0 | 0 |
| src/backend/optimizer/util/pathnode.c | 0 | 0 |
| src/backend/optimizer/util/relnode.c | 0 | 0 |
| src/include/optimizer/pathnode.h | 0 | 0 |
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index be626be..391cd0d 100644
*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
*************** fileGetForeignPaths(PlannerInfo *root,
*** 556,561 ****
--- 556,565 ----
* Create a ForeignPath node and add it as only possible path. We use the
* fdw_private list of the path to carry the convert_selectively option;
* it will be propagated into the fdw_private list of the Plan node.
+ *
+ * We don't support pushing join clauses into the quals of this path, but
+ * it could still have required parameterization due to LATERAL refs in
+ * its tlist.
*/
add_path(baserel, (Path *)
create_foreignscan_path(root, baserel,
*************** fileGetForeignPaths(PlannerInfo *root,
*** 564,570 ****
startup_cost,
total_cost,
NIL, /* no pathkeys */
! NULL, /* no outer rel either */
NULL, /* no extra plan */
coptions));
--- 568,574 ----
startup_cost,
total_cost,
NIL, /* no pathkeys */
! baserel->lateral_relids,
NULL, /* no extra plan */
coptions));
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 7fcac81..994cec5 100644
*** a/contrib/postgres_fdw/postgres_fdw.c
--- b/contrib/postgres_fdw/postgres_fdw.c
*************** postgresGetForeignPaths(PlannerInfo *roo
*** 937,942 ****
--- 937,945 ----
* baserestrict conditions we were able to send to remote, there might
* actually be an indexscan happening there). We already did all the work
* to estimate cost and size of this path.
+ *
+ * Although this path uses no join clauses, it could still have required
+ * parameterization due to LATERAL refs in its tlist.
*/
path = create_foreignscan_path(root, baserel,
NULL, /* default pathtarget */
*************** postgresGetForeignPaths(PlannerInfo *roo
*** 944,950 ****
fpinfo->startup_cost,
fpinfo->total_cost,
NIL, /* no pathkeys */
! NULL, /* no outer rel either */
NULL, /* no extra plan */
NIL); /* no fdw_private list */
add_path(baserel, (Path *) path);
--- 947,953 ----
fpinfo->startup_cost,
fpinfo->total_cost,
NIL, /* no pathkeys */
! baserel->lateral_relids,
NULL, /* no extra plan */
NIL); /* no fdw_private list */
add_path(baserel, (Path *) path);
*************** add_paths_with_pathkeys_for_rel(PlannerI
*** 4943,4958 ****
useful_pathkeys,
-1.0);
! add_path(rel, (Path *)
! create_foreignscan_path(root, rel,
! NULL,
! rows,
! startup_cost,
! total_cost,
! useful_pathkeys,
! NULL,
! sorted_epq_path,
! NIL));
}
}
--- 4947,4974 ----
useful_pathkeys,
-1.0);
! if (IS_SIMPLE_REL(rel))
! add_path(rel, (Path *)
! create_foreignscan_path(root, rel,
! NULL,
! rows,
! startup_cost,
! total_cost,
! useful_pathkeys,
! rel->lateral_relids,
! sorted_epq_path,
! NIL));
! else
! add_path(rel, (Path *)
! create_foreign_join_path(root, rel,
! NULL,
! rows,
! startup_cost,
! total_cost,
! useful_pathkeys,
! rel->lateral_relids,
! sorted_epq_path,
! NIL));
}
}
*************** postgresGetForeignJoinPaths(PlannerInfo
*** 5088,5093 ****
--- 5104,5116 ----
return;
/*
+ * This code does not work for joins with lateral references, since those
+ * must have parameterized paths, which we don't generate yet.
+ */
+ if (!bms_is_empty(joinrel->lateral_relids))
+ return;
+
+ /*
* Create unfinished PgFdwRelationInfo entry which is used to indicate
* that the join relation is already considered, so that we won't waste
* time in judging safety of join pushdown and adding the same paths again
*************** postgresGetForeignJoinPaths(PlannerInfo
*** 5171,5186 ****
* Create a new join path and add it to the joinrel which represents a
* join between foreign tables.
*/
! joinpath = create_foreignscan_path(root,
! joinrel,
! NULL, /* default pathtarget */
! rows,
! startup_cost,
! total_cost,
! NIL, /* no pathkeys */
! NULL, /* no required_outer */
! epq_path,
! NIL); /* no fdw_private */
/* Add generated path into joinrel by add_path(). */
add_path(joinrel, (Path *) joinpath);
--- 5194,5209 ----
* Create a new join path and add it to the joinrel which represents a
* join between foreign tables.
*/
! joinpath = create_foreign_join_path(root,
! joinrel,
! NULL, /* default pathtarget */
! rows,
! startup_cost,
! total_cost,
! NIL, /* no pathkeys */
! joinrel->lateral_relids,
! epq_path,
! NIL); /* no fdw_private */
/* Add generated path into joinrel by add_path(). */
add_path(joinrel, (Path *) joinpath);
*************** add_foreign_grouping_paths(PlannerInfo *
*** 5515,5530 ****
fpinfo->total_cost = total_cost;
/* Create and add foreign path to the grouping relation. */
! grouppath = create_foreignscan_path(root,
! grouped_rel,
! grouped_rel->reltarget,
! rows,
! startup_cost,
! total_cost,
! NIL, /* no pathkeys */
! NULL, /* no required_outer */
! NULL,
! NIL); /* no fdw_private */
/* Add generated path into grouped_rel by add_path(). */
add_path(grouped_rel, (Path *) grouppath);
--- 5538,5552 ----
fpinfo->total_cost = total_cost;
/* Create and add foreign path to the grouping relation. */
! grouppath = create_foreign_upper_path(root,
! grouped_rel,
! grouped_rel->reltarget,
! rows,
! startup_cost,
! total_cost,
! NIL, /* no pathkeys */
! NULL,
! NIL); /* no fdw_private */
/* Add generated path into grouped_rel by add_path(). */
add_path(grouped_rel, (Path *) grouppath);
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 452b776..77038f5 100644
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
*************** GetForeignJoinPaths(PlannerInfo *root,
*** 309,315 ****
function is called during query planning. As
with <function>GetForeignPaths</function>, this function should
generate <structname>ForeignPath</structname> path(s) for the
! supplied <literal>joinrel</literal>, and call <function>add_path</function> to add these
paths to the set of paths considered for the join. But unlike
<function>GetForeignPaths</function>, it is not necessary that this function
succeed in creating at least one path, since paths involving local
--- 309,317 ----
function is called during query planning. As
with <function>GetForeignPaths</function>, this function should
generate <structname>ForeignPath</structname> path(s) for the
! supplied <literal>joinrel</literal>
! (use <function>create_foreign_join_path</function> to build them),
! and call <function>add_path</function> to add these
paths to the set of paths considered for the join. But unlike
<function>GetForeignPaths</function>, it is not necessary that this function
succeed in creating at least one path, since paths involving local
*************** GetForeignUpperPaths(PlannerInfo *root,
*** 369,375 ****
called only if all base relation(s) involved in the query belong to the
same FDW. This function should generate <structname>ForeignPath</structname>
path(s) for any post-scan/join processing that the FDW knows how to
! perform remotely, and call <function>add_path</function> to add these paths to
the indicated upper relation. As with <function>GetForeignJoinPaths</function>,
it is not necessary that this function succeed in creating any paths,
since paths involving local processing are always possible.
--- 371,379 ----
called only if all base relation(s) involved in the query belong to the
same FDW. This function should generate <structname>ForeignPath</structname>
path(s) for any post-scan/join processing that the FDW knows how to
! perform remotely
! (use <function>create_foreign_upper_path</function> to build them),
! and call <function>add_path</function> to add these paths to
the indicated upper relation. As with <function>GetForeignJoinPaths</function>,
it is not necessary that this function succeed in creating any paths,
since paths involving local processing are always possible.
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index b57de6b..9a53e04 100644
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
*************** create_worktablescan_path(PlannerInfo *r
*** 2079,2093 ****
/*
* create_foreignscan_path
! * Creates a path corresponding to a scan of a foreign table, foreign join,
! * or foreign upper-relation processing, returning the pathnode.
*
* This function is never called from core Postgres; rather, it's expected
! * to be called by the GetForeignPaths, GetForeignJoinPaths, or
! * GetForeignUpperPaths function of a foreign data wrapper. We make the FDW
! * supply all fields of the path, since we do not have any way to calculate
! * them in core. However, there is a usually-sane default for the pathtarget
! * (rel->reltarget), so we let a NULL for "target" select that.
*/
ForeignPath *
create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
--- 2079,2092 ----
/*
* create_foreignscan_path
! * Creates a path corresponding to a scan of a foreign base table,
! * returning the pathnode.
*
* This function is never called from core Postgres; rather, it's expected
! * to be called by the GetForeignPaths function of a foreign data wrapper.
! * We make the FDW supply all fields of the path, since we do not have any way
! * to calculate them in core. However, there is a usually-sane default for
! * the pathtarget (rel->reltarget), so we let a NULL for "target" select that.
*/
ForeignPath *
create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
*************** create_foreignscan_path(PlannerInfo *roo
*** 2100,2105 ****
--- 2099,2125 ----
{
ForeignPath *pathnode = makeNode(ForeignPath);
+ /*
+ * Since the path's required_outer should always include all the rel's
+ * lateral_relids, forcibly add those if necessary. This is a bit of a
+ * hack, but up till early 2019 the contrib FDWs failed to ensure that,
+ * and it's likely that the same error has propagated into many external
+ * FDWs. Don't risk modifying the passed-in relid set here.
+ */
+ if (rel->lateral_relids && !bms_is_subset(rel->lateral_relids,
+ required_outer))
+ required_outer = bms_union(required_outer, rel->lateral_relids);
+
+ /*
+ * Although this function is only meant to be used for scans of baserels,
+ * up till early 2019 postgres_fdw abused it to make paths for join and
+ * upper rels, and the same error may have propagated elsewhere. It will
+ * work for such cases as long as required_outer is empty (otherwise
+ * get_baserel_parampathinfo does the wrong thing), so this assertion lets
+ * that past.
+ */
+ Assert(bms_is_empty(required_outer) || IS_SIMPLE_REL(rel));
+
pathnode->path.pathtype = T_ForeignScan;
pathnode->path.parent = rel;
pathnode->path.pathtarget = target ? target : rel->reltarget;
*************** create_foreignscan_path(PlannerInfo *roo
*** 2120,2125 ****
--- 2140,2251 ----
}
/*
+ * create_foreign_join_path
+ * Creates a path corresponding to a scan of a foreign join,
+ * returning the pathnode.
+ *
+ * This function is never called from core Postgres; rather, it's expected
+ * to be called by the GetForeignJoinPaths function of a foreign data wrapper.
+ * We make the FDW supply all fields of the path, since we do not have any way
+ * to calculate them in core. However, there is a usually-sane default for
+ * the pathtarget (rel->reltarget), so we let a NULL for "target" select that.
+ */
+ ForeignPath *
+ create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
+ PathTarget *target,
+ double rows, Cost startup_cost, Cost total_cost,
+ List *pathkeys,
+ Relids required_outer,
+ Path *fdw_outerpath,
+ List *fdw_private)
+ {
+ ForeignPath *pathnode = makeNode(ForeignPath);
+
+ /*
+ * Since the path's required_outer should always include all the rel's
+ * lateral_relids, forcibly add those if necessary. This is a bit of a
+ * hack, but up till early 2019 the contrib FDWs failed to ensure that,
+ * and it's likely that the same error has propagated into many external
+ * FDWs. Don't risk modifying the passed-in relid set here.
+ */
+ if (rel->lateral_relids && !bms_is_subset(rel->lateral_relids,
+ required_outer))
+ required_outer = bms_union(required_outer, rel->lateral_relids);
+
+ /*
+ * We should use get_joinrel_parampathinfo to handle parameterized paths,
+ * but the API of this function doesn't support it, and existing
+ * extensions aren't trying to build such paths yet anyway. For the
+ * moment just throw an error if someone tries it; eventually we should
+ * revisit this.
+ */
+ if (!bms_is_empty(required_outer))
+ elog(ERROR, "parameterized foreign joins are not supported yet");
+
+ pathnode->path.pathtype = T_ForeignScan;
+ pathnode->path.parent = rel;
+ pathnode->path.pathtarget = target ? target : rel->reltarget;
+ pathnode->path.param_info = NULL; /* XXX see above */
+ pathnode->path.parallel_aware = false;
+ pathnode->path.parallel_safe = rel->consider_parallel;
+ pathnode->path.parallel_workers = 0;
+ pathnode->path.rows = rows;
+ pathnode->path.startup_cost = startup_cost;
+ pathnode->path.total_cost = total_cost;
+ pathnode->path.pathkeys = pathkeys;
+
+ pathnode->fdw_outerpath = fdw_outerpath;
+ pathnode->fdw_private = fdw_private;
+
+ return pathnode;
+ }
+
+ /*
+ * create_foreign_upper_path
+ * Creates a path corresponding to an upper relation that's computed
+ * directly by an FDW, returning the pathnode.
+ *
+ * This function is never called from core Postgres; rather, it's expected to
+ * be called by the GetForeignUpperPaths function of a foreign data wrapper.
+ * We make the FDW supply all fields of the path, since we do not have any way
+ * to calculate them in core. However, there is a usually-sane default for
+ * the pathtarget (rel->reltarget), so we let a NULL for "target" select that.
+ */
+ ForeignPath *
+ create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
+ PathTarget *target,
+ double rows, Cost startup_cost, Cost total_cost,
+ List *pathkeys,
+ Path *fdw_outerpath,
+ List *fdw_private)
+ {
+ ForeignPath *pathnode = makeNode(ForeignPath);
+
+ /*
+ * Upper relations should never have any lateral references, since joining
+ * is complete.
+ */
+ Assert(bms_is_empty(rel->lateral_relids));
+
+ pathnode->path.pathtype = T_ForeignScan;
+ pathnode->path.parent = rel;
+ pathnode->path.pathtarget = target ? target : rel->reltarget;
+ pathnode->path.param_info = NULL;
+ pathnode->path.parallel_aware = false;
+ pathnode->path.parallel_safe = rel->consider_parallel;
+ pathnode->path.parallel_workers = 0;
+ pathnode->path.rows = rows;
+ pathnode->path.startup_cost = startup_cost;
+ pathnode->path.total_cost = total_cost;
+ pathnode->path.pathkeys = pathkeys;
+
+ pathnode->fdw_outerpath = fdw_outerpath;
+ pathnode->fdw_private = fdw_private;
+
+ return pathnode;
+ }
+
+ /*
* calc_nestloop_required_outer
* Compute the required_outer set for a nestloop join path
*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index f04c6b7..4130514 100644
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** get_baserel_parampathinfo(PlannerInfo *r
*** 1225,1230 ****
--- 1225,1233 ----
double rows;
ListCell *lc;
+ /* If rel has LATERAL refs, every path for it should account for them */
+ Assert(bms_is_subset(baserel->lateral_relids, required_outer));
+
/* Unparameterized paths have no ParamPathInfo */
if (bms_is_empty(required_outer))
return NULL;
*************** get_joinrel_parampathinfo(PlannerInfo *r
*** 1320,1325 ****
--- 1323,1331 ----
double rows;
ListCell *lc;
+ /* If rel has LATERAL refs, every path for it should account for them */
+ Assert(bms_is_subset(joinrel->lateral_relids, required_outer));
+
/* Unparameterized paths have no ParamPathInfo or extra join clauses */
if (bms_is_empty(required_outer))
return NULL;
*************** get_appendrel_parampathinfo(RelOptInfo *
*** 1511,1516 ****
--- 1517,1525 ----
{
ParamPathInfo *ppi;
+ /* If rel has LATERAL refs, every path for it should account for them */
+ Assert(bms_is_subset(appendrel->lateral_relids, required_outer));
+
/* Unparameterized paths have no ParamPathInfo */
if (bms_is_empty(required_outer))
return NULL;
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index d0c8f99..ef2c9b4 100644
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
*************** extern ForeignPath *create_foreignscan_p
*** 118,123 ****
--- 118,136 ----
Relids required_outer,
Path *fdw_outerpath,
List *fdw_private);
+ extern ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
+ PathTarget *target,
+ double rows, Cost startup_cost, Cost total_cost,
+ List *pathkeys,
+ Relids required_outer,
+ Path *fdw_outerpath,
+ List *fdw_private);
+ extern ForeignPath *create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
+ PathTarget *target,
+ double rows, Cost startup_cost, Cost total_cost,
+ List *pathkeys,
+ Path *fdw_outerpath,
+ List *fdw_private);
extern Relids calc_nestloop_required_outer(Relids outerrelids,
Relids outer_paramrels,