The way to get custom plan interface committed? (RE: Custom Scan APIs)
Hackers,
We now have about two weeks by the final feature freeze towards of v9.4.
What is the best way to get the custom-plan interface committed in this
limited time?
Fortunately, we could have a series of active discussion three weeks
before, then we reached a consensus that custom "scan" interface should
perform as custom "plan" interface; that gives custom plannode polymorphism
using a set of function pointers as like an object in object oriented
programming language.
The attached patch is the latest version of custom-plan interface being
re-designed according to the suggestion by Tom. It allows extensions to
create an object that extends the base custom types, with a set of
callback function pointers. This approach works well; at least, I could
demonstrate an implementation of existing merge-join on top of this
interface.
One negative factor of my submission is scale of patch. Even though
the main part of this patch is less than 1KL (incl 300lines sgml),
the demonstration part is more than 4.7KL but 99% of them were copied
from the core code.
Probably, it makes sense to focus on the main part of this patch.
The contrib portion I attached have no sense more than its demonstration
because its functionality is identical with existing merge join code.
Also, two other small patches that implements a callback on vacuum-pages
and allows HeapTupleSatisfiesVisibility on cache-only tuples.
These enhancements are needed to synchronize in-memory cache structure
on-heap data structure, as I demonstrated in the cache-only scan module.
I'd like to see above new features in v9.4, that will help people who
want to implement experimental, niche or edge-features on top of the
mainline PostgreSQL.
Your opinions?
Thanks,
--
NEC OSS Promotion Center / PG-Strom Project
KaiGai Kohei <kaigai@ak.jp.nec.com>
Show quoted text
-----Original Message-----
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kouhei Kaigai
Sent: Monday, March 24, 2014 7:26 PM
To: Tom Lane; PgHacker
Cc: Kohei KaiGai; Stephen Frost; Shigeru Hanada; Jim Mlodgenski; Robert
Haas; Peter Eisentraut
Subject: Re: Custom Scan APIs (Re: [HACKERS] Custom Plan node)Hello,
Because of patch conflict towards the latest master branch, I rebased the
custom-plan interface patches; no functional difference from the v11.Brief summary of the current approach that has been revised from my original
submission through the discussion on pgsql-hackers:The plannode was renamed to CustomPlan, instead of CustomScan, because it
dropped all the hardcoded portion that assumes the custom-node shall perform
as alternative scan or join method, because it prevents this custom-node
to perform as other stuff; like sort or append potentially.
According to the suggestion by Tom, I put a structure that contains several
function pointers on the new CustomPlan node, and extension will allocate
an object that extends CustomPlan node.
It looks like polymorphism in object oriented programming language.
The core backend knows abstracted set of methods defined in the tables of
function pointers, and extension can implement its own logic on the callback,
using private state on the extended object.Some issues are still under discussion:
* Design of add_join_path_hook
Tom suggested that core backend can support to check join legality and
parameterization stuff, however, it looks to me existing code handles join
legality checks within the function of individual join logic.
So, I'm not certain whether we can have a common legality check for all
the (potential) alternative join implementation.
The part-2 is a demonstration module that implemented existing merge- join
logic, but on top of this interface.* Functions to be exposed for extensions Some utility functions useful to
implement plan node are declared as static functions, because most of the
stuff are implemented within createplan.c, setrefs.c and so on, thus these
were not needed to expose other stuff. However, extension will become a
caller for these functions due to custom-plan interface, even though they
are implemented out of the core.
In my investigation, class of functions that recursively walk on subplan
tree have to be exposed, like ExplainPreScanNode, create_plan_recurse,
set_plan_refs, fix_expr_common or finalize_plan.
Do we have other criteria to determine what function shall be exposed?Any help and feedback are welcome.
Thanks,
--
NEC OSS Promotion Center / PG-Strom Project KaiGai Kohei
<kaigai@ak.jp.nec.com>-----Original Message-----
From: Kaigai Kouhei(海外 浩平)
Sent: Thursday, March 20, 2014 10:46 AM
To: Kaigai Kouhei(海外 浩平); Tom Lane
Cc: Kohei KaiGai; Stephen Frost; Shigeru Hanada; Jim Mlodgenski;
Robert Haas; PgHacker; Peter Eisentraut
Subject: RE: Custom Scan APIs (Re: [HACKERS] Custom Plan node)Hello,
* Definition of add_join_path_hook
I didn't have idea to improve the definition and location of this
hook, so it is still on the tail of the add_paths_to_joinrel().
Its definition was a bit adjusted according to the feedback on the
pgsql-hackers. I omitted the "mergeclause_list" and " semifactors"
from the argument list. Indeed, these are specific to the built-in
MergeJoin logic and easy to reproduce.After the submission, I'm still investigating better way to put a hook
to add custom join paths.Regarding to the comment from Tom:
| * The API for add_join_path_hook seems overcomplex, as well as too
| full of implementation details that should remain private to joinpath.c.
| I particularly object to passing the mergeclause lists, which seem
| unlikely to be of interest for non-mergejoin plan types anyway.
| More generally, it seems likely that this hook is at the wrong level
| of abstraction; it forces the hook function to concern itself with a
| lot of stuff about join legality and parameterization (which I note
| your patch3 code fails to do; but that's not an optional thing).
|
The earlier half was already done. My trouble is the later portion.The overall jobs of add_join_path_hook are below:
1. construction of parameterized path information; being saved at
param_source_rel and extra_lateral_rels.
2. Try to add mergejoin paths with underlying Sort node 3. Try to add
mergejoin/nestloop paths without underlying Sort node 4. Try to add
hashjoin pathsIt seems to me the check for join legality and parameterization are
built within individual routines for each join algorithm.
(what does the "join legality check" actually mean?)Probably, it makes sense to provide a common utility function to be
called back from the extension if we can find out a common part for
all the join logics, however, I don't have clear idea to cut off the commonportion.
What's jobs can be done independent from the join algorithm??
I'd like to see ideas around this issue. Of course, I also think it is
still an option to handle it by extension on the initial version.Thanks,
--
NEC OSS Promotion Center / PG-Strom Project KaiGai Kohei
<kaigai@ak.jp.nec.com>-----Original Message-----
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kouhei
Kaigai
Sent: Monday, March 17, 2014 9:29 AM
To: Kaigai Kouhei(海外 浩平); Tom Lane
Cc: Kohei KaiGai; Stephen Frost; Shigeru Hanada; Jim Mlodgenski;
Robert Haas; PgHacker; Peter Eisentraut
Subject: Re: Custom Scan APIs (Re: [HACKERS] Custom Plan node)Hello,
I adjusted the custom-plan interface patch little bit for the
cache-only scan patch; that is a demonstration module for
vacuum-page hook on top of the custom-plan interface.fix_scan_expr() looks to me useful for custom-plan providers that
want to implement its own relation scan logic, even though they can
implement it using fix_expr_common() being already exposed.Also, I removed the hardcoded portion from the nodeCustom.c
although, it may make sense to provide a few template functions to
be called by
custom- plan providers, that performs usual common jobs like
construction of expr- context, assignment of result-slot, open
relations,and so on.
I though the idea during implementation of BeginCustomPlan handler.
(These template functions are not in the attached patch yet.) How
about your opinion?The major portion of this patch is not changed from v10.
Thanks,
--
NEC OSS Promotion Center / PG-Strom Project KaiGai Kohei
<kaigai@ak.jp.nec.com>-----Original Message-----
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kouhei
Kaigai
Sent: Wednesday, March 12, 2014 1:55 PM
To: Tom Lane
Cc: Kohei KaiGai; Stephen Frost; Shigeru Hanada; Jim Mlodgenski;
Robert Haas; PgHacker; Peter Eisentraut
Subject: Re: Custom Scan APIs (Re: [HACKERS] Custom Plan node)Hello,
The attached two patches are the revised custom-plan interface and
example usage that implements existing MergeJoin on top of thisinterface.
According to the discussion last week, I revised the portion where
custom-node is expected to perform a particular kind of task, like
scanning a relation, by putting polymorphism with a set of
callbacks set by custom-plan provider.
So, the core backend can handle this custom-plan node just an
abstracted plan-node with no anticipation.
Even though the subject of this message says "custom-scan", I'd
like to name the interface "custom-plan" instead, because it
became fully arbitrary of extension whether it scan on a particularrelation.
Definition of CustomXXXX data types were simplified:
typedef struct CustomPath
{
Path path;
const struct CustomPathMethods *methods;
} CustomPath;typedef struct CustomPlan
{
Plan plan;
const struct CustomPlanMethods *methods;
} CustomPlan;typedef struct CustomPlanState
{
PlanState ps;
const CustomPlanMethods *methods;
} CustomPlanState;Each types have a base class and a set of function pointers that
characterize the behavior of this custom-plan node.
In usual use-cases, extension is expected to extend these classes
to keep their private data fields needed to implement its ownfunctionalities.
Most of the methods are designed to work as a thin layer towards
existing planner / executor functions, so custom-plan provides has
to be responsible to implement its method to communicate with core
backend as built-in ones doing.Regarding to the topic we discussed last week,
* CUSTOM_VAR has gone.
The reason why CUSTOM_VAR was needed is, we have to handle EXPLAIN
command output (including column names being referenced) even if a
custom-plan node replaced a join but has no underlying subplans onleft/right subtrees.
A typical situation like this is a remote-join implementation that
I tried to extend postgres_fdw on top of the previous interface.
It retrieves a flat result set of the remote join execution, thus
has no subplan locally. On the other hand, EXPLAIN tries to find
out "actual" Var node from the underlying subplan if a Var node
has special varno (INNER/OUTER/INDEX).
I put a special method to solve the problem. GetSpecialCustomVar
method is called if a certain Var node of custom-plan has a
special varno, then custom-plan provider can inform the core
backend an expression node to be referenced by this Var node.
It allows to solve the column name without recursive walking on
the subtrees, so it enables a custom-plan node that replaces a
part ofplan-tree.
This method is optional, so available to adopt existing way if
custom-plan provider does not do anything special.* Functions to be exposed, from static declaration
Right now, static functions are randomly exposed on demand.
So, we need more investigation which functions are needed, and
which others are not.
According to my trial, the part-2 patch that is MergeJoin on top
of the custom-plan interface, class of functions that recursively
walk on subplan tree have to be exposed. Like, ExplainPreScanNode,
create_plan_recurse, set_plan_refs, fix_expr_common orfinalize_plan.
In case when custom-plan performs like built-in Append node, it
keeps a list of sub-plans in its private field, so the core
backend cannot know existence of sub-plans, thus its unavailable
to make subplan, unavailable to output EXPLAIN and so on.
It does not make sense to reworking on the extension side again.
Also, createplan.c has many useful functions to construct
plan-node, however, most of them are static because all the
built-in plan-node are constructed by the routines in this file,
we didn't need to expose them to others. I think, functions in
createplan.c being called by
create_xxxx_plan() functions to construct plan-node should be
exposed for extension's convenient.* Definition of add_join_path_hook
I didn't have idea to improve the definition and location of this
hook, so it is still on the tail of the add_paths_to_joinrel().
Its definition was a bit adjusted according to the feedback on the
pgsql-hackers. I omitted the "mergeclause_list" and " semifactors"
from the argument list. Indeed, these are specific to the built-in
MergeJoin logic and easy to reproduce.* Hook location of add_scan_path_hook
I moved the add_scan_path_hook and set_cheapest() into
set_base_rel_pathlists() from various caller locations;
set_xxxx_pathlist() functions typically.
It enabled to consolidate the location to add custom-path for base
relations.* CustomMergeJoin as a proof-of-concept
The contrib module in the part-2 portion is, a merge-join
implementation on top of custom-plan interface, even though 99% of
its implementation is identical with built-in ones.
Its purpose is to demonstrate a custom join logic can be
implemented using custom-plan interface, even if custom-plan node
has underlying sub-plans unlike previous my examples.Thanks,
--
NEC OSS Promotion Center / PG-Strom Project KaiGai Kohei
<kaigai@ak.jp.nec.com>-----Original Message-----
From: Tom Lane [mailto:tgl@sss.pgh.pa.us]
Sent: Friday, March 07, 2014 3:09 AM
To: Kaigai Kouhei(海外 浩平)
Cc: Kohei KaiGai; Stephen Frost; Shigeru Hanada; Jim Mlodgenski;
Robert Haas; PgHacker; Peter Eisentraut
Subject: Re: Custom Scan APIs (Re: [HACKERS] Custom Plan node)Kouhei Kaigai <kaigai@ak.jp.nec.com> writes:
I expected to include simple function pointers for copying and
text-output as follows:typedef struct {
Plan plan;
:
NodeCopy_function node_copy;
NodeTextOut_function node_textout;
} Custom;I was thinking more like
typedef struct CustomPathFuncs {
const char *name; /* used for debugging purposesonly */
NodeCopy_function node_copy;
NodeTextOut_function node_textout;
... etc etc etc ...
} CustomPathFuncs;typedef struct CustomPath {
Path path;
const CustomPathFuncs *funcs;
... maybe a few more fields here, but not too darn many ...
} CustomPath;and similarly for CustomPlan.
The advantage of this way is it's very cheap for (what I expect
will
be) the common case where an extension has a fixed set of
support functions for its custom paths and plans. It just
declares a static constant CustomPathFuncs struct, and puts a
pointer to that into itspaths.
If an extension really needs to set the support functions on a
per-object basis, it can do this:typdef struct MyCustomPath {
CustomPath cpath;
CustomPathFuncs funcs;
... more fields ...
} MyCustomPath;and then initialization of a MyCustomPath would include
mypath->cpath.funcs = &mypath->funcs;
mypath->funcs.node_copy = MyCustomPathCopy;
... etc etc ...In this case we're arguably wasting one pointer worth of space
in the path, but considering the number of function pointers
such a path will be carrying, I don't think that's much of an objection.So? If you did that, then you wouldn't have renumbered the
Vars as INNER/OUTER. I don't believe that CUSTOM_VAR is
necessary at all; if it is needed, then there would also be a
need for an additional tuple slot in executor contexts, which
you haven'tprovided.
For example, the enhanced postgres_fdw fetches the result set
of remote join query, thus a tuple contains the fields come
from bothside.
In this case, what varno shall be suitable to put?
Not sure what we'd do for the general case, but CUSTOM_VAR isn't
thesolution.
Consider for example a join where both tables supply columns
named"id"
--- if you put them both in one tupledesc then there's no non-kluge way to identify them.Possibly the route to a solution involves adding another
plan-node callback function that ruleutils.c would use for
printing Vars in customjoin nodes.
Or maybe we could let the Vars keep their original RTE numbers,
though that would complicate life at execution time.Anyway, if we're going to punt on add_join_path_hook for the
time being, this problem can probably be left to solve later.
It won't arise for simple table-scan cases, nor for single-input
plan nodes suchas sorts.
regards, tom lane
Attachments:
pgsql-v9.4-mvcc_allows_cache.v13.patchapplication/octet-stream; name=pgsql-v9.4-mvcc_allows_cache.v13.patchDownload
src/backend/utils/time/tqual.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index 89f5ff8..ab59105 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -105,11 +105,18 @@ static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
*
* The caller should pass xid as the XID of the transaction to check, or
* InvalidTransactionId if no check is needed.
+ *
+ * In case when the supplied HeapTuple is not associated with a particular
+ * buffer, it just returns without any jobs. It may happen when an extension
+ * caches tuple with their own way.
*/
static inline void
SetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid)
{
+ if (BufferIsInvalid(buffer))
+ return;
+
if (TransactionIdIsValid(xid))
{
/* NB: xid must be known committed here! */
pgsql-v9.4-vacuum_page_hook.v13.patchapplication/octet-stream; name=pgsql-v9.4-vacuum_page_hook.v13.patchDownload
src/backend/access/heap/pruneheap.c | 13 +++++++++++++
src/include/access/heapam.h | 7 +++++++
2 files changed, 20 insertions(+)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 3c69e1b..5bf7cea 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -43,6 +43,9 @@ typedef struct
bool marked[MaxHeapTuplesPerPage + 1];
} PruneState;
+/* Callback for each page pruning */
+heap_page_prune_hook_type heap_page_prune_hook = NULL;
+
/* Local functions */
static int heap_prune_chain(Relation relation, Buffer buffer,
OffsetNumber rootoffnum,
@@ -311,6 +314,16 @@ heap_page_prune(Relation relation, Buffer buffer, TransactionId OldestXmin,
* and update FSM with the remaining space.
*/
+ /*
+ * This callback allows extensions to synchronize their own status with
+ * heap image on the disk, when this buffer page is vacuumed.
+ */
+ if (heap_page_prune_hook)
+ (*heap_page_prune_hook)(relation,
+ buffer,
+ ndeleted,
+ OldestXmin,
+ prstate.latestRemovedXid);
return ndeleted;
}
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 0f80257..e88e839 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -164,6 +164,13 @@ extern void heap_restrpos(HeapScanDesc scan);
extern void heap_sync(Relation relation);
/* in heap/pruneheap.c */
+typedef void (*heap_page_prune_hook_type)(Relation relation,
+ Buffer buffer,
+ int ndeleted,
+ TransactionId OldestXmin,
+ TransactionId latestRemovedXid);
+extern PGDLLIMPORT heap_page_prune_hook_type heap_page_prune_hook;
+
extern void heap_page_prune_opt(Relation relation, Buffer buffer);
extern int heap_page_prune(Relation relation, Buffer buffer,
TransactionId OldestXmin,
pgsql-v9.4-custom-scan.part-1.v13.patchapplication/octet-stream; name=pgsql-v9.4-custom-scan.part-1.v13.patchDownload
doc/src/sgml/custom-plan.sgml | 315 ++++++++++++++++++++++++++++++++
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/postgres.sgml | 1 +
src/backend/commands/explain.c | 45 ++++-
src/backend/executor/Makefile | 2 +-
src/backend/executor/execAmi.c | 23 +++
src/backend/executor/execProcnode.c | 15 ++
src/backend/executor/nodeCustom.c | 73 ++++++++
src/backend/nodes/copyfuncs.c | 42 +++++
src/backend/nodes/outfuncs.c | 40 ++++
src/backend/optimizer/path/allpaths.c | 34 ++--
src/backend/optimizer/path/joinpath.c | 16 ++
src/backend/optimizer/plan/createplan.c | 55 ++++--
src/backend/optimizer/plan/setrefs.c | 25 ++-
src/backend/optimizer/plan/subselect.c | 128 +++++++------
src/backend/utils/adt/ruleutils.c | 56 ++++++
src/include/commands/explain.h | 1 +
src/include/executor/nodeCustom.h | 30 +++
src/include/nodes/execnodes.h | 12 ++
src/include/nodes/nodes.h | 6 +
src/include/nodes/plannodes.h | 81 ++++++++
src/include/nodes/relation.h | 29 +++
src/include/optimizer/paths.h | 17 ++
src/include/optimizer/planmain.h | 12 ++
src/include/optimizer/subselect.h | 7 +
25 files changed, 962 insertions(+), 104 deletions(-)
diff --git a/doc/src/sgml/custom-plan.sgml b/doc/src/sgml/custom-plan.sgml
new file mode 100644
index 0000000..8d456f9
--- /dev/null
+++ b/doc/src/sgml/custom-plan.sgml
@@ -0,0 +1,315 @@
+<!-- doc/src/sgml/custom-plan.sgml -->
+
+<chapter id="custom-plan">
+ <title>Writing A Custom Plan Provider</title>
+
+ <indexterm zone="custom-plan">
+ <primary>custom plan</primary>
+ <secondary>handler for</secondary>
+ </indexterm>
+ <para>
+ The custom-plan interface enables extensions to implement its custom
+ behavior, instead of built-in plan node, according to the cost based
+ optimizer manner.
+ Its key component is <literal>CustomPlan</> node that has usual
+ <literal>Plan</> field and a table of function-pointers; that performs
+ like methods of base class in object oriented programming language,
+ thus <literal>CustomPlan</> node works as a polymorphism plan / execution
+ node.
+ The core backend does not assume anything about behavior of this node
+ type, thus, note that it is responsibility of the custom-plan provider
+ to work its custom node as if the built-in plan / execution node being
+ replaced.
+ </para>
+ <para>
+ Overall steps to use this custom-plan interface is below.
+ </para>
+ <para>
+ Custom-plan provider can add <literal>CustomPath</> on a particular
+ relation scan using <literal>add_scan_path_hook</> or a particular
+ relations join using <literal>add_join_path_hook</>.
+ Then, the planner chooses the cheapest path towards a particular
+ scan or join in the built-in and custom paths.
+ So, <literal>CustomPath</> node has to have proper cost estimation
+ for right plan selection, no need to say.
+ </para>
+ <para>
+ Usually, custom-plan provider extends <literal>CustomPath</> type
+ to have its private fields, like:
+<programlisting>
+typedef struct {
+ CustomPath cpath;
+ :
+ List *somethin_private;
+ :
+} YourOwnCustomPath;
+</programlisting>
+ You can also extend <literal>CustomPlan</> and <literal>CustomPlanState</>
+ type with similar manner.
+ </para>
+ <para>
+ <literal>CustomPathMethods</> is table of function-pointers
+ for <literal>CustomPath</>, and <literal>CustomPlanMethods</> is
+ table of function-pointers for <literal>CustomPlan</> and
+ <literal>CustomPlanState</>.
+ Extension has to implement the functions according to the specification
+ in the next section.
+ </para>
+
+ <sect1 id="custom-plan-spec">
+ <title>Specification of Custom Plan Interface</title>
+ <sect2 id="custom-scan-register">
+ <title>Registration of custom-plan path</title>
+ <para>
+ The first task of custom-plan provide is to add <literal>CustomPath</>
+ towards a particular relation scan or relations join.
+ Right now, only scan and join are supported by planner thus cost-based
+ optimization shall be applied, however, other kind of nodes (like sort,
+ aggregate and so on...) are not supported.
+ </para>
+ <para>
+<programlisting>
+typedef void (*add_scan_path_hook_type)(PlannerInfo *root,
+ RelOptInfo *baserel,
+ RangeTblEntry *rte);
+extern PGDLLIMPORT add_scan_path_hook_type add_scan_path_hook;
+</programlisting>
+ Custom-plan provider can add its custom-path using
+ <literal>add_scan_path_hook</> to provide alternative way to scan
+ the relation being specified.
+ </para>
+ <para>
+<programlisting>
+typedef void (*add_join_path_hook_type)(PlannerInfo *root,
+ RelOptInfo *joinrel,
+ RelOptInfo *outerrel,
+ RelOptInfo *innerrel,
+ JoinType jointype,
+ SpecialJoinInfo *sjinfo,
+ List *restrictlist,
+ Relids param_source_rels,
+ Relids extra_lateral_rels);
+extern PGDLLIMPORT add_join_path_hook_type add_join_path_hook;
+</programlisting>
+ Also, custom-plan provider can add its custom-path using
+ <literal>add_join_path_hook</> to provide alternative way to join
+ two relations (note that both or either of relations are also joined
+ relations, not only base relations) being specified.
+ </para>
+ </sect2>
+
+ <sect2 id="custom-path-methods">
+ <title>Methods of CustomPath</title>
+ <para>
+ This section introduces the method functions of <literal>CustomPath</>.
+ </para>
+ <para>
+<programlisting>
+CustomPlan *
+CreateCustomPlan(PlannerInfo *root,
+ CustomPath *custom_path);
+</programlisting>
+ This method pupolates a node object that (at least) extends
+ <literal>CustomPlan</> data type, according to the supplied
+ <literal>CustomPath</>.
+ If this custom-plan support mark-and-restore position, its
+ node tag should be <literal>CustomPlanMarkPos</>, instead of
+ <literal>CustomPlan</>.
+ </para>
+ <para>
+<programlisting>
+void
+TextOutCustomPath(StringInfo str, Node *node);
+</programlisting>
+ This method is needed to support <literal>nodeToString</> for your
+ custom path type to dump its private fields also.
+ The message format has to follow the manner in <filename>outfuncs.c</>.
+ </para>
+ </sect2>
+ <sect2 id="custom-plan-methods">
+ <title>Methods of CustomPlan</title>
+ <para>
+ This section introduces the method functions of <literal>CustomPlan</>.
+ </para>
+ <para>
+<programlisting>
+void
+SetCustomPlanRef(PlannerInfo *root,
+ CustomPlan *custom_plan,
+ int rtoffset);
+</programlisting>
+ This method requires custom-plan provides to adjust <literal>Var</> node
+ references in the supplied <literal>CustomPlan</> node.
+ Usually, it shall be shifted by <literal>rtoffset</>, or replaced by
+ <literal>INNER_VAR</> or <literal>OUTER_VAR</> if it references either
+ left or right subplan.
+ </para>
+ <para>
+<programlisting>
+bool
+SupportBackwardScan(CustomPlan *custom_plan);
+</programlisting>
+ This optional method informs the core backend whether this custom-plan
+ supports backward scan capability, or not.
+ If this method is implemented and returns <literal>true</>, it means
+ this custom-plan node supports backward scan. Elsewhere, it is not
+ available.
+ </para>
+ <para>
+<programlisting>
+void
+FinalizeCustomPlan(PlannerInfo *root,
+ CustomPlan *custom_plan,
+ Bitmapset **paramids,
+ Bitmapset **valid_params,
+ Bitmapset **scan_params);
+</programlisting>
+ This optional method informs the core backend which query parameters
+ are referenced in this custom-plan node, in addition to the ones
+ considered in the <literal>targetlist</> and <literal>qual</> fields
+ of the base <literal>Plan</> node.
+ If parameters are found in the private data field managed by custom-
+ plan provider, it needs to update the supplied bitmapset as expected
+ in the <literal>finalize_plan()</>.
+ </para>
+ <para>
+<programlisting>
+CustomPlanState *
+BeginCustomPlan(CustomPlan *custom_plan,
+ EState *estate,
+ int eflags);
+</programlisting>
+ This method populates a <literal>CustomPlanState</> object according to
+ the supplied <literal>CustomPlan</>, and initializes execution of this
+ custom-plan node, first of all.
+ </para>
+ <para>
+<programlisting>
+TupleTableSlot *
+ExecCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+ It fetches one tuple from this custom-plan node. This custom-plan node
+ has to set a valid tuple on the <literal>ps_ResultTupleSlot</> and
+ return if any, or returns <literal>NULL</> to inform the upper node
+ it already reached end of the scan.
+ </para>
+ <para>
+<programlisting>
+Node *
+MultiExecCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+ Unlike <literal>ExecCustomPlan</>, it allows upper node to fetch
+ multiple tuples, however, you need to pay attention the data format
+ and the way to return it because it fully depends on the type of
+ upper node.
+ </para>
+ <para>
+<programlisting>
+void
+EndCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+ It ends the execution of this custom-plan node, and releases the
+ resources being allocated. Usually, it is not important to release
+ memory in the per execution memory context, so custom-plan provider
+ should be responsible to its own resources regardless of the framework.
+ </para>
+ <para>
+<programlisting>
+void
+ReScanCustomPlan(CustomPlanState *cpstate);
+</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
+MarkPosCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+ It is optional, but should be implemented if <literal>CustomPlanMarkPos</>
+ was applied, instead of <literal>CustomPlan</>.
+ It saves the current position of the custom-plan on somewhere private
+ state, to restore the position later.
+ </para>
+ <para>
+<programlisting>
+void
+RestrPosCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+ It is optional, but should be implemented if <literal>CustomPlanMarkPos</>
+ was applied, instead of <literal>CustomPlan</>.
+ It restores the current position of the custom-plan from the private
+ information being saved somewhere at <literal>MarkPosCustomPlan</>.
+ </para>
+ <para>
+<programlisting>
+void
+ExplainCustomPlanTargetRel(CustomPlanState *cpstate,
+ ExplainState *es);
+</programlisting>
+ It shows the target relation, if this custom-plan node replaced
+ a particular relation scan. Because of implementation reason, this
+ method is separated from the <literal>ExplainCustomPlan</>.
+ </para>
+ <para>
+<programlisting>
+void
+ExplainCustomPlan(CustomPlanState *cpstate,
+ List *ancestors,
+ ExplainState *es);
+</programlisting>
+ It put properties of this custom-plan node into the supplied
+ <literal>ExplainState</> according to the usual <command>EXPLAIN</>
+ manner.
+ </para>
+ <para>
+<programlisting>
+Bitmapset *
+GetRelidsCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+ It returns a set of range-table indexes being scanned by this custom-
+ plan node. In case of multiple relations are underlying, it is not
+ always singleton bitmap.
+ </para>
+ <para>
+<programlisting>
+Node *
+GetSpecialCustomVar(CustomPlanState *cpstate,
+ Var *varnode);
+</programlisting>
+ This optional method returns an expression node to be referenced by
+ the supplied <literal>varnode</> that has special <literal>varno</>
+ (<literal>INNER_VAR</>, <literal>OUTER_VAR</> or <literal>INDEX_VAR</>).
+ <command>EXPLAIN</> command shows column name being referenced in the
+ targetlist or qualifiers of plan nodes. If a var node has special
+ <literal>varno</>, it recursively walks down the underlying subplan to
+ ensure the actual expression referenced by this special varno.
+ In case when a custom-plan node replaced a join node but does not have
+ underlying sub-plan on the left- and right-tree, it is unavailable to
+ use a usual logic, so custom-plan provider has to implement this method
+ to inform the core backend the expression node being referenced by
+ the supplied <literal>varnode</> that has special <literal>varno</>.
+ If this method is not implemented or returns <literal>NULL</>,
+ the core backend solves the special varnode reference as usual.
+ </para>
+ <para>
+<programlisting>
+void
+TextOutCustomPlan(StringInfo str, const CustomPlan *node);
+</programlisting>
+ This method is needed to support <literal>nodeToString</> for your
+ custom plan type to dump its private fields also.
+ The message format has to follow the manner in <filename>outfuncs.c</>.
+ </para>
+ <para>
+<programlisting>
+CustomPlan *
+CopyCustomPlan(const CustomPlan *from);
+</programlisting>
+ This methos is needed to support <literal>copyObject</> for your
+ custom plan type to copy its private fields also.
+ </para>
+ </sect2>
+ </sect1>
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index ab6fcf7..f3b8362 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -92,6 +92,7 @@
<!ENTITY nls SYSTEM "nls.sgml">
<!ENTITY plhandler SYSTEM "plhandler.sgml">
<!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
+<!ENTITY custom-plan SYSTEM "custom-plan.sgml">
<!ENTITY logicaldecoding SYSTEM "logicaldecoding.sgml">
<!ENTITY protocol SYSTEM "protocol.sgml">
<!ENTITY sources SYSTEM "sources.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index 9bde108..5f415c6 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -242,6 +242,7 @@
&nls;
&plhandler;
&fdwhandler;
+ &custom-plan;
&geqo;
&indexam;
&gist;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 08f3167..ff9fc7b 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -19,6 +19,7 @@
#include "commands/defrem.h"
#include "commands/prepare.h"
#include "executor/hashjoin.h"
+#include "executor/nodeCustom.h"
#include "foreign/fdwapi.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
@@ -51,7 +52,6 @@ static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
ExplainState *es);
static double elapsed_time(instr_time *starttime);
-static void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
static void ExplainPreScanMemberNodes(List *plans, PlanState **planstates,
Bitmapset **rels_used);
static void ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used);
@@ -700,7 +700,7 @@ elapsed_time(instr_time *starttime)
* This ensures that we don't confusingly assign un-suffixed aliases to RTEs
* that never appear in the EXPLAIN output (such as inheritance parents).
*/
-static void
+void
ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
{
Plan *plan = planstate->plan;
@@ -721,6 +721,16 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
*rels_used = bms_add_member(*rels_used,
((Scan *) plan)->scanrelid);
break;
+ case T_CustomPlan:
+ case T_CustomPlanMarkPos:
+ {
+ CustomPlanState *cpstate = (CustomPlanState *)planstate;
+ Bitmapset *temp
+ = cpstate->methods->GetRelidsCustomPlan(cpstate);
+
+ *rels_used = bms_union(*rels_used, temp);
+ }
+ break;
case T_ModifyTable:
/* cf ExplainModifyTarget */
*rels_used = bms_add_member(*rels_used,
@@ -847,6 +857,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
const char *operation = NULL;
+ const char *custom_name = NULL;
int save_indent = es->indent;
bool haschildren;
@@ -935,6 +946,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_ForeignScan:
pname = sname = "Foreign Scan";
break;
+ case T_CustomPlan:
+ sname = "Custom";
+ custom_name = ((CustomPlan *) plan)->methods->CustomName;
+ if (custom_name != NULL)
+ pname = psprintf("Custom (%s)", custom_name);
+ else
+ pname = sname;
+ break;
case T_Material:
pname = sname = "Materialize";
break;
@@ -1036,6 +1055,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
ExplainPropertyText("Parent Relationship", relationship, es);
if (plan_name)
ExplainPropertyText("Subplan Name", plan_name, es);
+ if (custom_name)
+ ExplainPropertyText("Custom", custom_name, es);
}
switch (nodeTag(plan))
@@ -1051,6 +1072,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_ForeignScan:
ExplainScanTarget((Scan *) plan, es);
break;
+ case T_CustomPlan:
+ {
+ CustomPlanState *cps = (CustomPlanState *)planstate;
+
+ if (cps->methods->ExplainCustomPlanTargetRel)
+ cps->methods->ExplainCustomPlanTargetRel(cps, es);
+ }
+ break;
case T_IndexScan:
{
IndexScan *indexscan = (IndexScan *) plan;
@@ -1347,6 +1376,18 @@ ExplainNode(PlanState *planstate, List *ancestors,
planstate, es);
show_foreignscan_info((ForeignScanState *) planstate, es);
break;
+ case T_CustomPlan:
+ show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+ if (plan->qual)
+ show_instrumentation_count("Rows Removed by Filter", 1,
+ planstate, es);
+ if (((CustomPlanState *) planstate)->methods->ExplainCustomPlan)
+ {
+ CustomPlanState *cpstate = (CustomPlanState *) planstate;
+
+ cpstate->methods->ExplainCustomPlan(cpstate, ancestors, es);
+ }
+ break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual,
"Join Filter", planstate, ancestors, es);
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 6081b56..4dece5a 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -15,7 +15,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
- nodeBitmapAnd.o nodeBitmapOr.o \
+ nodeBitmapAnd.o nodeBitmapOr.o nodeCustom.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
nodeLimit.o nodeLockRows.o \
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 8c01a63..47e7a3c 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -21,6 +21,7 @@
#include "executor/nodeBitmapIndexscan.h"
#include "executor/nodeBitmapOr.h"
#include "executor/nodeCtescan.h"
+#include "executor/nodeCustom.h"
#include "executor/nodeForeignscan.h"
#include "executor/nodeFunctionscan.h"
#include "executor/nodeGroup.h"
@@ -197,6 +198,10 @@ ExecReScan(PlanState *node)
ExecReScanForeignScan((ForeignScanState *) node);
break;
+ case T_CustomPlanState:
+ ExecReScanCustomPlan((CustomPlanState *) node);
+ break;
+
case T_NestLoopState:
ExecReScanNestLoop((NestLoopState *) node);
break;
@@ -291,6 +296,10 @@ ExecMarkPos(PlanState *node)
ExecValuesMarkPos((ValuesScanState *) node);
break;
+ case T_CustomPlanState:
+ ExecCustomMarkPos((CustomPlanState *) node);
+ break;
+
case T_MaterialState:
ExecMaterialMarkPos((MaterialState *) node);
break;
@@ -348,6 +357,10 @@ ExecRestrPos(PlanState *node)
ExecValuesRestrPos((ValuesScanState *) node);
break;
+ case T_CustomPlanState:
+ ExecCustomRestrPos((CustomPlanState *) node);
+ break;
+
case T_MaterialState:
ExecMaterialRestrPos((MaterialState *) node);
break;
@@ -390,6 +403,7 @@ ExecSupportsMarkRestore(NodeTag plantype)
case T_ValuesScan:
case T_Material:
case T_Sort:
+ case T_CustomPlanMarkPos:
return true;
case T_Result:
@@ -465,6 +479,15 @@ ExecSupportsBackwardScan(Plan *node)
return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
TargetListSupportsBackwardScan(node->targetlist);
+ case T_CustomPlan:
+ {
+ CustomPlan *cplan = (CustomPlan *) node;
+
+ if (cplan->methods->SupportBackwardScan)
+ return cplan->methods->SupportBackwardScan(cplan);
+ }
+ return false;
+
case T_Material:
case T_Sort:
/* these don't evaluate tlist */
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index c5ecd18..5aa117b 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -85,6 +85,7 @@
#include "executor/nodeBitmapIndexscan.h"
#include "executor/nodeBitmapOr.h"
#include "executor/nodeCtescan.h"
+#include "executor/nodeCustom.h"
#include "executor/nodeForeignscan.h"
#include "executor/nodeFunctionscan.h"
#include "executor/nodeGroup.h"
@@ -244,6 +245,12 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags);
break;
+ case T_CustomPlan:
+ case T_CustomPlanMarkPos:
+ result = (PlanState *) ExecInitCustomPlan((CustomPlan *) node,
+ estate, eflags);
+ break;
+
/*
* join nodes
*/
@@ -442,6 +449,10 @@ ExecProcNode(PlanState *node)
result = ExecForeignScan((ForeignScanState *) node);
break;
+ case T_CustomPlanState:
+ result = ExecCustomPlan((CustomPlanState *) node);
+ break;
+
/*
* join nodes
*/
@@ -678,6 +689,10 @@ ExecEndNode(PlanState *node)
ExecEndForeignScan((ForeignScanState *) node);
break;
+ case T_CustomPlanState:
+ ExecEndCustomPlan((CustomPlanState *) node);
+ break;
+
/*
* join nodes
*/
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
new file mode 100644
index 0000000..e3c8f58
--- /dev/null
+++ b/src/backend/executor/nodeCustom.c
@@ -0,0 +1,73 @@
+/* ------------------------------------------------------------------------
+ *
+ * nodeCustom.c
+ * Routines to handle execution of custom plan node
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * ------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "executor/executor.h"
+#include "executor/nodeCustom.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "parser/parsetree.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+CustomPlanState *
+ExecInitCustomPlan(CustomPlan *custom_plan, EState *estate, int eflags)
+{
+ CustomPlanState *cpstate
+ = custom_plan->methods->BeginCustomPlan(custom_plan, estate, eflags);
+
+ Assert(IsA(cpstate, CustomPlanState));
+
+ return cpstate;
+}
+
+TupleTableSlot *
+ExecCustomPlan(CustomPlanState *cpstate)
+{
+ Assert(cpstate->methods->ExecCustomPlan != NULL);
+ return cpstate->methods->ExecCustomPlan(cpstate);
+}
+
+Node *
+MultiExecCustomPlan(CustomPlanState *cpstate)
+{
+ Assert(cpstate->methods->MultiExecCustomPlan != NULL);
+ return cpstate->methods->MultiExecCustomPlan(cpstate);
+}
+
+void
+ExecEndCustomPlan(CustomPlanState *cpstate)
+{
+ Assert(cpstate->methods->EndCustomPlan != NULL);
+ cpstate->methods->EndCustomPlan(cpstate);
+}
+
+void
+ExecReScanCustomPlan(CustomPlanState *cpstate)
+{
+ Assert(cpstate->methods->ReScanCustomPlan != NULL);
+ cpstate->methods->ReScanCustomPlan(cpstate);
+}
+
+void
+ExecCustomMarkPos(CustomPlanState *cpstate)
+{
+ Assert(cpstate->methods->MarkPosCustomPlan != NULL);
+ cpstate->methods->MarkPosCustomPlan(cpstate);
+}
+
+void
+ExecCustomRestrPos(CustomPlanState *cpstate)
+{
+ Assert(cpstate->methods->RestrPosCustomPlan != NULL);
+ cpstate->methods->RestrPosCustomPlan(cpstate);
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c89d808..18505cd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -598,6 +598,42 @@ _copyForeignScan(const ForeignScan *from)
}
/*
+ * _copyCustomPlan
+ */
+static CustomPlan *
+_copyCustomPlan(const CustomPlan *from)
+{
+ CustomPlan *newnode = from->methods->CopyCustomPlan(from);
+
+ Assert(IsA(newnode, CustomPlan));
+ return newnode;
+}
+
+/*
+ * _copyCustomPlanMarkPos
+ */
+static CustomPlanMarkPos *
+_copyCustomPlanMarkPos(const CustomPlanMarkPos *from)
+{
+ CustomPlanMarkPos *newnode = from->methods->CopyCustomPlan(from);
+
+ Assert(IsA(newnode, CustomPlanMarkPos));
+ return newnode;
+}
+
+/* copy common part of CustomPlan */
+void
+CopyCustomPlanCommon(const Node *__from, Node *__newnode)
+{
+ CustomPlan *from = (CustomPlan *) __from;
+ CustomPlan *newnode = (CustomPlan *) __newnode;
+
+ ((Node *) newnode)->type = nodeTag(from);
+ CopyPlanFields((const Plan *) from, (Plan *) newnode);
+ COPY_SCALAR_FIELD(methods);
+}
+
+/*
* CopyJoinFields
*
* This function copies the fields of the Join node. It is used by
@@ -3983,6 +4019,12 @@ copyObject(const void *from)
case T_ForeignScan:
retval = _copyForeignScan(from);
break;
+ case T_CustomPlan:
+ retval = _copyCustomPlan(from);
+ break;
+ case T_CustomPlanMarkPos:
+ retval = _copyCustomPlanMarkPos(from);
+ break;
case T_Join:
retval = _copyJoin(from);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bfb4b9f..8a93bc5 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -563,6 +563,27 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
WRITE_BOOL_FIELD(fsSystemCol);
}
+/* dump common part of CustomPlan structure */
+static void
+_outCustomPlan(StringInfo str, const CustomPlan *node)
+{
+ WRITE_NODE_TYPE("CUSTOMPLAN");
+ _outPlanInfo(str, (const Plan *) node);
+ appendStringInfo(str, " :methods");
+ _outToken(str, node->methods->CustomName);
+ node->methods->TextOutCustomPlan(str, node);
+}
+
+static void
+_outCustomPlanMarkPos(StringInfo str, const CustomPlanMarkPos *node)
+{
+ WRITE_NODE_TYPE("CUSTOMPLANMARKPOS");
+ _outPlanInfo(str, (const Plan *) node);
+ appendStringInfo(str, " :methods");
+ _outToken(str, node->methods->CustomName);
+ node->methods->TextOutCustomPlan(str, node);
+}
+
static void
_outJoin(StringInfo str, const Join *node)
{
@@ -1581,6 +1602,16 @@ _outForeignPath(StringInfo str, const ForeignPath *node)
}
static void
+_outCustomPath(StringInfo str, const CustomPath *node)
+{
+ WRITE_NODE_TYPE("CUSTOMPATH");
+ _outPathInfo(str, (const Path *) node);
+ appendStringInfo(str, " :methods");
+ _outToken(str, node->methods->CustomName);
+ node->methods->TextOutCustomPath(str, (Node *)node);
+}
+
+static void
_outAppendPath(StringInfo str, const AppendPath *node)
{
WRITE_NODE_TYPE("APPENDPATH");
@@ -2828,6 +2859,12 @@ _outNode(StringInfo str, const void *obj)
case T_ForeignScan:
_outForeignScan(str, obj);
break;
+ case T_CustomPlan:
+ _outCustomPlan(str, obj);
+ break;
+ case T_CustomPlanMarkPos:
+ _outCustomPlanMarkPos(str, obj);
+ break;
case T_Join:
_outJoin(str, obj);
break;
@@ -3036,6 +3073,9 @@ _outNode(StringInfo str, const void *obj)
case T_ForeignPath:
_outForeignPath(str, obj);
break;
+ case T_CustomPath:
+ _outCustomPath(str, obj);
+ break;
case T_AppendPath:
_outAppendPath(str, obj);
break;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 03be7b1..6c1ea7e 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -47,6 +47,8 @@ int geqo_threshold;
/* Hook for plugins to replace standard_join_search() */
join_search_hook_type join_search_hook = NULL;
+/* Hook for plugins to add custom scan paths */
+add_scan_path_hook_type add_scan_path_hook = NULL;
static void set_base_rel_sizes(PlannerInfo *root);
static void set_base_rel_pathlists(PlannerInfo *root);
@@ -323,7 +325,7 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
}
break;
case RTE_SUBQUERY:
- /* Subquery --- fully handled during set_rel_size */
+ /* Subquery --- path was added during set_rel_size */
break;
case RTE_FUNCTION:
/* RangeFunction */
@@ -334,12 +336,19 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
set_values_pathlist(root, rel, rte);
break;
case RTE_CTE:
- /* CTE reference --- fully handled during set_rel_size */
+ /* CTE reference --- path was added during set_rel_size */
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
}
+
+ /* Also, consider custom plans */
+ if (add_scan_path_hook)
+ (*add_scan_path_hook)(root, rel, rte);
+
+ /* Select cheapest path */
+ set_cheapest(rel);
}
#ifdef OPTIMIZER_DEBUG
@@ -388,9 +397,6 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Consider TID scans */
create_tidscan_paths(root, rel);
-
- /* Now find the cheapest of the paths for this rel */
- set_cheapest(rel);
}
/*
@@ -416,9 +422,6 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Call the FDW's GetForeignPaths function to generate path(s) */
rel->fdwroutine->GetForeignPaths(root, rel, rte->relid);
-
- /* Select cheapest path */
- set_cheapest(rel);
}
/*
@@ -1235,9 +1238,6 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
/* Generate appropriate path */
add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer));
-
- /* Select cheapest path (pretty easy in this case...) */
- set_cheapest(rel);
}
/*
@@ -1306,9 +1306,6 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_functionscan_path(root, rel,
pathkeys, required_outer));
-
- /* Select cheapest path (pretty easy in this case...) */
- set_cheapest(rel);
}
/*
@@ -1329,9 +1326,6 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_valuesscan_path(root, rel, required_outer));
-
- /* Select cheapest path (pretty easy in this case...) */
- set_cheapest(rel);
}
/*
@@ -1398,9 +1392,6 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_ctescan_path(root, rel, required_outer));
-
- /* Select cheapest path (pretty easy in this case...) */
- set_cheapest(rel);
}
/*
@@ -1451,9 +1442,6 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Generate appropriate path */
add_path(rel, create_worktablescan_path(root, rel, required_outer));
-
- /* Select cheapest path (pretty easy in this case...) */
- set_cheapest(rel);
}
/*
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index a996116..2fb6678 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -21,6 +21,8 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
+/* Hook for plugins to add custom join paths */
+add_join_path_hook_type add_join_path_hook = NULL;
#define PATH_PARAM_BY_REL(path, rel) \
((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))
@@ -259,6 +261,20 @@ add_paths_to_joinrel(PlannerInfo *root,
restrictlist, jointype,
sjinfo, &semifactors,
param_source_rels, extra_lateral_rels);
+
+ /*
+ * 5. Also consider paths being provided with custom execution provider.
+ */
+ if (add_join_path_hook)
+ (*add_join_path_hook)(root,
+ joinrel,
+ outerrel,
+ innerrel,
+ jointype,
+ sjinfo,
+ restrictlist,
+ param_source_rels,
+ extra_lateral_rels);
}
/*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 184d37a..055a818 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -42,11 +42,7 @@
#include "utils/lsyscache.h"
-static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
-static List *build_path_tlist(PlannerInfo *root, Path *path);
-static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
-static void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path);
static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
@@ -77,23 +73,20 @@ static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_pa
List *tlist, List *scan_clauses);
static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
List *tlist, List *scan_clauses);
+static CustomPlan *create_custom_plan(PlannerInfo *root,
+ CustomPath *best_path);
static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
Plan *outer_plan, Plan *inner_plan);
static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
Plan *outer_plan, Plan *inner_plan);
static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan);
-static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
static void process_subquery_nestloop_params(PlannerInfo *root,
List *subplan_params);
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
-static List *get_switched_clauses(List *clauses, Relids outerrelids);
-static List *order_qual_clauses(PlannerInfo *root, List *clauses);
-static void copy_path_costsize(Plan *dest, Path *src);
-static void copy_plan_costsize(Plan *dest, Plan *src);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
Oid indexid, List *indexqual, List *indexqualorig,
@@ -215,7 +208,7 @@ create_plan(PlannerInfo *root, Path *best_path)
* create_plan_recurse
* Recursive guts of create_plan().
*/
-static Plan *
+Plan *
create_plan_recurse(PlannerInfo *root, Path *best_path)
{
Plan *plan;
@@ -261,6 +254,9 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
plan = create_unique_plan(root,
(UniquePath *) best_path);
break;
+ case T_CustomPlan:
+ plan = (Plan *) create_custom_plan(root, (CustomPath *) best_path);
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) best_path->pathtype);
@@ -430,7 +426,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
/*
* Build a target list (ie, a list of TargetEntry) for the Path's output.
*/
-static List *
+List *
build_path_tlist(PlannerInfo *root, Path *path)
{
RelOptInfo *rel = path->parent;
@@ -466,7 +462,7 @@ build_path_tlist(PlannerInfo *root, Path *path)
* Decide whether to use a tlist matching relation structure,
* rather than only those Vars actually referenced.
*/
-static bool
+bool
use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
{
int i;
@@ -526,7 +522,7 @@ use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
* undo the decision made by use_physical_tlist(). Currently, Hash, Sort,
* and Material nodes want this, so they don't have to store useless columns.
*/
-static void
+void
disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
{
/* Only need to undo it for path types handled by create_scan_plan() */
@@ -569,7 +565,7 @@ disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
* in most cases we have only a very bad idea of the probability of the gating
* qual being true.
*/
-static Plan *
+Plan *
create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
{
List *pseudoconstants;
@@ -1072,6 +1068,26 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
return plan;
}
+/*
+ * create_custom_plan
+ * Returns a custom-scan plan for the base relation scanned by 'best_path'
+ * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static CustomPlan *
+create_custom_plan(PlannerInfo *root, CustomPath *best_path)
+{
+ CustomPlan *cplan;
+
+ /* Populate CustomPlan according to the CustomPath */
+ Assert(best_path->methods->CreateCustomPlan != NULL);
+ cplan = best_path->methods->CreateCustomPlan(root, best_path);
+ Assert(IsA(cplan, CustomPlan) || IsA(cplan, CustomPlanMarkPos));
+
+ /* Copy cost data from Path to Plan; no need to make callback do this */
+ copy_path_costsize(&cplan->plan, &best_path->path);
+
+ return cplan;
+}
/*****************************************************************************
*
@@ -2006,7 +2022,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
return scan_plan;
}
-
/*****************************************************************************
*
* JOIN METHODS
@@ -2540,7 +2555,7 @@ create_hashjoin_plan(PlannerInfo *root,
* root->curOuterRels are replaced by Params, and entries are added to
* root->curOuterParams if not already present.
*/
-static Node *
+Node *
replace_nestloop_params(PlannerInfo *root, Node *expr)
{
/* No setup needed for tree walk, so away we go */
@@ -3023,7 +3038,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
* touched; a modified list is returned. We do, however, set the transient
* outer_is_left field in each RestrictInfo to show which side was which.
*/
-static List *
+List *
get_switched_clauses(List *clauses, Relids outerrelids)
{
List *t_list = NIL;
@@ -3089,7 +3104,7 @@ get_switched_clauses(List *clauses, Relids outerrelids)
* instead of bare clauses. It's OK because we only sort by cost, but
* a cost/selectivity combination would likely do the wrong thing.
*/
-static List *
+List *
order_qual_clauses(PlannerInfo *root, List *clauses)
{
typedef struct
@@ -3156,7 +3171,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
* Copy cost and size info from a Path node to the Plan node created from it.
* The executor usually won't use this info, but it's needed by EXPLAIN.
*/
-static void
+void
copy_path_costsize(Plan *dest, Path *src)
{
if (src)
@@ -3179,7 +3194,7 @@ copy_path_costsize(Plan *dest, Path *src)
* Copy cost and size info from a lower plan node to an inserted node.
* (Most callers alter the info after copying it.)
*/
-static void
+void
copy_plan_costsize(Plan *dest, Plan *src)
{
if (src)
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 46affe7..e0fd9a2 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -17,6 +17,7 @@
#include "access/transam.h"
#include "catalog/pg_type.h"
+#include "executor/nodeCustom.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/pathnode.h"
@@ -86,7 +87,6 @@ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob);
static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte);
-static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
static Plan *set_indexonlyscan_references(PlannerInfo *root,
IndexOnlyScan *plan,
int rtoffset);
@@ -94,7 +94,6 @@ static Plan *set_subqueryscan_references(PlannerInfo *root,
SubqueryScan *plan,
int rtoffset);
static bool trivial_subqueryscan(SubqueryScan *plan);
-static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -419,7 +418,7 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
/*
* set_plan_refs: recurse through the Plan nodes of a single subquery level
*/
-static Plan *
+Plan *
set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
{
ListCell *l;
@@ -576,6 +575,22 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
}
break;
+ case T_CustomPlan:
+ case T_CustomPlanMarkPos:
+ {
+ CustomPlan *cplan = (CustomPlan *) plan;
+
+ /*
+ * Extension is responsible to handle set-reference
+ * correctly.
+ */
+ Assert(cplan->methods->SetCustomPlanRef != NULL);
+ cplan->methods->SetCustomPlanRef(root,
+ cplan,
+ rtoffset);
+ }
+ break;
+
case T_NestLoop:
case T_MergeJoin:
case T_HashJoin:
@@ -1057,7 +1072,7 @@ copyVar(Var *var)
* We assume it's okay to update opcode info in-place. So this could possibly
* scribble on the planner's input data structures, but it's OK.
*/
-static void
+void
fix_expr_common(PlannerInfo *root, Node *node)
{
/* We assume callers won't call us on a NULL pointer */
@@ -1126,7 +1141,7 @@ fix_expr_common(PlannerInfo *root, Node *node)
* looking up operator opcode info for OpExpr and related nodes,
* and adding OIDs from regclass Const nodes into root->glob->relationOids.
*/
-static Node *
+Node *
fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
{
fix_scan_expr_context context;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index a3f3583..6b0c762 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -75,12 +75,8 @@ static Query *convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);
static Node *process_sublinks_mutator(Node *node,
process_sublinks_context *context);
-static Bitmapset *finalize_plan(PlannerInfo *root,
- Plan *plan,
- Bitmapset *valid_params,
- Bitmapset *scan_params);
-static bool finalize_primnode(Node *node, finalize_primnode_context *context);
-
+static bool finalize_primnode_walker(Node *node,
+ finalize_primnode_context *context);
/*
* Select a PARAM_EXEC number to identify the given Var as a parameter for
@@ -2045,7 +2041,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
* The return value is the computed allParam set for the given Plan node.
* This is just an internal notational convenience.
*/
-static Bitmapset *
+Bitmapset *
finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
Bitmapset *scan_params)
{
@@ -2070,15 +2066,15 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
*/
/* Find params in targetlist and qual */
- finalize_primnode((Node *) plan->targetlist, &context);
- finalize_primnode((Node *) plan->qual, &context);
+ finalize_primnode_walker((Node *) plan->targetlist, &context);
+ finalize_primnode_walker((Node *) plan->qual, &context);
/* Check additional node-type-specific fields */
switch (nodeTag(plan))
{
case T_Result:
- finalize_primnode(((Result *) plan)->resconstantqual,
- &context);
+ finalize_primnode_walker(((Result *) plan)->resconstantqual,
+ &context);
break;
case T_SeqScan:
@@ -2086,10 +2082,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_IndexScan:
- finalize_primnode((Node *) ((IndexScan *) plan)->indexqual,
- &context);
- finalize_primnode((Node *) ((IndexScan *) plan)->indexorderby,
- &context);
+ finalize_primnode_walker((Node *)((IndexScan *)plan)->indexqual,
+ &context);
+ finalize_primnode_walker((Node *)((IndexScan *)plan)->indexorderby,
+ &context);
/*
* we need not look at indexqualorig, since it will have the same
@@ -2100,10 +2096,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_IndexOnlyScan:
- finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexqual,
- &context);
- finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexorderby,
- &context);
+ finalize_primnode_walker((Node *)((IndexOnlyScan *) plan)->indexqual,
+ &context);
+ finalize_primnode_walker((Node *) ((IndexOnlyScan *) plan)->indexorderby,
+ &context);
/*
* we need not look at indextlist, since it cannot contain Params.
@@ -2112,8 +2108,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_BitmapIndexScan:
- finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual,
- &context);
+ finalize_primnode_walker((Node *) ((BitmapIndexScan *) plan)->indexqual,
+ &context);
/*
* we need not look at indexqualorig, since it will have the same
@@ -2122,14 +2118,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_BitmapHeapScan:
- finalize_primnode((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
- &context);
+ finalize_primnode_walker((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
+ &context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_TidScan:
- finalize_primnode((Node *) ((TidScan *) plan)->tidquals,
- &context);
+ finalize_primnode_walker((Node *) ((TidScan *) plan)->tidquals,
+ &context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
@@ -2167,7 +2163,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
funccontext = context;
funccontext.paramids = NULL;
- finalize_primnode(rtfunc->funcexpr, &funccontext);
+ finalize_primnode_walker(rtfunc->funcexpr, &funccontext);
/* remember results for execution */
rtfunc->funcparams = funccontext.paramids;
@@ -2183,8 +2179,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_ValuesScan:
- finalize_primnode((Node *) ((ValuesScan *) plan)->values_lists,
- &context);
+ finalize_primnode_walker((Node *) ((ValuesScan *) plan)->values_lists,
+ &context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
@@ -2231,11 +2227,24 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_ForeignScan:
- finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
- &context);
+ finalize_primnode_walker((Node *)((ForeignScan *) plan)->fdw_exprs,
+ &context);
context.paramids = bms_add_members(context.paramids, scan_params);
break;
+ case T_CustomPlan:
+ {
+ CustomPlan *cplan = (CustomPlan *) plan;
+
+ if (cplan->methods->FinalizeCustomPlan)
+ cplan->methods->FinalizeCustomPlan(root,
+ cplan,
+ &context.paramids,
+ &valid_params,
+ &scan_params);
+ }
+ break;
+
case T_ModifyTable:
{
ModifyTable *mtplan = (ModifyTable *) plan;
@@ -2247,8 +2256,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
locally_added_param);
scan_params = bms_add_member(bms_copy(scan_params),
locally_added_param);
- finalize_primnode((Node *) mtplan->returningLists,
- &context);
+ finalize_primnode_walker((Node *) mtplan->returningLists,
+ &context);
foreach(l, mtplan->plans)
{
context.paramids =
@@ -2329,8 +2338,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
{
ListCell *l;
- finalize_primnode((Node *) ((Join *) plan)->joinqual,
- &context);
+ finalize_primnode_walker((Node *) ((Join *) plan)->joinqual,
+ &context);
/* collect set of params that will be passed to right child */
foreach(l, ((NestLoop *) plan)->nestParams)
{
@@ -2343,24 +2352,24 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_MergeJoin:
- finalize_primnode((Node *) ((Join *) plan)->joinqual,
- &context);
- finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
- &context);
+ finalize_primnode_walker((Node *) ((Join *) plan)->joinqual,
+ &context);
+ finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses,
+ &context);
break;
case T_HashJoin:
- finalize_primnode((Node *) ((Join *) plan)->joinqual,
- &context);
- finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
+ finalize_primnode_walker((Node *) ((Join *) plan)->joinqual,
+ &context);
+ finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses,
&context);
break;
case T_Limit:
- finalize_primnode(((Limit *) plan)->limitOffset,
- &context);
- finalize_primnode(((Limit *) plan)->limitCount,
- &context);
+ finalize_primnode_walker(((Limit *) plan)->limitOffset,
+ &context);
+ finalize_primnode_walker(((Limit *) plan)->limitCount,
+ &context);
break;
case T_RecursiveUnion:
@@ -2381,10 +2390,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
break;
case T_WindowAgg:
- finalize_primnode(((WindowAgg *) plan)->startOffset,
- &context);
- finalize_primnode(((WindowAgg *) plan)->endOffset,
- &context);
+ finalize_primnode_walker(((WindowAgg *) plan)->startOffset,
+ &context);
+ finalize_primnode_walker(((WindowAgg *) plan)->endOffset,
+ &context);
break;
case T_Hash:
@@ -2473,8 +2482,21 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
* finalize_primnode: add IDs of all PARAM_EXEC params appearing in the given
* expression tree to the result set.
*/
+Bitmapset *
+finalize_primnode(PlannerInfo *root, Node *node, Bitmapset *paramids)
+{
+ finalize_primnode_context context;
+
+ context.root = root;
+ context.paramids = paramids;
+
+ finalize_primnode_walker(node, &context);
+
+ return context.paramids;
+}
+
static bool
-finalize_primnode(Node *node, finalize_primnode_context *context)
+finalize_primnode_walker(Node *node, finalize_primnode_context *context)
{
if (node == NULL)
return false;
@@ -2496,7 +2518,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
Bitmapset *subparamids;
/* Recurse into the testexpr, but not into the Plan */
- finalize_primnode(subplan->testexpr, context);
+ finalize_primnode_walker(subplan->testexpr, context);
/*
* Remove any param IDs of output parameters of the subplan that were
@@ -2513,7 +2535,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
}
/* Also examine args list */
- finalize_primnode((Node *) subplan->args, context);
+ finalize_primnode_walker((Node *) subplan->args, context);
/*
* Add params needed by the subplan to paramids, but excluding those
@@ -2528,7 +2550,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
return false; /* no more to do here */
}
- return expression_tree_walker(node, finalize_primnode,
+ return expression_tree_walker(node, finalize_primnode_walker,
(void *) context);
}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 566b4c9..934d796 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -5292,6 +5292,25 @@ get_utility_query_def(Query *query, deparse_context *context)
}
}
+/*
+ * GetSpecialCustomVar
+ *
+ * Utility routine to call optional GetSpecialCustomVar method of
+ * CustomPlanState
+ */
+static Node *
+GetSpecialCustomVar(PlanState *ps, Var *varnode)
+{
+ CustomPlanState *cps = (CustomPlanState *) ps;
+
+ Assert(IsA(ps, CustomPlanState));
+ Assert(IS_SPECIAL_VARNO(varnode->varno));
+
+ if (cps->methods->GetSpecialCustomVar)
+ return (Node *)cps->methods->GetSpecialCustomVar(cps, varnode);
+
+ return NULL;
+}
/*
* Display a Var appropriately.
@@ -5323,6 +5342,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
deparse_columns *colinfo;
char *refname;
char *attname;
+ Node *expr;
/* Find appropriate nesting depth */
netlevelsup = var->varlevelsup + levelsup;
@@ -5345,6 +5365,22 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
colinfo = deparse_columns_fetch(var->varno, dpns);
attnum = var->varattno;
}
+ else if (IS_SPECIAL_VARNO(var->varno) &&
+ IsA(dpns->planstate, CustomPlanState) &&
+ (expr = GetSpecialCustomVar(dpns->planstate, var)) != NULL)
+ {
+ /*
+ * Force parentheses because our caller probably assumed a Var is a
+ * simple expression.
+ */
+ if (!IsA(expr, Var))
+ appendStringInfoChar(buf, '(');
+ get_rule_expr((Node *) expr, context, true);
+ if (!IsA(expr, Var))
+ appendStringInfoChar(buf, ')');
+
+ return NULL;
+ }
else if (var->varno == OUTER_VAR && dpns->outer_tlist)
{
TargetEntry *tle;
@@ -5633,6 +5669,26 @@ get_name_for_var_field(Var *var, int fieldno,
rte = rt_fetch(var->varno, dpns->rtable);
attnum = var->varattno;
}
+ else if (IS_SPECIAL_VARNO(var->varno) &&
+ IsA(dpns->planstate, CustomPlanState) &&
+ (expr = GetSpecialCustomVar(dpns->planstate, var)) != NULL)
+ {
+ StringInfo saved = context->buf;
+ StringInfoData temp;
+
+ initStringInfo(&temp);
+ context->buf = &temp;
+
+ if (!IsA(expr, Var))
+ appendStringInfoChar(context->buf, '(');
+ get_rule_expr((Node *) expr, context, true);
+ if (!IsA(expr, Var))
+ appendStringInfoChar(context->buf, ')');
+
+ context->buf = saved;
+
+ return temp.data;
+ }
else if (var->varno == OUTER_VAR && dpns->outer_tlist)
{
TargetEntry *tle;
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 3488be3..f914696 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -54,6 +54,7 @@ extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook;
typedef const char *(*explain_get_index_name_hook_type) (Oid indexId);
extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook;
+extern void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
extern void ExplainQuery(ExplainStmt *stmt, const char *queryString,
ParamListInfo params, DestReceiver *dest);
diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
new file mode 100644
index 0000000..e6e049e
--- /dev/null
+++ b/src/include/executor/nodeCustom.h
@@ -0,0 +1,30 @@
+/* ------------------------------------------------------------------------
+ *
+ * nodeCustom.h
+ *
+ * prototypes for CustomScan nodes
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * ------------------------------------------------------------------------
+ */
+#ifndef NODECUSTOM_H
+#define NODECUSTOM_H
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+
+/*
+ * General executor code
+ */
+extern CustomPlanState *ExecInitCustomPlan(CustomPlan *custom_plan,
+ EState *estate, int eflags);
+extern TupleTableSlot *ExecCustomPlan(CustomPlanState *cpstate);
+extern Node *MultiExecCustomPlan(CustomPlanState *cpstate);
+extern void ExecEndCustomPlan(CustomPlanState *cpstate);
+
+extern void ExecReScanCustomPlan(CustomPlanState *cpstate);
+extern void ExecCustomMarkPos(CustomPlanState *cpstate);
+extern void ExecCustomRestrPos(CustomPlanState *cpstate);
+
+#endif /* NODECUSTOM_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a301a08..8af5bf2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1501,6 +1501,18 @@ typedef struct ForeignScanState
void *fdw_state; /* foreign-data wrapper can keep state here */
} ForeignScanState;
+/* ----------------
+ * CustomPlanState information
+ *
+ * CustomPlan nodes are used to execute custom code within executor.
+ * ----------------
+ */
+typedef struct CustomPlanState
+{
+ PlanState ps;
+ const CustomPlanMethods *methods;
+} CustomPlanState;
+
/* ----------------------------------------------------------------
* Join State Information
* ----------------------------------------------------------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5b8df59..f4a1246 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -62,6 +62,8 @@ typedef enum NodeTag
T_CteScan,
T_WorkTableScan,
T_ForeignScan,
+ T_CustomPlan,
+ T_CustomPlanMarkPos,
T_Join,
T_NestLoop,
T_MergeJoin,
@@ -107,6 +109,7 @@ typedef enum NodeTag
T_CteScanState,
T_WorkTableScanState,
T_ForeignScanState,
+ T_CustomPlanState,
T_JoinState,
T_NestLoopState,
T_MergeJoinState,
@@ -224,6 +227,7 @@ typedef enum NodeTag
T_HashPath,
T_TidPath,
T_ForeignPath,
+ T_CustomPath,
T_AppendPath,
T_MergeAppendPath,
T_ResultPath,
@@ -513,6 +517,8 @@ extern void *stringToNode(char *str);
*/
extern void *copyObject(const void *obj);
+extern void CopyCustomPlanCommon(const Node *from, Node *newnode);
+
/*
* nodes/equalfuncs.c
*/
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 38c039c..8dfcbf8 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -15,8 +15,10 @@
#define PLANNODES_H
#include "access/sdir.h"
+#include "lib/stringinfo.h"
#include "nodes/bitmapset.h"
#include "nodes/primnodes.h"
+#include "nodes/relation.h"
/* ----------------------------------------------------------------
@@ -479,6 +481,85 @@ typedef struct ForeignScan
bool fsSystemCol; /* true if any "system column" is needed */
} ForeignScan;
+/* ----------------
+ * CustomPlan node
+ * ----------------
+ */
+struct CustomPlanMethods;
+
+typedef struct CustomPlan
+{
+ Plan plan;
+ const struct CustomPlanMethods *methods;
+} CustomPlan;
+
+/* almost same to CustomPlan, but support MarkPos/RestorePos */
+typedef CustomPlan CustomPlanMarkPos;
+
+/* not to include execnodes.h here */
+struct CustomPlanState;
+struct EState;
+struct ExplainState;
+struct TupleTableSlot;
+
+typedef void (*SetCustomPlanRef_function)(PlannerInfo *root,
+ CustomPlan *custom_plan,
+ int rtoffset);
+typedef bool (*SupportCustomBackwardScan_function)(CustomPlan *custom_plan);
+typedef void (*FinalizeCustomPlan_function)(PlannerInfo *root,
+ CustomPlan *custom_plan,
+ Bitmapset **paramids,
+ Bitmapset **valid_params,
+ Bitmapset **scan_params);
+typedef struct CustomPlanState *
+ (*BeginCustomPlan_function)(CustomPlan *custom_plan,
+ struct EState *estate,
+ int eflags);
+typedef struct TupleTableSlot *
+ (*ExecCustomPlan_function)(struct CustomPlanState *cpstate);
+typedef Node *(*MultiExecCustomPlan_function)(struct CustomPlanState *cpstate);
+typedef void (*EndCustomPlan_function)(struct CustomPlanState *cpstate);
+typedef void (*ReScanCustomPlan_function)(struct CustomPlanState *cpstate);
+typedef void (*MarkPosCustomPlan_function)(struct CustomPlanState *cpstate);
+typedef void (*RestrPosCustomPlan_function)(struct CustomPlanState *cpstate);
+typedef void
+ (*ExplainCustomPlanTargetRel_function)(struct CustomPlanState *cpstate,
+ struct ExplainState *es);
+typedef void (*ExplainCustomPlan_function)(struct CustomPlanState *cpstate,
+ List *ancestors,
+ struct ExplainState *es);
+typedef Bitmapset *
+ (*GetRelidsCustomPlan_function)(struct CustomPlanState *cpstate);
+typedef Node *(*GetSpecialCustomVar_function)(struct CustomPlanState *cpstate,
+ Var *varnode);
+typedef void (*TextOutCustomPlan_function)(StringInfo str,
+ const CustomPlan *node);
+typedef CustomPlan *(*CopyCustomPlan_function)(const CustomPlan *from);
+
+typedef struct CustomPlanMethods
+{
+ const char *CustomName;
+ /* callbacks for the planner stage */
+ SetCustomPlanRef_function SetCustomPlanRef;
+ SupportCustomBackwardScan_function SupportBackwardScan;
+ FinalizeCustomPlan_function FinalizeCustomPlan;
+ /* callbacks for the executor stage */
+ BeginCustomPlan_function BeginCustomPlan;
+ ExecCustomPlan_function ExecCustomPlan;
+ MultiExecCustomPlan_function MultiExecCustomPlan;
+ EndCustomPlan_function EndCustomPlan;
+ ReScanCustomPlan_function ReScanCustomPlan;
+ MarkPosCustomPlan_function MarkPosCustomPlan;
+ RestrPosCustomPlan_function RestrPosCustomPlan;
+ /* callbacks for EXPLAIN */
+ ExplainCustomPlanTargetRel_function ExplainCustomPlanTargetRel;
+ ExplainCustomPlan_function ExplainCustomPlan;
+ GetRelidsCustomPlan_function GetRelidsCustomPlan;
+ GetSpecialCustomVar_function GetSpecialCustomVar;
+ /* callbacks for general node management */
+ TextOutCustomPlan_function TextOutCustomPlan;
+ CopyCustomPlan_function CopyCustomPlan;
+} CustomPlanMethods;
/*
* ==========
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index c607b36..12d5289 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -15,6 +15,7 @@
#define RELATION_H
#include "access/sdir.h"
+#include "lib/stringinfo.h"
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "storage/block.h"
@@ -878,6 +879,34 @@ typedef struct ForeignPath
} ForeignPath;
/*
+ * CustomPath represents a scan using custom logic
+ *
+ * custom_flags is a set of CUSTOM_* bits to control its behavior.
+ * custom_methods is a set of function pointers that are declared in
+ * CustomPathMethods structure; extension has to set up correctly.
+ */
+struct CustomPathMethods;
+
+typedef struct CustomPath
+{
+ Path path;
+ const struct CustomPathMethods *methods;
+} CustomPath;
+
+struct CustomPlan; /* to avoid to include plannode.h here */
+
+typedef struct CustomPlan *(*CreateCustomPlan_function)(PlannerInfo *root,
+ CustomPath *best_path);
+typedef void (*TextOutCustomPath_function)(StringInfo str, Node *node);
+
+typedef struct CustomPathMethods
+{
+ const char *CustomName;
+ CreateCustomPlan_function CreateCustomPlan;
+ TextOutCustomPath_function TextOutCustomPath;
+} CustomPathMethods;
+
+/*
* AppendPath represents an Append plan, ie, successive execution of
* several member plans.
*
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9b22fda..3047d3d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -29,6 +29,23 @@ typedef RelOptInfo *(*join_search_hook_type) (PlannerInfo *root,
List *initial_rels);
extern PGDLLIMPORT join_search_hook_type join_search_hook;
+/* Hook for plugins to add custom scan path, in addition to default ones */
+typedef void (*add_scan_path_hook_type)(PlannerInfo *root,
+ RelOptInfo *baserel,
+ RangeTblEntry *rte);
+extern PGDLLIMPORT add_scan_path_hook_type add_scan_path_hook;
+
+/* Hook for plugins to add custom join path, in addition to default ones */
+typedef void (*add_join_path_hook_type)(PlannerInfo *root,
+ RelOptInfo *joinrel,
+ RelOptInfo *outerrel,
+ RelOptInfo *innerrel,
+ JoinType jointype,
+ SpecialJoinInfo *sjinfo,
+ List *restrictlist,
+ Relids param_source_rels,
+ Relids extra_lateral_rels);
+extern PGDLLIMPORT add_join_path_hook_type add_join_path_hook;
extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist);
extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 8bdb7db..28b89d9 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -41,6 +41,10 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
* prototypes for plan/createplan.c
*/
extern Plan *create_plan(PlannerInfo *root, Path *best_path);
+extern Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
+extern List *build_path_tlist(PlannerInfo *root, Path *path);
+extern bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
+extern void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path);
extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
Index scanrelid, Plan *subplan);
extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
@@ -86,6 +90,11 @@ extern ModifyTable *make_modifytable(PlannerInfo *root,
List *withCheckOptionLists, List *returningLists,
List *rowMarks, int epqParam);
extern bool is_projection_capable_plan(Plan *plan);
+extern List *order_qual_clauses(PlannerInfo *root, List *clauses);
+extern List *get_switched_clauses(List *clauses, Relids outerrelids);
+extern void copy_path_costsize(Plan *dest, Path *src);
+extern void copy_plan_costsize(Plan *dest, Plan *src);
+extern Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
/*
* prototypes for plan/initsplan.c
@@ -127,6 +136,9 @@ extern List *remove_useless_joins(PlannerInfo *root, List *joinlist);
* prototypes for plan/setrefs.c
*/
extern Plan *set_plan_references(PlannerInfo *root, Plan *plan);
+extern Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
+extern void fix_expr_common(PlannerInfo *root, Node *node);
+extern Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
extern void fix_opfuncids(Node *node);
extern void set_opfuncid(OpExpr *opexpr);
extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h
index 5607e98..138b60b 100644
--- a/src/include/optimizer/subselect.h
+++ b/src/include/optimizer/subselect.h
@@ -29,6 +29,13 @@ extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,
bool attach_initplans);
extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
Oid resulttype, int32 resulttypmod, Oid resultcollation);
+extern Bitmapset *finalize_plan(PlannerInfo *root,
+ Plan *plan,
+ Bitmapset *valid_params,
+ Bitmapset *scan_params);
+extern Bitmapset *finalize_primnode(PlannerInfo *root,
+ Node *node,
+ Bitmapset *paramids);
extern Param *assign_nestloop_param_var(PlannerInfo *root, Var *var);
extern Param *assign_nestloop_param_placeholdervar(PlannerInfo *root,
PlaceHolderVar *phv);