doc/src/sgml/custom-plan.sgml | 315 ++++++++++++++++++++++++++++++++ doc/src/sgml/filelist.sgml | 1 + doc/src/sgml/postgres.sgml | 1 + src/backend/commands/explain.c | 45 ++++- src/backend/executor/Makefile | 2 +- src/backend/executor/execAmi.c | 23 +++ src/backend/executor/execProcnode.c | 15 ++ src/backend/executor/nodeCustom.c | 73 ++++++++ src/backend/nodes/copyfuncs.c | 42 +++++ src/backend/nodes/outfuncs.c | 40 ++++ src/backend/optimizer/path/allpaths.c | 34 ++-- src/backend/optimizer/path/joinpath.c | 16 ++ src/backend/optimizer/plan/createplan.c | 55 ++++-- src/backend/optimizer/plan/setrefs.c | 25 ++- src/backend/optimizer/plan/subselect.c | 128 +++++++------ src/backend/utils/adt/ruleutils.c | 56 ++++++ src/include/commands/explain.h | 1 + src/include/executor/nodeCustom.h | 30 +++ src/include/nodes/execnodes.h | 12 ++ src/include/nodes/nodes.h | 6 + src/include/nodes/plannodes.h | 77 ++++++++ src/include/nodes/relation.h | 29 +++ src/include/optimizer/paths.h | 17 ++ src/include/optimizer/planmain.h | 12 ++ src/include/optimizer/subselect.h | 7 + 25 files changed, 958 insertions(+), 104 deletions(-) diff --git a/doc/src/sgml/custom-plan.sgml b/doc/src/sgml/custom-plan.sgml new file mode 100644 index 0000000..8d456f9 --- /dev/null +++ b/doc/src/sgml/custom-plan.sgml @@ -0,0 +1,315 @@ + + + + Writing A Custom Plan Provider + + + custom plan + handler for + + + The custom-plan interface enables extensions to implement its custom + behavior, instead of built-in plan node, according to the cost based + optimizer manner. + Its key component is CustomPlan node that has usual + Plan field and a table of function-pointers; that performs + like methods of base class in object oriented programming language, + thus CustomPlan node works as a polymorphism plan / execution + node. + The core backend does not assume anything about behavior of this node + type, thus, note that it is responsibility of the custom-plan provider + to work its custom node as if the built-in plan / execution node being + replaced. + + + Overall steps to use this custom-plan interface is below. + + + Custom-plan provider can add CustomPath on a particular + relation scan using add_scan_path_hook or a particular + relations join using add_join_path_hook. + Then, the planner chooses the cheapest path towards a particular + scan or join in the built-in and custom paths. + So, CustomPath node has to have proper cost estimation + for right plan selection, no need to say. + + + Usually, custom-plan provider extends CustomPath type + to have its private fields, like: + +typedef struct { + CustomPath cpath; + : + List *somethin_private; + : +} YourOwnCustomPath; + + You can also extend CustomPlan and CustomPlanState + type with similar manner. + + + CustomPathMethods is table of function-pointers + for CustomPath, and CustomPlanMethods is + table of function-pointers for CustomPlan and + CustomPlanState. + Extension has to implement the functions according to the specification + in the next section. + + + + Specification of Custom Plan Interface + + Registration of custom-plan path + + The first task of custom-plan provide is to add CustomPath + towards a particular relation scan or relations join. + Right now, only scan and join are supported by planner thus cost-based + optimization shall be applied, however, other kind of nodes (like sort, + aggregate and so on...) are not supported. + + + +typedef void (*add_scan_path_hook_type)(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); +extern PGDLLIMPORT add_scan_path_hook_type add_scan_path_hook; + + Custom-plan provider can add its custom-path using + add_scan_path_hook to provide alternative way to scan + the relation being specified. + + + +typedef void (*add_join_path_hook_type)(PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + SpecialJoinInfo *sjinfo, + List *restrictlist, + Relids param_source_rels, + Relids extra_lateral_rels); +extern PGDLLIMPORT add_join_path_hook_type add_join_path_hook; + + Also, custom-plan provider can add its custom-path using + add_join_path_hook to provide alternative way to join + two relations (note that both or either of relations are also joined + relations, not only base relations) being specified. + + + + + Methods of CustomPath + + This section introduces the method functions of CustomPath. + + + +CustomPlan * +CreateCustomPlan(PlannerInfo *root, + CustomPath *custom_path); + + This method pupolates a node object that (at least) extends + CustomPlan data type, according to the supplied + CustomPath. + If this custom-plan support mark-and-restore position, its + node tag should be CustomPlanMarkPos, instead of + CustomPlan. + + + +void +TextOutCustomPath(StringInfo str, Node *node); + + This method is needed to support nodeToString for your + custom path type to dump its private fields also. + The message format has to follow the manner in outfuncs.c. + + + + Methods of CustomPlan + + This section introduces the method functions of CustomPlan. + + + +void +SetCustomPlanRef(PlannerInfo *root, + CustomPlan *custom_plan, + int rtoffset); + + This method requires custom-plan provides to adjust Var node + references in the supplied CustomPlan node. + Usually, it shall be shifted by rtoffset, or replaced by + INNER_VAR or OUTER_VAR if it references either + left or right subplan. + + + +bool +SupportBackwardScan(CustomPlan *custom_plan); + + This optional method informs the core backend whether this custom-plan + supports backward scan capability, or not. + If this method is implemented and returns true, it means + this custom-plan node supports backward scan. Elsewhere, it is not + available. + + + +void +FinalizeCustomPlan(PlannerInfo *root, + CustomPlan *custom_plan, + Bitmapset **paramids, + Bitmapset **valid_params, + Bitmapset **scan_params); + + This optional method informs the core backend which query parameters + are referenced in this custom-plan node, in addition to the ones + considered in the targetlist and qual fields + of the base Plan node. + If parameters are found in the private data field managed by custom- + plan provider, it needs to update the supplied bitmapset as expected + in the finalize_plan(). + + + +CustomPlanState * +BeginCustomPlan(CustomPlan *custom_plan, + EState *estate, + int eflags); + + This method populates a CustomPlanState object according to + the supplied CustomPlan, and initializes execution of this + custom-plan node, first of all. + + + +TupleTableSlot * +ExecCustomPlan(CustomPlanState *cpstate); + + It fetches one tuple from this custom-plan node. This custom-plan node + has to set a valid tuple on the ps_ResultTupleSlot and + return if any, or returns NULL to inform the upper node + it already reached end of the scan. + + + +Node * +MultiExecCustomPlan(CustomPlanState *cpstate); + + Unlike ExecCustomPlan, it allows upper node to fetch + multiple tuples, however, you need to pay attention the data format + and the way to return it because it fully depends on the type of + upper node. + + + +void +EndCustomPlan(CustomPlanState *cpstate); + + It ends the execution of this custom-plan node, and releases the + resources being allocated. Usually, it is not important to release + memory in the per execution memory context, so custom-plan provider + should be responsible to its own resources regardless of the framework. + + + +void +ReScanCustomPlan(CustomPlanState *cpstate); + + It restarts the current scan from the beginning. + Note that parameters of the scan depends on may change values, + so rewinded scan does not need to return exactly identical tuples. + + + +void +MarkPosCustomPlan(CustomPlanState *cpstate); + + It is optional, but should be implemented if CustomPlanMarkPos + was applied, instead of CustomPlan. + It saves the current position of the custom-plan on somewhere private + state, to restore the position later. + + + +void +RestrPosCustomPlan(CustomPlanState *cpstate); + + It is optional, but should be implemented if CustomPlanMarkPos + was applied, instead of CustomPlan. + It restores the current position of the custom-plan from the private + information being saved somewhere at MarkPosCustomPlan. + + + +void +ExplainCustomPlanTargetRel(CustomPlanState *cpstate, + ExplainState *es); + + It shows the target relation, if this custom-plan node replaced + a particular relation scan. Because of implementation reason, this + method is separated from the ExplainCustomPlan. + + + +void +ExplainCustomPlan(CustomPlanState *cpstate, + List *ancestors, + ExplainState *es); + + It put properties of this custom-plan node into the supplied + ExplainState according to the usual EXPLAIN + manner. + + + +Bitmapset * +GetRelidsCustomPlan(CustomPlanState *cpstate); + + It returns a set of range-table indexes being scanned by this custom- + plan node. In case of multiple relations are underlying, it is not + always singleton bitmap. + + + +Node * +GetSpecialCustomVar(CustomPlanState *cpstate, + Var *varnode); + + This optional method returns an expression node to be referenced by + the supplied varnode that has special varno + (INNER_VAR, OUTER_VAR or INDEX_VAR). + EXPLAIN command shows column name being referenced in the + targetlist or qualifiers of plan nodes. If a var node has special + varno, it recursively walks down the underlying subplan to + ensure the actual expression referenced by this special varno. + In case when a custom-plan node replaced a join node but does not have + underlying sub-plan on the left- and right-tree, it is unavailable to + use a usual logic, so custom-plan provider has to implement this method + to inform the core backend the expression node being referenced by + the supplied varnode that has special varno. + If this method is not implemented or returns NULL, + the core backend solves the special varnode reference as usual. + + + +void +TextOutCustomPlan(StringInfo str, const CustomPlan *node); + + This method is needed to support nodeToString for your + custom plan type to dump its private fields also. + The message format has to follow the manner in outfuncs.c. + + + +CustomPlan * +CopyCustomPlan(const CustomPlan *from); + + This methos is needed to support copyObject for your + custom plan type to copy its private fields also. + + + + diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml index ab6fcf7..f3b8362 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 08f3167..ff9fc7b 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -19,6 +19,7 @@ #include "commands/defrem.h" #include "commands/prepare.h" #include "executor/hashjoin.h" +#include "executor/nodeCustom.h" #include "foreign/fdwapi.h" #include "optimizer/clauses.h" #include "parser/parsetree.h" @@ -51,7 +52,6 @@ static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es, static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es); static double elapsed_time(instr_time *starttime); -static void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used); static void ExplainPreScanMemberNodes(List *plans, PlanState **planstates, Bitmapset **rels_used); static void ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used); @@ -700,7 +700,7 @@ elapsed_time(instr_time *starttime) * This ensures that we don't confusingly assign un-suffixed aliases to RTEs * that never appear in the EXPLAIN output (such as inheritance parents). */ -static void +void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used) { Plan *plan = planstate->plan; @@ -721,6 +721,16 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used) *rels_used = bms_add_member(*rels_used, ((Scan *) plan)->scanrelid); break; + case T_CustomPlan: + case T_CustomPlanMarkPos: + { + CustomPlanState *cpstate = (CustomPlanState *)planstate; + Bitmapset *temp + = cpstate->methods->GetRelidsCustomPlan(cpstate); + + *rels_used = bms_union(*rels_used, temp); + } + break; case T_ModifyTable: /* cf ExplainModifyTarget */ *rels_used = bms_add_member(*rels_used, @@ -847,6 +857,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; @@ -935,6 +946,14 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_ForeignScan: pname = sname = "Foreign Scan"; break; + case T_CustomPlan: + sname = "Custom"; + custom_name = ((CustomPlan *) plan)->methods->CustomName; + if (custom_name != NULL) + pname = psprintf("Custom (%s)", custom_name); + else + pname = sname; + break; case T_Material: pname = sname = "Materialize"; break; @@ -1036,6 +1055,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", custom_name, es); } switch (nodeTag(plan)) @@ -1051,6 +1072,14 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_ForeignScan: ExplainScanTarget((Scan *) plan, es); break; + case T_CustomPlan: + { + CustomPlanState *cps = (CustomPlanState *)planstate; + + if (cps->methods->ExplainCustomPlanTargetRel) + cps->methods->ExplainCustomPlanTargetRel(cps, es); + } + break; case T_IndexScan: { IndexScan *indexscan = (IndexScan *) plan; @@ -1347,6 +1376,18 @@ ExplainNode(PlanState *planstate, List *ancestors, planstate, es); show_foreignscan_info((ForeignScanState *) planstate, es); break; + case T_CustomPlan: + show_scan_qual(plan->qual, "Filter", planstate, ancestors, es); + if (plan->qual) + show_instrumentation_count("Rows Removed by Filter", 1, + planstate, es); + if (((CustomPlanState *) planstate)->methods->ExplainCustomPlan) + { + CustomPlanState *cpstate = (CustomPlanState *) planstate; + + cpstate->methods->ExplainCustomPlan(cpstate, 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..4dece5a 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -15,7 +15,7 @@ include $(top_builddir)/src/Makefile.global 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 \ + nodeBitmapAnd.o nodeBitmapOr.o nodeCustom.o \ nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \ nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \ nodeLimit.o nodeLockRows.o \ diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index 8c01a63..47e7a3c 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" @@ -197,6 +198,10 @@ ExecReScan(PlanState *node) ExecReScanForeignScan((ForeignScanState *) node); break; + case T_CustomPlanState: + ExecReScanCustomPlan((CustomPlanState *) node); + break; + case T_NestLoopState: ExecReScanNestLoop((NestLoopState *) node); break; @@ -291,6 +296,10 @@ ExecMarkPos(PlanState *node) ExecValuesMarkPos((ValuesScanState *) node); break; + case T_CustomPlanState: + ExecCustomMarkPos((CustomPlanState *) node); + break; + case T_MaterialState: ExecMaterialMarkPos((MaterialState *) node); break; @@ -348,6 +357,10 @@ ExecRestrPos(PlanState *node) ExecValuesRestrPos((ValuesScanState *) node); break; + case T_CustomPlanState: + ExecCustomRestrPos((CustomPlanState *) node); + break; + case T_MaterialState: ExecMaterialRestrPos((MaterialState *) node); break; @@ -390,6 +403,7 @@ ExecSupportsMarkRestore(NodeTag plantype) case T_ValuesScan: case T_Material: case T_Sort: + case T_CustomPlanMarkPos: return true; case T_Result: @@ -465,6 +479,15 @@ ExecSupportsBackwardScan(Plan *node) return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) && TargetListSupportsBackwardScan(node->targetlist); + case T_CustomPlan: + { + CustomPlan *cplan = (CustomPlan *) node; + + if (cplan->methods->SupportBackwardScan) + return cplan->methods->SupportBackwardScan(cplan); + } + 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 c5ecd18..5aa117b 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,12 @@ ExecInitNode(Plan *node, EState *estate, int eflags) estate, eflags); break; + case T_CustomPlan: + case T_CustomPlanMarkPos: + result = (PlanState *) ExecInitCustomPlan((CustomPlan *) node, + estate, eflags); + break; + /* * join nodes */ @@ -442,6 +449,10 @@ ExecProcNode(PlanState *node) result = ExecForeignScan((ForeignScanState *) node); break; + case T_CustomPlanState: + result = ExecCustomPlan((CustomPlanState *) node); + break; + /* * join nodes */ @@ -678,6 +689,10 @@ ExecEndNode(PlanState *node) ExecEndForeignScan((ForeignScanState *) node); break; + case T_CustomPlanState: + ExecEndCustomPlan((CustomPlanState *) node); + break; + /* * join nodes */ diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c new file mode 100644 index 0000000..e3c8f58 --- /dev/null +++ b/src/backend/executor/nodeCustom.c @@ -0,0 +1,73 @@ +/* ------------------------------------------------------------------------ + * + * nodeCustom.c + * Routines to handle execution of custom plan node + * + * Portions Copyright (c) 1996-2013, 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" + +CustomPlanState * +ExecInitCustomPlan(CustomPlan *custom_plan, EState *estate, int eflags) +{ + CustomPlanState *cpstate + = custom_plan->methods->BeginCustomPlan(custom_plan, estate, eflags); + + Assert(IsA(cpstate, CustomPlanState)); + + return cpstate; +} + +TupleTableSlot * +ExecCustomPlan(CustomPlanState *cpstate) +{ + Assert(cpstate->methods->ExecCustomPlan != NULL); + return cpstate->methods->ExecCustomPlan(cpstate); +} + +Node * +MultiExecCustomPlan(CustomPlanState *cpstate) +{ + Assert(cpstate->methods->MultiExecCustomPlan != NULL); + return cpstate->methods->MultiExecCustomPlan(cpstate); +} + +void +ExecEndCustomPlan(CustomPlanState *cpstate) +{ + Assert(cpstate->methods->EndCustomPlan != NULL); + cpstate->methods->EndCustomPlan(cpstate); +} + +void +ExecReScanCustomPlan(CustomPlanState *cpstate) +{ + Assert(cpstate->methods->ReScanCustomPlan != NULL); + cpstate->methods->ReScanCustomPlan(cpstate); +} + +void +ExecCustomMarkPos(CustomPlanState *cpstate) +{ + Assert(cpstate->methods->MarkPosCustomPlan != NULL); + cpstate->methods->MarkPosCustomPlan(cpstate); +} + +void +ExecCustomRestrPos(CustomPlanState *cpstate) +{ + Assert(cpstate->methods->RestrPosCustomPlan != NULL); + cpstate->methods->RestrPosCustomPlan(cpstate); +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index c89d808..18505cd 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -598,6 +598,42 @@ _copyForeignScan(const ForeignScan *from) } /* + * _copyCustomPlan + */ +static CustomPlan * +_copyCustomPlan(const CustomPlan *from) +{ + CustomPlan *newnode = from->methods->CopyCustomPlan(from); + + Assert(IsA(newnode, CustomPlan)); + return newnode; +} + +/* + * _copyCustomPlanMarkPos + */ +static CustomPlanMarkPos * +_copyCustomPlanMarkPos(const CustomPlanMarkPos *from) +{ + CustomPlanMarkPos *newnode = from->methods->CopyCustomPlan(from); + + Assert(IsA(newnode, CustomPlanMarkPos)); + return newnode; +} + +/* copy common part of CustomPlan */ +void +CopyCustomPlanCommon(const Node *__from, Node *__newnode) +{ + CustomPlan *from = (CustomPlan *) __from; + CustomPlan *newnode = (CustomPlan *) __newnode; + + ((Node *) newnode)->type = nodeTag(from); + CopyPlanFields((const Plan *) from, (Plan *) newnode); + COPY_SCALAR_FIELD(methods); +} + +/* * CopyJoinFields * * This function copies the fields of the Join node. It is used by @@ -3983,6 +4019,12 @@ copyObject(const void *from) case T_ForeignScan: retval = _copyForeignScan(from); break; + case T_CustomPlan: + retval = _copyCustomPlan(from); + break; + case T_CustomPlanMarkPos: + retval = _copyCustomPlanMarkPos(from); + break; case T_Join: retval = _copyJoin(from); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index bfb4b9f..8a93bc5 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -563,6 +563,27 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_BOOL_FIELD(fsSystemCol); } +/* dump common part of CustomPlan structure */ +static void +_outCustomPlan(StringInfo str, const CustomPlan *node) +{ + WRITE_NODE_TYPE("CUSTOMPLAN"); + _outPlanInfo(str, (const Plan *) node); + appendStringInfo(str, " :methods"); + _outToken(str, node->methods->CustomName); + node->methods->TextOutCustomPlan(str, node); +} + +static void +_outCustomPlanMarkPos(StringInfo str, const CustomPlanMarkPos *node) +{ + WRITE_NODE_TYPE("CUSTOMPLANMARKPOS"); + _outPlanInfo(str, (const Plan *) node); + appendStringInfo(str, " :methods"); + _outToken(str, node->methods->CustomName); + node->methods->TextOutCustomPlan(str, node); +} + static void _outJoin(StringInfo str, const Join *node) { @@ -1581,6 +1602,16 @@ _outForeignPath(StringInfo str, const ForeignPath *node) } static void +_outCustomPath(StringInfo str, const CustomPath *node) +{ + WRITE_NODE_TYPE("CUSTOMPATH"); + _outPathInfo(str, (const Path *) node); + appendStringInfo(str, " :methods"); + _outToken(str, node->methods->CustomName); + node->methods->TextOutCustomPath(str, (Node *)node); +} + +static void _outAppendPath(StringInfo str, const AppendPath *node) { WRITE_NODE_TYPE("APPENDPATH"); @@ -2828,6 +2859,12 @@ _outNode(StringInfo str, const void *obj) case T_ForeignScan: _outForeignScan(str, obj); break; + case T_CustomPlan: + _outCustomPlan(str, obj); + break; + case T_CustomPlanMarkPos: + _outCustomPlanMarkPos(str, obj); + break; case T_Join: _outJoin(str, obj); break; @@ -3036,6 +3073,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 03be7b1..6c1ea7e 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -47,6 +47,8 @@ int geqo_threshold; /* Hook for plugins to replace standard_join_search() */ join_search_hook_type join_search_hook = NULL; +/* Hook for plugins to add custom scan paths */ +add_scan_path_hook_type add_scan_path_hook = NULL; static void set_base_rel_sizes(PlannerInfo *root); static void set_base_rel_pathlists(PlannerInfo *root); @@ -323,7 +325,7 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, } break; case RTE_SUBQUERY: - /* Subquery --- fully handled during set_rel_size */ + /* Subquery --- path was added during set_rel_size */ break; case RTE_FUNCTION: /* RangeFunction */ @@ -334,12 +336,19 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, set_values_pathlist(root, rel, rte); break; case RTE_CTE: - /* CTE reference --- fully handled during set_rel_size */ + /* CTE reference --- path was added during set_rel_size */ break; default: elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind); break; } + + /* Also, consider custom plans */ + if (add_scan_path_hook) + (*add_scan_path_hook)(root, rel, rte); + + /* Select cheapest path */ + set_cheapest(rel); } #ifdef OPTIMIZER_DEBUG @@ -388,9 +397,6 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Consider TID scans */ create_tidscan_paths(root, rel); - - /* Now find the cheapest of the paths for this rel */ - set_cheapest(rel); } /* @@ -416,9 +422,6 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { /* Call the FDW's GetForeignPaths function to generate path(s) */ rel->fdwroutine->GetForeignPaths(root, rel, rte->relid); - - /* Select cheapest path */ - set_cheapest(rel); } /* @@ -1235,9 +1238,6 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, /* Generate appropriate path */ add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer)); - - /* Select cheapest path (pretty easy in this case...) */ - set_cheapest(rel); } /* @@ -1306,9 +1306,6 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_functionscan_path(root, rel, pathkeys, required_outer)); - - /* Select cheapest path (pretty easy in this case...) */ - set_cheapest(rel); } /* @@ -1329,9 +1326,6 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_valuesscan_path(root, rel, required_outer)); - - /* Select cheapest path (pretty easy in this case...) */ - set_cheapest(rel); } /* @@ -1398,9 +1392,6 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_ctescan_path(root, rel, required_outer)); - - /* Select cheapest path (pretty easy in this case...) */ - set_cheapest(rel); } /* @@ -1451,9 +1442,6 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) /* Generate appropriate path */ add_path(rel, create_worktablescan_path(root, rel, required_outer)); - - /* Select cheapest path (pretty easy in this case...) */ - set_cheapest(rel); } /* diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index a996116..2fb6678 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -21,6 +21,8 @@ #include "optimizer/pathnode.h" #include "optimizer/paths.h" +/* Hook for plugins to add custom join paths */ +add_join_path_hook_type add_join_path_hook = NULL; #define PATH_PARAM_BY_REL(path, rel) \ ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids)) @@ -259,6 +261,20 @@ add_paths_to_joinrel(PlannerInfo *root, restrictlist, jointype, sjinfo, &semifactors, param_source_rels, extra_lateral_rels); + + /* + * 5. Also consider paths being provided with custom execution provider. + */ + if (add_join_path_hook) + (*add_join_path_hook)(root, + joinrel, + outerrel, + innerrel, + jointype, + sjinfo, + restrictlist, + param_source_rels, + extra_lateral_rels); } /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 184d37a..055a818 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -42,11 +42,7 @@ #include "utils/lsyscache.h" -static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path); static Plan *create_scan_plan(PlannerInfo *root, Path *best_path); -static List *build_path_tlist(PlannerInfo *root, Path *path); -static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel); -static void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path); static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals); static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path); static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path); @@ -77,23 +73,20 @@ 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 CustomPlan *create_custom_plan(PlannerInfo *root, + CustomPath *best_path); 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); static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path); static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path); static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol); -static List *get_switched_clauses(List *clauses, Relids outerrelids); -static List *order_qual_clauses(PlannerInfo *root, List *clauses); -static void copy_path_costsize(Plan *dest, Path *src); -static void copy_plan_costsize(Plan *dest, Plan *src); static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, Oid indexid, List *indexqual, List *indexqualorig, @@ -215,7 +208,7 @@ create_plan(PlannerInfo *root, Path *best_path) * create_plan_recurse * Recursive guts of create_plan(). */ -static Plan * +Plan * create_plan_recurse(PlannerInfo *root, Path *best_path) { Plan *plan; @@ -261,6 +254,9 @@ create_plan_recurse(PlannerInfo *root, Path *best_path) plan = create_unique_plan(root, (UniquePath *) best_path); break; + case T_CustomPlan: + plan = (Plan *) create_custom_plan(root, (CustomPath *) best_path); + break; default: elog(ERROR, "unrecognized node type: %d", (int) best_path->pathtype); @@ -430,7 +426,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path) /* * Build a target list (ie, a list of TargetEntry) for the Path's output. */ -static List * +List * build_path_tlist(PlannerInfo *root, Path *path) { RelOptInfo *rel = path->parent; @@ -466,7 +462,7 @@ build_path_tlist(PlannerInfo *root, Path *path) * Decide whether to use a tlist matching relation structure, * rather than only those Vars actually referenced. */ -static bool +bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel) { int i; @@ -526,7 +522,7 @@ use_physical_tlist(PlannerInfo *root, RelOptInfo *rel) * undo the decision made by use_physical_tlist(). Currently, Hash, Sort, * and Material nodes want this, so they don't have to store useless columns. */ -static void +void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path) { /* Only need to undo it for path types handled by create_scan_plan() */ @@ -569,7 +565,7 @@ disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path) * in most cases we have only a very bad idea of the probability of the gating * qual being true. */ -static Plan * +Plan * create_gating_plan(PlannerInfo *root, Plan *plan, List *quals) { List *pseudoconstants; @@ -1072,6 +1068,26 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) return plan; } +/* + * create_custom_plan + * Returns a custom-scan plan for the base relation scanned by 'best_path' + * with restriction clauses 'scan_clauses' and targetlist 'tlist'. + */ +static CustomPlan * +create_custom_plan(PlannerInfo *root, CustomPath *best_path) +{ + CustomPlan *cplan; + + /* Populate CustomPlan according to the CustomPath */ + Assert(best_path->methods->CreateCustomPlan != NULL); + cplan = best_path->methods->CreateCustomPlan(root, best_path); + Assert(IsA(cplan, CustomPlan) || IsA(cplan, CustomPlanMarkPos)); + + /* Copy cost data from Path to Plan; no need to make callback do this */ + copy_path_costsize(&cplan->plan, &best_path->path); + + return cplan; +} /***************************************************************************** * @@ -2006,7 +2022,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, return scan_plan; } - /***************************************************************************** * * JOIN METHODS @@ -2540,7 +2555,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 */ @@ -3023,7 +3038,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol) * touched; a modified list is returned. We do, however, set the transient * outer_is_left field in each RestrictInfo to show which side was which. */ -static List * +List * get_switched_clauses(List *clauses, Relids outerrelids) { List *t_list = NIL; @@ -3089,7 +3104,7 @@ get_switched_clauses(List *clauses, Relids outerrelids) * instead of bare clauses. It's OK because we only sort by cost, but * a cost/selectivity combination would likely do the wrong thing. */ -static List * +List * order_qual_clauses(PlannerInfo *root, List *clauses) { typedef struct @@ -3156,7 +3171,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses) * Copy cost and size info from a Path node to the Plan node created from it. * The executor usually won't use this info, but it's needed by EXPLAIN. */ -static void +void copy_path_costsize(Plan *dest, Path *src) { if (src) @@ -3179,7 +3194,7 @@ copy_path_costsize(Plan *dest, Path *src) * Copy cost and size info from a lower plan node to an inserted node. * (Most callers alter the info after copying it.) */ -static void +void copy_plan_costsize(Plan *dest, Plan *src) { if (src) diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 46affe7..e0fd9a2 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -17,6 +17,7 @@ #include "access/transam.h" #include "catalog/pg_type.h" +#include "executor/nodeCustom.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/pathnode.h" @@ -86,7 +87,6 @@ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); -static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, IndexOnlyScan *plan, int rtoffset); @@ -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); @@ -419,7 +418,7 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) /* * set_plan_refs: recurse through the Plan nodes of a single subquery level */ -static Plan * +Plan * set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) { ListCell *l; @@ -576,6 +575,22 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) } break; + case T_CustomPlan: + case T_CustomPlanMarkPos: + { + CustomPlan *cplan = (CustomPlan *) plan; + + /* + * Extension is responsible to handle set-reference + * correctly. + */ + Assert(cplan->methods->SetCustomPlanRef != NULL); + cplan->methods->SetCustomPlanRef(root, + cplan, + rtoffset); + } + break; + case T_NestLoop: case T_MergeJoin: case T_HashJoin: @@ -1057,7 +1072,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 */ @@ -1126,7 +1141,7 @@ fix_expr_common(PlannerInfo *root, Node *node) * 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 a3f3583..6b0c762 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -75,12 +75,8 @@ static Query *convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect, static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root); static Node *process_sublinks_mutator(Node *node, process_sublinks_context *context); -static Bitmapset *finalize_plan(PlannerInfo *root, - Plan *plan, - Bitmapset *valid_params, - Bitmapset *scan_params); -static bool finalize_primnode(Node *node, finalize_primnode_context *context); - +static bool finalize_primnode_walker(Node *node, + finalize_primnode_context *context); /* * Select a PARAM_EXEC number to identify the given Var as a parameter for @@ -2045,7 +2041,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans) * The return value is the computed allParam set for the given Plan node. * This is just an internal notational convenience. */ -static Bitmapset * +Bitmapset * finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, Bitmapset *scan_params) { @@ -2070,15 +2066,15 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, */ /* Find params in targetlist and qual */ - finalize_primnode((Node *) plan->targetlist, &context); - finalize_primnode((Node *) plan->qual, &context); + finalize_primnode_walker((Node *) plan->targetlist, &context); + finalize_primnode_walker((Node *) plan->qual, &context); /* Check additional node-type-specific fields */ switch (nodeTag(plan)) { case T_Result: - finalize_primnode(((Result *) plan)->resconstantqual, - &context); + finalize_primnode_walker(((Result *) plan)->resconstantqual, + &context); break; case T_SeqScan: @@ -2086,10 +2082,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_IndexScan: - finalize_primnode((Node *) ((IndexScan *) plan)->indexqual, - &context); - finalize_primnode((Node *) ((IndexScan *) plan)->indexorderby, - &context); + finalize_primnode_walker((Node *)((IndexScan *)plan)->indexqual, + &context); + finalize_primnode_walker((Node *)((IndexScan *)plan)->indexorderby, + &context); /* * we need not look at indexqualorig, since it will have the same @@ -2100,10 +2096,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_IndexOnlyScan: - finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexqual, - &context); - finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexorderby, - &context); + finalize_primnode_walker((Node *)((IndexOnlyScan *) plan)->indexqual, + &context); + finalize_primnode_walker((Node *) ((IndexOnlyScan *) plan)->indexorderby, + &context); /* * we need not look at indextlist, since it cannot contain Params. @@ -2112,8 +2108,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_BitmapIndexScan: - finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual, - &context); + finalize_primnode_walker((Node *) ((BitmapIndexScan *) plan)->indexqual, + &context); /* * we need not look at indexqualorig, since it will have the same @@ -2122,14 +2118,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_BitmapHeapScan: - finalize_primnode((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig, - &context); + finalize_primnode_walker((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig, + &context); context.paramids = bms_add_members(context.paramids, scan_params); break; case T_TidScan: - finalize_primnode((Node *) ((TidScan *) plan)->tidquals, - &context); + finalize_primnode_walker((Node *) ((TidScan *) plan)->tidquals, + &context); context.paramids = bms_add_members(context.paramids, scan_params); break; @@ -2167,7 +2163,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, funccontext = context; funccontext.paramids = NULL; - finalize_primnode(rtfunc->funcexpr, &funccontext); + finalize_primnode_walker(rtfunc->funcexpr, &funccontext); /* remember results for execution */ rtfunc->funcparams = funccontext.paramids; @@ -2183,8 +2179,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_ValuesScan: - finalize_primnode((Node *) ((ValuesScan *) plan)->values_lists, - &context); + finalize_primnode_walker((Node *) ((ValuesScan *) plan)->values_lists, + &context); context.paramids = bms_add_members(context.paramids, scan_params); break; @@ -2231,11 +2227,24 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_ForeignScan: - finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs, - &context); + finalize_primnode_walker((Node *)((ForeignScan *) plan)->fdw_exprs, + &context); context.paramids = bms_add_members(context.paramids, scan_params); break; + case T_CustomPlan: + { + CustomPlan *cplan = (CustomPlan *) plan; + + if (cplan->methods->FinalizeCustomPlan) + cplan->methods->FinalizeCustomPlan(root, + cplan, + &context.paramids, + &valid_params, + &scan_params); + } + break; + case T_ModifyTable: { ModifyTable *mtplan = (ModifyTable *) plan; @@ -2247,8 +2256,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, locally_added_param); scan_params = bms_add_member(bms_copy(scan_params), locally_added_param); - finalize_primnode((Node *) mtplan->returningLists, - &context); + finalize_primnode_walker((Node *) mtplan->returningLists, + &context); foreach(l, mtplan->plans) { context.paramids = @@ -2329,8 +2338,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, { ListCell *l; - finalize_primnode((Node *) ((Join *) plan)->joinqual, - &context); + finalize_primnode_walker((Node *) ((Join *) plan)->joinqual, + &context); /* collect set of params that will be passed to right child */ foreach(l, ((NestLoop *) plan)->nestParams) { @@ -2343,24 +2352,24 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_MergeJoin: - finalize_primnode((Node *) ((Join *) plan)->joinqual, - &context); - finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses, - &context); + finalize_primnode_walker((Node *) ((Join *) plan)->joinqual, + &context); + finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses, + &context); break; case T_HashJoin: - finalize_primnode((Node *) ((Join *) plan)->joinqual, - &context); - finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses, + finalize_primnode_walker((Node *) ((Join *) plan)->joinqual, + &context); + finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses, &context); break; case T_Limit: - finalize_primnode(((Limit *) plan)->limitOffset, - &context); - finalize_primnode(((Limit *) plan)->limitCount, - &context); + finalize_primnode_walker(((Limit *) plan)->limitOffset, + &context); + finalize_primnode_walker(((Limit *) plan)->limitCount, + &context); break; case T_RecursiveUnion: @@ -2381,10 +2390,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_WindowAgg: - finalize_primnode(((WindowAgg *) plan)->startOffset, - &context); - finalize_primnode(((WindowAgg *) plan)->endOffset, - &context); + finalize_primnode_walker(((WindowAgg *) plan)->startOffset, + &context); + finalize_primnode_walker(((WindowAgg *) plan)->endOffset, + &context); break; case T_Hash: @@ -2473,8 +2482,21 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, * finalize_primnode: add IDs of all PARAM_EXEC params appearing in the given * expression tree to the result set. */ +Bitmapset * +finalize_primnode(PlannerInfo *root, Node *node, Bitmapset *paramids) +{ + finalize_primnode_context context; + + context.root = root; + context.paramids = paramids; + + finalize_primnode_walker(node, &context); + + return context.paramids; +} + static bool -finalize_primnode(Node *node, finalize_primnode_context *context) +finalize_primnode_walker(Node *node, finalize_primnode_context *context) { if (node == NULL) return false; @@ -2496,7 +2518,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context) Bitmapset *subparamids; /* Recurse into the testexpr, but not into the Plan */ - finalize_primnode(subplan->testexpr, context); + finalize_primnode_walker(subplan->testexpr, context); /* * Remove any param IDs of output parameters of the subplan that were @@ -2513,7 +2535,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context) } /* Also examine args list */ - finalize_primnode((Node *) subplan->args, context); + finalize_primnode_walker((Node *) subplan->args, context); /* * Add params needed by the subplan to paramids, but excluding those @@ -2528,7 +2550,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context) return false; /* no more to do here */ } - return expression_tree_walker(node, finalize_primnode, + return expression_tree_walker(node, finalize_primnode_walker, (void *) context); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 566b4c9..934d796 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -5292,6 +5292,25 @@ get_utility_query_def(Query *query, deparse_context *context) } } +/* + * GetSpecialCustomVar + * + * Utility routine to call optional GetSpecialCustomVar method of + * CustomPlanState + */ +static Node * +GetSpecialCustomVar(PlanState *ps, Var *varnode) +{ + CustomPlanState *cps = (CustomPlanState *) ps; + + Assert(IsA(ps, CustomPlanState)); + Assert(IS_SPECIAL_VARNO(varnode->varno)); + + if (cps->methods->GetSpecialCustomVar) + return (Node *)cps->methods->GetSpecialCustomVar(cps, varnode); + + return NULL; +} /* * Display a Var appropriately. @@ -5323,6 +5342,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) deparse_columns *colinfo; char *refname; char *attname; + Node *expr; /* Find appropriate nesting depth */ netlevelsup = var->varlevelsup + levelsup; @@ -5345,6 +5365,22 @@ 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, CustomPlanState) && + (expr = GetSpecialCustomVar(dpns->planstate, var)) != NULL) + { + /* + * 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, ')'); + + return NULL; + } else if (var->varno == OUTER_VAR && dpns->outer_tlist) { TargetEntry *tle; @@ -5633,6 +5669,26 @@ 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, CustomPlanState) && + (expr = GetSpecialCustomVar(dpns->planstate, var)) != NULL) + { + StringInfo saved = context->buf; + StringInfoData temp; + + initStringInfo(&temp); + context->buf = &temp; + + if (!IsA(expr, Var)) + appendStringInfoChar(context->buf, '('); + get_rule_expr((Node *) expr, context, true); + if (!IsA(expr, Var)) + appendStringInfoChar(context->buf, ')'); + + context->buf = saved; + + return temp.data; + } else if (var->varno == OUTER_VAR && dpns->outer_tlist) { TargetEntry *tle; diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index 3488be3..f914696 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -54,6 +54,7 @@ extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook; typedef const char *(*explain_get_index_name_hook_type) (Oid indexId); extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook; +extern void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used); extern void ExplainQuery(ExplainStmt *stmt, const char *queryString, ParamListInfo params, DestReceiver *dest); diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h new file mode 100644 index 0000000..e6e049e --- /dev/null +++ b/src/include/executor/nodeCustom.h @@ -0,0 +1,30 @@ +/* ------------------------------------------------------------------------ + * + * nodeCustom.h + * + * prototypes for CustomScan nodes + * + * Portions Copyright (c) 1996-2013, 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 CustomPlanState *ExecInitCustomPlan(CustomPlan *custom_plan, + EState *estate, int eflags); +extern TupleTableSlot *ExecCustomPlan(CustomPlanState *cpstate); +extern Node *MultiExecCustomPlan(CustomPlanState *cpstate); +extern void ExecEndCustomPlan(CustomPlanState *cpstate); + +extern void ExecReScanCustomPlan(CustomPlanState *cpstate); +extern void ExecCustomMarkPos(CustomPlanState *cpstate); +extern void ExecCustomRestrPos(CustomPlanState *cpstate); + +#endif /* NODECUSTOM_H */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index a301a08..8af5bf2 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1501,6 +1501,18 @@ typedef struct ForeignScanState void *fdw_state; /* foreign-data wrapper can keep state here */ } ForeignScanState; +/* ---------------- + * CustomPlanState information + * + * CustomPlan nodes are used to execute custom code within executor. + * ---------------- + */ +typedef struct CustomPlanState +{ + PlanState ps; + const CustomPlanMethods *methods; +} CustomPlanState; + /* ---------------------------------------------------------------- * Join State Information * ---------------------------------------------------------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 5b8df59..f4a1246 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -62,6 +62,8 @@ typedef enum NodeTag T_CteScan, T_WorkTableScan, T_ForeignScan, + T_CustomPlan, + T_CustomPlanMarkPos, T_Join, T_NestLoop, T_MergeJoin, @@ -107,6 +109,7 @@ typedef enum NodeTag T_CteScanState, T_WorkTableScanState, T_ForeignScanState, + T_CustomPlanState, T_JoinState, T_NestLoopState, T_MergeJoinState, @@ -224,6 +227,7 @@ typedef enum NodeTag T_HashPath, T_TidPath, T_ForeignPath, + T_CustomPath, T_AppendPath, T_MergeAppendPath, T_ResultPath, @@ -513,6 +517,8 @@ extern void *stringToNode(char *str); */ extern void *copyObject(const void *obj); +extern void CopyCustomPlanCommon(const Node *from, Node *newnode); + /* * nodes/equalfuncs.c */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 38c039c..7468d4c 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" /* ---------------------------------------------------------------- @@ -479,6 +481,81 @@ typedef struct ForeignScan bool fsSystemCol; /* true if any "system column" is needed */ } ForeignScan; +/* ---------------- + * CustomPlan node + * ---------------- + */ +struct CustomPlanMethods; + +typedef struct CustomPlan +{ + Plan plan; + const struct CustomPlanMethods *methods; +} CustomPlan; + +/* almost same to CustomPlan, but support MarkPos/RestorePos */ +typedef CustomPlan CustomPlanMarkPos; + +/* not to include execnodes.h here */ +typedef struct CustomPlanState CustomPlanState; +typedef struct EState EState; +typedef struct ExplainState ExplainState; +typedef struct TupleTableSlot TupleTableSlot; + +typedef void (*SetCustomPlanRef_function)(PlannerInfo *root, + CustomPlan *custom_plan, + int rtoffset); +typedef bool (*SupportCustomBackwardScan_function)(CustomPlan *custom_plan); +typedef void (*FinalizeCustomPlan_function)(PlannerInfo *root, + CustomPlan *custom_plan, + Bitmapset **paramids, + Bitmapset **valid_params, + Bitmapset **scan_params); +typedef CustomPlanState *(*BeginCustomPlan_function)(CustomPlan *custom_plan, + EState *estate, + int eflags); +typedef TupleTableSlot *(*ExecCustomPlan_function)(CustomPlanState *cpstate); +typedef Node *(*MultiExecCustomPlan_function)(CustomPlanState *cpstate); +typedef void (*EndCustomPlan_function)(CustomPlanState *cpstate); +typedef void (*ReScanCustomPlan_function)(CustomPlanState *cpstate); +typedef void (*MarkPosCustomPlan_function)(CustomPlanState *cpstate); +typedef void (*RestrPosCustomPlan_function)(CustomPlanState *cpstate); +typedef void (*ExplainCustomPlanTargetRel_function)(CustomPlanState *cpstate, + ExplainState *es); +typedef void (*ExplainCustomPlan_function)(CustomPlanState *cpstate, + List *ancestors, + ExplainState *es); +typedef Bitmapset *(*GetRelidsCustomPlan_function)(CustomPlanState *cpstate); +typedef Node *(*GetSpecialCustomVar_function)(CustomPlanState *cpstate, + Var *varnode); +typedef void (*TextOutCustomPlan_function)(StringInfo str, + const CustomPlan *node); +typedef CustomPlan *(*CopyCustomPlan_function)(const CustomPlan *from); + +typedef struct CustomPlanMethods +{ + const char *CustomName; + /* callbacks for the planner stage */ + SetCustomPlanRef_function SetCustomPlanRef; + SupportCustomBackwardScan_function SupportBackwardScan; + FinalizeCustomPlan_function FinalizeCustomPlan; + /* callbacks for the executor stage */ + BeginCustomPlan_function BeginCustomPlan; + ExecCustomPlan_function ExecCustomPlan; + MultiExecCustomPlan_function MultiExecCustomPlan; + EndCustomPlan_function EndCustomPlan; + ReScanCustomPlan_function ReScanCustomPlan; + MarkPosCustomPlan_function MarkPosCustomPlan; + RestrPosCustomPlan_function RestrPosCustomPlan; + /* callbacks for EXPLAIN */ + ExplainCustomPlanTargetRel_function ExplainCustomPlanTargetRel; + ExplainCustomPlan_function ExplainCustomPlan; + GetRelidsCustomPlan_function GetRelidsCustomPlan; + GetSpecialCustomVar_function GetSpecialCustomVar; + /* callbacks for general node management */ + TextOutCustomPlan_function TextOutCustomPlan; + CopyCustomPlan_function CopyCustomPlan; +} CustomPlanMethods; /* * ========== diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index c607b36..cbbf1e0 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" @@ -878,6 +879,34 @@ typedef struct ForeignPath } ForeignPath; /* + * CustomPath represents a scan using custom logic + * + * custom_flags is a set of CUSTOM_* bits to control its behavior. + * custom_methods is a set of function pointers that are declared in + * CustomPathMethods structure; extension has to set up correctly. + */ +struct CustomPathMethods; + +typedef struct CustomPath +{ + Path path; + const struct CustomPathMethods *methods; +} CustomPath; + +typedef struct CustomPlan CustomPlan; + +typedef CustomPlan *(*CreateCustomPlan_function)(PlannerInfo *root, + CustomPath *custom_path); +typedef void (*TextOutCustomPath_function)(StringInfo str, Node *node); + +typedef struct CustomPathMethods +{ + const char *CustomName; + CreateCustomPlan_function CreateCustomPlan; + TextOutCustomPath_function TextOutCustomPath; +} CustomPathMethods; + +/* * AppendPath represents an Append plan, ie, successive execution of * several member plans. * diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 9b22fda..3047d3d 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -29,6 +29,23 @@ typedef RelOptInfo *(*join_search_hook_type) (PlannerInfo *root, List *initial_rels); extern PGDLLIMPORT join_search_hook_type join_search_hook; +/* Hook for plugins to add custom scan path, in addition to default ones */ +typedef void (*add_scan_path_hook_type)(PlannerInfo *root, + RelOptInfo *baserel, + RangeTblEntry *rte); +extern PGDLLIMPORT add_scan_path_hook_type add_scan_path_hook; + +/* Hook for plugins to add custom join path, in addition to default ones */ +typedef void (*add_join_path_hook_type)(PlannerInfo *root, + RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + SpecialJoinInfo *sjinfo, + List *restrictlist, + Relids param_source_rels, + Relids extra_lateral_rels); +extern PGDLLIMPORT add_join_path_hook_type add_join_path_hook; extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist); extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed, diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h index 8bdb7db..28b89d9 100644 --- a/src/include/optimizer/planmain.h +++ b/src/include/optimizer/planmain.h @@ -41,6 +41,10 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist, * prototypes for plan/createplan.c */ extern Plan *create_plan(PlannerInfo *root, Path *best_path); +extern Plan *create_plan_recurse(PlannerInfo *root, Path *best_path); +extern List *build_path_tlist(PlannerInfo *root, Path *path); +extern bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel); +extern void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path); extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, Index scanrelid, Plan *subplan); extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual, @@ -86,6 +90,11 @@ extern ModifyTable *make_modifytable(PlannerInfo *root, List *withCheckOptionLists, List *returningLists, List *rowMarks, int epqParam); extern bool is_projection_capable_plan(Plan *plan); +extern List *order_qual_clauses(PlannerInfo *root, List *clauses); +extern List *get_switched_clauses(List *clauses, Relids outerrelids); +extern void copy_path_costsize(Plan *dest, Path *src); +extern void copy_plan_costsize(Plan *dest, Plan *src); +extern Node *replace_nestloop_params(PlannerInfo *root, Node *expr); /* * prototypes for plan/initsplan.c @@ -127,6 +136,9 @@ extern List *remove_useless_joins(PlannerInfo *root, List *joinlist); * prototypes for plan/setrefs.c */ extern Plan *set_plan_references(PlannerInfo *root, Plan *plan); +extern Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); +extern void fix_expr_common(PlannerInfo *root, Node *node); +extern Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset); extern void fix_opfuncids(Node *node); extern void set_opfuncid(OpExpr *opexpr); extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr); diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h index 5607e98..138b60b 100644 --- a/src/include/optimizer/subselect.h +++ b/src/include/optimizer/subselect.h @@ -29,6 +29,13 @@ extern void SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans); extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, Oid resulttype, int32 resulttypmod, Oid resultcollation); +extern Bitmapset *finalize_plan(PlannerInfo *root, + Plan *plan, + Bitmapset *valid_params, + Bitmapset *scan_params); +extern Bitmapset *finalize_primnode(PlannerInfo *root, + Node *node, + Bitmapset *paramids); extern Param *assign_nestloop_param_var(PlannerInfo *root, Var *var); extern Param *assign_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv);