]> granicus.if.org Git - postgresql/commitdiff
Introduce custom path and scan providers.
authorRobert Haas <rhaas@postgresql.org>
Fri, 7 Nov 2014 22:26:02 +0000 (17:26 -0500)
committerRobert Haas <rhaas@postgresql.org>
Fri, 7 Nov 2014 22:34:36 +0000 (17:34 -0500)
This allows extension modules to define their own methods for
scanning a relation, and get the core code to use them.  It's
unclear as yet how much use this capability will find, but we
won't find out if we never commit it.

KaiGai Kohei, reviewed at various times and in various levels
of detail by Shigeru Hanada, Tom Lane, Andres Freund, Álvaro
Herrera, and myself.

22 files changed:
src/backend/commands/explain.c
src/backend/executor/Makefile
src/backend/executor/execAmi.c
src/backend/executor/execProcnode.c
src/backend/executor/nodeCustom.c [new file with mode: 0644]
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/pathnode.c
src/backend/utils/adt/ruleutils.c
src/include/executor/executor.h
src/include/executor/nodeCustom.h [new file with mode: 0644]
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/plannodes.h
src/include/nodes/relation.h
src/include/optimizer/pathnode.h
src/include/optimizer/planmain.h

index 3fcc1ddc69c5babb72bc77c07228762bc158a43f..99aa0f09338d285a063f7f2f982c1ed317c4b514 100644 (file)
@@ -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);
index 6081b56c08637df6a93130637a5d2b6d533e6bdc..af707b03751c33a9f63dcb83d7a9ba3d3b9fb52a 100644 (file)
@@ -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 \
index 640964c5b7c6c538b7cc71818a84aaa844226884..b14e08cd1af5b72d292f9fdeda1b36fac3a95175 100644 (file)
@@ -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 */
index c0189eb5a12802c1ba2cd76d0b9650d20a425b4b..e27c0627bc47e6c66d9298075ad55c763cfadd81 100644 (file)
@@ -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 (file)
index 0000000..576b295
--- /dev/null
@@ -0,0 +1,126 @@
+/* ------------------------------------------------------------------------
+ *
+ * nodeCustom.c
+ *             Routines to handle execution of custom scan node
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * ------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "executor/executor.h"
+#include "executor/nodeCustom.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "parser/parsetree.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+CustomScanState *
+ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
+{
+       CustomScanState    *css;
+       Relation                        scan_rel;
+
+       /* populate a CustomScanState according to the CustomScan */
+       css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan);
+       Assert(IsA(css, CustomScanState));
+
+       /* fill up fields of ScanState */
+       css->ss.ps.plan = &cscan->scan.plan;
+       css->ss.ps.state = estate;
+
+       /* create expression context for node */
+       ExecAssignExprContext(estate, &css->ss.ps);
+
+       /* initialize child expressions */
+       css->ss.ps.targetlist = (List *)
+               ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
+                                        (PlanState *) css);
+       css->ss.ps.qual = (List *)
+               ExecInitExpr((Expr *) cscan->scan.plan.qual,
+                                        (PlanState *) css);
+
+       /* tuple table initialization */
+       ExecInitScanTupleSlot(estate, &css->ss);
+       ExecInitResultTupleSlot(estate, &css->ss.ps);
+
+       /* initialize scan relation */
+       scan_rel = ExecOpenScanRelation(estate, cscan->scan.scanrelid, eflags);
+       css->ss.ss_currentRelation = scan_rel;
+       css->ss.ss_currentScanDesc = NULL;      /* set by provider */
+       ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
+
+       css->ss.ps.ps_TupFromTlist = false;
+
+       /*
+        * Initialize result tuple type and projection info.
+        */
+       ExecAssignResultTypeFromTL(&css->ss.ps);
+       ExecAssignScanProjectionInfo(&css->ss);
+
+       /*
+        * The callback of custom-scan provider applies the final initialization
+        * of the custom-scan-state node according to its logic.
+        */
+       css->methods->BeginCustomScan(css, estate, eflags);
+
+       return css;
+}
+
+TupleTableSlot *
+ExecCustomScan(CustomScanState *node)
+{
+       Assert(node->methods->ExecCustomScan != NULL);
+       return node->methods->ExecCustomScan(node);
+}
+
+void
+ExecEndCustomScan(CustomScanState *node)
+{
+       Assert(node->methods->EndCustomScan != NULL);
+       node->methods->EndCustomScan(node);
+
+       /* Free the exprcontext */
+       ExecFreeExprContext(&node->ss.ps);
+
+       /* Clean out the tuple table */
+       ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+       if (node->ss.ss_ScanTupleSlot)
+               ExecClearTuple(node->ss.ss_ScanTupleSlot);
+
+       /* Close the heap relation */
+       ExecCloseScanRelation(node->ss.ss_currentRelation);
+}
+
+void
+ExecReScanCustomScan(CustomScanState *node)
+{
+       Assert(node->methods->ReScanCustomScan != NULL);
+       node->methods->ReScanCustomScan(node);
+}
+
+void
+ExecCustomMarkPos(CustomScanState *node)
+{
+       if (!node->methods->MarkPosCustomScan)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("custom-scan \"%s\" does not support MarkPos",
+                                               node->methods->CustomName)));
+       node->methods->MarkPosCustomScan(node);
+}
+
+void
+ExecCustomRestrPos(CustomScanState *node)
+{
+       if (!node->methods->RestrPosCustomScan)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("custom-scan \"%s\" does not support MarkPos",
+                                               node->methods->CustomName)));
+       node->methods->RestrPosCustomScan(node);
+}
index 7b51d331777e6d51a840ea5e7f118925828843ac..e76b5b3f6339126d4a428725c9c0d3915a1c785e 100644 (file)
@@ -597,6 +597,29 @@ _copyForeignScan(const ForeignScan *from)
        return newnode;
 }
 
+/*
+ * _copyCustomScan
+ */
+static CustomScan *
+_copyCustomScan(const CustomScan *from)
+{
+       CustomScan                 *newnode;
+
+       newnode = from->methods->CopyCustomScan(from);
+       Assert(nodeTag(newnode) == nodeTag(from));
+
+       CopyScanFields((const Scan *) from, (Scan *) newnode);
+       COPY_SCALAR_FIELD(flags);
+       /*
+        * NOTE: The method field of CustomScan is required to be a pointer
+        * to a static table of callback functions. So, we don't copy the
+        * table itself, just reference the original one.
+        */
+       COPY_SCALAR_FIELD(methods);
+
+       return newnode;
+}
+
 /*
  * CopyJoinFields
  *
@@ -4043,6 +4066,9 @@ copyObject(const void *from)
                case T_ForeignScan:
                        retval = _copyForeignScan(from);
                        break;
+               case T_CustomScan:
+                       retval = _copyCustomScan(from);
+                       break;
                case T_Join:
                        retval = _copyJoin(from);
                        break;
index 4bbfa629d69d7327cd545942784f68529f8e8f70..cdf1e7ece1ff12a95a790a69fdd73c59599dbf8b 100644 (file)
@@ -563,6 +563,18 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
        WRITE_BOOL_FIELD(fsSystemCol);
 }
 
+static void
+_outCustomScan(StringInfo str, const CustomScan *node)
+{
+       WRITE_NODE_TYPE("CUSTOMSCAN");
+
+       _outScanInfo(str, (const Scan *) node);
+       WRITE_UINT_FIELD(flags);
+       appendStringInfo(str, " :methods");
+       _outToken(str, node->methods->CustomName);
+       node->methods->TextOutCustomScan(str, node);
+}
+
 static void
 _outJoin(StringInfo str, const Join *node)
 {
@@ -1584,6 +1596,17 @@ _outForeignPath(StringInfo str, const ForeignPath *node)
        WRITE_NODE_FIELD(fdw_private);
 }
 
+static void
+_outCustomPath(StringInfo str, const CustomPath *node)
+{
+       WRITE_NODE_TYPE("CUSTOMPATH");
+       _outPathInfo(str, (const Path *) node);
+       WRITE_UINT_FIELD(flags);
+       appendStringInfo(str, " :methods");
+       _outToken(str, node->methods->CustomName);
+       node->methods->TextOutCustomPath(str, node);
+}
+
 static void
 _outAppendPath(StringInfo str, const AppendPath *node)
 {
@@ -2855,6 +2878,9 @@ _outNode(StringInfo str, const void *obj)
                        case T_ForeignScan:
                                _outForeignScan(str, obj);
                                break;
+                       case T_CustomScan:
+                               _outCustomScan(str, obj);
+                               break;
                        case T_Join:
                                _outJoin(str, obj);
                                break;
@@ -3063,6 +3089,9 @@ _outNode(StringInfo str, const void *obj)
                        case T_ForeignPath:
                                _outForeignPath(str, obj);
                                break;
+                       case T_CustomPath:
+                               _outCustomPath(str, obj);
+                               break;
                        case T_AppendPath:
                                _outAppendPath(str, obj);
                                break;
index c81efe93a47a3a8b9f9afd02a9041d66fea00164..8b42e36b6d40cff9c75fa84d92dd44b8d7aa5f84 100644 (file)
@@ -402,6 +402,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
        /* Consider TID scans */
        create_tidscan_paths(root, rel);
 
+       /* Consider custom scans, if any */
+       create_customscan_paths(root, rel, rte);
+
        /* Now find the cheapest of the paths for this rel */
        set_cheapest(rel);
 }
index 0cdb7905a2fb38dee3977e09b101fd244bdbc471..659daa202654f931511d92430312ef743eaccd5e 100644 (file)
@@ -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;
 
        /*
index 4b641a2ca1f3127ced41cd050ede65ab99156f28..8d9237ccac44dffbab0b7906cf6b55ce762eb20d 100644 (file)
@@ -77,13 +77,15 @@ static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_pa
                                                  List *tlist, List *scan_clauses);
 static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
                                                List *tlist, List *scan_clauses);
+static Plan *create_customscan_plan(PlannerInfo *root,
+                                                                       CustomPath *best_path,
+                                                                       List *tlist, List *scan_clauses);
 static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
                                         Plan *outer_plan, Plan *inner_plan);
 static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
                                          Plan *outer_plan, Plan *inner_plan);
 static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
                                         Plan *outer_plan, Plan *inner_plan);
-static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
 static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
 static void process_subquery_nestloop_params(PlannerInfo *root,
                                                                 List *subplan_params);
@@ -233,6 +235,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
                case T_CteScan:
                case T_WorkTableScan:
                case T_ForeignScan:
+               case T_CustomScan:
                        plan = create_scan_plan(root, best_path);
                        break;
                case T_HashJoin:
@@ -409,6 +412,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
                                                                                                        scan_clauses);
                        break;
 
+               case T_CustomScan:
+                       plan = create_customscan_plan(root,
+                                                                                 (CustomPath *) best_path,
+                                                                                 tlist,
+                                                                                 scan_clauses);
+                       break;
+
                default:
                        elog(ERROR, "unrecognized node type: %d",
                                 (int) best_path->pathtype);
@@ -1072,6 +1082,52 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
        return plan;
 }
 
+/*
+ * create_custom_plan
+ *
+ * Transform a CustomPath into a Plan.
+ */
+static Plan *
+create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
+                                          List *tlist, List *scan_clauses)
+{
+       Plan               *plan;
+       RelOptInfo         *rel = best_path->path.parent;
+
+       /*
+        * Right now, all we can support is CustomScan node which is associated
+        * with a particular base relation to be scanned.
+        */
+       Assert(rel && rel->reloptkind == RELOPT_BASEREL);
+
+       /*
+        * Sort clauses into the best execution order, although custom-scan
+        * provider can reorder them again.
+        */
+       scan_clauses = order_qual_clauses(root, scan_clauses);
+
+       /*
+        * Create a CustomScan (or its inheritance) node according to
+        * the supplied CustomPath.
+        */
+       plan = best_path->methods->PlanCustomPath(root, rel, best_path, tlist,
+                                                                                         scan_clauses);
+
+       /*
+        * NOTE: unlike create_foreignscan_plan(), it is responsibility of
+        * the custom plan provider to replace outer-relation variables
+        * with nestloop params, because we cannot know how many expression
+        * trees are held in the private fields.
+        */
+
+       /*
+        * Copy cost data from Path to Plan; no need to make custom-plan
+        * providers do this
+        */
+       copy_path_costsize(plan, &best_path->path);
+
+       return plan;
+}
 
 /*****************************************************************************
  *
@@ -2540,7 +2596,7 @@ create_hashjoin_plan(PlannerInfo *root,
  * root->curOuterRels are replaced by Params, and entries are added to
  * root->curOuterParams if not already present.
  */
-static Node *
+Node *
 replace_nestloop_params(PlannerInfo *root, Node *expr)
 {
        /* No setup needed for tree walk, so away we go */
index 9ddc8adcf98915b17c1732e0bbe0e1488a458810..bbc68a05a6c7617ccfe2ddf0e16f59332e49b284 100644 (file)
@@ -94,7 +94,6 @@ static Plan *set_subqueryscan_references(PlannerInfo *root,
                                                        SubqueryScan *plan,
                                                        int rtoffset);
 static bool trivial_subqueryscan(SubqueryScan *plan);
-static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
 static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
 static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
 static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -579,6 +578,27 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
                        }
                        break;
 
+               case T_CustomScan:
+                       {
+                               CustomScan *cscan = (CustomScan *) plan;
+
+                               cscan->scan.scanrelid += rtoffset;
+                               cscan->scan.plan.targetlist =
+                                       fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
+                               cscan->scan.plan.qual =
+                                       fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
+                               /*
+                                * The core implementation applies the routine to fixup
+                                * varno on the target-list and scan qualifier.
+                                * If custom-scan has additional expression nodes on its
+                                * private fields, it has to apply same fixup on them.
+                                * Otherwise, the custom-plan provider can skip this callback.
+                                */
+                               if (cscan->methods->SetCustomScanRef)
+                                       cscan->methods->SetCustomScanRef(root, cscan, rtoffset);
+                       }
+                       break;
+
                case T_NestLoop:
                case T_MergeJoin:
                case T_HashJoin:
@@ -1063,7 +1083,7 @@ copyVar(Var *var)
  * We assume it's okay to update opcode info in-place.  So this could possibly
  * scribble on the planner's input data structures, but it's OK.
  */
-static void
+void
 fix_expr_common(PlannerInfo *root, Node *node)
 {
        /* We assume callers won't call us on a NULL pointer */
@@ -1161,7 +1181,7 @@ fix_param_node(PlannerInfo *root, Param *p)
  * looking up operator opcode info for OpExpr and related nodes,
  * and adding OIDs from regclass Const nodes into root->glob->relationOids.
  */
-static Node *
+Node *
 fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
 {
        fix_scan_expr_context context;
index 3e7dc8515793358c1e9a2045c65721ed3a438313..4200ec0a5a7a65aaaa84e4e094dcf360911e1a5c 100644 (file)
@@ -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;
index 319e8b2c37968882cdc70d465e17f9042a572d40..6f1c6cfb2aaedefa27f84bac5d854baf88c0f53b 100644 (file)
@@ -27,6 +27,7 @@
 #include "optimizer/var.h"
 #include "parser/parsetree.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/selfuncs.h"
 
 
@@ -1926,3 +1927,49 @@ reparameterize_path(PlannerInfo *root, Path *path,
        }
        return NULL;
 }
+
+/*****************************************************************************
+ *     creation of custom-plan paths
+ *****************************************************************************/
+
+static List       *custom_path_providers = NIL;
+
+/*
+ * register_custom_path_provider
+ *
+ * Register a table of callback functions which implements a custom-path
+ * provider.  This allows extension to provide additional (hopefully faster)
+ * methods of scanning a relation.
+ */
+void
+register_custom_path_provider(CustomPathMethods *cpp_methods)
+{
+       MemoryContext   oldcxt;
+
+       oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+       custom_path_providers = lappend(custom_path_providers, cpp_methods);
+       MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * create_customscan_paths
+ *
+ * Invoke custom path provider callbacks.  If the callback determines that
+ * the custom-path provider can handle this relation, it can add one or more
+ * paths using add_path().
+ */
+void
+create_customscan_paths(PlannerInfo *root,
+                                               RelOptInfo *baserel,
+                                               RangeTblEntry *rte)
+{
+       ListCell           *cell;
+
+       foreach (cell, custom_path_providers)
+       {
+               const CustomPathMethods *cpp_methods = lfirst(cell);
+
+               if (cpp_methods->CreateCustomScanPath)
+                       cpp_methods->CreateCustomScanPath(root, baserel, rte);
+       }
+}
index 24ade6cc201a5ae637e66ab292e7a359ea706d5e..bf4e81f554a6dff915f8aa6fb5ed43f8fec07db4 100644 (file)
@@ -5493,6 +5493,26 @@ get_utility_query_def(Query *query, deparse_context *context)
        }
 }
 
+/*
+ * GetSpecialCustomVar
+ *
+ * If a custom-scan provider uses a special varnode, this function will be
+ * called when deparsing; it should return an Expr node to be reversed-listed
+ * in lieu of the special Var.
+ */
+static Node *
+GetSpecialCustomVar(CustomScanState *css, Var *varnode, PlanState **child_ps)
+{
+       Assert(IsA(css, CustomScanState));
+       Assert(IS_SPECIAL_VARNO(varnode->varno));
+
+       if (!css->methods->GetSpecialCustomVar)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("%s does not support special varno reference",
+                                               css->methods->CustomName)));
+       return (Node *) css->methods->GetSpecialCustomVar(css, varnode, child_ps);
+}
 
 /*
  * Display a Var appropriately.
@@ -5522,6 +5542,8 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
        int                     netlevelsup;
        deparse_namespace *dpns;
        deparse_columns *colinfo;
+       PlanState  *child_ps = NULL;
+       Node       *expr;
        char       *refname;
        char       *attname;
 
@@ -5546,6 +5568,29 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
                colinfo = deparse_columns_fetch(var->varno, dpns);
                attnum = var->varattno;
        }
+       else if (IS_SPECIAL_VARNO(var->varno) &&
+                        IsA(dpns->planstate, CustomScanState) &&
+                        (expr = GetSpecialCustomVar((CustomScanState *) dpns->planstate,
+                                                                                var, &child_ps)) != NULL)
+       {
+               deparse_namespace       save_dpns;
+
+               if (child_ps)
+                       push_child_plan(dpns, child_ps, &save_dpns);
+               /*
+                * Force parentheses because our caller probably assumed a Var is a
+                * simple expression.
+                */
+               if (!IsA(expr, Var))
+                       appendStringInfoChar(buf, '(');
+               get_rule_expr((Node *) expr, context, true);
+               if (!IsA(expr, Var))
+                       appendStringInfoChar(buf, ')');
+
+               if (child_ps)
+                       pop_child_plan(dpns, &save_dpns);
+               return NULL;
+       }
        else if (var->varno == OUTER_VAR && dpns->outer_tlist)
        {
                TargetEntry *tle;
@@ -5760,6 +5805,7 @@ get_name_for_var_field(Var *var, int fieldno,
        AttrNumber      attnum;
        int                     netlevelsup;
        deparse_namespace *dpns;
+       PlanState  *child_ps = NULL;
        TupleDesc       tupleDesc;
        Node       *expr;
 
@@ -5834,6 +5880,30 @@ get_name_for_var_field(Var *var, int fieldno,
                rte = rt_fetch(var->varno, dpns->rtable);
                attnum = var->varattno;
        }
+       else if (IS_SPECIAL_VARNO(var->varno) &&
+                        IsA(dpns->planstate, CustomScanState) &&
+                        (expr = GetSpecialCustomVar((CustomScanState *) dpns->planstate,
+                                                                                var, &child_ps)) != NULL)
+       {
+               StringInfo              saved = context->buf;
+               StringInfoData  temp;
+               deparse_namespace save_dpns;
+
+               initStringInfo(&temp);
+               context->buf = &temp;
+
+               if (child_ps)
+                       push_child_plan(dpns, child_ps, &save_dpns);
+               if (!IsA(expr, Var))
+                       appendStringInfoChar(context->buf, '(');
+               get_rule_expr((Node *) expr, context, true);
+               if (!IsA(expr, Var))
+                       appendStringInfoChar(context->buf, ')');
+               if (child_ps)
+                       pop_child_plan(dpns, &save_dpns);
+               context->buf = saved;
+               return temp.data;
+       }
        else if (var->varno == OUTER_VAR && dpns->outer_tlist)
        {
                TargetEntry *tle;
index d167b496fceb18e8c47664d92e637979db00fd24..a44b4cde0faedc8b0003ca0ff04f46e1ca954f3b 100644 (file)
@@ -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 (file)
index 0000000..1736d48
--- /dev/null
@@ -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 */
index 39d2c10bdfecb2e484bca6858cc6026947419fec..b72e605e4fefe5850b1138d9aa239fb4fcd310a3 100644 (file)
@@ -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
  * ----------------------------------------------------------------
index 154d943d581473f0a10d2271dd073b0af84f2f6b..bc71fea78d074010cfe70f76a4d0912cee843f10 100644 (file)
@@ -62,6 +62,7 @@ typedef enum NodeTag
        T_CteScan,
        T_WorkTableScan,
        T_ForeignScan,
+       T_CustomScan,
        T_Join,
        T_NestLoop,
        T_MergeJoin,
@@ -107,6 +108,7 @@ typedef enum NodeTag
        T_CteScanState,
        T_WorkTableScanState,
        T_ForeignScanState,
+       T_CustomScanState,
        T_JoinState,
        T_NestLoopState,
        T_MergeJoinState,
@@ -224,6 +226,7 @@ typedef enum NodeTag
        T_HashPath,
        T_TidPath,
        T_ForeignPath,
+       T_CustomPath,
        T_AppendPath,
        T_MergeAppendPath,
        T_ResultPath,
index fb02390da51a7b2818603c23b4a399feafa2575b..9dbb91cb90d897248c351adfd1c053f66b196181 100644 (file)
 #define PLANNODES_H
 
 #include "access/sdir.h"
+#include "lib/stringinfo.h"
 #include "nodes/bitmapset.h"
 #include "nodes/primnodes.h"
+#include "nodes/relation.h"
 #include "utils/lockwaitpolicy.h"
 
 
@@ -483,6 +485,33 @@ typedef struct ForeignScan
        bool            fsSystemCol;    /* true if any "system column" is needed */
 } ForeignScan;
 
+/* ----------------
+ *     CustomScan node
+ * ----------------
+ */
+struct CustomScanMethods;
+
+typedef struct CustomScan
+{
+       Scan            scan;
+       uint32          flags;  /* mask of CUSTOMPATH_* flags defined in relation.h */
+       struct CustomScanMethods *methods;
+} CustomScan;
+
+typedef struct CustomScanMethods
+{
+       const char *CustomName;
+       void       (*SetCustomScanRef)(struct PlannerInfo *root,
+                                                                  CustomScan *cscan,
+                                                                  int rtoffset);
+       void       (*FinalizeCustomScan)(struct PlannerInfo *root,
+                                                                        CustomScan *cscan,
+                                                                        bool (*finalize_primnode)(),
+                                                                        void *finalize_context);
+       Node      *(*CreateCustomScanState)(CustomScan *cscan);
+       void       (*TextOutCustomScan)(StringInfo str, const CustomScan *node);
+       CustomScan *(*CopyCustomScan)(const CustomScan *from);
+} CustomScanMethods;
 
 /*
  * ==========
index f1a0504c0d4af2aeab107416107118a4d8235fc9..05cfbcd2aa158eb1056095c1c712cc3aa566f7ab 100644 (file)
@@ -15,6 +15,7 @@
 #define RELATION_H
 
 #include "access/sdir.h"
+#include "lib/stringinfo.h"
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "storage/block.h"
@@ -883,6 +884,47 @@ typedef struct ForeignPath
        List       *fdw_private;
 } ForeignPath;
 
+/*
+ * CustomPath represents a scan by some out-of-core extension.
+ *
+ * We provide a set of hooks here - which the provider must take care to
+ * set up correctly - to allow extensions to supply their own methods of
+ * scanning a relation.  For example, a provider might provide GPU
+ * acceleration, a cache-based scan, or some other kind of logic we haven't
+ * dreamed up yet.
+ *
+ * Core code should avoid assuming that the CustomPath is only as large as
+ * the structure declared here; providers are expected to make it the first
+ * element in a larger structure.
+ */
+
+struct CustomPathMethods;
+struct Plan;           /* not to include plannodes.h here */
+
+#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN       0x0001
+#define CUSTOMPATH_SUPPORT_MARK_RESTORE                0x0002
+
+typedef struct CustomPath
+{
+       Path        path;
+       uint32          flags;
+       const struct CustomPathMethods *methods;
+} CustomPath;
+
+typedef struct CustomPathMethods
+{
+       const char *CustomName;
+       void    (*CreateCustomScanPath)(PlannerInfo *root,
+                                                                       RelOptInfo *baserel,
+                                                                       RangeTblEntry *rte);
+       struct Plan     *(*PlanCustomPath)(PlannerInfo *root,
+                                                                  RelOptInfo *rel,
+                                                                  CustomPath *best_path,
+                                                                  List *tlist,
+                                                                  List *clauses);
+       void    (*TextOutCustomPath)(StringInfo str, const CustomPath *node);
+} CustomPathMethods;
+
 /*
  * AppendPath represents an Append plan, ie, successive execution of
  * several member plans.
index 26b17f5f7afcfcbcaa5c9b9aae4603b37128e2f3..2b67ae6187b61299c9b9bb1ef2de4a22bd2d23f5 100644 (file)
@@ -128,6 +128,15 @@ extern Path *reparameterize_path(PlannerInfo *root, Path *path,
                                        Relids required_outer,
                                        double loop_count);
 
+/*
+ * Interface definition of custom-scan providers
+ */
+extern void register_custom_path_provider(CustomPathMethods *cpp_methods);
+
+extern void create_customscan_paths(PlannerInfo *root,
+                                                                       RelOptInfo *baserel,
+                                                                       RangeTblEntry *rte);
+
 /*
  * prototypes for relnode.c
  */
index 3fdc2cba0ed16ec0057743d93e08a1d0cff5ff3c..c97c5777a07f7d6811d78b1ec19faec46b9a9de9 100644 (file)
@@ -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);