diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 4368897581a705f1e27ac6224060eef1f039b916..4c56444751b4f810de70bc10befccfa9e453252d 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -561,7 +561,8 @@ fileGetForeignPlan(PlannerInfo *root, scan_clauses, scan_relid, NIL, /* no expressions to evaluate */ - best_path->fdw_private); + best_path->fdw_private, + NIL /* no custom tlist */ ); } /* diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 173b4f06e65d1f0079238c4767689c8aaf3df90b..ec89b25f61a7ab8b4d9fe793821f5999e622d644 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -872,7 +872,8 @@ postgresGetForeignPlan(PlannerInfo *root, local_exprs, scan_relid, params_list, - fdw_private); + fdw_private, + NIL /* no custom tlist */ ); } /* diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml index 9fd1db6fde48643feeaa92847f4c8c9150790965..62a8a3305bb29a5e7e405a8a1c5b391e1c8299eb 100644 --- a/doc/src/sgml/custom-scan.sgml +++ b/doc/src/sgml/custom-scan.sgml @@ -32,12 +32,13 @@ </para> <sect1 id="custom-scan-path"> - <title>Implementing Custom Paths</title> + <title>Creating Custom Scan Paths</title> <para> - A custom scan provider will typically add paths by setting the following - hook, which is called after the core code has generated what it believes - to be the complete and correct set of access paths for the relation. + A custom scan provider will typically add paths for a base relation by + setting the following hook, which is called after the core code has + generated what it believes to be the complete and correct set of access + paths for the relation. <programlisting> typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root, RelOptInfo *rel, @@ -74,7 +75,7 @@ typedef struct CustomPath can support mark and restore. Both capabilities are optional. <structfield>custom_private</> can be used to store the custom path's private data. Private data should be stored in a form that can be handled - by <literal>nodeToString</>, so that debugging routines which attempt to + by <literal>nodeToString</>, so that debugging routines that attempt to print the custom path will work as designed. <structfield>methods</> must point to a (usually statically allocated) object implementing the required custom path methods, of which there are currently only two, as further @@ -82,29 +83,28 @@ typedef struct CustomPath </para> <para> - A custom scan provider can also add join paths; in this case, the scan - must produce the same output as would normally be produced by the join - it replaces. To do this, the join provider should set the following hook. - This hook may be invoked repeatedly for the same pair of relations, with - different combinations of inner and outer relations; it is the - responsibility of the hook to minimize duplicated work. + A custom scan provider can also provide join paths. Just as for base + relations, such a path must produce the same output as would normally be + produced by the join it replaces. To do this, the join provider should + set the following hook, and then within the hook function, + create <structname>CustomPath</> path(s) for the join relation. <programlisting> 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); + JoinPathExtraData *extra); extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook; </programlisting> + + This hook will be invoked repeatedly for the same join relation, with + different combinations of inner and outer relations; it is the + responsibility of the hook to minimize duplicated work. </para> <sect2 id="custom-scan-path-callbacks"> - <title>Custom Path Callbacks</title> + <title>Custom Scan Path Callbacks</title> <para> <programlisting> @@ -125,7 +125,7 @@ void (*TextOutCustomPath) (StringInfo str, const CustomPath *node); </programlisting> Generate additional output when <function>nodeToString</> is invoked on - this custom path. This callback is optional. Since + this custom path. This callback is optional. Since <function>nodeToString</> will automatically dump all fields in the structure that it can see, including <structfield>custom_private</>, this is only useful if the <structname>CustomPath</> is actually embedded in a @@ -135,7 +135,7 @@ void (*TextOutCustomPath) (StringInfo str, </sect1> <sect1 id="custom-scan-plan"> - <title>Implementing Custom Plans</title> + <title>Creating Custom Scan Plans</title> <para> A custom scan is represented in a finished plan tree using the following @@ -146,9 +146,9 @@ typedef struct CustomScan Scan scan; uint32 flags; List *custom_exprs; - List *custom_ps_tlist; List *custom_private; - List *custom_relids; + List *custom_scan_tlist; + Bitmapset *custom_relids; const CustomScanMethods *methods; } CustomScan; </programlisting> @@ -158,16 +158,21 @@ typedef struct CustomScan <structfield>scan</> must be initialized as for any other scan, including estimated costs, target lists, qualifications, and so on. <structfield>flags</> is a bitmask with the same meaning as in - <structname>CustomPath</>. <structfield>custom_exprs</> should be used to + <structname>CustomPath</>. + <structfield>custom_exprs</> should be used to store expression trees that will need to be fixed up by <filename>setrefs.c</> and <filename>subselect.c</>, while - <literal>custom_private</> should be used to store other private data that - is only used by the custom scan provider itself. Plan trees must be able - to be duplicated using <function>copyObject</>, so all the data stored - within these two fields must consist of nodes that function can handle. - <literal>custom_relids</> is set by the core code to the set of relations - which this scan node must handle; except when this scan is replacing a - join, it will have only one member. + <structfield>custom_private</> should be used to store other private data + that is only used by the custom scan provider itself. + <structfield>custom_scan_tlist</> can be NIL when scanning a base + relation, indicating that the custom scan returns scan tuples that match + the base relation's rowtype. Otherwise it is a targetlist describing + the actual scan tuples. <structfield>custom_scan_tlist</> must be + provided for joins, and could be provided for scans if the custom scan + provider can compute some non-Var expressions. + <structfield>custom_relids</> is set by the core code to the set of + relations (rangetable indexes) that this scan node handles; except when + this scan is replacing a join, it will have only one member. <structfield>methods</> must point to a (usually statically allocated) object implementing the required custom scan methods, which are further detailed below. @@ -175,19 +180,22 @@ typedef struct CustomScan <para> When a <structname>CustomScan</> scans a single relation, - <structfield>scan.scanrelid</> should be the range table index of the table - to be scanned, and <structfield>custom_ps_tlist</> should be - <literal>NULL</>. When it replaces a join, <structfield>scan.scanrelid</> - should be zero, and <structfield>custom_ps_tlist</> should be a list of - <structname>TargetEntry</> nodes. This is necessary because, when a join - is replaced, the target list cannot be constructed from the table - definition. At execution time, this list will be used to initialize the - tuple descriptor of the <structname>TupleTableSlot</>. It will also be - used by <command>EXPLAIN</>, when deparsing. + <structfield>scan.scanrelid</> must be the range table index of the table + to be scanned. When it replaces a join, <structfield>scan.scanrelid</> + should be zero. + </para> + + <para> + Plan trees must be able to be duplicated using <function>copyObject</>, + so all the data stored within the <quote>custom</> fields must consist of + nodes that that function can handle. Furthermore, custom scan providers + cannot substitute a larger structure that embeds + a <structname>CustomScan</> for the structure itself, as would be possible + for a <structname>CustomPath</> or <structname>CustomScanState</>. </para> <sect2 id="custom-scan-plan-callbacks"> - <title>Custom Scan Callbacks</title> + <title>Custom Scan Plan Callbacks</title> <para> <programlisting> Node *(*CreateCustomScanState) (CustomScan *cscan); @@ -195,12 +203,12 @@ Node *(*CreateCustomScanState) (CustomScan *cscan); Allocate a <structname>CustomScanState</> for this <structname>CustomScan</>. The actual allocation will often be larger than required for an ordinary <structname>CustomScanState</>, because many - scan types will wish to embed that as the first field of a large structure. + providers will wish to embed that as the first field of a larger structure. The value returned must have the node tag and <structfield>methods</> - set appropriately, but the other fields need not be initialized at this + set appropriately, but other fields should be left as zeroes at this stage; after <function>ExecInitCustomScan</> performs basic initialization, the <function>BeginCustomScan</> callback will be invoked to give the - custom scan state a chance to do whatever else is needed. + custom scan provider a chance to do whatever else is needed. </para> <para> @@ -209,23 +217,21 @@ void (*TextOutCustomScan) (StringInfo str, const CustomScan *node); </programlisting> Generate additional output when <function>nodeToString</> is invoked on - this custom plan. This callback is optional. Since a - <structname>CustomScan</> must be copyable by <function>copyObject</>, - custom scan providers cannot substitute a larger structure that embeds a - <structname>CustomScan</> for the structure itself, as would be possible - for a <structname>CustomPath</> or <structname>CustomScanState</>. - Therefore, providing this callback is unlikely to be useful. + this custom plan node. This callback is optional. Since + <function>nodeToString</> will automatically dump all fields in the + structure, including the substructure of the <quote>custom</> fields, + there is usually not much need for this callback. </para> </sect2> </sect1> - <sect1 id="custom-scan-scan"> - <title>Implementing Custom Scans</title> + <sect1 id="custom-scan-execution"> + <title>Executing Custom Scans</title> <para> When a <structfield>CustomScan</> is executed, its execution state is represented by a <structfield>CustomScanState</>, which is declared as - follows. + follows: <programlisting> typedef struct CustomScanState { @@ -237,7 +243,9 @@ typedef struct CustomScanState </para> <para> - <structfield>ss</> must be initialized as for any other scanstate; + <structfield>ss</> is initialized as for any other scanstate, + except that if the scan is for a join rather than a base relation, + <literal>ss.ss_currentRelation</> is left NULL. <structfield>flags</> is a bitmask with the same meaning as in <structname>CustomPath</> and <structname>CustomScan</>. <structfield>methods</> must point to a (usually statically allocated) @@ -247,8 +255,8 @@ typedef struct CustomScanState structure embedding the above as its first member. </para> - <sect2 id="custom-scan-scan-callbacks"> - <title>Custom Execution-Time Callbacks</title> + <sect2 id="custom-scan-execution-callbacks"> + <title>Custom Scan Execution Callbacks</title> <para> <programlisting> @@ -257,8 +265,8 @@ void (*BeginCustomScan) (CustomScanState *node, int eflags); </programlisting> Complete initialization of the supplied <structname>CustomScanState</>. - Some initialization is performed by <function>ExecInitCustomScan</>, but - any private fields should be initialized here. + Standard fields have been initialized by <function>ExecInitCustomScan</>, + but any private fields should be initialized here. </para> <para> @@ -276,8 +284,8 @@ TupleTableSlot *(*ExecCustomScan) (CustomScanState *node); void (*EndCustomScan) (CustomScanState *node); </programlisting> Clean up any private data associated with the <literal>CustomScanState</>. - This method is required, but may not need to do anything if the associated - data does not exist or will be cleaned up automatically. + This method is required, but it does not need to do anything if there is + no associated data or it will be cleaned up automatically. </para> <para> @@ -293,8 +301,8 @@ void (*ReScanCustomScan) (CustomScanState *node); void (*MarkPosCustomScan) (CustomScanState *node); </programlisting> Save the current scan position so that it can subsequently be restored - by the <function>RestrPosCustomScan</> callback. This calback is optional, - and need only be supplied if + by the <function>RestrPosCustomScan</> callback. This callback is + optional, and need only be supplied if the <literal>CUSTOMPATH_SUPPORT_MARK_RESTORE</> flag is set. </para> @@ -304,7 +312,7 @@ void (*RestrPosCustomScan) (CustomScanState *node); </programlisting> Restore the previous scan position as saved by the <function>MarkPosCustomScan</> callback. This callback is optional, - and need only be supplied if + and need only be supplied if the <literal>CUSTOMPATH_SUPPORT_MARK_RESTORE</> flag is set. </para> @@ -314,8 +322,8 @@ void (*ExplainCustomScan) (CustomScanState *node, List *ancestors, ExplainState *es); </programlisting> - Output additional information on <command>EXPLAIN</> that involves - custom-scan node. This callback is optional. Common data stored in the + Output additional information for <command>EXPLAIN</> of a custom-scan + plan node. This callback is optional. Common data stored in the <structname>ScanState</>, such as the target list and scan relation, will be shown even without this callback, but the callback allows the display of additional, private state. diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index bc06d2cbb26f8c9d28faa5696bcd1659c011ea7b..33863f04f82899bf1f306e8fc7777527f23e479c 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -175,8 +175,11 @@ GetForeignPlan (PlannerInfo *root, access path. This is called at the end of query planning. The parameters are as for <function>GetForeignRelSize</>, plus the selected <structname>ForeignPath</> (previously produced by - <function>GetForeignPaths</>), the target list to be emitted by the - plan node, and the restriction clauses to be enforced by the plan node. + <function>GetForeignPaths</> or <function>GetForeignJoinPaths</>), + the target list to be emitted by the plan node, + and the restriction clauses to be enforced by the plan node. + (If the path is for a join rather than a base + relation, <literal>foreigntableid</> is <literal>InvalidOid</>.) </para> <para> @@ -235,9 +238,12 @@ IterateForeignScan (ForeignScanState *node); </para> <para> - The rows returned must match the column signature of the foreign table - being scanned. If you choose to optimize away fetching columns that - are not needed, you should insert nulls in those column positions. + The rows returned must match the <structfield>fdw_scan_tlist</> target + list if one was supplied, otherwise they must match the rowtype of the + foreign table being scanned. If you choose to optimize away fetching + columns that are not needed, you should insert nulls in those column + positions, or else generate a <structfield>fdw_scan_tlist</> list with + those columns omitted. </para> <para> @@ -275,6 +281,67 @@ EndForeignScan (ForeignScanState *node); </sect2> + <sect2 id="fdw-callbacks-join-scan"> + <title>FDW Routines For Scanning Foreign Joins</title> + + <para> + If an FDW supports performing foreign joins remotely (rather than + by fetching both tables' data and doing the join locally), it should + provide this callback function: + </para> + + <para> +<programlisting> +void +GetForeignJoinPaths (PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + JoinPathExtraData *extra); +</programlisting> + Create possible access paths for a join of two (or more) foreign tables + that all belong to the same foreign server. This optional + function is called during query planning. As + with <function>GetForeignPaths</>, this function should + generate <structname>ForeignPath</> path(s) for the + supplied <literal>joinrel</>, and call <function>add_path</> to add these + paths to the set of paths considered for the join. But unlike + <function>GetForeignPaths</>, it is not necessary that this function + succeed in creating at least one path, since paths involving local + joining are always possible. + </para> + + <para> + Note that this function will be invoked repeatedly for the same join + relation, with different combinations of inner and outer relations; it is + the responsibility of the FDW to minimize duplicated work. + </para> + + <para> + If a <structname>ForeignPath</> path is chosen for the join, it will + represent the entire join process; paths generated for the component + tables and subsidiary joins will not be used. Subsequent processing of + the join path proceeds much as it does for a path scanning a single + foreign table. One difference is that the <structfield>scanrelid</> of + the resulting <structname>ForeignScan</> plan node should be set to zero, + since there is no single relation that it represents; instead, + the <structfield>fs_relids</> field of the <structname>ForeignScan</> + node represents the set of relations that were joined. (The latter field + is set up automatically by the core planner code, and need not be filled + by the FDW.) Another difference is that, because the column list for a + remote join cannot be found from the system catalogs, the FDW must + fill <structfield>fdw_scan_tlist</> with an appropriate list + of <structfield>TargetEntry</> nodes, representing the set of columns + it will supply at runtime in the tuples it returns. + </para> + + <para> + See <xref linkend="fdw-planning"> for additional information. + </para> + + </sect2> + <sect2 id="fdw-callbacks-update"> <title>FDW Routines For Updating Foreign Tables</title> @@ -598,42 +665,6 @@ IsForeignRelUpdatable (Relation rel); </sect2> - <sect2> - <title>FDW Routines For Remote Joins</title> - <para> -<programlisting> -void -GetForeignJoinPaths(PlannerInfo *root, - RelOptInfo *joinrel, - RelOptInfo *outerrel, - RelOptInfo *innerrel, - List *restrictlist, - JoinType jointype, - SpecialJoinInfo *sjinfo, - SemiAntiJoinFactors *semifactors, - Relids param_source_rels, - Relids extra_lateral_rels); -</programlisting> - Create possible access paths for a join of two foreign tables managed - by the same foreign data wrapper. - This optional function is called during query planning. - </para> - <para> - This function the FDW to add <structname>ForeignScan</> paths for the - supplied <literal>joinrel</>. Typically, the FDW will send the whole - join to the remote server as a single query, as performing the join - remotely rather than locally is typically much more efficient. - </para> - <para> - Since we cannot construct the slot descriptor for a remote join from - the catalogs, the FDW should set the <structfield>scanrelid</> of the - <structname>ForeignScan</> to zero and <structfield>fdw_ps_tlist</> - to an appropriate list of <structfield>TargetEntry</> nodes. - Junk entries will be ignored, but can be present for the benefit of - deparsing performed by <command>EXPLAIN</>. - </para> - </sect2> - <sect2 id="fdw-callbacks-explain"> <title>FDW Routines for <command>EXPLAIN</></title> @@ -904,10 +935,10 @@ GetForeignServerByName(const char *name, bool missing_ok); <para> The FDW callback functions <function>GetForeignRelSize</>, - <function>GetForeignPaths</>, <function>GetForeignPlan</>, and - <function>PlanForeignModify</> must fit into the workings of the - <productname>PostgreSQL</> planner. Here are some notes about what - they must do. + <function>GetForeignPaths</>, <function>GetForeignPlan</>, + <function>PlanForeignModify</>, and <function>GetForeignJoinPaths</> + must fit into the workings of the <productname>PostgreSQL</> planner. + Here are some notes about what they must do. </para> <para> @@ -934,7 +965,7 @@ GetForeignServerByName(const char *name, bool missing_ok); <literal>baserel->fdw_private</> is a <type>void</> pointer that is available for FDW planning functions to store information relevant to the particular foreign table. The core planner does not touch it except - to initialize it to NULL when the <literal>baserel</> node is created. + to initialize it to NULL when the <literal>RelOptInfo</> node is created. It is useful for passing information forward from <function>GetForeignRelSize</> to <function>GetForeignPaths</> and/or <function>GetForeignPaths</> to <function>GetForeignPlan</>, thereby @@ -1002,6 +1033,23 @@ GetForeignServerByName(const char *name, bool missing_ok); evaluation of the <structfield>fdw_exprs</> expression tree. </para> + <para> + Another <structname>ForeignScan</> field that can be filled by FDWs + is <structfield>fdw_scan_tlist</>, which describes the tuples returned by + the FDW for this plan node. For simple foreign table scans this can be + set to <literal>NIL</>, implying that the returned tuples have the + rowtype declared for the foreign table. A non-NIL value must be a + targetlist (list of <structname>TargetEntry</>s) containing Vars and/or + expressions representing the returned columns. This might be used, for + example, to show that the FDW has omitted some columns that it noticed + won't be needed for the query. Also, if the FDW can compute expressions + used by the query more cheaply than can be done locally, it could add + those expressions to <structfield>fdw_scan_tlist</>. Note that join + plans (created from paths made by <function>GetForeignJoinPaths</>) must + always supply <structfield>fdw_scan_tlist</> to describe the set of + columns they will return. + </para> + <para> The FDW should always construct at least one path that depends only on the table's restriction clauses. In join queries, it might also choose @@ -1019,6 +1067,18 @@ GetForeignServerByName(const char *name, bool missing_ok); same as for an ordinary restriction clause. </para> + <para> + If an FDW supports remote joins, <function>GetForeignJoinPaths</> should + produce <structname>ForeignPath</>s for potential remote joins in much + the same way as <function>GetForeignPaths</> works for base tables. + Information about the intended join can be passed forward + to <function>GetForeignPlan</> in the same ways described above. + However, <structfield>baserestrictinfo</> is not relevant for join + relations; instead, the relevant join clauses for a particular join are + passed to <function>GetForeignJoinPaths</> as a separate parameter + (<literal>extra->restrictlist</>). + </para> + <para> When planning an <command>UPDATE</> or <command>DELETE</>, <function>PlanForeignModify</> can look up the <structname>RelOptInfo</> diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index c5452e3cb6ab7440d75b429d20a3651c275bfe59..eeb8f19017efcae22937b44a6a07b5a276fa6bf2 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 fa475014f134d527498c5481573c788678a4f0b5..a96e826ba4257a843f478a4d22582196162f40a6 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 db1b4f2ffa4dbaffcff031277815d5b77c31014b..0a022dff940b7627191831e5648075747a53e832 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 fa553ace5d687960f4aefc5124834397041018ca..bb28a7372d1be4a8dfc4cc9bd378d531af3a745d 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 06b7c3c457abc5f4f12eefac4f5d1290bec06586..61bd644ab7135c446e3b467c455f415a917f5cde 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 cdbd550fd4363a17d394df04018b1724682a9175..763ee7c9bcea0b9aa9e654cfec2a6d89202d828d 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 a3139d3eb5db5c6ad6250a9db3fb58c9a5f8946a..ed8fa72621ece43657d7d647600946f7d0e3ec2d 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 bc891d391f56943030cfa27c20b77eed6da005f4..fe868b889d96a93c025ae6871a6dec3f7726fbc3 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 dabef3c3c7fe4ff5df5b3c3c67c15cdd13186d1e..ba78252b8f998e03ca23fb87212d228f71fbb877 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 3246332d6e3bda4cb5aab2fdce2a6b823722f84d..c809237283198ad4d29e6ddce1acd057371cf6bb 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 @@ -2106,42 +2087,22 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, scan_clauses); 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 612d32571af390a61b6eb0561d5c557ea2770a38..fac51c9147470556bf23a063fc375e5f52f715ce 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; @@ -890,121 +889,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) return plan; } -/* - * 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 @@ -1179,6 +1063,134 @@ trivial_subqueryscan(SubqueryScan *plan) return true; } +/* + * 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 0220672fc4382cad381ff564e474d3fb6bcf3798..afccee53acf562bf5006f7c63ed89ff6dd192ac5 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 894e0db802de81e0eaa506c92aeeb6a6121d4e51..b425680f47647f19eaa89da02cfac8d3c8d9f765 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 56235663d7f99c6193f8ea128f15abbe4374eca2..1d635cd6d214bcd25b2fef6cab1c09e91e9b3dcb 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; @@ -393,6 +393,17 @@ build_join_rel(PlannerInfo *root, joinrel->joininfo = NIL; 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). @@ -428,18 +439,6 @@ build_join_rel(PlannerInfo *root, set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel, 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 diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 4b3cd85ad9043d6b6e5c7d49ef95ab8492102c8a..156b5331f36fb1965c3185770b9a23ddbc0028e7 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 1b68b54c7ddff721a89ed0a321b360cef581f80b..6c646091976ce84629fd478d67162b64c3ba8c2f 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 c683d9259e445aadd7e90145db693ca51bfad566..511c96b093275a697c7055c53cfeae3069905c13 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 c63492fa0be486a06354bbc641e857baa71e5974..9313292222afb97dd5e8b49f322073c5361d8476 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 8f2c64847e2f699f43825351a8fdee4a06e21a99..f10ae4efa881dc3a00acc659972ffa5f76d0f78f 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 1713d298de256deb47c0bda3a8659de1283e0b66..d3ee61c4d046101b1a6dcbceb6821958ee72f939 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 */ @@ -1667,6 +1672,28 @@ typedef struct SemiAntiJoinFactors Selectivity match_count; } 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 diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index c42c69d7460751bb73edf96f015cc2f54fabfff2..3e2378aeb7df731fd600890190f1f9fc34ceb63d 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 1d4ab0488e1e6e47de94fe1539c1f5d0c67b06e0..da15fca1f6c4374062ce8ffc8aaf28c46e416915 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,