pgsql-v9.4-custom-scan-part1.v4.patch
application/octet-stream
Filename: pgsql-v9.4-custom-scan-part1.v4.patch
Type: application/octet-stream
Part: 2
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v9
| File | + | − |
|---|---|---|
| doc/src/sgml/custom-scan.sgml | 298 | 0 |
| doc/src/sgml/filelist.sgml | 1 | 0 |
| doc/src/sgml/postgres.sgml | 1 | 0 |
| src/backend/commands/explain.c | 78 | 0 |
| src/backend/executor/execAmi.c | 32 | 2 |
| src/backend/executor/execProcnode.c | 14 | 0 |
| src/backend/executor/execQual.c | 5 | 5 |
| src/backend/executor/execUtils.c | 2 | 2 |
| src/backend/executor/Makefile | 1 | 1 |
| src/backend/executor/nodeCustom.c | 252 | 0 |
| src/backend/nodes/copyfuncs.c | 30 | 0 |
| src/backend/nodes/outfuncs.c | 19 | 0 |
| src/backend/nodes/print.c | 4 | 0 |
| src/backend/optimizer/path/allpaths.c | 23 | 0 |
| src/backend/optimizer/path/costsize.c | 1 | 1 |
| src/backend/optimizer/path/joinpath.c | 18 | 0 |
| src/backend/optimizer/plan/createplan.c | 103 | 0 |
| src/backend/optimizer/plan/setrefs.c | 25 | 0 |
| src/backend/optimizer/plan/subselect.c | 10 | 0 |
| src/backend/optimizer/util/pathnode.c | 40 | 0 |
| src/backend/utils/adt/ruleutils.c | 41 | 3 |
| src/include/executor/executor.h | 2 | 1 |
| src/include/executor/nodeCustom.h | 94 | 0 |
| src/include/nodes/execnodes.h | 17 | 0 |
| src/include/nodes/nodes.h | 3 | 0 |
| src/include/nodes/plannodes.h | 16 | 0 |
| src/include/nodes/primnodes.h | 1 | 0 |
| src/include/nodes/relation.h | 16 | 0 |
| src/include/optimizer/pathnode.h | 10 | 0 |
| src/include/optimizer/paths.h | 25 | 0 |
doc/src/sgml/custom-scan.sgml | 298 ++++++++++++++++++++++++++++++++
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/postgres.sgml | 1 +
src/backend/commands/explain.c | 78 +++++++++
src/backend/executor/Makefile | 2 +-
src/backend/executor/execAmi.c | 34 +++-
src/backend/executor/execProcnode.c | 14 ++
src/backend/executor/execQual.c | 10 +-
src/backend/executor/execUtils.c | 4 +-
src/backend/executor/nodeCustom.c | 252 +++++++++++++++++++++++++++
src/backend/nodes/copyfuncs.c | 30 ++++
src/backend/nodes/outfuncs.c | 19 ++
src/backend/nodes/print.c | 4 +
src/backend/optimizer/path/allpaths.c | 23 +++
src/backend/optimizer/path/costsize.c | 2 +-
src/backend/optimizer/path/joinpath.c | 18 ++
src/backend/optimizer/plan/createplan.c | 103 +++++++++++
src/backend/optimizer/plan/setrefs.c | 25 +++
src/backend/optimizer/plan/subselect.c | 10 ++
src/backend/optimizer/util/pathnode.c | 40 +++++
src/backend/utils/adt/ruleutils.c | 44 ++++-
src/include/executor/executor.h | 3 +-
src/include/executor/nodeCustom.h | 94 ++++++++++
src/include/nodes/execnodes.h | 17 ++
src/include/nodes/nodes.h | 3 +
src/include/nodes/plannodes.h | 16 ++
src/include/nodes/primnodes.h | 1 +
src/include/nodes/relation.h | 16 ++
src/include/optimizer/pathnode.h | 10 ++
src/include/optimizer/paths.h | 25 +++
30 files changed, 1182 insertions(+), 15 deletions(-)
diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
new file mode 100644
index 0000000..bb00078
--- /dev/null
+++ b/doc/src/sgml/custom-scan.sgml
@@ -0,0 +1,298 @@
+<!-- doc/src/sgml/custom-scan.sgml -->
+
+<chapter id="custom-scan">
+ <title>Writing A Custom Scan Provider</title>
+
+ <indexterm zone="custom-scan">
+ <primary>custom scan</primary>
+ <secondary>handler for</secondary>
+ </indexterm>
+ <para>
+ Custom-scan API enables extension to provide alternative ways to scan or
+ join relations, being fully integrated with cost based optimizer,
+ in addition to the built-in implementation.
+ It consists of a set of callbacks, with a unique name, to be invoked during
+ query planning and execution. Custom-scan provider should implement these
+ callback functions according to the expectation of API.
+ </para>
+ <para>
+ Overall, here is four major jobs that custom-scan provider should implement.
+ The first one is registration of custom-scan provider itself. Usually, it
+ shall be done once at <literal>_PG_init()</literal> entrypoint on module
+ loading.
+ The other three jobs shall be done for each query planning and execution.
+ The second one is submission of candidate paths to scan or join relations,
+ with an adequate cost, for the core planner.
+ Then, planner shall chooses a cheapest path from all the candidates.
+ If custom path survived, the planner kicks the third job; construction of
+ <literal>CustomScan</literal> plan node, being located within query plan
+ tree instead of the built-in plan node.
+ The last one is execution of its implementation in answer to invocations
+ by the core executor.
+ </para>
+ <para>
+ Some of contrib module utilize the custom-scan API. It may be able to
+ provide a good example for new development.
+ <variablelist>
+ <varlistentry>
+ <term><xref linkend="ctidscan"></term>
+ <listitem>
+ <para>
+ Its logic enables to skip earlier pages or terminate scan prior to
+ end of the relation, if inequality operator on <literal>ctid</literal>
+ system column can narrow down the scope to be scanned, instead of
+ the sequential scan that reads a relation from the head to the end.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><xref linkend="postgres-fdw"></term>
+ <listitem>
+ <para>
+ Its logic replaces a local join of foreign tables managed by
+ <literal>postgres_fdw</literal> with a custom scan that fetches
+ remotely joined relations.
+ It shows the way to implement a custom scan node that performs
+ instead join nodes.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ Right now, only scan and join are supported to have fully integrated cost
+ based query optimization performing on custom scan API.
+ You might be able to implement other stuff, like sort or aggregation, with
+ manipulation of the planned tree, however, extension has to be responsible
+ to handle this replacement correctly. Here is no support by the core.
+ </para>
+
+ <sect1 id="custom-scan-spec">
+ <title>Custom Scan API Functions and Callbacks</title>
+ <sect2 id="custom-scan-register">
+ <title>Registration of custom scan provider</title>
+ <para>
+ The first job for custom scan provider is registration of a set of
+ callbacks with a unique name. Usually, it shall be done once on
+ <literal>_PG_init()</literal> entrypoint of module loading.
+<programlisting>
+void
+register_custom_provider(const CustomProvider *provider);
+</programlisting>
+ Its argument, <literal>CustomProvider</literal> structure, contains
+ a name and a set of callback function pointers but some of them are
+ optional.
+ Once registered, it is copied on the internal table, so the caller
+ does not need to keep this structure any more.
+ </para>
+ </sect2>
+
+ <sect2 id="custom-scan-path">
+ <title>Submission of custom paths</title>
+ <para>
+ The query planner finds out the best way to scan or join relations from
+ the various potential paths; combination of a scan algorithm and target
+ relations.
+ Prior to this selection, we list up all the potential paths towards
+ a target relation (if base relation) or a pair of relations (if join).
+ The <literal>add_scan_path_hook</> and <literal>add_join_path_hook</>
+ allows extensions to add alternative scan paths in addition to built-in
+ ones.
+ If custom-scan provider can submit a potential scan path towards the
+ supplied relation, it shall construct <literal>CustomPath</> object
+ with appropriate parameters.
+<programlisting>
+typedef struct CustomPath
+{
+ Path path;
+ const char *custom_name; /* name of custom scan provider */
+ int custom_flags; /* CUSTOM__* flags in nodeCustom.h */
+ List *custom_private; /* can be used for private data */
+} CustomPath;
+</programlisting>
+ Its <literal>path</> is common field for all the path nodes to store
+ cost estimation. In addition, <literal>custom_name</> is the name of
+ registered custom scan provider, <literal>custom_flags</> is a set of
+ flags below, and <literal>custom_private</> can be used to store private
+ data of the custom scan provider.
+ </para>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term><literal>CUSTOM__SUPPORT_MARK_RESTORE</></term>
+ <listitem>
+ <para>
+ It informs the query planner this custom scan node supports
+ <literal>ExecMarkPosCustomScan</> and
+ <literal>ExecRestorePosCustomScan</> methods.
+ Also, custom scan provider has to be responsible to mark and restore
+ a particular position.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><literal>CUSTOM__SUPPORT_BACKWARD_SCAN</></term>
+ <listitem>
+ <para>
+ It informs the query planner this custom scan node supports
+ backward scan.
+ Also, custom scan provider has to be responsible to scan with
+ backward direction.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </sect2>
+
+ <sect2 id="custom-scan-plan">
+ <title>Construction of custom plan node</title>
+ <para>
+ Once <literal>CustomPath</literal> got choosen by query planner,
+ it calls back its associated custom scan provider to complete setting
+ up <literal>CustomScan</literal> plan node according to the path
+ information.
+<programlisting>
+void
+InitCustomScanPlan(PlannerInfo *root,
+ CustomScan *cscan_plan,
+ CustomPath *cscan_path,
+ List *tlist,
+ List *scan_clauses);
+</programlisting>
+ Query planner does basic initialization on the <literal>cscan_plan</>
+ being allocated, then custom scan provider can apply final initialization.
+ <literal>cscan_path</> is the path node that was constructed on the
+ previous stage then got choosen.
+ <literal>tlist</> is a list of <literal>TargetEntry</> to be assigned
+ on the <literal>Plan</> portion in the <literal>cscan_plan</>.
+ Also, <literal>scan_clauses</> is a list of <literal>RestrictInfo</> to
+ be checked during relation scan. Its expression portion shall be also
+ assigned on the <literal>Plan</> portion, but can be eliminated from
+ this list if custom scan provider can handle these checks by itself.
+ </para>
+ <para>
+ It often needs to adjust <literal>varno</> of <literal>Var</> node that
+ references a particular scan node, after conscruction of plan node.
+ For example, Var node in the target list of join node originally
+ references a particular relation underlying a join, however, it has to
+ be adjusted to either inner or outer reference.
+<programlisting>
+void
+SetPlanRefCustomScan(PlannerInfo *root,
+ CustomScan *cscan_plan,
+ int rtoffset);
+</programlisting>
+ This callback is optional if custom scan node is a vanilla relation
+ scan because here is nothing special to do. Elsewhere, it needs to
+ be handled by custom scan provider in case when a custom scan replaced
+ a join with two or more relations for example.
+ </para>
+ </sect2>
+
+ <sect2 id="custom-scan-exec">
+ <title>Execution of custom scan node</title>
+ <para>
+ Query execuror also launches associated callbacks to begin, execute and
+ end custom scan according to the executor's manner.
+ </para>
+ <para>
+<programlisting>
+void
+BeginCustomScan(CustomScanState *csstate, int eflags);
+</programlisting>
+ It begins execution of the custom scan on starting up executor.
+ It allows custom scan provider to do any initialization job around this
+ plan, however, it is not a good idea to launch actual scanning jobs.
+ (It shall be done on the first invocation of <literal>ExecCustomScan</>
+ instead.)
+ The <literal>custom_state</> field of <literal>CustomScanState</> is
+ intended to save the private state being managed by custom scan provider.
+ Also, <literal>eflags</> has flag bits of the executor's operating mode
+ for this plan node.
+ Note that custom scan provider should not perform anything visible
+ externally if <literal>EXEC_FLAG_EXPLAIN_ONLY</> would be given,
+ </para>
+
+ <para>
+<programlisting>
+TupleTableSlot *
+ExecCustomScan(CustomScanState *csstate);
+</programlisting>
+ It fetches one tuple from the underlying relation or relations if join
+ according to the custom logic. Unlike <literal>IterateForeignScan</>
+ method in foreign table, it is also responsible to check whether next
+ tuple matches the qualifier of this scan, or not.
+ A usual way to implement this method is the callback performs just an
+ entrypoint of <literal>ExecQual</> with its own access method.
+ </para>
+
+ <para>
+<programlisting>
+Node *
+MultiExecCustomScan(CustomScanState *csstate);
+</programlisting>
+ It fetches multiple tuples from the underlying relation or relations if
+ join according to the custom logic. Pay attention the data format (and
+ the way to return also) depends on the type of upper node.
+ </para>
+
+ <para>
+<programlisting>
+void
+EndCustomScan(CustomScanState *csstate);
+</programlisting>
+ It ends the scan and release resources privately allocated.
+ It is usually not important to release memory in per-execution memory
+ context. So, all this callback should be responsible is its own
+ resources regardless from the framework.
+ </para>
+ </sect2>
+
+ <sect2 id="custom-scan-misc">
+ <title>Miscellaneous jobs</title>
+ <para>
+<programlisting>
+void
+ReScanCustomScan(CustomScanState *csstate);
+</programlisting>
+ It restarts the current scan from the beginning.
+ Note that parameters of the scan depends on might change values,
+ so rewinded scan does not need to return exactly identical tuples.
+ </para>
+ <para>
+<programlisting>
+void
+MarkPosCustomScan(CustomScanState *csstate);
+</programlisting>
+ It saves the current position of the custom scan on somewhere private
+ state.
+ Note that it is optional to implement, only when
+ <literal>CUSTOM__SUPPORT_MARK_RESTORE</> is set.
+ </para>
+ <para>
+<programlisting>
+void
+RestorePosCustom(CustomScanState *csstate);
+</programlisting>
+ It rewinds the current position of the custom scan to the position
+ where <literal>MarkPosCustomScan</> saved before.
+ Note that it is optional to implement, only when
+ <literal>CUSTOM__SUPPORT_MARK_RESTORE</> is set.
+ </para>
+ <para>
+<programlisting>
+void
+ExplainCustomScan(CustomScanState *csstate,
+ ExplainState *es);
+</programlisting>
+ It prints additional <command>EXPLAIN</> output for a custom scan plan.
+ This callback is expected to call <literal>ExplainPropertyText</> to
+ make additional field of <command>EXPLAIN</> output.
+ The flag fields in <literal>ExplainState</> indicates what shall be
+ printed, and the state of the <literal>CustomScanState</> will provide
+ run-time statistics in the <command>EXPLAIN ANALYZE</> case.
+ </para>
+ </sect2>
+ </sect1>
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index d1b7dc6..1e96829 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -91,6 +91,7 @@
<!ENTITY nls SYSTEM "nls.sgml">
<!ENTITY plhandler SYSTEM "plhandler.sgml">
<!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
+<!ENTITY custom-scan SYSTEM "custom-scan.sgml">
<!ENTITY protocol SYSTEM "protocol.sgml">
<!ENTITY sources SYSTEM "sources.sgml">
<!ENTITY storage SYSTEM "storage.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 522316c..cce0cd8 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -241,6 +241,7 @@
&nls;
&plhandler;
&fdwhandler;
+ &custom-scan;
&geqo;
&indexam;
&gist;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 4e93df2..39d2c12 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"
@@ -84,6 +85,7 @@ static void show_hash_info(HashState *hashstate, ExplainState *es);
static void show_instrumentation_count(const char *qlabel, int which,
PlanState *planstate, ExplainState *es);
static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
+static void show_customscan_info(CustomScanState *cstate, ExplainState *es);
static const char *explain_get_index_name(Oid indexId);
static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
ExplainState *es);
@@ -683,6 +685,11 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
*rels_used = bms_add_member(*rels_used,
((Scan *) plan)->scanrelid);
break;
+ case T_CustomScan:
+ if (((Scan *) plan)->scanrelid > 0)
+ *rels_used = bms_add_member(*rels_used,
+ ((Scan *) plan)->scanrelid);
+ break;
case T_ModifyTable:
/* cf ExplainModifyTarget */
*rels_used = bms_add_member(*rels_used,
@@ -809,6 +816,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
const char *operation = NULL;
+ char namebuf[NAMEDATALEN + 32];
int save_indent = es->indent;
bool haschildren;
@@ -897,6 +905,13 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_ForeignScan:
pname = sname = "Foreign Scan";
break;
+ case T_CustomScan:
+ snprintf(namebuf, sizeof(namebuf), "Custom Scan (%s)",
+ ((CustomScan *) plan)->custom_name);
+ pname = pstrdup(namebuf);
+ sname = "Custom Scan";
+ operation = ((CustomScan *) plan)->custom_name;
+ break;
case T_Material:
pname = sname = "Materialize";
break;
@@ -1013,6 +1028,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_ForeignScan:
ExplainScanTarget((Scan *) plan, es);
break;
+ case T_CustomScan:
+ if (((Scan *) plan)->scanrelid > 0)
+ ExplainScanTarget((Scan *) plan, es);
+ break;
case T_IndexScan:
{
IndexScan *indexscan = (IndexScan *) plan;
@@ -1291,6 +1310,17 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
show_foreignscan_info((ForeignScanState *) planstate, es);
break;
+ case T_CustomScan:
+ if (((CustomScan *)plan)->funcexpr != NULL && es->verbose)
+ show_expression(((CustomScan *)plan)->funcexpr,
+ "Function Call", planstate, ancestors,
+ es->verbose, es);
+ show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+ if (plan->qual)
+ show_instrumentation_count("Rows Removed by Filter", 1,
+ planstate, es);
+ show_customscan_info((CustomScanState *) planstate, es);
+ break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual,
"Join Filter", planstate, ancestors, es);
@@ -1858,6 +1888,19 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
}
/*
+ * Show extra information for a CustomScan node.
+ */
+static void
+show_customscan_info(CustomScanState *cstate, ExplainState *es)
+{
+ CustomProvider *provider = cstate->custom_provider;
+
+ /* Let custom scan provider emit whatever fields it wants */
+ if (provider->ExplainCustomScan != NULL)
+ provider->ExplainCustomScan(cstate, es);
+}
+
+/*
* Fetch the name of an index in an EXPLAIN
*
* We allow plugins to get control here so that plans involving hypothetical
@@ -2025,6 +2068,41 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
objectname = rte->ctename;
objecttag = "CTE Name";
break;
+ case T_CustomScan:
+ if (rte->rtekind == RTE_RELATION)
+ {
+ objectname = get_rel_name(rte->relid);
+ if (es->verbose)
+ namespace =
+ get_namespace_name(get_rel_namespace(rte->relid));
+ objecttag = "Relation Name";
+ }
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ objectname = rte->eref->aliasname;
+ objecttag = "Join Alias";
+ }
+ else if (rte->rtekind == RTE_FUNCTION)
+ {
+ Node *funcexpr = ((CustomScan *) plan)->funcexpr;
+
+ if (funcexpr && IsA(funcexpr, FuncExpr))
+ {
+ Oid funcid = ((FuncExpr *) funcexpr)->funcid;
+
+ objectname = get_func_name(funcid);
+ if (es->verbose)
+ namespace =
+ get_namespace_name(get_func_namespace(funcid));
+ }
+ objecttag = "Function Name";
+ }
+ else if (rte->rtekind == RTE_CTE)
+ {
+ objectname = rte->ctename;
+ objecttag = "CTE Name";
+ }
+ break;
default:
break;
}
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 a078104..f80e6c4 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_CustomScanState:
+ ExecReScanCustomScan((CustomScanState *) node);
+ break;
+
case T_NestLoopState:
ExecReScanNestLoop((NestLoopState *) node);
break;
@@ -291,6 +296,10 @@ ExecMarkPos(PlanState *node)
ExecValuesMarkPos((ValuesScanState *) node);
break;
+ case T_CustomScanState:
+ ExecCustomMarkPos((CustomScanState *) node);
+ break;
+
case T_MaterialState:
ExecMaterialMarkPos((MaterialState *) node);
break;
@@ -348,6 +357,10 @@ ExecRestrPos(PlanState *node)
ExecValuesRestrPos((ValuesScanState *) node);
break;
+ case T_CustomScanState:
+ ExecCustomRestrPos((CustomScanState *) node);
+ break;
+
case T_MaterialState:
ExecMaterialRestrPos((MaterialState *) node);
break;
@@ -379,9 +392,9 @@ ExecRestrPos(PlanState *node)
* and valuesscan support is actually useless code at present.)
*/
bool
-ExecSupportsMarkRestore(NodeTag plantype)
+ExecSupportsMarkRestore(Path *path)
{
- switch (plantype)
+ switch (path->pathtype)
{
case T_SeqScan:
case T_IndexScan:
@@ -392,6 +405,14 @@ ExecSupportsMarkRestore(NodeTag plantype)
case T_Sort:
return true;
+ case T_CustomPath:
+ {
+ int flags = ((CustomPath *) path)->custom_flags;
+ if (flags & CUSTOM__SUPPORT_MARK_RESTORE)
+ return true;
+ return false;
+ }
+
case T_Result:
/*
@@ -465,6 +486,15 @@ ExecSupportsBackwardScan(Plan *node)
return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
TargetListSupportsBackwardScan(node->targetlist);
+ case T_CustomScan:
+ {
+ int flags = ((CustomScan *) node)->custom_flags;
+
+ if (flags & CUSTOM__SUPPORT_BACKWARD_SCAN)
+ return TargetListSupportsBackwardScan(node->targetlist);
+ }
+ 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 76dd62f..b1110b9 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/execQual.c b/src/backend/executor/execQual.c
index 90c2753..e60ac67 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -592,7 +592,7 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
slot = econtext->ecxt_outertuple;
break;
- /* INDEX_VAR is handled by default case */
+ /* INDEX_VAR and CUSTOM_VAR are handled by default case */
default: /* get the tuple from the relation being
* scanned */
@@ -680,7 +680,7 @@ ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
slot = econtext->ecxt_outertuple;
break;
- /* INDEX_VAR is handled by default case */
+ /* INDEX_VAR and CUSTOM_VAR are handled by default case */
default: /* get the tuple from the relation being
* scanned */
@@ -732,7 +732,7 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
slot = econtext->ecxt_outertuple;
break;
- /* INDEX_VAR is handled by default case */
+ /* INDEX_VAR and CUSTOM_VAR are handled by default case */
default: /* get the tuple from the relation being
* scanned */
@@ -915,7 +915,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
slot = econtext->ecxt_outertuple;
break;
- /* INDEX_VAR is handled by default case */
+ /* INDEX_VAR and CUSTOM_VAR are handled by default case */
default: /* get the tuple from the relation being
* scanned */
@@ -991,7 +991,7 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
slot = econtext->ecxt_outertuple;
break;
- /* INDEX_VAR is handled by default case */
+ /* INDEX_VAR and CUSTOM_VAR are handled by default case */
default: /* get the tuple from the relation being
* scanned */
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 39e3b2e..df0d295 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -578,7 +578,7 @@ ExecBuildProjectionInfo(List *targetList,
projInfo->pi_lastOuterVar = attnum;
break;
- /* INDEX_VAR is handled by default case */
+ /* INDEX_VAR and CUSTOM_VAR are handled by default case */
default:
varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
@@ -638,7 +638,7 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo)
projInfo->pi_lastOuterVar = attnum;
break;
- /* INDEX_VAR is handled by default case */
+ /* INDEX_VAR and CUSTOM_VAR are handled by default case */
default:
if (projInfo->pi_lastScanVar < attnum)
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
new file mode 100644
index 0000000..2d89d7a
--- /dev/null
+++ b/src/backend/executor/nodeCustom.c
@@ -0,0 +1,252 @@
+/* ------------------------------------------------------------------------
+ *
+ * nodeCustom.c
+ * Routines to handle execution of custom plan, scan and join 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/nodeCustom.h"
+#include "parser/parsetree.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+/* static variables */
+static HTAB *custom_provider_hash = NULL;
+
+/*
+ * register_custom_provider
+ *
+ * It registers a custom execution provider; that consists of a set of
+ * callbacks and is identified with a unique name.
+ */
+void
+register_custom_provider(const CustomProvider *provider)
+{
+ CustomProvider *entry;
+ bool found;
+
+ if (!custom_provider_hash)
+ {
+ HASHCTL ctl;
+
+ memset(&ctl, 0, sizeof(ctl));
+ ctl.hcxt = CacheMemoryContext;
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(CustomProvider);
+
+ custom_provider_hash = hash_create("custom execution providers",
+ 32,
+ &ctl,
+ HASH_ELEM | HASH_CONTEXT);
+ }
+
+ entry = hash_search(custom_provider_hash,
+ provider->name,
+ HASH_ENTER, &found);
+ if (found)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("duplicate custom execution provider \"%s\"",
+ provider->name)));
+
+ Assert(strcmp(provider->name, entry->name) == 0);
+ memcpy(entry, provider, sizeof(CustomProvider));
+}
+
+/*
+ * get_custom_provider
+ *
+ * It finds a registered custom execution provide by its name
+ */
+CustomProvider *
+get_custom_provider(const char *custom_name)
+{
+ CustomProvider *entry;
+
+ /* lookup custom execution provider */
+ if (!custom_provider_hash)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("no custom execution provider was registered")));
+
+ entry = (CustomProvider *) hash_search(custom_provider_hash,
+ custom_name, HASH_FIND, NULL);
+ if (!entry)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("custom execution provider \"%s\" was not registered",
+ custom_name)));
+
+ return entry;
+}
+
+/*
+ * ExecInitCustomScan
+ *
+ * Allocation of CustomScanState and various initialization stuff.
+ * Note that some of initialization jobs are skipped if scanrelid is zero
+ * (that means this custom scan plan is not associated with a particular
+ * relation in range-table list.)
+ */
+CustomScanState *
+ExecInitCustomScan(CustomScan *node, EState *estate, int eflags)
+{
+ CustomProvider *provider = get_custom_provider(node->custom_name);
+ CustomScanState *csstate;
+ Plan *plan = &node->scan.plan;
+ Index scanrelid = node->scan.scanrelid;
+
+ /*
+ * Create state structure
+ */
+ csstate = makeNode(CustomScanState);
+ csstate->ss.ps.plan = plan;
+ csstate->ss.ps.state = estate;
+ csstate->custom_provider = provider;
+ csstate->custom_flags = node->custom_flags;
+ csstate->custom_state = NULL;
+
+ /*
+ * Miscellaneous initialization
+ */
+ ExecAssignExprContext(estate, &csstate->ss.ps);
+
+ /*
+ * Initialization of child expressions
+ */
+ csstate->ss.ps.targetlist =
+ (List *) ExecInitExpr((Expr *) plan->targetlist, &csstate->ss.ps);
+ csstate->ss.ps.qual =
+ (List *) ExecInitExpr((Expr *) plan->qual, &csstate->ss.ps);
+
+ /*
+ * tuple table initialization
+ *
+ * Note that ss_ScanTupleSlot is set only when scanrelid is associated
+ * with a particular relation. Elsewhere, it needs to be initialized by
+ * custom-scan provider itself if it internally uses ss_ScanTupleSlot.
+ * If it replaces varno of Var node by CUSTOM_VAR, it has to be set to
+ * reference underlying attribute name to generate EXPLAIN output.
+ */
+ ExecInitResultTupleSlot(estate, &csstate->ss.ps);
+ if (scanrelid > 0)
+ ExecInitScanTupleSlot(estate, &csstate->ss);
+
+ /*
+ * open the base relation and acquire appropriate lock on it,
+ * if this custom scan is connected with a particular relaion.
+ * Also, assign its scan type according to the table definition.
+ */
+ if (scanrelid > 0)
+ {
+ Relation rel = ExecOpenScanRelation(estate, scanrelid, eflags);
+
+ csstate->ss.ss_currentRelation = rel;
+ ExecAssignScanType(&csstate->ss, RelationGetDescr(rel));
+
+ csstate->ss.ps.ps_TupFromTlist = false;
+ }
+
+ /*
+ * Initialize result tuple type and projection info.
+ */
+ ExecAssignResultTypeFromTL(&csstate->ss.ps);
+
+ if (scanrelid > 0)
+ ExecAssignScanProjectionInfo(&csstate->ss);
+ else
+ ExecAssignProjectionInfo(&csstate->ss.ps, NULL);
+
+ /*
+ * Final initialization based on callback of BeginCustomScan method.
+ * Extension may be able to override initialization stuff above, if
+ * needed.
+ */
+ csstate->custom_provider->BeginCustomScan(csstate, eflags);
+
+ return csstate;
+}
+
+/*
+ * ExecCustomScan
+ *
+ * Just an entrypoint of ExecCustomScan method. All the stuff to fetch
+ * a tuple is a job of custom-scan provider.
+ */
+TupleTableSlot *
+ExecCustomScan(CustomScanState *csstate)
+{
+ return csstate->custom_provider->ExecCustomScan(csstate);
+}
+
+/*
+ * MultiExecCustomScan
+ *
+ * Aldo, just an entrypoint of MultiExecCustomScan method. All the stuff
+ * to fetch multiple tuples (according to expectation of upper node) is
+ * a job of custom-scan provider.
+ */
+Node *
+MultiExecCustomScan(CustomScanState *csstate)
+{
+ return csstate->custom_provider->MultiExecCustomScan(csstate);
+}
+
+/*
+ * ExecEndCustomScan
+ *
+ * It releases all the resources allocated on this scan.
+ */
+void
+ExecEndCustomScan(CustomScanState *csstate)
+{
+ /* Let the custom-exec shut down */
+ csstate->custom_provider->EndCustomScan(csstate);
+
+ /* Free the exprcontext */
+ ExecFreeExprContext(&csstate->ss.ps);
+
+ /* Clean out the tuple table, if exists */
+ ExecClearTuple(csstate->ss.ps.ps_ResultTupleSlot);
+ if (csstate->ss.ss_ScanTupleSlot)
+ ExecClearTuple(csstate->ss.ss_ScanTupleSlot);
+
+ /* close the relation, if opened */
+ if (csstate->ss.ss_currentRelation)
+ ExecCloseScanRelation(csstate->ss.ss_currentRelation);
+}
+
+/*
+ * ExecReScanCustomScan
+ */
+void
+ExecReScanCustomScan(CustomScanState *csstate)
+{
+ csstate->custom_provider->ReScanCustomScan(csstate);
+}
+
+/*
+ * ExecCustomMarkPos
+ */
+void
+ExecCustomMarkPos(CustomScanState *csstate)
+{
+ Assert((csstate->custom_flags & CUSTOM__SUPPORT_MARK_RESTORE) != 0);
+ csstate->custom_provider->MarkPosCustomScan(csstate);
+}
+
+/*
+ * ExecCustomRestrPos
+ */
+void
+ExecCustomRestrPos(CustomScanState *csstate)
+{
+ Assert((csstate->custom_flags & CUSTOM__SUPPORT_MARK_RESTORE) != 0);
+ csstate->custom_provider->RestorePosCustom(csstate);
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1733da6..9aaca17 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -602,6 +602,33 @@ _copyForeignScan(const ForeignScan *from)
}
/*
+ * _copyCustomScan
+ */
+static CustomScan *
+_copyCustomScan(const CustomScan *from)
+{
+ CustomScan *newnode = makeNode(CustomScan);
+
+ /*
+ * copy node superclass fields
+ */
+ CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+ /*
+ * copy remainder of node
+ */
+ COPY_STRING_FIELD(custom_name);
+ COPY_SCALAR_FIELD(custom_flags);
+ COPY_NODE_FIELD(custom_private);
+ COPY_NODE_FIELD(custom_exprs);
+
+ COPY_NODE_FIELD(subqry_plan);
+ COPY_NODE_FIELD(funcexpr);
+
+ return newnode;
+}
+
+/*
* CopyJoinFields
*
* This function copies the fields of the Join node. It is used by
@@ -3940,6 +3967,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 b39927e..7f0297f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -568,6 +568,22 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
}
static void
+_outCustomScan(StringInfo str, const CustomScan *node)
+{
+ WRITE_NODE_TYPE("CUSTOMSCAN");
+
+ _outScanInfo(str, (const Scan *) node);
+
+ WRITE_STRING_FIELD(custom_name);
+ WRITE_INT_FIELD(custom_flags);
+ WRITE_NODE_FIELD(custom_private);
+ WRITE_NODE_FIELD(custom_exprs);
+
+ WRITE_NODE_FIELD(subqry_plan);
+ WRITE_NODE_FIELD(funcexpr);
+}
+
+static void
_outJoin(StringInfo str, const Join *node)
{
WRITE_NODE_TYPE("JOIN");
@@ -2815,6 +2831,9 @@ _outNode(StringInfo str, const void *obj)
case T_ForeignScan:
_outForeignScan(str, obj);
break;
+ case T_CustomScan:
+ _outCustomScan(str, obj);
+ break;
case T_Join:
_outJoin(str, obj);
break;
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index 48ef325..29fcba9 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -333,6 +333,10 @@ print_expr(const Node *expr, const List *rtable)
relname = "INDEX";
attname = "?";
break;
+ case CUSTOM_VAR:
+ relname = "CUSTOM";
+ attname = "?";
+ break;
default:
{
RangeTblEntry *rte;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index bfd3809..9d0cbf5 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -46,6 +46,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);
@@ -399,6 +401,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Consider TID scans */
create_tidscan_paths(root, rel);
+ /* Consider Custom scans */
+ add_custom_scan_paths(root,rel,rte);
+
/* Now find the cheapest of the paths for this rel */
set_cheapest(rel);
}
@@ -427,6 +432,9 @@ 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);
+ /* Consider Custom scans */
+ add_custom_scan_paths(root,rel,rte);
+
/* Select cheapest path */
set_cheapest(rel);
}
@@ -1246,6 +1254,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
/* Generate appropriate path */
add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer));
+ /* Consider Custom scans */
+ add_custom_scan_paths(root,rel,rte);
+
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
@@ -1269,6 +1280,9 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_functionscan_path(root, rel, required_outer));
+ /* Consider Custom scans */
+ add_custom_scan_paths(root,rel,rte);
+
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
@@ -1292,6 +1306,9 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_valuesscan_path(root, rel, required_outer));
+ /* Consider Custom scans */
+ add_custom_scan_paths(root,rel,rte);
+
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
@@ -1361,6 +1378,9 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_ctescan_path(root, rel, required_outer));
+ /* Consider Custom scans */
+ add_custom_scan_paths(root,rel,rte);
+
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
@@ -1414,6 +1434,9 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_worktablescan_path(root, rel, required_outer));
+ /* Consider Custom scans */
+ add_custom_scan_paths(root,rel,rte);
+
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index e7f8cec..e5b0cd7 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2312,7 +2312,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/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 5b477e5..9483614 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,22 @@ 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,
+ mergeclause_list,
+ &semifactors,
+ param_source_rels,
+ extra_lateral_rels);
}
/*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 5947e5b..f830ab8 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -21,6 +21,7 @@
#include "access/skey.h"
#include "catalog/pg_class.h"
+#include "executor/nodeCustom.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -77,6 +78,9 @@ 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 CustomScan *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,
@@ -235,6 +239,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:
@@ -411,6 +416,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
scan_clauses);
break;
+ case T_CustomScan:
+ plan = (Plan *) create_customscan_plan(root,
+ (CustomPath *) best_path,
+ tlist,
+ scan_clauses);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) best_path->pathtype);
@@ -2016,6 +2028,97 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
return scan_plan;
}
+/*
+ * create_customscan_plan
+ * Returns a custom-scan plan for the base relation scanned by 'best_path'
+ * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static CustomScan *
+create_customscan_plan(PlannerInfo *root,
+ CustomPath *best_path,
+ List *tlist,
+ List *scan_clauses)
+{
+ CustomProvider *provider = get_custom_provider(best_path->custom_name);
+ CustomScan *scan_plan = makeNode(CustomScan);
+ RelOptKind reloptkind = best_path->path.parent->reloptkind;
+ RangeTblEntry *rte;
+ Index scan_relid;
+
+ if (reloptkind == RELOPT_BASEREL ||
+ reloptkind == RELOPT_OTHER_MEMBER_REL)
+ {
+ scan_relid = best_path->path.parent->relid;
+
+ rte = planner_rt_fetch(scan_relid, root);
+ /*
+ * For EXPLAIN output, we save various information in CustomScan plan
+ * structure. Custom-scan provider can utilize them, but it is not
+ * recommendablt to adjust.
+ */
+ if (rte->rtekind == RTE_SUBQUERY)
+ {
+ if (best_path->path.param_info)
+ {
+ List *subplan_params
+ = best_path->path.parent->subplan_params;
+ process_subquery_nestloop_params(root, subplan_params);
+ }
+ scan_plan->subqry_plan = best_path->path.parent->subplan;
+ }
+ else if (rte->rtekind == RTE_FUNCTION)
+ {
+ Node *funcexpr = rte->funcexpr;
+
+ if (best_path->path.param_info)
+ funcexpr = replace_nestloop_params(root, funcexpr);
+ scan_plan->funcexpr = funcexpr;
+ }
+ }
+ else if (reloptkind == RELOPT_JOINREL)
+ scan_relid = 0;
+ else
+ elog(ERROR, "unexpected reloptkind: %d", (int)reloptkind);
+
+ scan_clauses = order_qual_clauses(root, scan_clauses);
+ scan_plan->scan.plan.targetlist = NULL; /* to be set by callback */
+ scan_plan->scan.plan.qual = NULL; /* to be set by callback */
+ scan_plan->scan.plan.lefttree = NULL;
+ scan_plan->scan.plan.righttree = NULL;
+ scan_plan->scan.scanrelid = scan_relid;
+
+ scan_plan->custom_name = pstrdup(best_path->custom_name);
+ scan_plan->custom_flags = best_path->custom_flags;
+ scan_plan->custom_private = NIL;
+ scan_plan->custom_exprs = NULL;
+
+ /*
+ * Let custom scan provider perform to set up this custom-scan plan
+ * according to the given path information.
+ */
+ provider->InitCustomScanPlan(root, scan_plan,
+ best_path, tlist, scan_clauses);
+
+ /* Copy cost data from Path to Plan; no need to make callback do this */
+ copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+
+ /*
+ * Replace any outer-relation variables with nestloop params in the qual
+ * and custom_exprs expressions. We do this last so that the FDW doesn't
+ * have to be involved. (Note that parts of custom_exprs could have come
+ * from join clauses, so doing this beforehand on the scan_clauses
+ * wouldn't work.)
+ */
+ if (best_path->path.param_info)
+ {
+ scan_plan->scan.plan.qual = (List *)
+ replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual);
+ scan_plan->custom_exprs = (List *)
+ replace_nestloop_params(root, (Node *) scan_plan->custom_exprs);
+ }
+
+ return scan_plan;
+}
/*****************************************************************************
*
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index b78d727..aae5a1c 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"
@@ -578,6 +579,30 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
}
break;
+ case T_CustomScan:
+ {
+ CustomScan *splan = (CustomScan *) plan;
+ CustomProvider *provider
+ = get_custom_provider(splan->custom_name);
+
+ if (provider->SetPlanRefCustomScan)
+ provider->SetPlanRefCustomScan(root, splan, rtoffset);
+ else if (splan->scan.scanrelid > 0)
+ {
+ splan->scan.scanrelid += rtoffset;
+ splan->scan.plan.targetlist =
+ fix_scan_list(root, splan->scan.plan.targetlist,
+ rtoffset);
+ splan->scan.plan.qual =
+ fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ splan->custom_exprs =
+ fix_scan_list(root, splan->custom_exprs, rtoffset);
+ }
+ else
+ elog(ERROR, "No implementation to set plan reference");
+ }
+ break;
+
case T_NestLoop:
case T_MergeJoin:
case T_HashJoin:
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 0df70c4..644a532 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2194,6 +2194,16 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
context.paramids = bms_add_members(context.paramids, scan_params);
break;
+ case T_CustomScan:
+ finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs,
+ &context);
+ context.paramids = bms_add_members(context.paramids, scan_params);
+ /*
+ * XXX - Is it sufficient to do? Don't we need something special
+ * if CustomScan override FunctionScan or SubqueryScan.
+ */
+ 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 64b17051..46e814d 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1738,6 +1738,46 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
}
/*
+ * create_customscan_path
+ * Creates a path corresponding to a scan of a relation based on logic
+ * logic being provided by extensions.
+ *
+ * This function is never called from core PostgreSQL. An usual usage is
+ * invocation from callbacks on add_scan_path_hook. We don't have any
+ * assumption on the custom scan logic, thus, caller is responsible to
+ * set adequate cost estimation here.
+ */
+CustomPath *
+create_customscan_path(PlannerInfo *root,
+ RelOptInfo *baserel,
+ double rows,
+ Cost startup_cost,
+ Cost total_cost,
+ List *pathkeys,
+ Relids required_outer,
+ const char *custom_name,
+ uint32 custom_flags,
+ List *custom_private)
+{
+ CustomPath *pathnode = makeNode(CustomPath);
+
+ pathnode->path.pathtype = T_CustomScan;
+ pathnode->path.parent = baserel;
+ pathnode->path.param_info = get_baserel_parampathinfo(root, baserel,
+ required_outer);
+ pathnode->path.rows = rows;
+ pathnode->path.startup_cost = startup_cost;
+ pathnode->path.total_cost = total_cost;
+ pathnode->path.pathkeys = pathkeys;
+
+ pathnode->custom_name = pstrdup(custom_name);
+ pathnode->custom_flags = custom_flags;
+ pathnode->custom_private = custom_private;
+
+ return pathnode;
+}
+
+/*
* calc_nestloop_required_outer
* Compute the required_outer set for a nestloop join path
*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5ffce68..bfceab8 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -143,6 +143,7 @@ typedef struct
List *outer_tlist; /* referent for OUTER_VAR Vars */
List *inner_tlist; /* referent for INNER_VAR Vars */
List *index_tlist; /* referent for INDEX_VAR Vars */
+ TupleDesc custom_tupdesc; /* referent for CUSTOM_VAR Vars */
} deparse_namespace;
/*
@@ -2363,14 +2364,19 @@ deparse_context_for(const char *aliasname, Oid relid)
* deparse_context_for_planstate - Build deparse context for a plan
*
* When deparsing an expression in a Plan tree, we might have to resolve
- * OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
- * provide the parent PlanState node. Then OUTER_VAR and INNER_VAR references
- * can be resolved by drilling down into the left and right child plans.
+ * special varno (OUTER_VAR, INNER_VAR, INDEX_VAR or CUSTOM_VAR) references.
+ * To do this, the caller must provide the parent PlanState node. Then
+ * OUTER_VAR and INNER_VAR references can be resolved by drilling down into
+ * the left and right child plans.
* Similarly, INDEX_VAR references can be resolved by reference to the
* indextlist given in the parent IndexOnlyScan node. (Note that we don't
* currently support deparsing of indexquals in regular IndexScan or
* BitmapIndexScan nodes; for those, we can only deparse the indexqualorig
* fields, which won't contain INDEX_VAR Vars.)
+ * Also, CUSTOM_VAR references can be resolved by reference to the TupleDesc
+ * of ss_ScanTupleSlot in CustomScanState node. (Note that custom scan
+ * provider must be responsible to initialize the ss_ScanTupleSlot with
+ * appropriate TupleDesc; being likely constructed by ExecTypeFromTL).
*
* Note: planstate really ought to be declared as "PlanState *", but we use
* "Node *" to avoid having to include execnodes.h in builtins.h.
@@ -3628,6 +3634,14 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
else
dpns->index_tlist = NIL;
+
+ /* custom_tupdesc is set only if it's an CustomScan */
+ if (IsA(ps, CustomScanState) &&
+ ((CustomScanState *)ps)->ss.ss_ScanTupleSlot)
+ dpns->custom_tupdesc =
+ ((CustomScanState *)ps)->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
+ else
+ dpns->custom_tupdesc = NULL;
}
/*
@@ -5295,6 +5309,18 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
return NULL;
}
+ else if (var->varno == CUSTOM_VAR && dpns->custom_tupdesc)
+ {
+ TupleDesc tupdesc = dpns->custom_tupdesc;
+
+ Assert(netlevelsup == 0);
+ Assert(var->varattno > 0 && var->varattno <= tupdesc->natts);
+
+ attname = NameStr(tupdesc->attrs[var->varattno - 1]->attname);
+ appendStringInfoString(buf, quote_identifier(attname));
+
+ return attname;
+ }
else
{
elog(ERROR, "bogus varno: %d", var->varno);
@@ -5565,6 +5591,18 @@ get_name_for_var_field(Var *var, int fieldno,
return result;
}
+ else if (var->varno == CUSTOM_VAR && dpns->custom_tupdesc)
+ {
+ TupleDesc tupdesc = dpns->custom_tupdesc;
+ const char *result;
+
+ Assert(netlevelsup == 0);
+ Assert(var->varattno > 0 && var->varattno <= tupdesc->natts);
+
+ result = NameStr(tupdesc->attrs[var->varattno - 1]->attname);
+
+ return result;
+ }
else
{
elog(ERROR, "bogus varno: %d", var->varno);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 75841c8..51537d2 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"
/*
@@ -102,7 +103,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 *path);
extern bool ExecSupportsBackwardScan(Plan *node);
extern bool ExecMaterializesOutput(NodeTag plantype);
diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
new file mode 100644
index 0000000..a484f8b
--- /dev/null
+++ b/src/include/executor/nodeCustom.h
@@ -0,0 +1,94 @@
+/* ------------------------------------------------------------------------
+ *
+ * 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 "commands/explain.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+
+/*
+ * Definition of the custom execution provider callbacks
+ */
+typedef void (*InitCustomScanPlan_function)(PlannerInfo *root,
+ CustomScan *cscan_plan,
+ CustomPath *cscan_path,
+ List *tlist,
+ List *scan_clauses);
+typedef void (*SetPlanRefCustomScan_function)(PlannerInfo *root,
+ CustomScan *cscan_plan,
+ int rtoffset);
+typedef void (*BeginCustomScan_function)(CustomScanState *csstate, int eflags);
+typedef TupleTableSlot *(*ExecCustomScan_function)(CustomScanState *csstate);
+typedef Node *(*MultiExecCustomScan_function)(CustomScanState *csstate);
+typedef void (*EndCustomScan_function)(CustomScanState *csstate);
+
+typedef void (*ReScanCustomScan_function)(CustomScanState *csstate);
+typedef void (*MarkPosCustomScan_function)(CustomScanState *csstate);
+typedef void (*RestorePosCustom_function)(CustomScanState *csstate);
+
+typedef void (*ExplainCustomScan_function)(CustomScanState *csstate,
+ ExplainState *es);
+
+typedef struct CustomProvider
+{
+ char name[NAMEDATALEN];
+
+ InitCustomScanPlan_function InitCustomScanPlan;
+ SetPlanRefCustomScan_function SetPlanRefCustomScan;
+
+ BeginCustomScan_function BeginCustomScan;
+ ExecCustomScan_function ExecCustomScan;
+ MultiExecCustomScan_function MultiExecCustomScan;
+ EndCustomScan_function EndCustomScan;
+
+ ReScanCustomScan_function ReScanCustomScan;
+ MarkPosCustomScan_function MarkPosCustomScan;
+ RestorePosCustom_function RestorePosCustom;
+
+ ExplainCustomScan_function ExplainCustomScan;
+} CustomProvider;
+
+/* Flags of CustomScan */
+
+/*
+ * CUSTOM__SUPPORT_MARK_RESTORE informs optimizer this custom scan provider
+ * support ExecCustomMarkPos and ExecCustomRestrPos callbacks.
+ */
+#define CUSTOM__SUPPORT_MARK_RESTORE 0x0001
+
+/*
+ * CUSTOM__SUPPORT_BACKWARD_SCAN informs optimizer this custom scan provider
+ * is designed to support backward scan.
+ */
+#define CUSTOM__SUPPORT_BACKWARD_SCAN 0x0002
+
+/*
+ * Registration and lookup custom execution provider
+ */
+extern void register_custom_provider(const CustomProvider *provider);
+
+extern CustomProvider *get_custom_provider(const char *custom_name);
+
+/*
+ * General executor code
+ */
+extern CustomScanState *ExecInitCustomScan(CustomScan *csstate,
+ EState *estate, int eflags);
+extern TupleTableSlot *ExecCustomScan(CustomScanState *csstate);
+extern Node *MultiExecCustomScan(CustomScanState *csstate);
+extern void ExecEndCustomScan(CustomScanState *csstate);
+
+extern void ExecReScanCustomScan(CustomScanState *csstate);
+extern void ExecCustomMarkPos(CustomScanState *csstate);
+extern void ExecCustomRestrPos(CustomScanState *csstate);
+
+#endif /* NODECUSTOM_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index bedcf04..529930f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1494,6 +1494,23 @@ typedef struct ForeignScanState
void *fdw_state; /* foreign-data wrapper can keep state here */
} ForeignScanState;
+/* ----------------
+ * CustomScanState information
+ *
+ * CustomScan nodes are used to scan various relations using custom
+ * logic.
+ * ----------------
+ */
+typedef struct CustomScanState
+{
+ ScanState ss;
+
+ /* use struct pointer to avoid including nodeCustom.h here */
+ struct CustomProvider *custom_provider;
+ int custom_flags;
+ void *custom_state;
+} CustomScanState;
+
/* ----------------------------------------------------------------
* Join State Information
* ----------------------------------------------------------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fc6b1d7..7753a09 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 44ea0b7..936591b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -483,6 +483,22 @@ typedef struct ForeignScan
bool fsSystemCol; /* true if any "system column" is needed */
} ForeignScan;
+/* ----------------
+ * CustomScan node
+ * ----------------
+ */
+typedef struct CustomScan
+{
+ Scan scan;
+
+ const char *custom_name; /* name of custom scan provider */
+ int custom_flags; /* a set of CUSTOM__* flags */
+ List *custom_private; /* private data for CSP */
+ List *custom_exprs; /* expressions that CSP may execute */
+
+ Plan *subqry_plan; /* valid, if RTE_SUBQUERY */
+ Node *funcexpr; /* valid, if RTE_FUNCTION */
+} CustomScan;
/*
* ==========
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 7918537..b71c7ca 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -134,6 +134,7 @@ typedef struct Expr
#define INNER_VAR 65000 /* reference to inner subplan */
#define OUTER_VAR 65001 /* reference to outer subplan */
#define INDEX_VAR 65002 /* reference to index column */
+#define CUSTOM_VAR 65003 /* reference to custom column */
#define IS_SPECIAL_VARNO(varno) ((varno) >= INNER_VAR)
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6d7b594..50194f2 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -876,6 +876,22 @@ typedef struct ForeignPath
} ForeignPath;
/*
+ * CustomPath represents a scan using custom logic
+ *
+ * custom_name is the identifier of custom scan provider when it was
+ * registered. custom_flags is a set of CUSTOM__* bits to control its
+ * behavior. custom_private allows extension to store its private data
+ * but has to be safe for copyObject().
+ */
+typedef struct CustomPath
+{
+ Path path;
+ const char *custom_name; /* name of custom scan provider */
+ int custom_flags; /* CUSTOM__* flags in nodeCustom.h */
+ List *custom_private; /* can be used for private data */
+} CustomPath;
+
+/*
* 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 9686229..1225970 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -82,6 +82,16 @@ extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
List *pathkeys,
Relids required_outer,
List *fdw_private);
+extern CustomPath *create_customscan_path(PlannerInfo *root,
+ RelOptInfo *baserel,
+ double rows,
+ Cost startup_cost,
+ Cost total_cost,
+ List *pathkeys,
+ Relids required_outer,
+ const char *custom_name,
+ uint32 custom_flags,
+ List *custom_private);
extern Relids calc_nestloop_required_outer(Path *outer_path, Path *inner_path);
extern Relids calc_non_nestloop_required_outer(Path *outer_path, Path *inner_path);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 96ffdb1..d7c7ef7 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -29,6 +29,31 @@ 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;
+
+#define add_custom_scan_paths(root,baserel,rte) \
+ do { \
+ if (add_scan_path_hook) \
+ (*add_scan_path_hook)((root),(baserel),(rte)); \
+ } while(0)
+
+/* 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,
+ List *mergeclause_list,
+ SemiAntiJoinFactors *semifactors,
+ 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,