diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 3fcc1ddc69c..99aa0f09338 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 6081b56c086..af707b03751 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 640964c5b7c..b14e08cd1af 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 c0189eb5a12..e27c0627bc4 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 00000000000..576b295a49a --- /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 7b51d331777..e76b5b3f633 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 4bbfa629d69..cdf1e7ece1f 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 c81efe93a47..8b42e36b6d4 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 0cdb7905a2f..659daa20265 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 4b641a2ca1f..8d9237ccac4 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 9ddc8adcf98..bbc68a05a6c 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 3e7dc851579..4200ec0a5a7 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 319e8b2c379..6f1c6cfb2aa 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 24ade6cc201..bf4e81f554a 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 d167b496fce..a44b4cde0fa 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 00000000000..1736d48bfaf --- /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 39d2c10bdfe..b72e605e4fe 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 154d943d581..bc71fea78d0 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 fb02390da51..9dbb91cb90d 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 f1a0504c0d4..05cfbcd2aa1 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 26b17f5f7af..2b67ae6187b 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 3fdc2cba0ed..c97c5777a07 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);