1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

Make Vars be outer-join-aware.

Traditionally we used the same Var struct to represent the value
of a table column everywhere in parse and plan trees.  This choice
predates our support for SQL outer joins, and it's really a pretty
bad idea with outer joins, because the Var's value can depend on
where it is in the tree: it might go to NULL above an outer join.
So expression nodes that are equal() per equalfuncs.c might not
represent the same value, which is a huge correctness hazard for
the planner.

To improve this, decorate Var nodes with a bitmapset showing
which outer joins (identified by RTE indexes) may have nulled
them at the point in the parse tree where the Var appears.
This allows us to trust that equal() Vars represent the same value.
A certain amount of klugery is still needed to cope with cases
where we re-order two outer joins, but it's possible to make it
work without sacrificing that core principle.  PlaceHolderVars
receive similar decoration for the same reason.

In the planner, we include these outer join bitmapsets into the relids
that an expression is considered to depend on, and in consequence also
add outer-join relids to the relids of join RelOptInfos.  This allows
us to correctly perceive whether an expression can be calculated above
or below a particular outer join.

This change affects FDWs that want to plan foreign joins.  They *must*
follow suit when labeling foreign joins in order to match with the
core planner, but for many purposes (if postgres_fdw is any guide)
they'd prefer to consider only base relations within the join.
To support both requirements, redefine ForeignScan.fs_relids as
base+OJ relids, and add a new field fs_base_relids that's set up by
the core planner.

Large though it is, this commit just does the minimum necessary to
install the new mechanisms and get check-world passing again.
Follow-up patches will perform some cleanup.  (The README additions
and comments mention some stuff that will appear in the follow-up.)

Patch by me; thanks to Richard Guo for review.

Discussion: https://postgr.es/m/830269.1656693747@sss.pgh.pa.us
This commit is contained in:
Tom Lane
2023-01-30 13:16:20 -05:00
parent ec7e053a98
commit 2489d76c49
60 changed files with 3896 additions and 984 deletions

View File

@@ -4158,14 +4158,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
/*
* Likewise, copy the relids that are represented by this foreign scan. An
* upper rel doesn't have relids set, but it covers all the base relations
* participating in the underlying scan, so use root's all_baserels.
* upper rel doesn't have relids set, but it covers all the relations
* participating in the underlying scan/join, so use root->all_query_rels.
*/
if (rel->reloptkind == RELOPT_UPPER_REL)
scan_plan->fs_relids = root->all_baserels;
scan_plan->fs_relids = root->all_query_rels;
else
scan_plan->fs_relids = best_path->path.parent->relids;
/*
* Join relid sets include relevant outer joins, but FDWs may need to know
* which are the included base rels. That's a bit tedious to get without
* access to the plan-time data structures, so compute it here.
*/
scan_plan->fs_base_relids = bms_difference(scan_plan->fs_relids,
root->outer_join_rels);
/*
* If this is a foreign join, and to make it valid to push down we had to
* assume that the current user is the same as some user explicitly named
@@ -5806,8 +5814,9 @@ make_foreignscan(List *qptlist,
node->fdw_private = fdw_private;
node->fdw_scan_tlist = fdw_scan_tlist;
node->fdw_recheck_quals = fdw_recheck_quals;
/* fs_relids will be filled in by create_foreignscan_plan */
/* fs_relids, fs_base_relids will be filled by create_foreignscan_plan */
node->fs_relids = NULL;
node->fs_base_relids = NULL;
/* fsSystemCol will be filled in by create_foreignscan_plan */
node->fsSystemCol = false;