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