doc/src/sgml/custom-plan.sgml | 359 ++++++++++++++++++++++++++++++++ doc/src/sgml/filelist.sgml | 1 + doc/src/sgml/postgres.sgml | 1 + 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 | 127 +++++++++++ src/backend/nodes/copyfuncs.c | 26 +++ src/backend/nodes/outfuncs.c | 15 ++ src/backend/optimizer/path/costsize.c | 2 +- src/backend/optimizer/plan/createplan.c | 3 +- src/backend/optimizer/plan/setrefs.c | 26 ++- src/backend/optimizer/plan/subselect.c | 21 ++ 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 | 1 + src/include/nodes/plannodes.h | 20 ++ src/include/optimizer/planmain.h | 3 + 21 files changed, 817 insertions(+), 10 deletions(-) diff --git a/doc/src/sgml/custom-plan.sgml b/doc/src/sgml/custom-plan.sgml new file mode 100644 index 0000000..fc4b187 --- /dev/null +++ b/doc/src/sgml/custom-plan.sgml @@ -0,0 +1,359 @@ + + + + Writing A Custom Plan Provider + + custom plan provider + handler for + + + + PostgreSQL has various kind of built-in plan nodes that implement + a particular portion to process the supplied SQL queries. + For example, SeqScan node implements full table scan, HashJoin node + implements tables join using a hash table and so on. + + + The custom-plan interface allows extensions to provide alternative + query execution paths, in addition to the built-in ones. Query planner + will choose the cheapest path towards a particular relation(s) scan or + join in all the proposed ones by built-in and extensions. + Once a proposed custom plan got picked up, callback functions associated + with the node shall be called and extension will get control to process + the task. We call a set of callback functions associated with a certain + custom node the custom-path, custom-plan or custom-scan depending on + the things it provides; just "provider" in short. + + + + Custom Plan Overall Steps + + A custom-path provider shall be registered by + register_custom_path_provider that takes a pointer of + CustomPathMethods table which also holds some function + pointers. + It is usually installed on _PG_init() of extension + when either of preload configuration or + command loads the extension of the provider. + + + Onec a provider gets registered, the built-in query planner calls + back CreateCustomScanPath to ask whether the provider + can offer an alternative path to scan the referenced relation, or + not. + If it is available to provide, this callback will construct + CustomPath with cost estimation. + Then, query planner compares the alternative paths with built-in + paths based on the estimated cost, then the cheapest one shall be + chosen. + + + Usually, a provider may need some private fields to store something + valuable properties for the path. In this case, extension can extends + the CustomPath structure using a new data structure + definition that takes CustomPath on the head then followed + by private fields. + +typedef struct MySpecialPath +{ + CustomPath cpath; + List *some_list; + Bitmapset *some_bitmap; + : +} MySpecialPath; + + The above example shows a structure delivered from CustomPath + with some private fields. + We assume such kind of manner, like as object oriented language doing, + to inject private fields of the provider on other nodes like + CustomScan. + + + Once a CustomPath got chosen, its callback shall be kicked + to populate CustomScan (or its inheritance) node; that is + the only available node type, right now. It consists of a part of query + plan tree, instead of the built-in nodes. + In the same manner, the CustomScan node populates + a CustomScanState (or its inheritance) node; that manages + execution-time status of the custom-scan node and a set of callbacks. + + + + + Custom Path Callbacks + + This section introduces callback functions of CustomPath + structure; defined in the CustomPathMethods, and + related flags. + + + +void +CreateCustomScanPath(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); + + As mentioned above, it construct CustomPath (or inherited + data type) node if it is available to provide an alternative scan path + on the supplied relation and qualifiers; that shall be informed using + RelOptInfo argument. + The constructed CustomPath node shall be added to + candidate path of the relation using add_path. + + + +Plan * +PlanCustomPath(PlannerInfo *root, + RelOptInfo *rel, + CustomPath *best_path, + List *tlist, + List *clauses); + + It populates a CustomScan or its inheritance node + according to the supplied CustomPath node which was + constructed on the custom path handler function then chosen by the + query planner. + This callback has to allocate a CustomScan node because + only provider can know exact size to be allocated if it is extended + to have private fields. + This callback is assumed to allocate the CustomScan node + with CustomPlanMethods callbacks table, then initialize + common plan-node fields and arbitrary private fields. + + + + +void +TextOutCustomPath(StringInfo str, const CustomPath *node); + + It makes a text representation of custom path node. If provider extends + CustomPath data type, it shall to put private fields on + the supplied StringInfo with text form. + Note that common fields in CustomPath are handled by + backend, so extension needs to do nothing special. + + + + CustomPath structure can have flags that informs capability + of this custom-path to the query planner. Every flags can be combined + with OR-operation, then set to flags field. + The value of flags should be unchanged across node + population, because the plan tree is constructed based on the properties + being preliminary configured. + + + CUSTOMPATH_SUPPORT_BACKWARD_SCAN informs the planner + this custom-path supports backward scan. + CUSTOMPATH_SUPPORT_MARK_RESTORE informs the planner + this custom-path supports mark and restore position. + + + + + Custom Scan Callbacks + + This section introduces callback functions of CustomScan + structure; defined in the CustomScanMethods. + + + + +void +SetCustomScanRef(PlannerInfo *root, + CustomScan *cscan, + int rtoffset); + + It adjusts varno and varattno of var-nodes in + the expression tree chained from CustomScan node + (including private fields in the inherited data type). + In case of usual relation scan, rtoffset shall be added to + varno of var-nodes and scanrelid of plan node, + then fix_scan_expr shall be called on expression nodes to + track plan dependency. + + + + +void +FinalizeCustomScan(PlannerInfo *root, + CustomScan *cscan, + bool (*finalize_primnode)(), + void *finalize_context); + + It is an optional callback, which applies finalize_primenode + on the expression nodes of private fields because only provider knows + what private fields are expression node to be finalized. + Note that backend applies finalize_primenode on + the tlist and qual of the base plan node, + so provider shall do nothing special, if it has no private expression + node. Also, it handles recursive stuff on the lefttree and + righttree, so it does not need to handle recursive walks. + + + + +Node * +CreateCustomScanState(CustomScan *cscan); + + It populates a CustomScanState (or its inheritance) + node according to the supplied CustomScan node that was + preliminary constructed on the beginning of query executor. + Only provider can know exact size of the node to be allocated, this + callback allocate a CustomScanState node + with CustomExecMethods callbacks table and arbitrary + private fields. + + + Note that main purpose of this callback is allocation of + CustomScanState node, not initialization of individual + fields because it shall be handled on the BeginCustomPlan + callback to be invoked next to the common usual initialization. + + + + +void +TextOutCustomScan(StringInfo str, + const CustomScan *node); + + It makes a text representation of custom-scan node. If provider extends + CustomScan data type, it shall put private fields on + the supplied StringInfo with text form. + Note that common fields within CustomPlan are handled + by the backend, so extension needs to do nothing special. + + + + +CustomScan * +CopyCustomScan(const CustomScan *from); + + It duplicate every private fields of the inheritance of + CustomScan onto a newly allocated node. + In case when provider extends CustomScan node, only provider + can know exact size to be allocated and existence of the private fields. + So, it shall be responsible for node allocation and copy of private fields, + although the backend copies the fields of + CustomScan. + + + + + Custom Executor Callbacks + + This section introduces callback functions of CustomPlanState + structure; defined in the CustomExecMethods. + + + +void +BeginCustomScan(CustomScanState *node, + EState *estate, + int eflags); + + It begins execution of custom-scan. This callback is invoked during + executor startup to initialize the supplied CustomScanState + that was constructed on the CreateCustomScanState above. + The provider shall have initialization of its private fields and common + fields within CustomScanState if needed, because the backend + code already applies some fundamental initializations. + + + + +TupleTableSlot * +ExecCustomScan(CustomScanState *node); + + It fetches one row from the custom-plan node, returning it in a tuple + table slot (ps_ResultTupleSlot of PlanState + shall be used) or NULL if no more rows are available. + The tuple table slot infrastructure allows either a physical or virtual + tuple to be returned; in most cases the latter choice is preferable from + a performance standpoint. + The rows being returned have to match the tuple-descriptor of the + PlanState + + + Note that this call is under a short-lived memory context that will be + reset for each invocation. So, it may be a good choice to switch + es_query_cxt of the EState, to acquire + memory in per-scan duration. + + + + +void +EndCustomScan(CustomScanState *node); + + It ends the execution of custom-scan and release any resources held by + this node. If provider acquired resources that is not released + automatically at end of executor, it is responsibility of the provider. + + + + +void +ReScanCustomScan(CustomScanState *node); + + It restarts the scan from the beginning. Note that any parameters + the scan depends on may have changed value, so the new scan does not + necessarily return exactly the same rows. + + + + +void * +MarkPosCustomScan(CustomScanState *node); + + It is an optional callback if CUSTOMPATH_SUPPORT_MARK_RESTORE + is set on the flags. Elsewhere, it should put NULL + on the callback table because never called. + It saves current scan position on somewhere in private fields of + CustomScanState (or its inheritance), to restore + the position later. + + + + +void * +RestrPosCustomScan(CustomScanState *node); + + It is an optional callback if CUSTOMPATH_SUPPORT_MARK_RESTORE + is set on the flags. Elsewhere, it should put NULL + on the callback table because never called. + It restores the previous scan position saved by + the MarkPosCustomScan above. + + + + +void +ExplainCustomScan(CustomScanState *node, + List *ancestors, + ExplainState *es); + + It is an optional callback, to show custom-scan specific explain output. + + + + +Node * +GetSpecialCustomVar(CustomPlanState *node, + Var *varnode, + PlanState **child_ps); + + It is an optional callback, to solve references to special varno on + the CustomScanState when EXPLAIN needs the + text form of the column actually referenced. + In case when provider adjusted varno of varnodes on + the expression tree to use special varnos (INNER_VAR, + OUTER_VAR or INDEX_VAR), provider has + to inform the backend which column is mapped on the underlying plan-state. + + + This callback is expected to return Var node to reference + an actual variable on the underlying PlanState that shall + be set on the child_ps argument for recursive walking down. + + + diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml index 5902f97..8d20594 100644 --- a/doc/src/sgml/filelist.sgml +++ b/doc/src/sgml/filelist.sgml @@ -92,6 +92,7 @@ + diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml index 9bde108..5f415c6 100644 --- a/doc/src/sgml/postgres.sgml +++ b/doc/src/sgml/postgres.sgml @@ -242,6 +242,7 @@ &nls; &plhandler; &fdwhandler; + &custom-plan; &geqo; &indexam; &gist; diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 3fcc1dd..99aa0f0 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 6081b56..af707b0 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 640964c..b14e08c 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 c0189eb..e27c062 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 0000000..a8155d7 --- /dev/null +++ b/src/backend/executor/nodeCustom.c @@ -0,0 +1,127 @@ +/* ------------------------------------------------------------------------ + * + * 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); + css->ss.ps.ps_TupFromTlist = false; + + /* 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); + + /* initialization of result tuple slot */ + ExecInitResultTupleSlot(estate, &css->ss.ps); + ExecAssignResultTypeFromTL(&css->ss.ps); + + /* + * Also, initialization of relation scan stuff if custom-scan + * node intends to run on a particular plain relation. + * Elsewhere, custom-scan provider should be responsible to put + * proper initialization of scan tuple-slot and projection info + * by itself. + */ + scan_rel = ExecOpenScanRelation(estate, cscan->scan.scanrelid, eflags); + css->ss.ss_currentRelation = scan_rel; + css->ss.ss_currentScanDesc = NULL; /* set by provider on demand */ + ExecInitScanTupleSlot(estate, &css->ss); + ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel)); + 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 21b070a..3e03cd8 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -598,6 +598,29 @@ _copyForeignScan(const ForeignScan *from) } /* + * _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 * * This function copies the fields of the Join node. It is used by @@ -4042,6 +4065,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 d6da32c..cdf1e7e 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -564,6 +564,18 @@ _outForeignScan(StringInfo str, const ForeignScan *node) } 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) { WRITE_NODE_TYPE("JOIN"); @@ -2866,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; diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 0cdb790..659daa2 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 189f7ef..23a26d2 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -86,7 +86,6 @@ 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); @@ -2613,7 +2612,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 9ddc8ad..5589de8 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. + * Elsewhere, custom-plan provider 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 3e7dc85..4200ec0 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/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 24ade6c..5aa334c 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 + * + * It provides a way to solve a special varnode being managed by custom- + * scan provider. The callback informs the backend an expression tree + * which was replaced by a special varnode. + */ +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 d167b49..a44b4cd 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 0000000..1736d48 --- /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 39d2c10..b72e605 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 6584a2c..bc71fea 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -108,6 +108,7 @@ typedef enum NodeTag T_CteScanState, T_WorkTableScanState, T_ForeignScanState, + T_CustomScanState, T_JoinState, T_NestLoopState, T_MergeJoinState, diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index db02323..9dbb91c 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" @@ -487,12 +489,30 @@ typedef struct 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; + /* * ========== * Join nodes diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 3fdc2cb..c97c577 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);