pgsql-v9.4-custom-scan.part-1.v5.patch
application/octet-stream
Filename: pgsql-v9.4-custom-scan.part-1.v5.patch
Type: application/octet-stream
Part: 0
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 | 295 | 0 |
| doc/src/sgml/filelist.sgml | 1 | 0 |
| doc/src/sgml/postgres.sgml | 1 | 0 |
| src/backend/commands/explain.c | 99 | 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 | 104 | 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 | 295 ++++++++++++++++++++++++++++++++
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/postgres.sgml | 1 +
src/backend/commands/explain.c | 99 +++++++++++
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 | 104 +++++++++++
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, 1201 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..b57d82f
--- /dev/null
+++ b/doc/src/sgml/custom-scan.sgml
@@ -0,0 +1,295 @@
+<!-- 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>
+ The custom-scan API enables an extension to provide alternative ways to scan
+ or join relations leveraging the cost based optimizer. The API consists of a
+ set of callbacks, with a unique names, to be invoked during query planning
+ and execution. A custom-scan provider should implement these callback
+ functions according to the expectation of the API.
+ </para>
+ <para>
+ Overall, there are four major tasks that a custom-scan provider should
+ implement. The first task is the registration of custom-scan provider itself.
+ Usually, this needs to be done once at the <literal>_PG_init()</literal>
+ entrypoint when the module is loading. The remaing three tasks are all done
+ when a query is planning and executing. The second task is the submission of
+ candidate paths to either scan or join relations with an adequate cost for
+ the core planner. Then, the planner will choose the cheapest path from all of
+ the candidates. If the custom path survived, the planner starts the third
+ task; construction of a <literal>CustomScan</literal> plan node, located
+ within the query plan tree instead of the built-in plan node. The last task
+ is the execution of its implementation in answer to invocations by the core
+ executor.
+ </para>
+ <para>
+ Some of contrib modules utilize the custom-scan API. They may provide a good
+ example for new development.
+ <variablelist>
+ <varlistentry>
+ <term><xref linkend="ctidscan"></term>
+ <listitem>
+ <para>
+ This custom scan in this module enables a scan to skip earlier pages or
+ terminate prior to end of the relation, if the inequality operator on the
+ <literal>ctid</literal> system column can narrow down the scope to be
+ scanned, instead of a sequential scan which reads a relation from the
+ head to the end.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><xref linkend="postgres-fdw"></term>
+ <listitem>
+ <para>
+ This custom scan in this module replaces a local join of foreign tables
+ managed by <literal>postgres_fdw</literal> with a scan that fetches
+ remotely joined relations. It demostrates the way to implement a custom
+ scan node that performs join nodes.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ Currently, only scan and join are fully supported with integrated cost
+ based query optimization using the custom scan API. You might be able to
+ implement other stuff, like sort or aggregation, with manipulation of the
+ planned tree, however, the extension has to be responsible to handle this
+ replacement correctly. There is no support in 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 task for a custom scan provider is the registration of a set of
+ callbacks with a unique names. Usually, this is done once upon module
+ loading in the <literal>_PG_init()</literal> entrypoint.
+<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 the best way to scan or join relations from various
+ potential paths using a combination of scan algorithms and target
+ relations. Prior to this selection, we list all of the potential paths
+ towards a target relation (if it is a base relation) or a pair of relations
+ (if it is a join). The <literal>add_scan_path_hook</> and
+ <literal>add_join_path_hook</> allow extensions to add alternative scan
+ paths in addition to built-in paths.
+ If custom-scan provider can submit a potential scan path towards the
+ supplied relation, it shall construct a <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 a common field for all the path nodes to store
+ a cost estimation. In addition, <literal>custom_name</> is the name of
+ the 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, the 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 scans.
+ 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> was choosen by the query planner,
+ it calls back to its associated to the custom scan provider to complete
+ setting up the <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>
+ The query planner does basic initialization on the <literal>cscan_plan</>
+ being allocated, then the custom scan provider can apply final
+ initialization. <literal>cscan_path</> is the path node that was
+ constructed on the previous stage then was 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 a relation scan. Its expression portion will also be
+ 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 construction of the plan node.
+ For example, Var node in the target list of the 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 the custom scan node is a vanilla relation
+ scan because there is nothing special to do. Elsewhere, it needs to
+ be handled by the 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>
+ The query executor also launches the associated callbacks to begin, execute
+ and end the 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 the custom scan provider to do any initialization job around this
+ plan, however, it is not a good idea to launch the 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 the custom scan
+ provider. Also, <literal>eflags</> has flag bits of the executor's
+ operating mode for this plan node. Note that the 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 joining,
+ according to the custom logic. Unlike <literal>IterateForeignScan</>
+ method in foreign table, it is also responsible to check whether the next
+ tuple matches the qualifier of this scan, or not.
+ The 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
+ joining, according to the custom logic. Pay attention the data format (and
+ the way to return also) since it depends on the type of upper node.
+ </para>
+
+ <para>
+<programlisting>
+void
+EndCustomScan(CustomScanState *csstate);
+</programlisting>
+ It ends the scan and releases 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 may 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</> was 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 552c3aa..656fd2e 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 b47bf52..ed76d33 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 e604be3..4528058 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"
@@ -90,6 +91,7 @@ static void show_tidbitmap_info(BitmapHeapScanState *planstate,
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);
@@ -689,6 +691,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,
@@ -815,6 +822,8 @@ 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;
+ char namebuf[NAMEDATALEN + 32];
int save_indent = es->indent;
bool haschildren;
@@ -903,6 +912,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";
+ custom_name = ((CustomScan *) plan)->custom_name;
+ break;
case T_Material:
pname = sname = "Materialize";
break;
@@ -1004,6 +1020,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 Provider", custom_name, es);
}
switch (nodeTag(plan))
@@ -1019,6 +1037,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;
@@ -1315,6 +1337,29 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
show_foreignscan_info((ForeignScanState *) planstate, es);
break;
+ case T_CustomScan:
+ if (((CustomScan *)plan)->functions != NIL && es->verbose)
+ {
+ List *fexprs = NIL;
+ ListCell *lc;
+
+ foreach(lc, ((CustomScan *) plan)->functions)
+ {
+ RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
+
+ fexprs = lappend(fexprs, rtfunc->funcexpr);
+ }
+ /* We rely on show_expression to insert commas as needed */
+ show_expression((Node *) fexprs,
+ "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);
@@ -1954,6 +1999,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
@@ -2126,6 +2184,47 @@ 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)
+ {
+ List *functions = ((CustomScan *) plan)->functions;
+
+ if (functions && list_length(functions) == 1)
+ {
+ RangeTblFunction *rtfunc = linitial(functions);
+
+ if (IsA(rtfunc->funcexpr, FuncExpr))
+ {
+ FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
+ Oid funcid = 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 8c01a63..2443e24 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 c5ecd18..b4a7411 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 0eba025..e71ce9b 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 46895b2..58d7190 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 fb4ce2c..1a774f6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -598,6 +598,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(functions);
+
+ return newnode;
+}
+
+/*
* CopyJoinFields
*
* This function copies the fields of the Join node. It is used by
@@ -3964,6 +3991,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 568c3b8..28ea915 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -564,6 +564,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(functions);
+}
+
+static void
_outJoin(StringInfo str, const Join *node)
{
WRITE_NODE_TYPE("JOIN");
@@ -2827,6 +2843,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 9f7f322..9f2b6bb 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 03be7b1..c7fcb80 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -47,6 +47,8 @@ int geqo_threshold;
/* Hook for plugins to replace standard_join_search() */
join_search_hook_type join_search_hook = NULL;
+/* Hook for plugins to add custom scan paths */
+add_scan_path_hook_type add_scan_path_hook = NULL;
static void set_base_rel_sizes(PlannerInfo *root);
static void set_base_rel_pathlists(PlannerInfo *root);
@@ -389,6 +391,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);
}
@@ -417,6 +422,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);
}
@@ -1236,6 +1244,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);
}
@@ -1307,6 +1318,9 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
add_path(rel, create_functionscan_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);
}
@@ -1330,6 +1344,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);
}
@@ -1399,6 +1416,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);
}
@@ -1452,6 +1472,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 8492eed..e139316 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 a996116..48f5ad4 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 184d37a..c07e000 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,
@@ -233,6 +237,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 +414,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);
@@ -2006,6 +2018,98 @@ 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)
+ {
+ List *functions = rte->functions;
+
+ if (best_path->path.param_info)
+ functions = (List *)
+ replace_nestloop_params(root, (Node *)functions);
+ scan_plan->functions = functions;
+ }
+ }
+ 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 46affe7..b10a2c9 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"
@@ -576,6 +577,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 a3f3583..74ff415 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2236,6 +2236,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 b79af7a..17827e0 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 add5cd1..d099d16 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -145,6 +145,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;
/*
@@ -2482,14 +2483,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.
@@ -3747,6 +3753,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;
}
/*
@@ -5414,6 +5428,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);
@@ -5684,6 +5710,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 eb78776..7fe0998 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 a301a08..621830a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1501,6 +1501,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 ae12c0d..21fd0b4 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 38c039c..85d088d 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -479,6 +479,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 */
+ List *functions; /* valid, if RTE_FUNCTION */
+} CustomScan;
/*
* ==========
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4992bc0..7d9b0c0 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 8aa40d0..527a060 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -877,6 +877,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 a0bcc82..b99d841 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 9b22fda..a561387 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,