diff options
Diffstat (limited to 'src')
-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 | ||||
-rw-r--r-- | src/include/executor/executor.h | 1 | ||||
-rw-r--r-- | src/include/foreign/fdwapi.h | 27 | ||||
-rw-r--r-- | src/include/nodes/plannodes.h | 37 | ||||
-rw-r--r-- | src/include/nodes/primnodes.h | 10 | ||||
-rw-r--r-- | src/include/nodes/relation.h | 45 | ||||
-rw-r--r-- | src/include/optimizer/paths.h | 14 | ||||
-rw-r--r-- | src/include/optimizer/planmain.h | 4 |
22 files changed, 520 insertions, 542 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; } diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 1b68b54c7dd..6c646091976 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -257,6 +257,7 @@ typedef bool (*ExecScanRecheckMtd) (ScanState *node, TupleTableSlot *slot); extern TupleTableSlot *ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd); extern void ExecAssignScanProjectionInfo(ScanState *node); +extern void ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno); extern void ExecScanReScan(ScanState *node); /* diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index c683d9259e4..511c96b0932 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -47,6 +47,13 @@ typedef void (*ReScanForeignScan_function) (ForeignScanState *node); typedef void (*EndForeignScan_function) (ForeignScanState *node); +typedef void (*GetForeignJoinPaths_function) (PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + JoinPathExtraData *extra); + typedef void (*AddForeignUpdateTargets_function) (Query *parsetree, RangeTblEntry *target_rte, Relation target_relation); @@ -82,17 +89,6 @@ typedef void (*EndForeignModify_function) (EState *estate, typedef int (*IsForeignRelUpdatable_function) (Relation rel); -typedef void (*GetForeignJoinPaths_function) (PlannerInfo *root, - RelOptInfo *joinrel, - RelOptInfo *outerrel, - RelOptInfo *innerrel, - List *restrictlist, - JoinType jointype, - SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, - Relids extra_lateral_rels); - typedef void (*ExplainForeignScan_function) (ForeignScanState *node, struct ExplainState *es); @@ -142,6 +138,9 @@ typedef struct FdwRoutine * are not provided. */ + /* Functions for remote-join planning */ + GetForeignJoinPaths_function GetForeignJoinPaths; + /* Functions for updating foreign tables */ AddForeignUpdateTargets_function AddForeignUpdateTargets; PlanForeignModify_function PlanForeignModify; @@ -161,15 +160,13 @@ typedef struct FdwRoutine /* Support functions for IMPORT FOREIGN SCHEMA */ ImportForeignSchema_function ImportForeignSchema; - - /* Support functions for join push-down */ - GetForeignJoinPaths_function GetForeignJoinPaths; } FdwRoutine; /* Functions in foreign/foreign.c */ -extern Oid GetFdwHandlerByRelId(Oid relid); extern FdwRoutine *GetFdwRoutine(Oid fdwhandler); +extern Oid GetForeignServerIdByRelId(Oid relid); +extern FdwRoutine *GetFdwRoutineByServerId(Oid serverid); extern FdwRoutine *GetFdwRoutineByRelId(Oid relid); extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy); extern bool IsImportableForeignTable(const char *tablename, diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index c63492fa0be..9313292222a 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -479,32 +479,46 @@ typedef struct WorkTableScan * fdw_exprs and fdw_private are both under the control of the foreign-data * wrapper, but fdw_exprs is presumed to contain expression trees and will * be post-processed accordingly by the planner; fdw_private won't be. - * An optional fdw_ps_tlist is used to map a reference to an attribute of - * underlying relation(s) onto a pair of INDEX_VAR and alternative varattno. - * When fdw_ps_tlist is used, this represents a remote join, and the FDW - * is responsible for setting this field to an appropriate value. - * Note that everything in above lists must be copiable by copyObject(). + * Note that everything in both lists must be copiable by copyObject(). * One way to store an arbitrary blob of bytes is to represent it as a bytea * Const. Usually, though, you'll be better off choosing a representation * that can be dumped usefully by nodeToString(). + * + * fdw_scan_tlist is a targetlist describing the contents of the scan tuple + * returned by the FDW; it can be NIL if the scan tuple matches the declared + * rowtype of the foreign table, which is the normal case for a simple foreign + * table scan. (If the plan node represents a foreign join, fdw_scan_tlist + * is required since there is no rowtype available from the system catalogs.) + * When fdw_scan_tlist is provided, Vars in the node's tlist and quals must + * have varno INDEX_VAR, and their varattnos correspond to resnos in the + * fdw_scan_tlist (which are also column numbers in the actual scan tuple). + * fdw_scan_tlist is never actually executed; it just holds expression trees + * describing what is in the scan tuple's columns. + * + * When the plan node represents a foreign join, scan.scanrelid is zero and + * fs_relids must be consulted to identify the join relation. (fs_relids + * is valid for simple scans as well, but will always match scan.scanrelid.) * ---------------- */ typedef struct ForeignScan { Scan scan; - Oid fdw_handler; /* OID of FDW handler */ + Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ - List *fdw_ps_tlist; /* tlist, if replacing a join */ List *fdw_private; /* private data for FDW */ - Bitmapset *fdw_relids; /* RTIs generated by this scan */ + List *fdw_scan_tlist; /* optional tlist describing scan tuple */ + Bitmapset *fs_relids; /* RTIs generated by this scan */ bool fsSystemCol; /* true if any "system column" is needed */ } ForeignScan; /* ---------------- * CustomScan node * - * The comments for ForeignScan's fdw_exprs, fdw_varmap and fdw_private fields - * apply equally to custom_exprs, custom_ps_tlist and custom_private. + * The comments for ForeignScan's fdw_exprs, fdw_private, fdw_scan_tlist, + * and fs_relids fields apply equally to CustomScan's custom_exprs, + * custom_private, custom_scan_tlist, and custom_relids fields. The + * convention of setting scan.scanrelid to zero for joins applies as well. + * * Note that since Plan trees can be copied, custom scan providers *must* * fit all plan data they need into those fields; embedding CustomScan in * a larger struct will not work. @@ -528,8 +542,9 @@ typedef struct CustomScan Scan scan; uint32 flags; /* mask of CUSTOMPATH_* flags, see relation.h */ List *custom_exprs; /* expressions that custom code may evaluate */ - List *custom_ps_tlist;/* tlist, if replacing a join */ List *custom_private; /* private data for custom code */ + List *custom_scan_tlist; /* optional tlist describing scan + * tuple */ Bitmapset *custom_relids; /* RTIs generated by this scan */ const CustomScanMethods *methods; } CustomScan; diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 8f2c64847e2..f10ae4efa88 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -127,9 +127,13 @@ typedef struct Expr * upper-level plan nodes are reassigned to point to the outputs of their * subplans; for example, in a join node varno becomes INNER_VAR or OUTER_VAR * and varattno becomes the index of the proper element of that subplan's - * target list. But varnoold/varoattno continue to hold the original values. - * The code doesn't really need varnoold/varoattno, but they are very useful - * for debugging and interpreting completed plans, so we keep them around. + * target list. Similarly, INDEX_VAR is used to identify Vars that reference + * an index column rather than a heap column. (In ForeignScan and CustomScan + * plan nodes, INDEX_VAR is abused to signify references to columns of a + * custom scan tuple type.) In all these cases, varnoold/varoattno hold the + * original values. The code doesn't really need varnoold/varoattno, but they + * are very useful for debugging and interpreting completed plans, so we keep + * them around. */ #define INNER_VAR 65000 /* reference to inner subplan */ #define OUTER_VAR 65001 /* reference to outer subplan */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 1713d298de2..d3ee61c4d04 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -365,18 +365,21 @@ typedef struct PlannerInfo * subplan - plan for subquery (NULL if it's not a subquery) * subroot - PlannerInfo for subquery (NULL if it's not a subquery) * subplan_params - list of PlannerParamItems to be passed to subquery - * fdwroutine - function hooks for FDW, if foreign table (else NULL) - * fdw_handler - OID of FDW handler, if foreign table (else InvalidOid) - * fdw_private - private state for FDW, if foreign table (else NULL) * * Note: for a subquery, tuples, subplan, subroot are not set immediately * upon creation of the RelOptInfo object; they are filled in when - * set_subquery_pathlist processes the object. Likewise, fdwroutine - * and fdw_private are filled during initial path creation. + * set_subquery_pathlist processes the object. * * For otherrels that are appendrel members, these fields are filled * in just as for a baserel. * + * If the relation is either a foreign table or a join of foreign tables that + * all belong to the same foreign server, these fields will be set: + * + * serverid - OID of foreign server, if foreign table (else InvalidOid) + * fdwroutine - function hooks for FDW, if foreign table (else NULL) + * fdw_private - private state for FDW, if foreign table (else NULL) + * * The presence of the remaining fields depends on the restrictions * and joins that the relation participates in: * @@ -460,10 +463,12 @@ typedef struct RelOptInfo struct Plan *subplan; /* if subquery */ PlannerInfo *subroot; /* if subquery */ List *subplan_params; /* if subquery */ + + /* Information about foreign tables and foreign joins */ + Oid serverid; /* identifies server for the table or join */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ - struct FdwRoutine *fdwroutine; /* if foreign table */ - Oid fdw_handler; /* if foreign table */ - void *fdw_private; /* if foreign table */ + struct FdwRoutine *fdwroutine; + void *fdw_private; /* used by various scans and joins: */ List *baserestrictinfo; /* RestrictInfo structures (if base @@ -523,7 +528,7 @@ typedef struct IndexOptInfo bool *reverse_sort; /* is sort order descending? */ bool *nulls_first; /* do NULLs come first in the sort order? */ bool *canreturn; /* which index cols can be returned in an - index-only scan? */ + * index-only scan? */ Oid relam; /* OID of the access method (in pg_am) */ RegProcedure amcostestimate; /* OID of the access method's cost fcn */ @@ -1668,6 +1673,28 @@ typedef struct SemiAntiJoinFactors } SemiAntiJoinFactors; /* + * Struct for extra information passed to subroutines of add_paths_to_joinrel + * + * 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 + * sjinfo is extra info about special joins for selectivity estimation + * semifactors is as shown above (only valid for SEMI or ANTI joins) + * param_source_rels are OK targets for parameterization of result paths + * extra_lateral_rels are additional parameterization for result paths + */ +typedef struct JoinPathExtraData +{ + List *restrictlist; + List *mergeclause_list; + SpecialJoinInfo *sjinfo; + SemiAntiJoinFactors semifactors; + Relids param_source_rels; + Relids extra_lateral_rels; +} JoinPathExtraData; + +/* * For speed reasons, cost estimation for join paths is performed in two * phases: the first phase tries to quickly derive a lower bound for the * join cost, and then we check if that's sufficient to reject the path. diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index c42c69d7460..3e2378aeb7d 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -32,15 +32,11 @@ extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook; /* Hook for plugins to get control in add_paths_to_joinrel() */ typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root, - RelOptInfo *joinrel, - RelOptInfo *outerrel, - RelOptInfo *innerrel, - List *restrictlist, - JoinType jointype, - SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, - Relids extra_lateral_rels); + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + JoinPathExtraData *extra); extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook; /* Hook for plugins to replace standard_join_search() */ diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 1d4ab0488e1..da15fca1f6c 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -41,11 +41,11 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist, * prototypes for plan/createplan.c */ extern Plan *create_plan(PlannerInfo *root, Path *best_path); -extern Plan *create_plan_recurse(PlannerInfo *root, Path *best_path); extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, Index scanrelid, Plan *subplan); extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual, - Index scanrelid, List *fdw_exprs, List *fdw_private); + Index scanrelid, List *fdw_exprs, List *fdw_private, + List *fdw_scan_tlist); extern Append *make_append(List *appendplans, List *tlist); extern RecursiveUnion *make_recursive_union(List *tlist, Plan *lefttree, Plan *righttree, int wtParam, |