doc/src/sgml/custom-scan.sgml | 70 +++++++++++++++++++++++++++++++++++++++----
doc/src/sgml/fdwhandler.sgml | 64 +++++++++++++++++++++++++++++++++++++++
src/backend/nodes/copyfuncs.c | 33 ++++++++++++++++++--
src/backend/nodes/outfuncs.c | 15 ++++++++++
src/backend/nodes/readfuncs.c | 38 +++++++++++++++++++++--
src/include/foreign/fdwapi.h | 10 +++++++
src/include/nodes/plannodes.h | 15 ++++++++--
7 files changed, 233 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index 5bba125..03f1d87 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -197,11 +197,31 @@ typedef struct CustomScan
Plan trees must be able to be duplicated using copyObject>,
- so all the data stored within the custom> fields must consist of
- nodes that that function can handle. Furthermore, custom scan providers
- cannot substitute a larger structure that embeds
- a CustomScan> for the structure itself, as would be possible
- for a CustomPath> or CustomScanState>.
+ serialized using nodeToString> and deserialized using
+ stringToNode>.
+ CustomScan> can be located on head of larger structure
+ to keep private fields of custom scan provider.
+ Thus, if custom scan provider implements TextOutCustomScan>
+ to dump its private fields its own way, it also has responsibility of
+ TextReadCustomScan> to reconstruct the private structure.
+
+
+ Like any other fields, methods> pointer of the
+ CustomScanMethods> also has to be restored, however,
+ nobody can ensure a particular shared library that contains the custom
+ scan provider is loaded exactly same address, for example, when
+ CustomScan> node is deserialized on the background worker
+ under the control of Gather>.
+
+ Thus, here is a few additional interface contract. The first one is
+ symbol name of the CustomScanMethods> table has to be
+ visible to linker; that means the variable should not be declared as
+ static, because stringToNode> reconstructs the
+ methods> pointer using a pair of library and symbol
+ name. The second one is, CustomScanMethods> table also
+ has to be initialized using INIT_CUSTOM_SCAN_METHODS>
+ macro on _PG_init>, to assign exact library and symbol
+ name to be referenced.
@@ -220,6 +240,46 @@ Node *(*CreateCustomScanState) (CustomScan *cscan);
the BeginCustomScan> callback will be invoked to give the
custom scan provider a chance to do whatever else is needed.
+
+
+
+void (*TextOutCustomScan) (StringInfo str,
+ const CustomScan *node);
+
+ Generate additional output when nodeToString> is invoked on
+ this custom plan node. This callback is optional. Since
+ nodeToString> will automatically dump all fields in the
+ structure, including the substructure of the custom> fields,
+ there is usually not much need for this callback.
+
+
+
+
+CustomScan *(*TextReadCustomScan)(void);
+
+ Allocate a CustomScan> or larger structure embedding
+ CustomScan>, and read its private fields generated by
+ TextOutCustomScan> callback.
+ This callback is optional, however, must be implemented if custom
+ scan provider makes additional output for support of plan-tree
+ serialization and deserialization.
+ This callback shall be invoked under stringToNode>
+ context, so pg_strtok> will give the next token.
+
+
+
+
+CustomScan *(*NodeCopyCustomScan)(const struct CustomScan *from);
+
+ Allocate a CustomScan> or larger structure embedding
+ CustomScan>, and copies its private fields from the
+ original node.
+ This callback is optional, however, must be implemented if custom
+ scan provider extends CustomScan> structure to save
+ its private fields for safety of copyObject>.
+ The common fields are duplicated by the backend, so all extension
+ needs to focus on is its private fields, if exists.
+
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 1533a6b..bbe798c 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -195,6 +195,70 @@ GetForeignPlan (PlannerInfo *root,
void
+TextOutForeignScan(StringInfo str, const ForeignScan *node);
+
+ Write out private fields if FDW-driver defines a larger structure that
+ contains a ForeignScan> plan node, and wants to save its
+ private data on the extra area rather than fdw_private>.
+
+
+ The core backend already wrote out the known fields of
+ ForeignScan> on the supplied StringInfo>,
+ then, this callback enables to append some extra private information.
+
+
+ This callback is optional, however, it is required to define the
+ relevant TextReadForeignScan> callback also, to match
+ serialization with deserialization.
+
+
+
+
+ForeignScan *
+TextReadForeignScan(const ForeignScan *node);
+
+ Allocates a node then read in private fields if FDW-driver defines
+ a larger structure that contains a ForeignScan> plan
+ node, and wants to save its private data on the extra area rather
+ than fdw_private>.
+
+
+ The core backend already read in the known fields of
+ ForeignScan> from input stream; we can pick up tokens
+ using pg_strtok> because this callback shall be invoked
+ under the stringToNode> context.
+
+
+ This callback is optional, however, it is required to define the
+ relevant TextOutForeignScan> callback also, to match
+ serialization with deserialization.
+
+
+
+
+ForeignScan *
+NodeCopyForeignScan(const ForeignScan *from);
+
+ Allocates a node object then duplicate private fields if FDW-driver
+ defines a larger structure that contains a ForeignScan>
+ plan node, and wants to save its private data on the extra area rather
+ than fdw_private>.
+
+
+ The core backend will copy the known fields of ForeignScan>,
+ thus, all this function needs to do is allocation of the own larger
+ structure and fill up the private fields.
+
+
+ This callback is optional, but should be implemented if FDW-driver
+ wants to save its private fields on the extra field in a larger
+ structure, like TextOutForeignScan> or
+ TextReadForeignScan>.
+
+
+
+
+void
BeginForeignScan (ForeignScanState *node,
int eflags);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 26264cb..9eb1fd6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -22,6 +22,7 @@
#include "postgres.h"
+#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/plannodes.h"
#include "nodes/relation.h"
@@ -635,7 +636,21 @@ _copyWorkTableScan(const WorkTableScan *from)
static ForeignScan *
_copyForeignScan(const ForeignScan *from)
{
- ForeignScan *newnode = makeNode(ForeignScan);
+ FdwRoutine *fdwroutine = GetFdwRoutineByServerId(from->fs_server);
+ ForeignScan *newnode;
+
+ /*
+ * If FDW driver extends ForeignScan node to save extra private fields,
+ * it is role of the FDW driver to allocate a new node that includes
+ * the private fields.
+ */
+ if (!fdwroutine->NodeCopyForeignScan)
+ newnode = makeNode(ForeignScan);
+ else
+ {
+ newnode = fdwroutine->NodeCopyForeignScan(from);
+ Assert(IsA(newnode, ForeignScan));
+ }
/*
* copy node superclass fields
@@ -662,7 +677,21 @@ _copyForeignScan(const ForeignScan *from)
static CustomScan *
_copyCustomScan(const CustomScan *from)
{
- CustomScan *newnode = makeNode(CustomScan);
+ const CustomScanMethods *methods = from->methods;
+ CustomScan *newnode;
+
+ /*
+ * If custom-scan provider extends CustomScan node to save its private
+ * data, it is role of the provider to allocate a new node that includes
+ * the private fields.
+ */
+ if (!methods->NodeCopyCustomScan)
+ newnode = makeNode(CustomScan);
+ else
+ {
+ newnode = methods->NodeCopyCustomScan(from);
+ Assert(IsA(newnode, CustomScan));
+ }
/*
* copy node superclass fields
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 012c14b..937dc95 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -25,6 +25,7 @@
#include
+#include "foreign/fdwapi.h"
#include "lib/stringinfo.h"
#include "nodes/plannodes.h"
#include "nodes/relation.h"
@@ -587,6 +588,8 @@ _outWorkTableScan(StringInfo str, const WorkTableScan *node)
static void
_outForeignScan(StringInfo str, const ForeignScan *node)
{
+ FdwRoutine *routines = GetFdwRoutineByServerId(node->fs_server);
+
WRITE_NODE_TYPE("FOREIGNSCAN");
_outScanInfo(str, (const Scan *) node);
@@ -598,6 +601,14 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
WRITE_NODE_FIELD(fdw_recheck_quals);
WRITE_BITMAPSET_FIELD(fs_relids);
WRITE_BOOL_FIELD(fsSystemCol);
+
+ /*
+ * Optional: it allows FDW driver to dump extra private field, if supplied
+ * ForeignScan node is located within a larger structure that contains
+ * various private fields of individual FDW.
+ */
+ if (routines->TextOutForeignScan)
+ routines->TextOutForeignScan(str, node);
}
static void
@@ -618,6 +629,10 @@ _outCustomScan(StringInfo str, const CustomScan *node)
_outToken(str, node->methods->LibraryName);
appendStringInfoChar(str, ' ');
_outToken(str, node->methods->SymbolName);
+
+ /* Also, private fields if any */
+ if (node->methods->TextOutCustomScan)
+ node->methods->TextOutCustomScan(str, node);
}
static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 222e2ed..f9a19d8 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -29,6 +29,7 @@
#include
#include "fmgr.h"
+#include "foreign/fdwapi.h"
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
#include "nodes/readfuncs.h"
@@ -1792,8 +1793,12 @@ _readWorkTableScan(void)
static ForeignScan *
_readForeignScan(void)
{
- READ_LOCALS(ForeignScan);
+ READ_TEMP_LOCALS();
+ ForeignScan local_temp;
+ ForeignScan *local_node;
+ FdwRoutine *fdwroutine;
+ NodeSetTag(local_node, T_ForeignScan);
ReadCommonScan(&local_node->scan);
READ_OID_FIELD(fs_server);
@@ -1804,6 +1809,17 @@ _readForeignScan(void)
READ_BITMAPSET_FIELD(fs_relids);
READ_BOOL_FIELD(fsSystemCol);
+ fdwroutine = GetFdwRoutineByServerId(local_node->fs_server);
+ if (!fdwroutine->TextReadForeignScan)
+ local_node = makeNode(ForeignScan);
+ else
+ {
+ local_node = fdwroutine->TextReadForeignScan(&local_temp);
+ Assert(IsA(local_node, ForeignScan));
+ }
+ /* move the common fields of ForeignScan */
+ memcpy(local_node, &local_temp, sizeof(ForeignScan));
+
READ_DONE();
}
@@ -1813,11 +1829,14 @@ _readForeignScan(void)
static CustomScan *
_readCustomScan(void)
{
- READ_LOCALS(CustomScan);
+ READ_TEMP_LOCALS();
+ CustomScan local_temp;
+ CustomScan *local_node = &local_temp;
char *library_name;
char *symbol_name;
const CustomScanMethods *methods;
+ NodeSetTag(local_node, T_CustomScan);
ReadCommonScan(&local_node->scan);
READ_UINT_FIELD(flags);
@@ -1842,6 +1861,21 @@ _readCustomScan(void)
strcmp(methods->SymbolName, symbol_name) == 0);
local_node->methods = methods;
+ /*
+ * Then, read private data fields and reconstruct a struct that
+ * contains CustomScan on its head, if any. Elsewhere, construct
+ * usual CustomScan node.
+ */
+ if (!methods->TextReadCustomScan)
+ local_node = makeNode(CustomScan);
+ else
+ {
+ local_node = methods->TextReadCustomScan(&local_temp);
+ Assert(IsA(local_node, CustomScan));
+ }
+ /* move the common field of CustomScan */
+ memcpy(local_node, &local_temp, sizeof(CustomScan));
+
READ_DONE();
}
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 69b48b4..224d44c 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -38,6 +38,13 @@ typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root,
List *tlist,
List *scan_clauses);
+typedef void (*TextOutForeignScan_function) (StringInfo str,
+ const ForeignScan *node);
+
+typedef ForeignScan *(*TextReadForeignScan_function) (const ForeignScan *node);
+
+typedef ForeignScan *(*NodeCopyForeignScan_function) (const ForeignScan *from);
+
typedef void (*BeginForeignScan_function) (ForeignScanState *node,
int eflags);
@@ -136,6 +143,9 @@ typedef struct FdwRoutine
GetForeignRelSize_function GetForeignRelSize;
GetForeignPaths_function GetForeignPaths;
GetForeignPlan_function GetForeignPlan;
+ TextOutForeignScan_function TextOutForeignScan;
+ TextReadForeignScan_function TextReadForeignScan;
+ NodeCopyForeignScan_function NodeCopyForeignScan;
BeginForeignScan_function BeginForeignScan;
IterateForeignScan_function IterateForeignScan;
ReScanForeignScan_function ReScanForeignScan;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 37086c6..18639e6 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -547,9 +547,10 @@ typedef struct ForeignScan
* custom_private, custom_scan_tlist, and custom_relids fields. The
* convention of setting scan.scanrelid to zero for joins applies as well.
*
- * Note that since Plan trees can be copied, custom scan providers *must*
- * fit all plan data they need into those fields; embedding CustomScan in
- * a larger struct will not work.
+ * Note that Plan trees can be copied, displayed and read using node
+ * functions, thus, custom scan provider *must* support relevant callbacks
+ * if provider wants to embed CustomScan in a larger struct to save private
+ * fields.
* ----------------
*/
struct CustomScan;
@@ -562,6 +563,14 @@ typedef struct CustomScanMethods
/* Create execution state (CustomScanState) from a CustomScan plan node */
Node *(*CreateCustomScanState) (struct CustomScan *cscan);
+
+ /* Optional: print private fields in some special way */
+ void (*TextOutCustomScan) (StringInfo str,
+ const struct CustomScan *node);
+ /* Optional: read the private fields printed in special way */
+ struct CustomScan *(*TextReadCustomScan)(const struct CustomScan *node);
+ /* Optional: copy the original including private fields */
+ struct CustomScan *(*NodeCopyCustomScan)(const struct CustomScan *from);
} CustomScanMethods;
typedef struct CustomScan