diff options
author | Tom Lane | 2015-05-10 18:36:30 +0000 |
---|---|---|
committer | Tom Lane | 2015-05-10 18:36:36 +0000 |
commit | 1a8a4e5cde2b7755e11bde2ea7897bd650622d3e (patch) | |
tree | 17f08ac1fe14058a0b81a48fe437cc60b3e6e3a0 /src/backend | |
parent | c594c750789fd98a19dcdf974b87ba9833995cf5 (diff) |
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/explain.c | 4 | ||||
-rw-r--r-- | src/backend/executor/execScan.c | 23 | ||||
-rw-r--r-- | src/backend/executor/nodeCustom.c | 55 | ||||
-rw-r--r-- | src/backend/executor/nodeForeignscan.c | 41 | ||||
-rw-r--r-- | src/backend/executor/nodeIndexonlyscan.c | 6 | ||||
-rw-r--r-- | src/backend/foreign/foreign.c | 42 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 8 | ||||
-rw-r--r-- | src/backend/nodes/outfuncs.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/path/joinpath.c | 342 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 83 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 256 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 2 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 7 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 27 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 19 |
15 files changed, 431 insertions, 493 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index c5452e3cb6a..eeb8f19017e 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -736,11 +736,11 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used) break; case T_ForeignScan: *rels_used = bms_add_members(*rels_used, - ((ForeignScan *) plan)->fdw_relids); + ((ForeignScan *) plan)->fs_relids); break; case T_CustomScan: *rels_used = bms_add_members(*rels_used, - ((CustomScan *) plan)->custom_relids); + ((CustomScan *) plan)->custom_relids); break; case T_ModifyTable: *rels_used = bms_add_member(*rels_used, diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fa475014f13..a96e826ba42 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -246,19 +246,18 @@ void ExecAssignScanProjectionInfo(ScanState *node) { Scan *scan = (Scan *) node->ps.plan; - Index varno; - /* Vars in an index-only scan's tlist should be INDEX_VAR */ - if (IsA(scan, IndexOnlyScan)) - varno = INDEX_VAR; - /* Also foreign or custom scan on pseudo relation should be INDEX_VAR */ - else if (scan->scanrelid == 0) - { - Assert(IsA(scan, ForeignScan) || IsA(scan, CustomScan)); - varno = INDEX_VAR; - } - else - varno = scan->scanrelid; + ExecAssignScanProjectionInfoWithVarno(node, scan->scanrelid); +} + +/* + * ExecAssignScanProjectionInfoWithVarno + * As above, but caller can specify varno expected in Vars in the tlist. + */ +void +ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno) +{ + Scan *scan = (Scan *) node->ps.plan; if (tlist_matches_tupdesc(&node->ps, scan->plan.targetlist, diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index db1b4f2ffa4..0a022dff940 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -22,13 +22,24 @@ CustomScanState * ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) { - CustomScanState *css; - Index scan_relid = cscan->scan.scanrelid; + CustomScanState *css; + Relation scan_rel = NULL; + Index scanrelid = cscan->scan.scanrelid; + Index tlistvarno; - /* populate a CustomScanState according to the CustomScan */ + /* + * Allocate the CustomScanState object. We let the custom scan provider + * do the palloc, in case it wants to make a larger object that embeds + * CustomScanState as the first field. It must set the node tag and the + * methods field correctly at this time. Other standard fields should be + * set to zero. + */ css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan); Assert(IsA(css, CustomScanState)); + /* ensure flags is filled correctly */ + css->flags = cscan->flags; + /* fill up fields of ScanState */ css->ss.ps.plan = &cscan->scan.plan; css->ss.ps.state = estate; @@ -36,6 +47,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) /* create expression context for node */ ExecAssignExprContext(estate, &css->ss.ps); + css->ss.ps.ps_TupFromTlist = false; + /* initialize child expressions */ css->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) cscan->scan.plan.targetlist, @@ -49,32 +62,40 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) ExecInitResultTupleSlot(estate, &css->ss.ps); /* - * open the base relation and acquire an appropriate lock on it; - * also, get and assign the scan type + * open the base relation, if any, and acquire an appropriate lock on it */ - if (scan_relid > 0) + if (scanrelid > 0) { - Relation scan_rel; - - scan_rel = ExecOpenScanRelation(estate, scan_relid, eflags); + scan_rel = ExecOpenScanRelation(estate, scanrelid, eflags); css->ss.ss_currentRelation = scan_rel; - css->ss.ss_currentScanDesc = NULL; /* set by provider */ - ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel)); } - else + + /* + * Determine the scan tuple type. If the custom scan provider provided a + * targetlist describing the scan tuples, use that; else use base + * relation's rowtype. + */ + if (cscan->custom_scan_tlist != NIL || scan_rel == NULL) { - TupleDesc ps_tupdesc; + TupleDesc scan_tupdesc; - ps_tupdesc = ExecCleanTypeFromTL(cscan->custom_ps_tlist, false); - ExecAssignScanType(&css->ss, ps_tupdesc); + scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist, false); + ExecAssignScanType(&css->ss, scan_tupdesc); + /* Node's targetlist will contain Vars with varno = INDEX_VAR */ + tlistvarno = INDEX_VAR; + } + else + { + ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel)); + /* Node's targetlist will contain Vars with varno = scanrelid */ + tlistvarno = scanrelid; } - css->ss.ps.ps_TupFromTlist = false; /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&css->ss.ps); - ExecAssignScanProjectionInfo(&css->ss); + ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno); /* * The callback of custom-scan provider applies the final initialization diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index fa553ace5d6..bb28a7372d1 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -102,7 +102,9 @@ ForeignScanState * ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) { ForeignScanState *scanstate; + Relation currentRelation = NULL; Index scanrelid = node->scan.scanrelid; + Index tlistvarno; FdwRoutine *fdwroutine; /* check for unsupported flags */ @@ -141,40 +143,55 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) ExecInitScanTupleSlot(estate, &scanstate->ss); /* - * open the base relation and acquire an appropriate lock on it; - * also, get and assign the scan type + * open the base relation, if any, and acquire an appropriate lock on it; + * also acquire function pointers from the FDW's handler */ if (scanrelid > 0) { - Relation currentRelation; - currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags); scanstate->ss.ss_currentRelation = currentRelation; - ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation)); + fdwroutine = GetFdwRoutineForRelation(currentRelation, true); } else { - TupleDesc ps_tupdesc; + /* We can't use the relcache, so get fdwroutine the hard way */ + fdwroutine = GetFdwRoutineByServerId(node->fs_server); + } - ps_tupdesc = ExecCleanTypeFromTL(node->fdw_ps_tlist, false); - ExecAssignScanType(&scanstate->ss, ps_tupdesc); + /* + * Determine the scan tuple type. If the FDW provided a targetlist + * describing the scan tuples, use that; else use base relation's rowtype. + */ + if (node->fdw_scan_tlist != NIL || currentRelation == NULL) + { + TupleDesc scan_tupdesc; + + scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false); + ExecAssignScanType(&scanstate->ss, scan_tupdesc); + /* Node's targetlist will contain Vars with varno = INDEX_VAR */ + tlistvarno = INDEX_VAR; + } + else + { + ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation)); + /* Node's targetlist will contain Vars with varno = scanrelid */ + tlistvarno = scanrelid; } /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&scanstate->ss.ps); - ExecAssignScanProjectionInfo(&scanstate->ss); + ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno); /* - * Acquire function pointers from the FDW's handler, and init fdw_state. + * Initialize FDW-related state. */ - fdwroutine = GetFdwRoutine(node->fdw_handler); scanstate->fdwroutine = fdwroutine; scanstate->fdw_state = NULL; /* - * Tell the FDW to initiate the scan. + * Tell the FDW to initialize the scan. */ fdwroutine->BeginForeignScan(scanstate, eflags); diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 06b7c3c457a..61bd644ab71 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -442,10 +442,12 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) ExecAssignScanType(&indexstate->ss, tupDesc); /* - * Initialize result tuple type and projection info. + * Initialize result tuple type and projection info. The node's + * targetlist will contain Vars with varno = INDEX_VAR, referencing the + * scan tuple. */ ExecAssignResultTypeFromTL(&indexstate->ss.ps); - ExecAssignScanProjectionInfo(&indexstate->ss); + ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR); /* * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index cdbd550fd43..763ee7c9bce 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -304,21 +304,16 @@ GetFdwRoutine(Oid fdwhandler) /* - * GetFdwHandlerByRelId - look up the handler of the foreign-data wrapper - * for the given foreign table + * GetForeignServerIdByRelId - look up the foreign server + * for the given foreign table, and return its OID. */ Oid -GetFdwHandlerByRelId(Oid relid) +GetForeignServerIdByRelId(Oid relid) { HeapTuple tp; - Form_pg_foreign_data_wrapper fdwform; - Form_pg_foreign_server serverform; Form_pg_foreign_table tableform; Oid serverid; - Oid fdwid; - Oid fdwhandler; - /* Get server OID for the foreign table. */ tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for foreign table %u", relid); @@ -326,6 +321,23 @@ GetFdwHandlerByRelId(Oid relid) serverid = tableform->ftserver; ReleaseSysCache(tp); + return serverid; +} + + +/* + * GetFdwRoutineByServerId - look up the handler of the foreign-data wrapper + * for the given foreign server, and retrieve its FdwRoutine struct. + */ +FdwRoutine * +GetFdwRoutineByServerId(Oid serverid) +{ + HeapTuple tp; + Form_pg_foreign_data_wrapper fdwform; + Form_pg_foreign_server serverform; + Oid fdwid; + Oid fdwhandler; + /* Get foreign-data wrapper OID for the server. */ tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)); if (!HeapTupleIsValid(tp)) @@ -350,9 +362,11 @@ GetFdwHandlerByRelId(Oid relid) ReleaseSysCache(tp); - return fdwhandler; + /* And finally, call the handler function. */ + return GetFdwRoutine(fdwhandler); } + /* * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper * for the given foreign table, and retrieve its FdwRoutine struct. @@ -360,9 +374,13 @@ GetFdwHandlerByRelId(Oid relid) FdwRoutine * GetFdwRoutineByRelId(Oid relid) { - Oid fdwhandler = GetFdwHandlerByRelId(relid); + Oid serverid; - return GetFdwRoutine(fdwhandler); + /* Get server OID for the foreign table. */ + serverid = GetForeignServerIdByRelId(relid); + + /* Now retrieve server's FdwRoutine struct. */ + return GetFdwRoutineByServerId(serverid); } /* @@ -656,7 +674,7 @@ get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok) /* - * get_foreign_server_oid - given a FDW name, look up the OID + * get_foreign_server_oid - given a server name, look up the OID * * If missing_ok is false, throw an error if name not found. If true, just * return InvalidOid. diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index a3139d3eb5d..ed8fa72621e 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -599,11 +599,11 @@ _copyForeignScan(const ForeignScan *from) /* * copy remainder of node */ - COPY_SCALAR_FIELD(fdw_handler); + COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); - COPY_NODE_FIELD(fdw_ps_tlist); COPY_NODE_FIELD(fdw_private); - COPY_BITMAPSET_FIELD(fdw_relids); + COPY_NODE_FIELD(fdw_scan_tlist); + COPY_BITMAPSET_FIELD(fs_relids); COPY_SCALAR_FIELD(fsSystemCol); return newnode; @@ -627,8 +627,8 @@ _copyCustomScan(const CustomScan *from) */ COPY_SCALAR_FIELD(flags); COPY_NODE_FIELD(custom_exprs); - COPY_NODE_FIELD(custom_ps_tlist); COPY_NODE_FIELD(custom_private); + COPY_NODE_FIELD(custom_scan_tlist); COPY_BITMAPSET_FIELD(custom_relids); /* diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index bc891d391f5..fe868b889d9 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -565,11 +565,11 @@ _outForeignScan(StringInfo str, const ForeignScan *node) _outScanInfo(str, (const Scan *) node); - WRITE_OID_FIELD(fdw_handler); + WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); - WRITE_NODE_FIELD(fdw_ps_tlist); WRITE_NODE_FIELD(fdw_private); - WRITE_BITMAPSET_FIELD(fdw_relids); + WRITE_NODE_FIELD(fdw_scan_tlist); + WRITE_BITMAPSET_FIELD(fs_relids); WRITE_BOOL_FIELD(fsSystemCol); } @@ -582,8 +582,8 @@ _outCustomScan(StringInfo str, const CustomScan *node) WRITE_UINT_FIELD(flags); WRITE_NODE_FIELD(custom_exprs); - WRITE_NODE_FIELD(custom_ps_tlist); WRITE_NODE_FIELD(custom_private); + WRITE_NODE_FIELD(custom_scan_tlist); WRITE_BITMAPSET_FIELD(custom_relids); appendStringInfoString(str, " :methods "); _outToken(str, node->methods->CustomName); @@ -1844,6 +1844,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node) WRITE_NODE_FIELD(subplan); WRITE_NODE_FIELD(subroot); WRITE_NODE_FIELD(subplan_params); + WRITE_OID_FIELD(serverid); /* we don't try to print fdwroutine or fdw_private */ WRITE_NODE_FIELD(baserestrictinfo); WRITE_NODE_FIELD(joininfo); diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index dabef3c3c7f..ba78252b8f9 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -30,21 +30,13 @@ set_join_pathlist_hook_type set_join_pathlist_hook = NULL; static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, List *mergeclause_list, - JoinType jointype, SpecialJoinInfo *sjinfo, - Relids param_source_rels, Relids extra_lateral_rels); + JoinType jointype, JoinPathExtraData *extra); static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, List *mergeclause_list, - JoinType jointype, SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, Relids extra_lateral_rels); + JoinType jointype, JoinPathExtraData *extra); static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, - JoinType jointype, SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, Relids extra_lateral_rels); + JoinType jointype, JoinPathExtraData *extra); static List *select_mergejoin_clauses(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, @@ -86,13 +78,16 @@ add_paths_to_joinrel(PlannerInfo *root, SpecialJoinInfo *sjinfo, List *restrictlist) { - List *mergeclause_list = NIL; + JoinPathExtraData extra; bool mergejoin_allowed = true; - SemiAntiJoinFactors semifactors; - Relids param_source_rels = NULL; - Relids extra_lateral_rels = NULL; ListCell *lc; + extra.restrictlist = restrictlist; + extra.mergeclause_list = NIL; + extra.sjinfo = sjinfo; + extra.param_source_rels = NULL; + extra.extra_lateral_rels = NULL; + /* * Find potential mergejoin clauses. We can skip this if we are not * interested in doing a mergejoin. However, mergejoin may be our only @@ -100,13 +95,13 @@ add_paths_to_joinrel(PlannerInfo *root, * it's a full join. */ if (enable_mergejoin || jointype == JOIN_FULL) - mergeclause_list = select_mergejoin_clauses(root, - joinrel, - outerrel, - innerrel, - restrictlist, - jointype, - &mergejoin_allowed); + extra.mergeclause_list = select_mergejoin_clauses(root, + joinrel, + outerrel, + innerrel, + restrictlist, + jointype, + &mergejoin_allowed); /* * If it's SEMI or ANTI join, compute correction factors for cost @@ -115,7 +110,7 @@ add_paths_to_joinrel(PlannerInfo *root, if (jointype == JOIN_SEMI || jointype == JOIN_ANTI) compute_semi_anti_join_factors(root, outerrel, innerrel, jointype, sjinfo, restrictlist, - &semifactors); + &extra.semifactors); /* * Decide whether it's sensible to generate parameterized paths for this @@ -142,16 +137,16 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (bms_overlap(joinrel->relids, sjinfo->min_righthand) && !bms_overlap(joinrel->relids, sjinfo->min_lefthand)) - param_source_rels = bms_join(param_source_rels, - bms_difference(root->all_baserels, + extra.param_source_rels = bms_join(extra.param_source_rels, + bms_difference(root->all_baserels, sjinfo->min_righthand)); /* full joins constrain both sides symmetrically */ if (sjinfo->jointype == JOIN_FULL && bms_overlap(joinrel->relids, sjinfo->min_lefthand) && !bms_overlap(joinrel->relids, sjinfo->min_righthand)) - param_source_rels = bms_join(param_source_rels, - bms_difference(root->all_baserels, + extra.param_source_rels = bms_join(extra.param_source_rels, + bms_difference(root->all_baserels, sjinfo->min_lefthand)); } @@ -168,9 +163,9 @@ add_paths_to_joinrel(PlannerInfo *root, LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc); if (bms_is_subset(ljinfo->lateral_rhs, joinrel->relids)) - param_source_rels = bms_join(param_source_rels, - bms_difference(ljinfo->lateral_lhs, - joinrel->relids)); + extra.param_source_rels = bms_join(extra.param_source_rels, + bms_difference(ljinfo->lateral_lhs, + joinrel->relids)); } /* @@ -195,8 +190,8 @@ add_paths_to_joinrel(PlannerInfo *root, !bms_is_subset(phinfo->ph_eval_at, innerrel->relids)) { /* Yes, remember its lateral rels */ - extra_lateral_rels = bms_add_members(extra_lateral_rels, - phinfo->ph_lateral); + extra.extra_lateral_rels = bms_add_members(extra.extra_lateral_rels, + phinfo->ph_lateral); } } @@ -206,9 +201,10 @@ add_paths_to_joinrel(PlannerInfo *root, * it to required_outer below, while preserving the property that * required_outer is exactly NULL if empty.) */ - extra_lateral_rels = bms_del_members(extra_lateral_rels, joinrel->relids); - if (bms_is_empty(extra_lateral_rels)) - extra_lateral_rels = NULL; + extra.extra_lateral_rels = bms_del_members(extra.extra_lateral_rels, + joinrel->relids); + if (bms_is_empty(extra.extra_lateral_rels)) + extra.extra_lateral_rels = NULL; /* * 1. Consider mergejoin paths where both relations must be explicitly @@ -216,9 +212,7 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (mergejoin_allowed) sort_inner_and_outer(root, joinrel, outerrel, innerrel, - restrictlist, mergeclause_list, jointype, - sjinfo, - param_source_rels, extra_lateral_rels); + jointype, &extra); /* * 2. Consider paths where the outer relation need not be explicitly @@ -229,9 +223,7 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (mergejoin_allowed) match_unsorted_outer(root, joinrel, outerrel, innerrel, - restrictlist, mergeclause_list, jointype, - sjinfo, &semifactors, - param_source_rels, extra_lateral_rels); + jointype, &extra); #ifdef NOT_USED @@ -248,9 +240,7 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (mergejoin_allowed) match_unsorted_inner(root, joinrel, outerrel, innerrel, - restrictlist, mergeclause_list, jointype, - sjinfo, &semifactors, - param_source_rels, extra_lateral_rels); + jointype, &extra); #endif /* @@ -260,30 +250,24 @@ add_paths_to_joinrel(PlannerInfo *root, */ if (enable_hashjoin || jointype == JOIN_FULL) hash_inner_and_outer(root, joinrel, outerrel, innerrel, - restrictlist, jointype, - sjinfo, &semifactors, - param_source_rels, extra_lateral_rels); + jointype, &extra); /* - * 5. If both inner and outer relations are managed by the same FDW, - * give it a chance to push down joins. + * 5. If inner and outer relations are foreign tables (or joins) belonging + * to the same server, give the FDW a chance to push down joins. */ if (joinrel->fdwroutine && joinrel->fdwroutine->GetForeignJoinPaths) joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel, outerrel, innerrel, - restrictlist, jointype, sjinfo, - &semifactors, - param_source_rels, - extra_lateral_rels); + jointype, &extra); + /* * 6. Finally, give extensions a chance to manipulate the path list. */ if (set_join_pathlist_hook) set_join_pathlist_hook(root, joinrel, outerrel, innerrel, - restrictlist, jointype, - sjinfo, &semifactors, - param_source_rels, extra_lateral_rels); + jointype, &extra); } /* @@ -294,15 +278,11 @@ add_paths_to_joinrel(PlannerInfo *root, static void try_nestloop_path(PlannerInfo *root, RelOptInfo *joinrel, - JoinType jointype, - SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, - Relids extra_lateral_rels, Path *outer_path, Path *inner_path, - List *restrict_clauses, - List *pathkeys) + List *pathkeys, + JoinType jointype, + JoinPathExtraData *extra) { Relids required_outer; JoinCostWorkspace workspace; @@ -314,7 +294,7 @@ try_nestloop_path(PlannerInfo *root, required_outer = calc_nestloop_required_outer(outer_path, inner_path); if (required_outer && - !bms_overlap(required_outer, param_source_rels)) + !bms_overlap(required_outer, extra->param_source_rels)) { /* * We override the param_source_rels heuristic to accept nestloop @@ -345,7 +325,7 @@ try_nestloop_path(PlannerInfo *root, * Independently of that, add parameterization needed for any * PlaceHolderVars that need to be computed at the join. */ - required_outer = bms_add_members(required_outer, extra_lateral_rels); + required_outer = bms_add_members(required_outer, extra->extra_lateral_rels); /* * Do a precheck to quickly eliminate obviously-inferior paths. We @@ -358,7 +338,7 @@ try_nestloop_path(PlannerInfo *root, */ initial_cost_nestloop(root, &workspace, jointype, outer_path, inner_path, - sjinfo, semifactors); + extra->sjinfo, &extra->semifactors); if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, @@ -369,11 +349,11 @@ try_nestloop_path(PlannerInfo *root, joinrel, jointype, &workspace, - sjinfo, - semifactors, + extra->sjinfo, + &extra->semifactors, outer_path, inner_path, - restrict_clauses, + extra->restrictlist, pathkeys, required_outer)); } @@ -392,17 +372,14 @@ try_nestloop_path(PlannerInfo *root, static void try_mergejoin_path(PlannerInfo *root, RelOptInfo *joinrel, - JoinType jointype, - SpecialJoinInfo *sjinfo, - Relids param_source_rels, - Relids extra_lateral_rels, Path *outer_path, Path *inner_path, - List *restrict_clauses, List *pathkeys, List *mergeclauses, List *outersortkeys, - List *innersortkeys) + List *innersortkeys, + JoinType jointype, + JoinPathExtraData *extra) { Relids required_outer; JoinCostWorkspace workspace; @@ -414,7 +391,7 @@ try_mergejoin_path(PlannerInfo *root, required_outer = calc_non_nestloop_required_outer(outer_path, inner_path); if (required_outer && - !bms_overlap(required_outer, param_source_rels)) + !bms_overlap(required_outer, extra->param_source_rels)) { /* Waste no memory when we reject a path here */ bms_free(required_outer); @@ -425,7 +402,7 @@ try_mergejoin_path(PlannerInfo *root, * Independently of that, add parameterization needed for any * PlaceHolderVars that need to be computed at the join. */ - required_outer = bms_add_members(required_outer, extra_lateral_rels); + required_outer = bms_add_members(required_outer, extra->extra_lateral_rels); /* * If the given paths are already well enough ordered, we can skip doing @@ -444,7 +421,7 @@ try_mergejoin_path(PlannerInfo *root, initial_cost_mergejoin(root, &workspace, jointype, mergeclauses, outer_path, inner_path, outersortkeys, innersortkeys, - sjinfo); + extra->sjinfo); if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, @@ -455,10 +432,10 @@ try_mergejoin_path(PlannerInfo *root, joinrel, jointype, &workspace, - sjinfo, + extra->sjinfo, outer_path, inner_path, - restrict_clauses, + extra->restrictlist, pathkeys, required_outer, mergeclauses, @@ -480,15 +457,11 @@ try_mergejoin_path(PlannerInfo *root, static void try_hashjoin_path(PlannerInfo *root, RelOptInfo *joinrel, - JoinType jointype, - SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, - Relids extra_lateral_rels, Path *outer_path, Path *inner_path, - List *restrict_clauses, - List *hashclauses) + List *hashclauses, + JoinType jointype, + JoinPathExtraData *extra) { Relids required_outer; JoinCostWorkspace workspace; @@ -500,7 +473,7 @@ try_hashjoin_path(PlannerInfo *root, required_outer = calc_non_nestloop_required_outer(outer_path, inner_path); if (required_outer && - !bms_overlap(required_outer, param_source_rels)) + !bms_overlap(required_outer, extra->param_source_rels)) { /* Waste no memory when we reject a path here */ bms_free(required_outer); @@ -511,7 +484,7 @@ try_hashjoin_path(PlannerInfo *root, * Independently of that, add parameterization needed for any * PlaceHolderVars that need to be computed at the join. */ - required_outer = bms_add_members(required_outer, extra_lateral_rels); + required_outer = bms_add_members(required_outer, extra->extra_lateral_rels); /* * See comments in try_nestloop_path(). Also note that hashjoin paths @@ -519,7 +492,7 @@ try_hashjoin_path(PlannerInfo *root, */ initial_cost_hashjoin(root, &workspace, jointype, hashclauses, outer_path, inner_path, - sjinfo, semifactors); + extra->sjinfo, &extra->semifactors); if (add_path_precheck(joinrel, workspace.startup_cost, workspace.total_cost, @@ -530,11 +503,11 @@ try_hashjoin_path(PlannerInfo *root, joinrel, jointype, &workspace, - sjinfo, - semifactors, + extra->sjinfo, + &extra->semifactors, outer_path, inner_path, - restrict_clauses, + extra->restrictlist, required_outer, hashclauses)); } @@ -584,26 +557,16 @@ clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel, * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation - * 'restrictlist' contains all of the RestrictInfo nodes for restriction - * clauses that apply to this join - * 'mergeclause_list' is a list of RestrictInfo nodes for available - * mergejoin clauses in this join * 'jointype' is the type of join to do - * 'sjinfo' is extra info about the join for selectivity estimation - * 'param_source_rels' are OK targets for parameterization of result paths - * 'extra_lateral_rels' are additional parameterization for result paths + * 'extra' contains additional input values */ static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, - List *mergeclause_list, JoinType jointype, - SpecialJoinInfo *sjinfo, - Relids param_source_rels, - Relids extra_lateral_rels) + JoinPathExtraData *extra) { Path *outer_path; Path *inner_path; @@ -643,14 +606,14 @@ sort_inner_and_outer(PlannerInfo *root, if (jointype == JOIN_UNIQUE_OUTER) { outer_path = (Path *) create_unique_path(root, outerrel, - outer_path, sjinfo); + outer_path, extra->sjinfo); Assert(outer_path); jointype = JOIN_INNER; } else if (jointype == JOIN_UNIQUE_INNER) { inner_path = (Path *) create_unique_path(root, innerrel, - inner_path, sjinfo); + inner_path, extra->sjinfo); Assert(inner_path); jointype = JOIN_INNER; } @@ -684,7 +647,7 @@ sort_inner_and_outer(PlannerInfo *root, * exactly as-is as well as making variants. */ all_pathkeys = select_outer_pathkeys_for_merge(root, - mergeclause_list, + extra->mergeclause_list, joinrel); foreach(l, all_pathkeys) @@ -707,10 +670,10 @@ sort_inner_and_outer(PlannerInfo *root, cur_mergeclauses = find_mergeclauses_for_pathkeys(root, outerkeys, true, - mergeclause_list); + extra->mergeclause_list); /* Should have used them all... */ - Assert(list_length(cur_mergeclauses) == list_length(mergeclause_list)); + Assert(list_length(cur_mergeclauses) == list_length(extra->mergeclause_list)); /* Build sort pathkeys for the inner side */ innerkeys = make_inner_pathkeys_for_merge(root, @@ -730,17 +693,14 @@ sort_inner_and_outer(PlannerInfo *root, */ try_mergejoin_path(root, joinrel, - jointype, - sjinfo, - param_source_rels, - extra_lateral_rels, outer_path, inner_path, - restrictlist, merge_pathkeys, cur_mergeclauses, outerkeys, - innerkeys); + innerkeys, + jointype, + extra); } } @@ -771,28 +731,16 @@ sort_inner_and_outer(PlannerInfo *root, * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation - * 'restrictlist' contains all of the RestrictInfo nodes for restriction - * clauses that apply to this join - * 'mergeclause_list' is a list of RestrictInfo nodes for available - * mergejoin clauses in this join * 'jointype' is the type of join to do - * 'sjinfo' is extra info about the join for selectivity estimation - * 'semifactors' contains valid data if jointype is SEMI or ANTI - * 'param_source_rels' are OK targets for parameterization of result paths - * 'extra_lateral_rels' are additional parameterization for result paths + * 'extra' contains additional input values */ static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, - List *mergeclause_list, JoinType jointype, - SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, - Relids extra_lateral_rels) + JoinPathExtraData *extra) { JoinType save_jointype = jointype; bool nestjoinOK; @@ -854,7 +802,7 @@ match_unsorted_outer(PlannerInfo *root, if (inner_cheapest_total == NULL) return; inner_cheapest_total = (Path *) - create_unique_path(root, innerrel, inner_cheapest_total, sjinfo); + create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo); Assert(inner_cheapest_total); } else if (nestjoinOK) @@ -898,7 +846,7 @@ match_unsorted_outer(PlannerInfo *root, if (outerpath != outerrel->cheapest_total_path) continue; outerpath = (Path *) create_unique_path(root, outerrel, - outerpath, sjinfo); + outerpath, extra->sjinfo); Assert(outerpath); } @@ -918,15 +866,11 @@ match_unsorted_outer(PlannerInfo *root, */ try_nestloop_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, outerpath, inner_cheapest_total, - restrictlist, - merge_pathkeys); + merge_pathkeys, + jointype, + extra); } else if (nestjoinOK) { @@ -944,30 +888,22 @@ match_unsorted_outer(PlannerInfo *root, try_nestloop_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, outerpath, innerpath, - restrictlist, - merge_pathkeys); + merge_pathkeys, + jointype, + extra); } /* Also consider materialized form of the cheapest inner path */ if (matpath != NULL) try_nestloop_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, outerpath, matpath, - restrictlist, - merge_pathkeys); + merge_pathkeys, + jointype, + extra); } /* Can't do anything else if outer path needs to be unique'd */ @@ -982,7 +918,7 @@ match_unsorted_outer(PlannerInfo *root, mergeclauses = find_mergeclauses_for_pathkeys(root, outerpath->pathkeys, true, - mergeclause_list); + extra->mergeclause_list); /* * Done with this outer path if no chance for a mergejoin. @@ -1000,7 +936,7 @@ match_unsorted_outer(PlannerInfo *root, else continue; } - if (useallclauses && list_length(mergeclauses) != list_length(mergeclause_list)) + if (useallclauses && list_length(mergeclauses) != list_length(extra->mergeclause_list)) continue; /* Compute the required ordering of the inner path */ @@ -1016,17 +952,14 @@ match_unsorted_outer(PlannerInfo *root, */ try_mergejoin_path(root, joinrel, - jointype, - sjinfo, - param_source_rels, - extra_lateral_rels, outerpath, inner_cheapest_total, - restrictlist, merge_pathkeys, mergeclauses, NIL, - innersortkeys); + innersortkeys, + jointype, + extra); /* Can't do anything else if inner path needs to be unique'd */ if (save_jointype == JOIN_UNIQUE_INNER) @@ -1115,17 +1048,14 @@ match_unsorted_outer(PlannerInfo *root, newclauses = mergeclauses; try_mergejoin_path(root, joinrel, - jointype, - sjinfo, - param_source_rels, - extra_lateral_rels, outerpath, innerpath, - restrictlist, merge_pathkeys, newclauses, NIL, - NIL); + NIL, + jointype, + extra); cheapest_total_inner = innerpath; } /* Same on the basis of cheapest startup cost ... */ @@ -1161,17 +1091,14 @@ match_unsorted_outer(PlannerInfo *root, } try_mergejoin_path(root, joinrel, - jointype, - sjinfo, - param_source_rels, - extra_lateral_rels, outerpath, innerpath, - restrictlist, merge_pathkeys, newclauses, NIL, - NIL); + NIL, + jointype, + extra); } cheapest_startup_inner = innerpath; } @@ -1193,25 +1120,16 @@ match_unsorted_outer(PlannerInfo *root, * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation - * 'restrictlist' contains all of the RestrictInfo nodes for restriction - * clauses that apply to this join * 'jointype' is the type of join to do - * 'sjinfo' is extra info about the join for selectivity estimation - * 'semifactors' contains valid data if jointype is SEMI or ANTI - * 'param_source_rels' are OK targets for parameterization of result paths - * 'extra_lateral_rels' are additional parameterization for result paths + * 'extra' contains additional input values */ static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, - List *restrictlist, JoinType jointype, - SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, - Relids extra_lateral_rels) + JoinPathExtraData *extra) { bool isouterjoin = IS_OUTER_JOIN(jointype); List *hashclauses; @@ -1225,7 +1143,7 @@ hash_inner_and_outer(PlannerInfo *root, * usable with this pair of sub-relations. */ hashclauses = NIL; - foreach(l, restrictlist) + foreach(l, extra->restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l); @@ -1276,53 +1194,41 @@ hash_inner_and_outer(PlannerInfo *root, { cheapest_total_outer = (Path *) create_unique_path(root, outerrel, - cheapest_total_outer, sjinfo); + cheapest_total_outer, extra->sjinfo); Assert(cheapest_total_outer); jointype = JOIN_INNER; try_hashjoin_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, cheapest_total_outer, cheapest_total_inner, - restrictlist, - hashclauses); + hashclauses, + jointype, + extra); /* no possibility of cheap startup here */ } else if (jointype == JOIN_UNIQUE_INNER) { cheapest_total_inner = (Path *) create_unique_path(root, innerrel, - cheapest_total_inner, sjinfo); + cheapest_total_inner, extra->sjinfo); Assert(cheapest_total_inner); jointype = JOIN_INNER; try_hashjoin_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, cheapest_total_outer, cheapest_total_inner, - restrictlist, - hashclauses); + hashclauses, + jointype, + extra); if (cheapest_startup_outer != NULL && cheapest_startup_outer != cheapest_total_outer) try_hashjoin_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, cheapest_startup_outer, cheapest_total_inner, - restrictlist, - hashclauses); + hashclauses, + jointype, + extra); } else { @@ -1339,15 +1245,11 @@ hash_inner_and_outer(PlannerInfo *root, if (cheapest_startup_outer != NULL) try_hashjoin_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, cheapest_startup_outer, cheapest_total_inner, - restrictlist, - hashclauses); + hashclauses, + jointype, + extra); foreach(lc1, outerrel->cheapest_parameterized_paths) { @@ -1377,15 +1279,11 @@ hash_inner_and_outer(PlannerInfo *root, try_hashjoin_path(root, joinrel, - jointype, - sjinfo, - semifactors, - param_source_rels, - extra_lateral_rels, outerpath, innerpath, - restrictlist, - hashclauses); + hashclauses, + jointype, + extra); } } } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 3246332d6e3..c8092372831 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -44,6 +44,7 @@ #include "utils/lsyscache.h" +static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path); static Plan *create_scan_plan(PlannerInfo *root, Path *best_path); static List *build_path_tlist(PlannerInfo *root, Path *path); static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel); @@ -219,7 +220,7 @@ create_plan(PlannerInfo *root, Path *best_path) * create_plan_recurse * Recursive guts of create_plan(). */ -Plan * +static Plan * create_plan_recurse(PlannerInfo *root, Path *best_path) { Plan *plan; @@ -1950,7 +1951,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path, /* * create_foreignscan_plan - * Returns a foreignscan plan for the base relation scanned by 'best_path' + * Returns a foreignscan plan for the relation scanned by 'best_path' * with restriction clauses 'scan_clauses' and targetlist 'tlist'. */ static ForeignScan * @@ -1965,9 +1966,11 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, ListCell *lc; int i; + Assert(rel->fdwroutine != NULL); + /* - * If we're scanning a base relation, look up the OID. - * (We can skip this if scanning a join relation.) + * If we're scanning a base relation, fetch its OID. (Irrelevant if + * scanning a join relation.) */ if (scan_relid > 0) { @@ -1978,7 +1981,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, Assert(rte->rtekind == RTE_RELATION); rel_oid = rte->relid; } - Assert(rel->fdwroutine != NULL); /* * Sort clauses into best execution order. We do this first since the FDW @@ -1996,42 +1998,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid, best_path, tlist, scan_clauses); - /* - * Sanity check. There may be resjunk entries in fdw_ps_tlist that - * are included only to help EXPLAIN deparse plans properly. We require - * that these are at the end, so that when the executor builds the scan - * descriptor based on the non-junk entries, it gets the attribute - * numbers correct. - */ - if (scan_plan->scan.scanrelid == 0) - { - bool found_resjunk = false; - - foreach (lc, scan_plan->fdw_ps_tlist) - { - TargetEntry *tle = lfirst(lc); - - if (tle->resjunk) - found_resjunk = true; - else if (found_resjunk) - elog(ERROR, "junk TLE should not apper prior to valid one"); - } - } - /* Set the relids that are represented by this foreign scan for Explain */ - scan_plan->fdw_relids = best_path->path.parent->relids; /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_path_costsize(&scan_plan->scan.plan, &best_path->path); - /* Track FDW server-id; no need to make FDW do this */ - scan_plan->fdw_handler = rel->fdw_handler; + /* Copy foreign server OID; likewise, no need to make FDW do this */ + scan_plan->fs_server = rel->serverid; + + /* Likewise, copy the relids that are represented by this foreign scan */ + scan_plan->fs_relids = best_path->path.parent->relids; /* * Replace any outer-relation variables with nestloop params in the qual * and fdw_exprs expressions. We do this last so that the FDW doesn't * have to be involved. (Note that parts of fdw_exprs could have come * from join clauses, so doing this beforehand on the scan_clauses - * wouldn't work.) + * wouldn't work.) We assume fdw_scan_tlist contains no such variables. */ if (best_path->path.param_info) { @@ -2087,7 +2069,6 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, { CustomScan *cplan; RelOptInfo *rel = best_path->path.parent; - ListCell *lc; /* * Sort clauses into the best execution order, although custom-scan @@ -2107,41 +2088,21 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, Assert(IsA(cplan, CustomScan)); /* - * Sanity check. There may be resjunk entries in custom_ps_tlist that - * are included only to help EXPLAIN deparse plans properly. We require - * that these are at the end, so that when the executor builds the scan - * descriptor based on the non-junk entries, it gets the attribute - * numbers correct. - */ - if (cplan->scan.scanrelid == 0) - { - bool found_resjunk = false; - - foreach (lc, cplan->custom_ps_tlist) - { - TargetEntry *tle = lfirst(lc); - - if (tle->resjunk) - found_resjunk = true; - else if (found_resjunk) - elog(ERROR, "junk TLE should not apper prior to valid one"); - } - } - /* Set the relids that are represented by this custom scan for Explain */ - cplan->custom_relids = best_path->path.parent->relids; - - /* * Copy cost data from Path to Plan; no need to make custom-plan providers * do this */ copy_path_costsize(&cplan->scan.plan, &best_path->path); + /* Likewise, copy the relids that are represented by this custom scan */ + cplan->custom_relids = best_path->path.parent->relids; + /* * Replace any outer-relation variables with nestloop params in the qual * and custom_exprs expressions. We do this last so that the custom-plan * provider doesn't have to be involved. (Note that parts of custom_exprs * could have come from join clauses, so doing this beforehand on the - * scan_clauses wouldn't work.) + * scan_clauses wouldn't work.) We assume custom_scan_tlist contains no + * such variables. */ if (best_path->path.param_info) { @@ -3611,7 +3572,8 @@ make_foreignscan(List *qptlist, List *qpqual, Index scanrelid, List *fdw_exprs, - List *fdw_private) + List *fdw_private, + List *fdw_scan_tlist) { ForeignScan *node = makeNode(ForeignScan); Plan *plan = &node->scan.plan; @@ -3622,8 +3584,13 @@ make_foreignscan(List *qptlist, plan->lefttree = NULL; plan->righttree = NULL; node->scan.scanrelid = scanrelid; + /* fs_server will be filled in by create_foreignscan_plan */ + node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; + node->fdw_scan_tlist = fdw_scan_tlist; + /* fs_relids will be filled in by create_foreignscan_plan */ + node->fs_relids = NULL; /* fsSystemCol will be filled in by create_foreignscan_plan */ node->fsSystemCol = false; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 612d32571af..fac51c91474 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -86,12 +86,6 @@ static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); -static void set_foreignscan_references(PlannerInfo *root, - ForeignScan *fscan, - int rtoffset); -static void set_customscan_references(PlannerInfo *root, - CustomScan *cscan, - int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, IndexOnlyScan *plan, int rtoffset); @@ -99,6 +93,12 @@ static Plan *set_subqueryscan_references(PlannerInfo *root, SubqueryScan *plan, int rtoffset); static bool trivial_subqueryscan(SubqueryScan *plan); +static void set_foreignscan_references(PlannerInfo *root, + ForeignScan *fscan, + int rtoffset); +static void set_customscan_references(PlannerInfo *root, + CustomScan *cscan, + int rtoffset); static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset); static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context); static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context); @@ -573,7 +573,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) case T_ForeignScan: set_foreignscan_references(root, (ForeignScan *) plan, rtoffset); break; - case T_CustomScan: set_customscan_references(root, (CustomScan *) plan, rtoffset); break; @@ -891,121 +890,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) } /* - * set_foreignscan_references - * Do set_plan_references processing on an ForeignScan - */ -static void -set_foreignscan_references(PlannerInfo *root, - ForeignScan *fscan, - int rtoffset) -{ - if (rtoffset > 0) - { - Bitmapset *tempset = NULL; - int x = -1; - - while ((x = bms_next_member(fscan->fdw_relids, x)) >= 0) - tempset = bms_add_member(tempset, x + rtoffset); - fscan->fdw_relids = tempset; - } - - if (fscan->scan.scanrelid == 0) - { - indexed_tlist *pscan_itlist = build_tlist_index(fscan->fdw_ps_tlist); - - fscan->scan.plan.targetlist = (List *) - fix_upper_expr(root, - (Node *) fscan->scan.plan.targetlist, - pscan_itlist, - INDEX_VAR, - rtoffset); - fscan->scan.plan.qual = (List *) - fix_upper_expr(root, - (Node *) fscan->scan.plan.qual, - pscan_itlist, - INDEX_VAR, - rtoffset); - fscan->fdw_exprs = (List *) - fix_upper_expr(root, - (Node *) fscan->fdw_exprs, - pscan_itlist, - INDEX_VAR, - rtoffset); - fscan->fdw_ps_tlist = - fix_scan_list(root, fscan->fdw_ps_tlist, rtoffset); - pfree(pscan_itlist); - } - else - { - fscan->scan.scanrelid += rtoffset; - fscan->scan.plan.targetlist = - fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset); - fscan->scan.plan.qual = - fix_scan_list(root, fscan->scan.plan.qual, rtoffset); - fscan->fdw_exprs = - fix_scan_list(root, fscan->fdw_exprs, rtoffset); - } -} - -/* - * set_customscan_references - * Do set_plan_references processing on an CustomScan - */ -static void -set_customscan_references(PlannerInfo *root, - CustomScan *cscan, - int rtoffset) -{ - if (rtoffset > 0) - { - Bitmapset *tempset = NULL; - int x = -1; - - while ((x = bms_next_member(cscan->custom_relids, x)) >= 0) - tempset = bms_add_member(tempset, x + rtoffset); - cscan->custom_relids = tempset; - } - - if (cscan->scan.scanrelid == 0) - { - indexed_tlist *pscan_itlist = - build_tlist_index(cscan->custom_ps_tlist); - - cscan->scan.plan.targetlist = (List *) - fix_upper_expr(root, - (Node *) cscan->scan.plan.targetlist, - pscan_itlist, - INDEX_VAR, - rtoffset); - cscan->scan.plan.qual = (List *) - fix_upper_expr(root, - (Node *) cscan->scan.plan.qual, - pscan_itlist, - INDEX_VAR, - rtoffset); - cscan->custom_exprs = (List *) - fix_upper_expr(root, - (Node *) cscan->custom_exprs, - pscan_itlist, - INDEX_VAR, - rtoffset); - cscan->custom_ps_tlist = - fix_scan_list(root, cscan->custom_ps_tlist, rtoffset); - pfree(pscan_itlist); - } - else - { - cscan->scan.scanrelid += rtoffset; - cscan->scan.plan.targetlist = - fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset); - cscan->scan.plan.qual = - fix_scan_list(root, cscan->scan.plan.qual, rtoffset); - cscan->custom_exprs = - fix_scan_list(root, cscan->custom_exprs, rtoffset); - } -} - -/* * set_indexonlyscan_references * Do set_plan_references processing on an IndexOnlyScan * @@ -1180,6 +1064,134 @@ trivial_subqueryscan(SubqueryScan *plan) } /* + * set_foreignscan_references + * Do set_plan_references processing on a ForeignScan + */ +static void +set_foreignscan_references(PlannerInfo *root, + ForeignScan *fscan, + int rtoffset) +{ + /* Adjust scanrelid if it's valid */ + if (fscan->scan.scanrelid > 0) + fscan->scan.scanrelid += rtoffset; + + if (fscan->fdw_scan_tlist != NIL || fscan->scan.scanrelid == 0) + { + /* Adjust tlist, qual, fdw_exprs to reference custom scan tuple */ + indexed_tlist *itlist = build_tlist_index(fscan->fdw_scan_tlist); + + fscan->scan.plan.targetlist = (List *) + fix_upper_expr(root, + (Node *) fscan->scan.plan.targetlist, + itlist, + INDEX_VAR, + rtoffset); + fscan->scan.plan.qual = (List *) + fix_upper_expr(root, + (Node *) fscan->scan.plan.qual, + itlist, + INDEX_VAR, + rtoffset); + fscan->fdw_exprs = (List *) + fix_upper_expr(root, + (Node *) fscan->fdw_exprs, + itlist, + INDEX_VAR, + rtoffset); + pfree(itlist); + /* fdw_scan_tlist itself just needs fix_scan_list() adjustments */ + fscan->fdw_scan_tlist = + fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset); + } + else + { + /* Adjust tlist, qual, fdw_exprs in the standard way */ + fscan->scan.plan.targetlist = + fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset); + fscan->scan.plan.qual = + fix_scan_list(root, fscan->scan.plan.qual, rtoffset); + fscan->fdw_exprs = + fix_scan_list(root, fscan->fdw_exprs, rtoffset); + } + + /* Adjust fs_relids if needed */ + if (rtoffset > 0) + { + Bitmapset *tempset = NULL; + int x = -1; + + while ((x = bms_next_member(fscan->fs_relids, x)) >= 0) + tempset = bms_add_member(tempset, x + rtoffset); + fscan->fs_relids = tempset; + } +} + +/* + * set_customscan_references + * Do set_plan_references processing on a CustomScan + */ +static void +set_customscan_references(PlannerInfo *root, + CustomScan *cscan, + int rtoffset) +{ + /* Adjust scanrelid if it's valid */ + if (cscan->scan.scanrelid > 0) + cscan->scan.scanrelid += rtoffset; + + if (cscan->custom_scan_tlist != NIL || cscan->scan.scanrelid == 0) + { + /* Adjust tlist, qual, custom_exprs to reference custom scan tuple */ + indexed_tlist *itlist = build_tlist_index(cscan->custom_scan_tlist); + + cscan->scan.plan.targetlist = (List *) + fix_upper_expr(root, + (Node *) cscan->scan.plan.targetlist, + itlist, + INDEX_VAR, + rtoffset); + cscan->scan.plan.qual = (List *) + fix_upper_expr(root, + (Node *) cscan->scan.plan.qual, + itlist, + INDEX_VAR, + rtoffset); + cscan->custom_exprs = (List *) + fix_upper_expr(root, + (Node *) cscan->custom_exprs, + itlist, + INDEX_VAR, + rtoffset); + pfree(itlist); + /* custom_scan_tlist itself just needs fix_scan_list() adjustments */ + cscan->custom_scan_tlist = + fix_scan_list(root, cscan->custom_scan_tlist, rtoffset); + } + else + { + /* Adjust tlist, qual, custom_exprs in the standard way */ + cscan->scan.plan.targetlist = + fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset); + cscan->scan.plan.qual = + fix_scan_list(root, cscan->scan.plan.qual, rtoffset); + cscan->custom_exprs = + fix_scan_list(root, cscan->custom_exprs, rtoffset); + } + + /* Adjust custom_relids if needed */ + if (rtoffset > 0) + { + Bitmapset *tempset = NULL; + int x = -1; + + while ((x = bms_next_member(cscan->custom_relids, x)) >= 0) + tempset = bms_add_member(tempset, x + rtoffset); + cscan->custom_relids = tempset; + } +} + +/* * copyVar * Copy a Var node. * diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 0220672fc43..afccee53acf 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -2318,12 +2318,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, case T_ForeignScan: finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs, &context); + /* We assume fdw_scan_tlist cannot contain Params */ context.paramids = bms_add_members(context.paramids, scan_params); break; case T_CustomScan: finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs, &context); + /* We assume custom_scan_tlist cannot contain Params */ context.paramids = bms_add_members(context.paramids, scan_params); break; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 894e0db802d..b425680f476 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -380,17 +380,18 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, rel->indexlist = indexinfos; - /* Grab the fdwroutine info using the relcache, while we have it */ + /* Grab foreign-table info using the relcache, while we have it */ if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) { - rel->fdw_handler = GetFdwHandlerByRelId(RelationGetRelid(relation)); + rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation)); rel->fdwroutine = GetFdwRoutineForRelation(relation, true); } else { - rel->fdw_handler = InvalidOid; + rel->serverid = InvalidOid; rel->fdwroutine = NULL; } + heap_close(relation, NoLock); /* diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 56235663d7f..1d635cd6d21 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -14,7 +14,6 @@ */ #include "postgres.h" -#include "foreign/fdwapi.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -122,8 +121,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) rel->subplan = NULL; rel->subroot = NULL; rel->subplan_params = NIL; + rel->serverid = InvalidOid; rel->fdwroutine = NULL; - rel->fdw_handler = InvalidOid; rel->fdw_private = NULL; rel->baserestrictinfo = NIL; rel->baserestrictcost.startup = 0; @@ -385,6 +384,7 @@ build_join_rel(PlannerInfo *root, joinrel->subplan = NULL; joinrel->subroot = NULL; joinrel->subplan_params = NIL; + joinrel->serverid = InvalidOid; joinrel->fdwroutine = NULL; joinrel->fdw_private = NULL; joinrel->baserestrictinfo = NIL; @@ -394,6 +394,17 @@ build_join_rel(PlannerInfo *root, joinrel->has_eclass_joins = false; /* + * Set up foreign-join fields if outer and inner relation are foreign + * tables (or joins) belonging to the same server. + */ + if (OidIsValid(outer_rel->serverid) && + inner_rel->serverid == outer_rel->serverid) + { + joinrel->serverid = outer_rel->serverid; + joinrel->fdwroutine = outer_rel->fdwroutine; + } + + /* * Create a new tlist containing just the vars that need to be output from * this join (ie, are needed for higher joinclauses or final output). * @@ -429,18 +440,6 @@ build_join_rel(PlannerInfo *root, sjinfo, restrictlist); /* - * Set FDW handler and routine if both outer and inner relation - * are managed by same FDW driver. - */ - if (OidIsValid(outer_rel->fdw_handler) && - OidIsValid(inner_rel->fdw_handler) && - outer_rel->fdw_handler == inner_rel->fdw_handler) - { - joinrel->fdw_handler = outer_rel->fdw_handler; - joinrel->fdwroutine = GetFdwRoutine(joinrel->fdw_handler); - } - - /* * Add the joinrel to the query's joinrel list, and store it into the * auxiliary hashtable if there is one. NB: GEQO requires us to append * the new joinrel to the end of the list! diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 4b3cd85ad90..156b5331f36 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -128,8 +128,8 @@ typedef struct * varlevelsup > 0). We store the PlanState node that is the immediate * parent of the expression to be deparsed, as well as a list of that * PlanState's ancestors. In addition, we store its outer and inner subplan - * state nodes, as well as their plan nodes' targetlists, and the indextlist - * if the current PlanState is an IndexOnlyScanState. (These fields could + * state nodes, as well as their plan nodes' targetlists, and the index tlist + * if the current plan node might contain INDEX_VAR Vars. (These fields could * be derived on-the-fly from the current PlanState, but it seems notationally * clearer to set them up as separate fields.) */ @@ -2586,10 +2586,11 @@ deparse_context_for_plan_rtable(List *rtable, List *rtable_names) * provide the parent PlanState node. Then OUTER_VAR and INNER_VAR references * can be resolved by drilling down into the left and right child plans. * Similarly, INDEX_VAR references can be resolved by reference to the - * indextlist given in the parent IndexOnlyScan node. (Note that we don't - * currently support deparsing of indexquals in regular IndexScan or - * BitmapIndexScan nodes; for those, we can only deparse the indexqualorig - * fields, which won't contain INDEX_VAR Vars.) + * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in + * ForeignScan and CustomScan nodes. (Note that we don't currently support + * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes; + * for those, we can only deparse the indexqualorig fields, which won't + * contain INDEX_VAR Vars.) * * Note: planstate really ought to be declared as "PlanState *", but we use * "Node *" to avoid having to include execnodes.h in ruleutils.h. @@ -3870,13 +3871,13 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) else dpns->inner_tlist = NIL; - /* index_tlist is set only if it's an IndexOnlyScan */ + /* Set up referent for INDEX_VAR Vars, if needed */ if (IsA(ps->plan, IndexOnlyScan)) dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist; else if (IsA(ps->plan, ForeignScan)) - dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_ps_tlist; + dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist; else if (IsA(ps->plan, CustomScan)) - dpns->index_tlist = ((CustomScan *) ps->plan)->custom_ps_tlist; + dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist; else dpns->index_tlist = NIL; } |