From 0b03e5951bf0a1a8868db13f02049cf686a82165 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 7 Nov 2014 17:26:02 -0500 Subject: [PATCH] Introduce custom path and scan providers. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This allows extension modules to define their own methods for scanning a relation, and get the core code to use them. It's unclear as yet how much use this capability will find, but we won't find out if we never commit it. KaiGai Kohei, reviewed at various times and in various levels of detail by Shigeru Hanada, Tom Lane, Andres Freund, Álvaro Herrera, and myself. --- src/backend/commands/explain.c | 25 +++++ src/backend/executor/Makefile | 2 +- src/backend/executor/execAmi.c | 38 ++++++- src/backend/executor/execProcnode.c | 14 +++ src/backend/executor/nodeCustom.c | 126 ++++++++++++++++++++++++ src/backend/nodes/copyfuncs.c | 26 +++++ src/backend/nodes/outfuncs.c | 29 ++++++ src/backend/optimizer/path/allpaths.c | 3 + src/backend/optimizer/path/costsize.c | 2 +- src/backend/optimizer/plan/createplan.c | 60 ++++++++++- src/backend/optimizer/plan/setrefs.c | 26 ++++- src/backend/optimizer/plan/subselect.c | 21 ++++ src/backend/optimizer/util/pathnode.c | 47 +++++++++ src/backend/utils/adt/ruleutils.c | 70 +++++++++++++ src/include/executor/executor.h | 3 +- src/include/executor/nodeCustom.h | 30 ++++++ src/include/nodes/execnodes.h | 40 ++++++++ src/include/nodes/nodes.h | 3 + src/include/nodes/plannodes.h | 29 ++++++ src/include/nodes/relation.h | 42 ++++++++ src/include/optimizer/pathnode.h | 9 ++ src/include/optimizer/planmain.h | 3 + 22 files changed, 638 insertions(+), 10 deletions(-) create mode 100644 src/backend/executor/nodeCustom.c create mode 100644 src/include/executor/nodeCustom.h diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 3fcc1ddc69..99aa0f0933 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -724,6 +724,7 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used) case T_CteScan: case T_WorkTableScan: case T_ForeignScan: + case T_CustomScan: *rels_used = bms_add_member(*rels_used, ((Scan *) plan)->scanrelid); break; @@ -853,6 +854,7 @@ ExplainNode(PlanState *planstate, List *ancestors, const char *sname; /* node type name for non-text output */ const char *strategy = NULL; const char *operation = NULL; + const char *custom_name = NULL; int save_indent = es->indent; bool haschildren; @@ -941,6 +943,14 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_ForeignScan: pname = sname = "Foreign Scan"; break; + case T_CustomScan: + sname = "Custom Scan"; + custom_name = ((CustomScan *) plan)->methods->CustomName; + if (custom_name) + pname = psprintf("Custom Scan (%s)", custom_name); + else + pname = sname; + break; case T_Material: pname = sname = "Materialize"; break; @@ -1042,6 +1052,8 @@ ExplainNode(PlanState *planstate, List *ancestors, ExplainPropertyText("Parent Relationship", relationship, es); if (plan_name) ExplainPropertyText("Subplan Name", plan_name, es); + if (custom_name) + ExplainPropertyText("Custom Plan Provider", custom_name, es); } switch (nodeTag(plan)) @@ -1055,6 +1067,7 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_CteScan: case T_WorkTableScan: case T_ForeignScan: + case T_CustomScan: ExplainScanTarget((Scan *) plan, es); break; case T_IndexScan: @@ -1358,6 +1371,18 @@ ExplainNode(PlanState *planstate, List *ancestors, planstate, es); show_foreignscan_info((ForeignScanState *) planstate, es); break; + case T_CustomScan: + { + CustomScanState *css = (CustomScanState *) planstate; + + show_scan_qual(plan->qual, "Filter", planstate, ancestors, es); + if (plan->qual) + show_instrumentation_count("Rows Removed by Filter", 1, + planstate, es); + if (css->methods->ExplainCustomScan) + css->methods->ExplainCustomScan(css, ancestors, es); + } + break; case T_NestLoop: show_upper_qual(((NestLoop *) plan)->join.joinqual, "Join Filter", planstate, ancestors, es); diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 6081b56c08..af707b0375 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -16,7 +16,7 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \ execProcnode.o execQual.o execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ - nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \ + nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeCustom.o nodeHash.o \ nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \ nodeLimit.o nodeLockRows.o \ nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \ diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index 640964c5b7..b14e08cd1a 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -21,6 +21,7 @@ #include "executor/nodeBitmapIndexscan.h" #include "executor/nodeBitmapOr.h" #include "executor/nodeCtescan.h" +#include "executor/nodeCustom.h" #include "executor/nodeForeignscan.h" #include "executor/nodeFunctionscan.h" #include "executor/nodeGroup.h" @@ -49,6 +50,7 @@ #include "executor/nodeWindowAgg.h" #include "executor/nodeWorktablescan.h" #include "nodes/nodeFuncs.h" +#include "nodes/relation.h" #include "utils/rel.h" #include "utils/syscache.h" @@ -197,6 +199,10 @@ ExecReScan(PlanState *node) ExecReScanForeignScan((ForeignScanState *) node); break; + case T_CustomScanState: + ExecReScanCustomScan((CustomScanState *) node); + break; + case T_NestLoopState: ExecReScanNestLoop((NestLoopState *) node); break; @@ -291,6 +297,10 @@ ExecMarkPos(PlanState *node) ExecValuesMarkPos((ValuesScanState *) node); break; + case T_CustomScanState: + ExecCustomMarkPos((CustomScanState *) node); + break; + case T_MaterialState: ExecMaterialMarkPos((MaterialState *) node); break; @@ -348,6 +358,10 @@ ExecRestrPos(PlanState *node) ExecValuesRestrPos((ValuesScanState *) node); break; + case T_CustomScanState: + ExecCustomRestrPos((CustomScanState *) node); + break; + case T_MaterialState: ExecMaterialRestrPos((MaterialState *) node); break; @@ -379,9 +393,9 @@ ExecRestrPos(PlanState *node) * and valuesscan support is actually useless code at present.) */ bool -ExecSupportsMarkRestore(NodeTag plantype) +ExecSupportsMarkRestore(Path *pathnode) { - switch (plantype) + switch (pathnode->pathtype) { case T_SeqScan: case T_IndexScan: @@ -403,6 +417,16 @@ ExecSupportsMarkRestore(NodeTag plantype) */ return false; + case T_CustomScan: + { + CustomPath *cpath = (CustomPath *) pathnode; + + Assert(IsA(cpath, CustomPath)); + if (cpath->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE) + return true; + } + break; + default: break; } @@ -465,6 +489,16 @@ ExecSupportsBackwardScan(Plan *node) return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) && TargetListSupportsBackwardScan(node->targetlist); + case T_CustomScan: + { + uint32 flags = ((CustomScan *) node)->flags; + + if (TargetListSupportsBackwardScan(node->targetlist) && + (flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN) != 0) + return true; + } + return false; + case T_Material: case T_Sort: /* these don't evaluate tlist */ diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c index c0189eb5a1..e27c0627bc 100644 --- a/src/backend/executor/execProcnode.c +++ b/src/backend/executor/execProcnode.c @@ -85,6 +85,7 @@ #include "executor/nodeBitmapIndexscan.h" #include "executor/nodeBitmapOr.h" #include "executor/nodeCtescan.h" +#include "executor/nodeCustom.h" #include "executor/nodeForeignscan.h" #include "executor/nodeFunctionscan.h" #include "executor/nodeGroup.h" @@ -244,6 +245,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags) estate, eflags); break; + case T_CustomScan: + result = (PlanState *) ExecInitCustomScan((CustomScan *) node, + estate, eflags); + break; + /* * join nodes */ @@ -442,6 +448,10 @@ ExecProcNode(PlanState *node) result = ExecForeignScan((ForeignScanState *) node); break; + case T_CustomScanState: + result = ExecCustomScan((CustomScanState *) node); + break; + /* * join nodes */ @@ -678,6 +688,10 @@ ExecEndNode(PlanState *node) ExecEndForeignScan((ForeignScanState *) node); break; + case T_CustomScanState: + ExecEndCustomScan((CustomScanState *) node); + break; + /* * join nodes */ diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c new file mode 100644 index 0000000000..576b295a49 --- /dev/null +++ b/src/backend/executor/nodeCustom.c @@ -0,0 +1,126 @@ +/* ------------------------------------------------------------------------ + * + * nodeCustom.c + * Routines to handle execution of custom scan node + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * ------------------------------------------------------------------------ + */ +#include "postgres.h" + +#include "executor/executor.h" +#include "executor/nodeCustom.h" +#include "nodes/execnodes.h" +#include "nodes/plannodes.h" +#include "parser/parsetree.h" +#include "utils/hsearch.h" +#include "utils/memutils.h" +#include "utils/rel.h" + +CustomScanState * +ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) +{ + CustomScanState *css; + Relation scan_rel; + + /* populate a CustomScanState according to the CustomScan */ + css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan); + Assert(IsA(css, CustomScanState)); + + /* fill up fields of ScanState */ + css->ss.ps.plan = &cscan->scan.plan; + css->ss.ps.state = estate; + + /* create expression context for node */ + ExecAssignExprContext(estate, &css->ss.ps); + + /* initialize child expressions */ + css->ss.ps.targetlist = (List *) + ExecInitExpr((Expr *) cscan->scan.plan.targetlist, + (PlanState *) css); + css->ss.ps.qual = (List *) + ExecInitExpr((Expr *) cscan->scan.plan.qual, + (PlanState *) css); + + /* tuple table initialization */ + 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)); + + css->ss.ps.ps_TupFromTlist = false; + + /* + * Initialize result tuple type and projection info. + */ + ExecAssignResultTypeFromTL(&css->ss.ps); + ExecAssignScanProjectionInfo(&css->ss); + + /* + * The callback of custom-scan provider applies the final initialization + * of the custom-scan-state node according to its logic. + */ + css->methods->BeginCustomScan(css, estate, eflags); + + return css; +} + +TupleTableSlot * +ExecCustomScan(CustomScanState *node) +{ + Assert(node->methods->ExecCustomScan != NULL); + return node->methods->ExecCustomScan(node); +} + +void +ExecEndCustomScan(CustomScanState *node) +{ + Assert(node->methods->EndCustomScan != NULL); + node->methods->EndCustomScan(node); + + /* Free the exprcontext */ + ExecFreeExprContext(&node->ss.ps); + + /* Clean out the tuple table */ + ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); + if (node->ss.ss_ScanTupleSlot) + ExecClearTuple(node->ss.ss_ScanTupleSlot); + + /* Close the heap relation */ + ExecCloseScanRelation(node->ss.ss_currentRelation); +} + +void +ExecReScanCustomScan(CustomScanState *node) +{ + Assert(node->methods->ReScanCustomScan != NULL); + node->methods->ReScanCustomScan(node); +} + +void +ExecCustomMarkPos(CustomScanState *node) +{ + if (!node->methods->MarkPosCustomScan) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("custom-scan \"%s\" does not support MarkPos", + node->methods->CustomName))); + node->methods->MarkPosCustomScan(node); +} + +void +ExecCustomRestrPos(CustomScanState *node) +{ + if (!node->methods->RestrPosCustomScan) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("custom-scan \"%s\" does not support MarkPos", + node->methods->CustomName))); + node->methods->RestrPosCustomScan(node); +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 7b51d33177..e76b5b3f63 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -597,6 +597,29 @@ _copyForeignScan(const ForeignScan *from) return newnode; } +/* + * _copyCustomScan + */ +static CustomScan * +_copyCustomScan(const CustomScan *from) +{ + CustomScan *newnode; + + newnode = from->methods->CopyCustomScan(from); + Assert(nodeTag(newnode) == nodeTag(from)); + + CopyScanFields((const Scan *) from, (Scan *) newnode); + COPY_SCALAR_FIELD(flags); + /* + * NOTE: The method field of CustomScan is required to be a pointer + * to a static table of callback functions. So, we don't copy the + * table itself, just reference the original one. + */ + COPY_SCALAR_FIELD(methods); + + return newnode; +} + /* * CopyJoinFields * @@ -4043,6 +4066,9 @@ copyObject(const void *from) case T_ForeignScan: retval = _copyForeignScan(from); break; + case T_CustomScan: + retval = _copyCustomScan(from); + break; case T_Join: retval = _copyJoin(from); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 4bbfa629d6..cdf1e7ece1 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -563,6 +563,18 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_BOOL_FIELD(fsSystemCol); } +static void +_outCustomScan(StringInfo str, const CustomScan *node) +{ + WRITE_NODE_TYPE("CUSTOMSCAN"); + + _outScanInfo(str, (const Scan *) node); + WRITE_UINT_FIELD(flags); + appendStringInfo(str, " :methods"); + _outToken(str, node->methods->CustomName); + node->methods->TextOutCustomScan(str, node); +} + static void _outJoin(StringInfo str, const Join *node) { @@ -1584,6 +1596,17 @@ _outForeignPath(StringInfo str, const ForeignPath *node) WRITE_NODE_FIELD(fdw_private); } +static void +_outCustomPath(StringInfo str, const CustomPath *node) +{ + WRITE_NODE_TYPE("CUSTOMPATH"); + _outPathInfo(str, (const Path *) node); + WRITE_UINT_FIELD(flags); + appendStringInfo(str, " :methods"); + _outToken(str, node->methods->CustomName); + node->methods->TextOutCustomPath(str, node); +} + static void _outAppendPath(StringInfo str, const AppendPath *node) { @@ -2855,6 +2878,9 @@ _outNode(StringInfo str, const void *obj) case T_ForeignScan: _outForeignScan(str, obj); break; + case T_CustomScan: + _outCustomScan(str, obj); + break; case T_Join: _outJoin(str, obj); break; @@ -3063,6 +3089,9 @@ _outNode(StringInfo str, const void *obj) case T_ForeignPath: _outForeignPath(str, obj); break; + case T_CustomPath: + _outCustomPath(str, obj); + break; case T_AppendPath: _outAppendPath(str, obj); break; diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index c81efe93a4..8b42e36b6d 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -402,6 +402,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Consider TID scans */ create_tidscan_paths(root, rel); + /* Consider custom scans, if any */ + create_customscan_paths(root, rel, rte); + /* Now find the cheapest of the paths for this rel */ set_cheapest(rel); } diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 0cdb7905a2..659daa2026 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -2266,7 +2266,7 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path, * it off does not entitle us to deliver an invalid plan. */ else if (innersortkeys == NIL && - !ExecSupportsMarkRestore(inner_path->pathtype)) + !ExecSupportsMarkRestore(inner_path)) path->materialize_inner = true; /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 4b641a2ca1..8d9237ccac 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -77,13 +77,15 @@ static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_pa List *tlist, List *scan_clauses); static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, List *tlist, List *scan_clauses); +static Plan *create_customscan_plan(PlannerInfo *root, + CustomPath *best_path, + List *tlist, List *scan_clauses); static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path, Plan *outer_plan, Plan *inner_plan); static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path, Plan *outer_plan, Plan *inner_plan); static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, Plan *outer_plan, Plan *inner_plan); -static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); static void process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params); @@ -233,6 +235,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path) case T_CteScan: case T_WorkTableScan: case T_ForeignScan: + case T_CustomScan: plan = create_scan_plan(root, best_path); break; case T_HashJoin: @@ -409,6 +412,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path) scan_clauses); break; + case T_CustomScan: + plan = create_customscan_plan(root, + (CustomPath *) best_path, + tlist, + scan_clauses); + break; + default: elog(ERROR, "unrecognized node type: %d", (int) best_path->pathtype); @@ -1072,6 +1082,52 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) return plan; } +/* + * create_custom_plan + * + * Transform a CustomPath into a Plan. + */ +static Plan * +create_customscan_plan(PlannerInfo *root, CustomPath *best_path, + List *tlist, List *scan_clauses) +{ + Plan *plan; + 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. + */ + scan_clauses = order_qual_clauses(root, scan_clauses); + + /* + * Create a CustomScan (or its inheritance) node according to + * the supplied CustomPath. + */ + plan = best_path->methods->PlanCustomPath(root, rel, best_path, tlist, + scan_clauses); + + /* + * NOTE: unlike create_foreignscan_plan(), it is responsibility of + * the custom plan provider to replace outer-relation variables + * with nestloop params, because we cannot know how many expression + * trees are held in the private fields. + */ + + /* + * Copy cost data from Path to Plan; no need to make custom-plan + * providers do this + */ + copy_path_costsize(plan, &best_path->path); + + return plan; +} /***************************************************************************** * @@ -2540,7 +2596,7 @@ create_hashjoin_plan(PlannerInfo *root, * root->curOuterRels are replaced by Params, and entries are added to * root->curOuterParams if not already present. */ -static Node * +Node * replace_nestloop_params(PlannerInfo *root, Node *expr) { /* No setup needed for tree walk, so away we go */ diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 9ddc8adcf9..bbc68a05a6 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -94,7 +94,6 @@ static Plan *set_subqueryscan_references(PlannerInfo *root, SubqueryScan *plan, int rtoffset); static bool trivial_subqueryscan(SubqueryScan *plan); -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); static void set_join_references(PlannerInfo *root, Join *join, int rtoffset); @@ -579,6 +578,27 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) } break; + case T_CustomScan: + { + CustomScan *cscan = (CustomScan *) plan; + + 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); + /* + * The core implementation applies the routine to fixup + * varno on the target-list and scan qualifier. + * If custom-scan has additional expression nodes on its + * private fields, it has to apply same fixup on them. + * Otherwise, the custom-plan provider can skip this callback. + */ + if (cscan->methods->SetCustomScanRef) + cscan->methods->SetCustomScanRef(root, cscan, rtoffset); + } + break; + case T_NestLoop: case T_MergeJoin: case T_HashJoin: @@ -1063,7 +1083,7 @@ copyVar(Var *var) * We assume it's okay to update opcode info in-place. So this could possibly * scribble on the planner's input data structures, but it's OK. */ -static void +void fix_expr_common(PlannerInfo *root, Node *node) { /* We assume callers won't call us on a NULL pointer */ @@ -1161,7 +1181,7 @@ fix_param_node(PlannerInfo *root, Param *p) * looking up operator opcode info for OpExpr and related nodes, * and adding OIDs from regclass Const nodes into root->glob->relationOids. */ -static Node * +Node * fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset) { fix_scan_expr_context context; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 3e7dc85157..4200ec0a5a 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -2283,6 +2283,27 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, context.paramids = bms_add_members(context.paramids, scan_params); break; + case T_CustomScan: + { + CustomScan *custom_scan = (CustomScan *) plan; + + context.paramids = bms_add_members(context.paramids, + scan_params); + /* + * custom-scan provider is responsible to apply + * finalize_primnode() on the expression node of + * its private fields, but no need to apply it + * on the tlist and qual of Plan node because it + * is already done above. + */ + if (custom_scan->methods->FinalizeCustomScan) + custom_scan->methods->FinalizeCustomScan(root, + custom_scan, + finalize_primnode, + (void *)&context); + } + break; + case T_ModifyTable: { ModifyTable *mtplan = (ModifyTable *) plan; diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 319e8b2c37..6f1c6cfb2a 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -27,6 +27,7 @@ #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" +#include "utils/memutils.h" #include "utils/selfuncs.h" @@ -1926,3 +1927,49 @@ reparameterize_path(PlannerInfo *root, Path *path, } return NULL; } + +/***************************************************************************** + * creation of custom-plan paths + *****************************************************************************/ + +static List *custom_path_providers = NIL; + +/* + * register_custom_path_provider + * + * Register a table of callback functions which implements a custom-path + * provider. This allows extension to provide additional (hopefully faster) + * methods of scanning a relation. + */ +void +register_custom_path_provider(CustomPathMethods *cpp_methods) +{ + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(TopMemoryContext); + custom_path_providers = lappend(custom_path_providers, cpp_methods); + MemoryContextSwitchTo(oldcxt); +} + +/* + * create_customscan_paths + * + * Invoke custom path provider callbacks. If the callback determines that + * the custom-path provider can handle this relation, it can add one or more + * paths using add_path(). + */ +void +create_customscan_paths(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte) +{ + ListCell *cell; + + foreach (cell, custom_path_providers) + { + const CustomPathMethods *cpp_methods = lfirst(cell); + + if (cpp_methods->CreateCustomScanPath) + cpp_methods->CreateCustomScanPath(root, baserel, rte); + } +} diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 24ade6cc20..bf4e81f554 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -5493,6 +5493,26 @@ get_utility_query_def(Query *query, deparse_context *context) } } +/* + * GetSpecialCustomVar + * + * If a custom-scan provider uses a special varnode, this function will be + * called when deparsing; it should return an Expr node to be reversed-listed + * in lieu of the special Var. + */ +static Node * +GetSpecialCustomVar(CustomScanState *css, Var *varnode, PlanState **child_ps) +{ + Assert(IsA(css, CustomScanState)); + Assert(IS_SPECIAL_VARNO(varnode->varno)); + + if (!css->methods->GetSpecialCustomVar) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("%s does not support special varno reference", + css->methods->CustomName))); + return (Node *) css->methods->GetSpecialCustomVar(css, varnode, child_ps); +} /* * Display a Var appropriately. @@ -5522,6 +5542,8 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) int netlevelsup; deparse_namespace *dpns; deparse_columns *colinfo; + PlanState *child_ps = NULL; + Node *expr; char *refname; char *attname; @@ -5546,6 +5568,29 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) colinfo = deparse_columns_fetch(var->varno, dpns); attnum = var->varattno; } + else if (IS_SPECIAL_VARNO(var->varno) && + IsA(dpns->planstate, CustomScanState) && + (expr = GetSpecialCustomVar((CustomScanState *) dpns->planstate, + var, &child_ps)) != NULL) + { + deparse_namespace save_dpns; + + if (child_ps) + push_child_plan(dpns, child_ps, &save_dpns); + /* + * Force parentheses because our caller probably assumed a Var is a + * simple expression. + */ + if (!IsA(expr, Var)) + appendStringInfoChar(buf, '('); + get_rule_expr((Node *) expr, context, true); + if (!IsA(expr, Var)) + appendStringInfoChar(buf, ')'); + + if (child_ps) + pop_child_plan(dpns, &save_dpns); + return NULL; + } else if (var->varno == OUTER_VAR && dpns->outer_tlist) { TargetEntry *tle; @@ -5760,6 +5805,7 @@ get_name_for_var_field(Var *var, int fieldno, AttrNumber attnum; int netlevelsup; deparse_namespace *dpns; + PlanState *child_ps = NULL; TupleDesc tupleDesc; Node *expr; @@ -5834,6 +5880,30 @@ get_name_for_var_field(Var *var, int fieldno, rte = rt_fetch(var->varno, dpns->rtable); attnum = var->varattno; } + else if (IS_SPECIAL_VARNO(var->varno) && + IsA(dpns->planstate, CustomScanState) && + (expr = GetSpecialCustomVar((CustomScanState *) dpns->planstate, + var, &child_ps)) != NULL) + { + StringInfo saved = context->buf; + StringInfoData temp; + deparse_namespace save_dpns; + + initStringInfo(&temp); + context->buf = &temp; + + if (child_ps) + push_child_plan(dpns, child_ps, &save_dpns); + if (!IsA(expr, Var)) + appendStringInfoChar(context->buf, '('); + get_rule_expr((Node *) expr, context, true); + if (!IsA(expr, Var)) + appendStringInfoChar(context->buf, ')'); + if (child_ps) + pop_child_plan(dpns, &save_dpns); + context->buf = saved; + return temp.data; + } else if (var->varno == OUTER_VAR && dpns->outer_tlist) { TargetEntry *tle; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index d167b496fc..a44b4cde0f 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -16,6 +16,7 @@ #include "executor/execdesc.h" #include "nodes/parsenodes.h" +#include "nodes/relation.h" #include "utils/lockwaitpolicy.h" @@ -103,7 +104,7 @@ extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; extern void ExecReScan(PlanState *node); extern void ExecMarkPos(PlanState *node); extern void ExecRestrPos(PlanState *node); -extern bool ExecSupportsMarkRestore(NodeTag plantype); +extern bool ExecSupportsMarkRestore(Path *pathnode); extern bool ExecSupportsBackwardScan(Plan *node); extern bool ExecMaterializesOutput(NodeTag plantype); diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h new file mode 100644 index 0000000000..1736d48bfa --- /dev/null +++ b/src/include/executor/nodeCustom.h @@ -0,0 +1,30 @@ +/* ------------------------------------------------------------------------ + * + * nodeCustom.h + * + * prototypes for CustomScan nodes + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * ------------------------------------------------------------------------ + */ +#ifndef NODECUSTOM_H +#define NODECUSTOM_H +#include "nodes/plannodes.h" +#include "nodes/execnodes.h" + +/* + * General executor code + */ +extern CustomScanState *ExecInitCustomScan(CustomScan *custom_scan, + EState *estate, int eflags); +extern TupleTableSlot *ExecCustomScan(CustomScanState *node); +extern Node *MultiExecCustomScan(CustomScanState *node); +extern void ExecEndCustomScan(CustomScanState *node); + +extern void ExecReScanCustomScan(CustomScanState *node); +extern void ExecCustomMarkPos(CustomScanState *node); +extern void ExecCustomRestrPos(CustomScanState *node); + +#endif /* NODECUSTOM_H */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 39d2c10bdf..b72e605e4f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -19,6 +19,7 @@ #include "executor/instrument.h" #include "nodes/params.h" #include "nodes/plannodes.h" +#include "nodes/relation.h" #include "utils/reltrigger.h" #include "utils/sortsupport.h" #include "utils/tuplestore.h" @@ -1504,6 +1505,45 @@ typedef struct ForeignScanState void *fdw_state; /* foreign-data wrapper can keep state here */ } ForeignScanState; +/* ---------------- + * CustomScanState information + * + * CustomScan nodes are used to execute custom code within executor. + * ---------------- + */ +struct CustomExecMethods; +struct ExplainState; /* to avoid to include explain.h here */ + +typedef struct CustomScanState +{ + ScanState ss; + uint32 flags; /* mask of CUSTOMPATH_* flags defined in relation.h*/ + const struct CustomExecMethods *methods; +} CustomScanState; + +typedef struct CustomExecMethods +{ + const char *CustomName; + + /* EXECUTOR methods */ + void (*BeginCustomScan)(CustomScanState *node, + EState *estate, + int eflags); + TupleTableSlot *(*ExecCustomScan)(CustomScanState *node); + void (*EndCustomScan)(CustomScanState *node); + void (*ReScanCustomScan)(CustomScanState *node); + void (*MarkPosCustomScan)(CustomScanState *node); + void (*RestrPosCustomScan)(CustomScanState *node); + + /* EXPLAIN support */ + void (*ExplainCustomScan)(CustomScanState *node, + List *ancestors, + struct ExplainState *es); + Node *(*GetSpecialCustomVar)(CustomScanState *node, + Var *varnode, + PlanState **child_ps); +} CustomExecMethods; + /* ---------------------------------------------------------------- * Join State Information * ---------------------------------------------------------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 154d943d58..bc71fea78d 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -62,6 +62,7 @@ typedef enum NodeTag T_CteScan, T_WorkTableScan, T_ForeignScan, + T_CustomScan, T_Join, T_NestLoop, T_MergeJoin, @@ -107,6 +108,7 @@ typedef enum NodeTag T_CteScanState, T_WorkTableScanState, T_ForeignScanState, + T_CustomScanState, T_JoinState, T_NestLoopState, T_MergeJoinState, @@ -224,6 +226,7 @@ typedef enum NodeTag T_HashPath, T_TidPath, T_ForeignPath, + T_CustomPath, T_AppendPath, T_MergeAppendPath, T_ResultPath, diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index fb02390da5..9dbb91cb90 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -15,8 +15,10 @@ #define PLANNODES_H #include "access/sdir.h" +#include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/primnodes.h" +#include "nodes/relation.h" #include "utils/lockwaitpolicy.h" @@ -483,6 +485,33 @@ typedef struct ForeignScan bool fsSystemCol; /* true if any "system column" is needed */ } ForeignScan; +/* ---------------- + * CustomScan node + * ---------------- + */ +struct CustomScanMethods; + +typedef struct CustomScan +{ + Scan scan; + uint32 flags; /* mask of CUSTOMPATH_* flags defined in relation.h */ + struct CustomScanMethods *methods; +} CustomScan; + +typedef struct CustomScanMethods +{ + const char *CustomName; + void (*SetCustomScanRef)(struct PlannerInfo *root, + CustomScan *cscan, + int rtoffset); + void (*FinalizeCustomScan)(struct PlannerInfo *root, + CustomScan *cscan, + bool (*finalize_primnode)(), + void *finalize_context); + Node *(*CreateCustomScanState)(CustomScan *cscan); + void (*TextOutCustomScan)(StringInfo str, const CustomScan *node); + CustomScan *(*CopyCustomScan)(const CustomScan *from); +} CustomScanMethods; /* * ========== diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index f1a0504c0d..05cfbcd2aa 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -15,6 +15,7 @@ #define RELATION_H #include "access/sdir.h" +#include "lib/stringinfo.h" #include "nodes/params.h" #include "nodes/parsenodes.h" #include "storage/block.h" @@ -883,6 +884,47 @@ typedef struct ForeignPath List *fdw_private; } ForeignPath; +/* + * CustomPath represents a scan by some out-of-core extension. + * + * We provide a set of hooks here - which the provider must take care to + * set up correctly - to allow extensions to supply their own methods of + * scanning a relation. For example, a provider might provide GPU + * acceleration, a cache-based scan, or some other kind of logic we haven't + * dreamed up yet. + * + * Core code should avoid assuming that the CustomPath is only as large as + * the structure declared here; providers are expected to make it the first + * element in a larger structure. + */ + +struct CustomPathMethods; +struct Plan; /* not to include plannodes.h here */ + +#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN 0x0001 +#define CUSTOMPATH_SUPPORT_MARK_RESTORE 0x0002 + +typedef struct CustomPath +{ + Path path; + uint32 flags; + const struct CustomPathMethods *methods; +} CustomPath; + +typedef struct CustomPathMethods +{ + const char *CustomName; + void (*CreateCustomScanPath)(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); + struct Plan *(*PlanCustomPath)(PlannerInfo *root, + RelOptInfo *rel, + CustomPath *best_path, + List *tlist, + List *clauses); + void (*TextOutCustomPath)(StringInfo str, const CustomPath *node); +} CustomPathMethods; + /* * AppendPath represents an Append plan, ie, successive execution of * several member plans. diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 26b17f5f7a..2b67ae6187 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -128,6 +128,15 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path, Relids required_outer, double loop_count); +/* + * Interface definition of custom-scan providers + */ +extern void register_custom_path_provider(CustomPathMethods *cpp_methods); + +extern void create_customscan_paths(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); + /* * prototypes for relnode.c */ diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 3fdc2cba0e..c97c5777a0 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -86,6 +86,7 @@ extern ModifyTable *make_modifytable(PlannerInfo *root, List *withCheckOptionLists, List *returningLists, List *rowMarks, int epqParam); extern bool is_projection_capable_plan(Plan *plan); +extern Node *replace_nestloop_params(PlannerInfo *root, Node *expr); /* * prototypes for plan/initsplan.c @@ -130,6 +131,8 @@ extern bool query_is_distinct_for(Query *query, List *colnos, List *opids); */ extern Plan *set_plan_references(PlannerInfo *root, Plan *plan); extern void fix_opfuncids(Node *node); +extern Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset); +extern void fix_expr_common(PlannerInfo *root, Node *node); extern void set_opfuncid(OpExpr *opexpr); extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr); extern void record_plan_function_dependency(PlannerInfo *root, Oid funcid); -- 2.40.0