src/backend/executor/execScan.c | 4 +++ src/backend/executor/nodeCustom.c | 39 ++++++++++++++++----- src/backend/executor/nodeForeignscan.c | 35 +++++++++++++------ src/backend/foreign/foreign.c | 32 ++++++++++++++---- src/backend/nodes/copyfuncs.c | 3 ++ src/backend/nodes/outfuncs.c | 3 ++ src/backend/optimizer/path/joinpath.c | 15 +++++++++ src/backend/optimizer/plan/createplan.c | 33 +++++++++++------- src/backend/optimizer/plan/setrefs.c | 60 +++++++++++++++++++++++++++++++++ src/backend/optimizer/util/plancat.c | 7 +++- src/backend/optimizer/util/relnode.c | 13 +++++++ src/backend/utils/adt/ruleutils.c | 4 +++ src/include/foreign/fdwapi.h | 1 + src/include/nodes/plannodes.h | 20 ++++++++--- src/include/nodes/relation.h | 2 ++ src/include/optimizer/paths.h | 13 +++++++ 16 files changed, 240 insertions(+), 44 deletions(-) diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 1319519..e8784d9 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -251,6 +251,10 @@ ExecAssignScanProjectionInfo(ScanState *node) /* Vars in an index-only scan's tlist should be INDEX_VAR */ if (IsA(scan, IndexOnlyScan)) varno = INDEX_VAR; + /* Also foreign-/custom-scan on pseudo relation should be INDEX_VAR */ + else if (scan->scanrelid == 0 && + (IsA(scan, ForeignScan) || IsA(scan, CustomScan))) + varno = INDEX_VAR; else varno = scan->scanrelid; diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 576b295..27c5790 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -23,6 +23,7 @@ CustomScanState * ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) { CustomScanState *css; + Index scan_relid = cscan->scan.scanrelid; Relation scan_rel; /* populate a CustomScanState according to the CustomScan */ @@ -48,12 +49,32 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) ExecInitScanTupleSlot(estate, &css->ss); ExecInitResultTupleSlot(estate, &css->ss.ps); - /* initialize scan relation */ - scan_rel = ExecOpenScanRelation(estate, cscan->scan.scanrelid, eflags); - css->ss.ss_currentRelation = scan_rel; - css->ss.ss_currentScanDesc = NULL; /* set by provider */ - ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel)); - + /* + * open the base relation and acquire appropriate lock on it, then + * get the scan type from the relation descriptor, if this custom + * scan is on actual relations. + * + * on the other hands, custom-scan may scan on a pseudo relation; + * that is usually a result-set of relations join by external + * computing resource, or others. It has to get the scan type from + * the pseudo-scan target-list that should be assigned by custom-scan + * provider. + */ + if (scan_relid > 0) + { + scan_rel = ExecOpenScanRelation(estate, scan_relid, eflags); + css->ss.ss_currentRelation = scan_rel; + css->ss.ss_currentScanDesc = NULL; /* set by provider */ + ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel)); + } + else + { + TupleDesc ps_tupdesc; + + Assert(cscan->custom_ps_tlist != NULL); + ps_tupdesc = ExecTypeFromTL(cscan->custom_ps_tlist, false); + ExecAssignScanType(&css->ss, ps_tupdesc); + } css->ss.ps.ps_TupFromTlist = false; /* @@ -89,11 +110,11 @@ ExecEndCustomScan(CustomScanState *node) /* Clean out the tuple table */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); - if (node->ss.ss_ScanTupleSlot) - ExecClearTuple(node->ss.ss_ScanTupleSlot); + ExecClearTuple(node->ss.ss_ScanTupleSlot); /* Close the heap relation */ - ExecCloseScanRelation(node->ss.ss_currentRelation); + if (node->ss.ss_currentRelation) + ExecCloseScanRelation(node->ss.ss_currentRelation); } void diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index 9cc5345..07cd883 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -102,6 +102,7 @@ ForeignScanState * ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) { ForeignScanState *scanstate; + Index scanrelid = node->scan.scanrelid; Relation currentRelation; FdwRoutine *fdwroutine; @@ -141,16 +142,29 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) ExecInitScanTupleSlot(estate, &scanstate->ss); /* - * open the base relation and acquire appropriate lock on it. + * open the base relation and acquire appropriate lock on it, then + * get the scan type from the relation descriptor, if this foreign + * scan is on actual foreign-table. + * + * on the other hands, foreign-scan may scan on a pseudo relation; + * that is usually a result-set of remote relations join. It has + * to get the scan type from the pseudo-scan target-list that should + * be assigned by FDW driver. */ - currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags); - scanstate->ss.ss_currentRelation = currentRelation; + if (scanrelid > 0) + { + currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags); + scanstate->ss.ss_currentRelation = currentRelation; + ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation)); + } + else + { + TupleDesc ps_tupdesc; - /* - * get the scan type from the relation descriptor. (XXX at some point we - * might want to let the FDW editorialize on the scan tupdesc.) - */ - ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation)); + Assert(node->fdw_ps_tlist != NULL); + ps_tupdesc = ExecTypeFromTL(node->fdw_ps_tlist, false); + ExecAssignScanType(&scanstate->ss, ps_tupdesc); + } /* * Initialize result tuple type and projection info. @@ -161,7 +175,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) /* * Acquire function pointers from the FDW's handler, and init fdw_state. */ - fdwroutine = GetFdwRoutineForRelation(currentRelation, true); + fdwroutine = GetFdwRoutine(node->fdw_handler); scanstate->fdwroutine = fdwroutine; scanstate->fdw_state = NULL; @@ -193,7 +207,8 @@ ExecEndForeignScan(ForeignScanState *node) ExecClearTuple(node->ss.ss_ScanTupleSlot); /* close the relation. */ - ExecCloseScanRelation(node->ss.ss_currentRelation); + if (node->ss.ss_currentRelation) + ExecCloseScanRelation(node->ss.ss_currentRelation); } /* ---------------------------------------------------------------- diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index 4f5f6ae..860b6ca 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -302,13 +302,12 @@ GetFdwRoutine(Oid fdwhandler) return routine; } - /* - * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper - * for the given foreign table, and retrieve its FdwRoutine struct. + * GetFdwHandlerByRelId - look up the handler of the foreign-data wrapper + * for the given foreign table */ -FdwRoutine * -GetFdwRoutineByRelId(Oid relid) +static Oid +GetFdwHandlerByRelId(Oid relid) { HeapTuple tp; Form_pg_foreign_data_wrapper fdwform; @@ -350,7 +349,18 @@ GetFdwRoutineByRelId(Oid relid) ReleaseSysCache(tp); - /* And finally, call the handler function. */ + return fdwhandler; +} + +/* + * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper + * for the given foreign table, and retrieve its FdwRoutine struct. + */ +FdwRoutine * +GetFdwRoutineByRelId(Oid relid) +{ + Oid fdwhandler = GetFdwHandlerByRelId(relid); + return GetFdwRoutine(fdwhandler); } @@ -398,6 +408,16 @@ GetFdwRoutineForRelation(Relation relation, bool makecopy) return relation->rd_fdwroutine; } +/* + * GetFdwHandlerForRelation + * + * returns OID of FDW handler which is associated with the given relation. + */ +Oid +GetFdwHandlerForRelation(Relation relation) +{ + return GetFdwHandlerByRelId(RelationGetRelid(relation)); +} /* * IsImportableForeignTable - filter table names for IMPORT FOREIGN SCHEMA diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 6b1bf7b..b88339c 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -590,7 +590,9 @@ _copyForeignScan(const ForeignScan *from) /* * copy remainder of node */ + COPY_SCALAR_FIELD(fdw_handler); COPY_NODE_FIELD(fdw_exprs); + COPY_NODE_FIELD(fdw_ps_tlist); COPY_NODE_FIELD(fdw_private); COPY_SCALAR_FIELD(fsSystemCol); @@ -615,6 +617,7 @@ _copyCustomScan(const CustomScan *from) */ COPY_SCALAR_FIELD(flags); COPY_NODE_FIELD(custom_exprs); + COPY_NODE_FIELD(custom_ps_tlist); COPY_NODE_FIELD(custom_private); /* diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index edbd09f..fa7dd37 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -556,7 +556,9 @@ _outForeignScan(StringInfo str, const ForeignScan *node) _outScanInfo(str, (const Scan *) node); + WRITE_OID_FIELD(fdw_handler); WRITE_NODE_FIELD(fdw_exprs); + WRITE_NODE_FIELD(fdw_ps_tlist); WRITE_NODE_FIELD(fdw_private); WRITE_BOOL_FIELD(fsSystemCol); } @@ -570,6 +572,7 @@ _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); appendStringInfoString(str, " :methods "); _outToken(str, node->methods->CustomName); diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index be54f3d..030158d 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -21,6 +21,8 @@ #include "optimizer/pathnode.h" #include "optimizer/paths.h" +/* Hook for plugins to get control in add_paths_to_joinrel() */ +set_join_pathlist_hook_type set_join_pathlist_hook = NULL; #define PATH_PARAM_BY_REL(path, rel) \ ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids)) @@ -259,6 +261,19 @@ add_paths_to_joinrel(PlannerInfo *root, restrictlist, jointype, sjinfo, &semifactors, param_source_rels, extra_lateral_rels); + + /* + * 5. Consider paths added by FDW drivers or custom-scan providers, in + * addition to built-in paths. + * + * XXX - In case of FDW, we may be able to omit invocation if joinrel's + * fdwhandler (set only if both relations are managed by same FDW server). + */ + if (set_join_pathlist_hook) + set_join_pathlist_hook(root, joinrel, outerrel, innerrel, + restrictlist, jointype, + sjinfo, &semifactors, + param_source_rels, extra_lateral_rels); } /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index bf8dbe0..a35809d 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -1957,16 +1957,26 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, ForeignScan *scan_plan; RelOptInfo *rel = best_path->path.parent; Index scan_relid = rel->relid; - RangeTblEntry *rte; + Oid rel_oid = InvalidOid; Bitmapset *attrs_used = NULL; ListCell *lc; int i; - /* it should be a base rel... */ - Assert(scan_relid > 0); - Assert(rel->rtekind == RTE_RELATION); - rte = planner_rt_fetch(scan_relid, root); - Assert(rte->rtekind == RTE_RELATION); + /* + * Fetch relation-id, if this foreign-scan node actuall scans on + * a particular real relation. Elsewhere, InvalidOid shall be + * informed to the FDW driver. + */ + if (scan_relid > 0) + { + RangeTblEntry *rte; + + Assert(rel->rtekind == RTE_RELATION); + rte = planner_rt_fetch(scan_relid, root); + 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 @@ -1981,13 +1991,16 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, * has selected some join clauses for remote use but also wants them * rechecked locally). */ - scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rte->relid, + scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid, best_path, tlist, scan_clauses); /* 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; + /* * 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 @@ -2051,12 +2064,6 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, RelOptInfo *rel = best_path->path.parent; /* - * Right now, all we can support is CustomScan node which is associated - * with a particular base relation to be scanned. - */ - Assert(rel && rel->reloptkind == RELOPT_BASEREL); - - /* * Sort clauses into the best execution order, although custom-scan * provider can reorder them again. */ diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 4d3fbca..cf7e8e9a 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -569,6 +569,36 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) { ForeignScan *splan = (ForeignScan *) plan; + if (splan->fdw_ps_tlist != NIL) + { + indexed_tlist *pscan_itlist = + build_tlist_index(splan->fdw_ps_tlist); + + Assert(splan->scan.scanrelid == 0); + + splan->scan.plan.targetlist = (List *) + fix_upper_expr(root, + (Node *) splan->scan.plan.targetlist, + pscan_itlist, + INDEX_VAR, + rtoffset); + splan->scan.plan.qual = (List *) + fix_upper_expr(root, + (Node *) splan->scan.plan.qual, + pscan_itlist, + INDEX_VAR, + rtoffset); + splan->fdw_exprs = (List *) + fix_upper_expr(root, + (Node *) splan->fdw_exprs, + pscan_itlist, + INDEX_VAR, + rtoffset); + splan->fdw_ps_tlist = + fix_scan_list(root, splan->fdw_ps_tlist, rtoffset); + pfree(pscan_itlist); + break; + } splan->scan.scanrelid += rtoffset; splan->scan.plan.targetlist = fix_scan_list(root, splan->scan.plan.targetlist, rtoffset); @@ -583,6 +613,36 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) { CustomScan *splan = (CustomScan *) plan; + if (splan->custom_ps_tlist != NIL) + { + indexed_tlist *pscan_itlist = + build_tlist_index(splan->custom_ps_tlist); + + Assert(splan->scan.scanrelid == 0); + + splan->scan.plan.targetlist = (List *) + fix_upper_expr(root, + (Node *) splan->scan.plan.targetlist, + pscan_itlist, + INDEX_VAR, + rtoffset); + splan->scan.plan.qual = (List *) + fix_upper_expr(root, + (Node *) splan->scan.plan.qual, + pscan_itlist, + INDEX_VAR, + rtoffset); + splan->custom_exprs = (List *) + fix_upper_expr(root, + (Node *) splan->custom_exprs, + pscan_itlist, + INDEX_VAR, + rtoffset); + splan->custom_ps_tlist = + fix_scan_list(root, splan->custom_ps_tlist, rtoffset); + pfree(pscan_itlist); + break; + } splan->scan.scanrelid += rtoffset; splan->scan.plan.targetlist = fix_scan_list(root, splan->scan.plan.targetlist, rtoffset); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index b2becfa..c269ac0 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -378,10 +378,15 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, /* Grab the fdwroutine info using the relcache, while we have it */ if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + { + rel->fdw_handler = GetFdwHandlerForRelation(relation); rel->fdwroutine = GetFdwRoutineForRelation(relation, true); + } else + { + rel->fdw_handler = 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 4c76f54..26589e3 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "foreign/fdwapi.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -427,6 +428,18 @@ build_join_rel(PlannerInfo *root, sjinfo, restrictlist); /* + * Set FDW handler and routine if both outer and inner relation + * are managed by same FDW driver. + */ + if (OidIsValid(outer_rel->fdw_handler) && + OidIsValid(inner_rel->fdw_handler) && + outer_rel->fdw_handler == inner_rel->fdw_handler) + { + joinrel->fdw_handler = outer_rel->fdw_handler; + joinrel->fdwroutine = GetFdwRoutine(joinrel->fdw_handler); + } + + /* * Add the joinrel to the query's joinrel list, and store it into the * auxiliary hashtable if there is one. NB: GEQO requires us to append * the new joinrel to the end of the list! diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 24ade6c..0cf2768 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3815,6 +3815,10 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) /* index_tlist is set only if it's an IndexOnlyScan */ 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; + else if (IsA(ps->plan, CustomScan)) + dpns->index_tlist = ((CustomScan *) ps->plan)->custom_ps_tlist; else dpns->index_tlist = NIL; } diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index dc0a7fc7..09b0823 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -157,6 +157,7 @@ typedef struct FdwRoutine extern FdwRoutine *GetFdwRoutine(Oid fdwhandler); extern FdwRoutine *GetFdwRoutineByRelId(Oid relid); extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy); +extern Oid GetFdwHandlerForRelation(Relation relation); extern bool IsImportableForeignTable(const char *tablename, ImportForeignSchemaStmt *stmt); diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 48203a0..26c992e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -470,7 +470,13 @@ 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. - * Note that everything in both lists must be copiable by copyObject(). + * An optional fdw_ps_tlist is used to map a reference to an attribute of + * underlying relation(s) on a pair of INDEX_VAR and alternative varattno. + * It looks like a scan on pseudo relation that is usually result of + * relations join on remote data source, and FDW driver is responsible to + * set expected target list for this. If FDW returns records as foreign- + * table definition, just put NIL here. + * Note that everything in above 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(). @@ -479,7 +485,9 @@ typedef struct WorkTableScan typedef struct ForeignScan { Scan scan; + Oid fdw_handler; /* OID of FDW handler */ List *fdw_exprs; /* expressions that FDW may evaluate */ + List *fdw_ps_tlist; /* optional pseudo-scan tlist for FDW */ List *fdw_private; /* private data for FDW */ bool fsSystemCol; /* true if any "system column" is needed */ } ForeignScan; @@ -487,10 +495,11 @@ typedef struct ForeignScan /* ---------------- * CustomScan node * - * The comments for ForeignScan's fdw_exprs and fdw_private fields apply - * equally to custom_exprs and custom_private. 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. + * The comments for ForeignScan's fdw_exprs, fdw_varmap and fdw_private fields + * apply equally to custom_exprs, custom_ps_tlist and custom_private. + * 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. * ---------------- */ struct CustomScan; @@ -511,6 +520,7 @@ 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;/* optional pseudo-scan target list */ List *custom_private; /* private data for custom code */ const CustomScanMethods *methods; } CustomScan; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 7116496..5fa4e39 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -366,6 +366,7 @@ typedef struct PlannerInfo * 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 @@ -461,6 +462,7 @@ typedef struct RelOptInfo List *subplan_params; /* if subquery */ /* 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 */ /* used by various scans and joins: */ diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index afa5f9b..093f9d1 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -30,6 +30,19 @@ typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root, RangeTblEntry *rte); 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); +extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook; + /* Hook for plugins to replace standard_join_search() */ typedef RelOptInfo *(*join_search_hook_type) (PlannerInfo *root, int levels_needed,