src/backend/commands/explain.c | 23 +++++++++++++++++++++++ src/backend/executor/execScan.c | 13 +++++++++++-- src/backend/executor/nodeForeignscan.c | 13 +++++++++++++ src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/outfuncs.c | 2 ++ src/backend/optimizer/plan/createplan.c | 13 ++++++++++++- src/backend/optimizer/plan/setrefs.c | 8 ++++++++ src/backend/optimizer/plan/subselect.c | 24 ++++++++++++++++++++---- src/include/foreign/fdwapi.h | 7 ++++++- src/include/nodes/execnodes.h | 1 + src/include/nodes/plannodes.h | 1 + src/include/nodes/relation.h | 1 + 12 files changed, 99 insertions(+), 8 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 5d06fa4..3396208 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -117,6 +117,8 @@ static void ExplainMemberNodes(List *plans, PlanState **planstates, List *ancestors, ExplainState *es); static void ExplainSubPlans(List *plans, List *ancestors, const char *relationship, ExplainState *es); +static void ExplainForeignChildren(ForeignScanState *fss, + List *ancestors, ExplainState *es); static void ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es); static void ExplainProperty(const char *qlabel, const char *value, @@ -1615,6 +1617,8 @@ ExplainNode(PlanState *planstate, List *ancestors, IsA(plan, BitmapAnd) || IsA(plan, BitmapOr) || IsA(plan, SubqueryScan) || + (IsA(planstate, ForeignScanState) && + ((ForeignScanState *) planstate)->fdw_ps != NIL) || (IsA(planstate, CustomScanState) && ((CustomScanState *) planstate)->custom_ps != NIL) || planstate->subPlan; @@ -1671,6 +1675,10 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors, "Subquery", NULL, es); break; + case T_ForeignScan: + ExplainForeignChildren((ForeignScanState *) planstate, + ancestors, es); + break; case T_CustomScan: ExplainCustomChildren((CustomScanState *) planstate, ancestors, es); @@ -2711,6 +2719,21 @@ ExplainSubPlans(List *plans, List *ancestors, } /* + * Explain a list of children of a ForeignScan. + */ +static void +ExplainForeignChildren(ForeignScanState *fss, + List *ancestors, ExplainState *es) +{ + ListCell *cell; + const char *label = + (list_length(fss->fdw_ps) != 1 ? "children" : "child"); + + foreach(cell, fss->fdw_ps) + ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es); +} + +/* * Explain a list of children of a CustomScan. */ static void diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index a96e826..1a9bcba 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -49,8 +49,17 @@ ExecScanFetch(ScanState *node, */ Index scanrelid = ((Scan *) node->ps.plan)->scanrelid; - Assert(scanrelid > 0); - if (estate->es_epqTupleSet[scanrelid - 1]) + if (scanrelid == 0) + { + TupleTableSlot *slot = node->ss_ScanTupleSlot; + + /* Check if it meets the access-method conditions */ + if (!(*recheckMtd) (node, slot)) + ExecClearTuple(slot); /* would not be returned by scan */ + + return slot; + } + else if (estate->es_epqTupleSet[scanrelid - 1]) { TupleTableSlot *slot = node->ss_ScanTupleSlot; diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index bb28a73..d1b36ab 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -72,6 +72,19 @@ ForeignNext(ForeignScanState *node) static bool ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot) { + /* + * This FDW callback have two tasks. (1) If this ForeignScanState + * represents an external join (thus scanrelid==0), it need to + * construct a tuple according to TupleDesc of the slot; that is + * initialized according to the fdw_scan_tlist. (2) If this node + * has any qualifiers not to be executed locally, it has to apply + * visibility checks by the qualifier (because ExecQual on ExecScan + * runs towards node->scan.plan.qual, not on the qualifier pushed- + * down). + */ + if (!node->fdwroutine->RecheckForeignScan) + return node->fdwroutine->RecheckForeignScan(node, slot); + /* There are no access-method-specific conditions to recheck. */ return true; } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 1c8425d..23f9942 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -621,6 +621,7 @@ _copyForeignScan(const ForeignScan *from) * copy remainder of node */ COPY_SCALAR_FIELD(fs_server); + COPY_NODE_FIELD(fdw_plans); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); COPY_NODE_FIELD(fdw_scan_tlist); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index a878498..fbe5d05 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -576,6 +576,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) _outScanInfo(str, (const Scan *) node); WRITE_OID_FIELD(fs_server); + WRITE_NODE_FIELD(fdw_plans); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); WRITE_NODE_FIELD(fdw_scan_tlist); @@ -1664,6 +1665,7 @@ _outForeignPath(StringInfo str, const ForeignPath *node) _outPathInfo(str, (const Path *) node); + WRITE_NODE_FIELD(fdw_paths); WRITE_NODE_FIELD(fdw_private); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 404c6f5..a915cb6 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -2059,11 +2059,20 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, Index scan_relid = rel->relid; Oid rel_oid = InvalidOid; Bitmapset *attrs_used = NULL; + List *fdw_plans = NIL; ListCell *lc; int i; Assert(rel->fdwroutine != NULL); + /* Recursively transform child paths. */ + foreach (lc, best_path->fdw_paths) + { + Plan *plan = create_plan_recurse(root, (Path *) lfirst(lc)); + + fdw_plans = lappend(fdw_plans, plan); + } + /* * If we're scanning a base relation, fetch its OID. (Irrelevant if * scanning a join relation.) @@ -2093,7 +2102,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, */ scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid, best_path, - tlist, scan_clauses); + tlist, + scan_clauses, + fdw_plans); /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_path_costsize(&scan_plan->scan.plan, &best_path->path); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index ee8710d..dba91ee 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -1093,6 +1093,8 @@ set_foreignscan_references(PlannerInfo *root, ForeignScan *fscan, int rtoffset) { + ListCell *lc; + /* Adjust scanrelid if it's valid */ if (fscan->scan.scanrelid > 0) fscan->scan.scanrelid += rtoffset; @@ -1136,6 +1138,12 @@ set_foreignscan_references(PlannerInfo *root, fix_scan_list(root, fscan->fdw_exprs, rtoffset); } + /* Adjust child plan-nodes recursively, if needed */ + foreach(lc, fscan->fdw_plans) + { + lfirst(lc) = set_plan_refs(root, (Plan *) lfirst(lc), rtoffset); + } + /* Adjust fs_relids if needed */ if (rtoffset > 0) { diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index d0bc412..26bfeb4 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -2394,10 +2394,26 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; 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); + { + ForeignScan *fscan = (ForeignScan *) plan; + ListCell *lc; + + finalize_primnode((Node *) fscan->fdw_exprs, &context); + /* We assume fdw_scan_tlist cannot contain Params */ + context.paramids = + bms_add_members(context.paramids, scan_params); + + /* child nodes if any */ + foreach (lc, fscan->fdw_plans) + { + context.paramids = + bms_add_members(context.paramids, + finalize_plan(root, + (Plan *) lfirst(lc), + valid_params, + scan_params)); + } + } break; case T_CustomScan: diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 69b48b4..4a41351 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -36,13 +36,17 @@ typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root, Oid foreigntableid, ForeignPath *best_path, List *tlist, - List *scan_clauses); + List *scan_clauses, + List *fdw_plans); typedef void (*BeginForeignScan_function) (ForeignScanState *node, int eflags); typedef TupleTableSlot *(*IterateForeignScan_function) (ForeignScanState *node); +typedef bool (*RecheckForeignScan_function) (ForeignScanState *node, + TupleTableSlot *slot); + typedef void (*ReScanForeignScan_function) (ForeignScanState *node); typedef void (*EndForeignScan_function) (ForeignScanState *node); @@ -138,6 +142,7 @@ typedef struct FdwRoutine GetForeignPlan_function GetForeignPlan; BeginForeignScan_function BeginForeignScan; IterateForeignScan_function IterateForeignScan; + RecheckForeignScan_function RecheckForeignScan; ReScanForeignScan_function ReScanForeignScan; EndForeignScan_function EndForeignScan; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 5796de8..1453be2 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1579,6 +1579,7 @@ typedef struct ForeignScanState ScanState ss; /* its first field is NodeTag */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; + List *fdw_ps; /* list of child PlanState nodes, if any */ void *fdw_state; /* foreign-data wrapper can keep state here */ } ForeignScanState; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 0654d02..2a5045f 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -518,6 +518,7 @@ typedef struct ForeignScan { Scan scan; Oid fs_server; /* OID of foreign server */ + List *fdw_plans; /* list of Plan nodes, if any */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ List *fdw_scan_tlist; /* optional tlist describing scan tuple */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 5dc23d9..78038d2 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -901,6 +901,7 @@ typedef struct TidPath typedef struct ForeignPath { Path path; + List *fdw_paths; List *fdw_private; } ForeignPath;