doc/src/sgml/custom-scan.sgml | 53 +++++++++++++++++++++++++++++++++-----
src/backend/nodes/copyfuncs.c | 16 +++++++++++-
src/backend/nodes/outfuncs.c | 8 +++++-
src/backend/nodes/readfuncs.c | 58 ++++++++++++++++++++++++++++++++++++++++++
src/backend/utils/fmgr/dfmgr.c | 21 +++++++++++++++
src/include/fmgr.h | 1 +
src/include/nodes/plannodes.h | 23 ++++++++++++++---
7 files changed, 168 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index a229326..867b47e 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -195,11 +195,22 @@ 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>, so so all the data stored within
+ the custom> fields must consist of nodes that that function
+ can handle.
+ Furthermore, custom scan providers have to implement optional>
+ callbacks if it defines substitute a larger structure that embeds
+ a CustomScan> for the structure itself.
+
+
+
+ Also note that symbol name of the CustomScanMethods> table
+ has to be visible to linker, because stringToNode>
+ reconstructs the methods> pointer using a pair of library
+ and symbol name. INIT_CUSTOM_SCAN_METHODS> initializes the
+ relevant fields, and expects custom-scan provider uses this macro on
+ _PG_init> timing.
@@ -228,8 +239,38 @@ void (*TextOutCustomScan) (StringInfo str,
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.
+ there is usually not much need for this callback, unless custom-scan
+ provider does not embed its
+
+
+
+
+struct 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.
+
+
+
+struct 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/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 62355aa..05d23aa 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -637,7 +637,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 c91273c..7ed8118 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -599,8 +599,14 @@ _outCustomScan(StringInfo str, const CustomScan *node)
WRITE_NODE_FIELD(custom_private);
WRITE_NODE_FIELD(custom_scan_tlist);
WRITE_BITMAPSET_FIELD(custom_relids);
+
+ /* Dump library and symbol name instead of raw pointer */
appendStringInfoString(str, " :methods ");
- _outToken(str, node->methods->CustomName);
+ _outToken(str, node->methods->methods_library_name);
+ appendStringInfoChar(str, ' ');
+ _outToken(str, node->methods->methods_symbol_name);
+
+ /* Also, private fields if any */
if (node->methods->TextOutCustomScan)
node->methods->TextOutCustomScan(str, node);
}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index ef88209..81449a2 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -28,6 +28,7 @@
#include
+#include "fmgr.h"
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
#include "nodes/readfuncs.h"
@@ -1849,6 +1850,61 @@ _readForeignScan(void)
}
/*
+ * _readCustomScan
+ */
+static CustomScan *
+_readCustomScan(void)
+{
+ 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);
+ READ_NODE_FIELD(custom_plans);
+ READ_NODE_FIELD(custom_exprs);
+ READ_NODE_FIELD(custom_private);
+ READ_NODE_FIELD(custom_scan_tlist);
+ READ_BITMAPSET_FIELD(custom_relids);
+
+ /*
+ * Reconstruction of methods using library and symbol name
+ */
+ token = pg_strtok(&length); /* skip methods: */
+ token = pg_strtok(&length); /* methods_library_name */
+ library_name = nullable_string(token, length);
+ token = pg_strtok(&length); /* methods_symbol_name */
+ symbol_name = nullable_string(token, length);
+
+ methods = (const CustomScanMethods *)
+ load_external_function(library_name, symbol_name, true, NULL);
+ Assert(strcmp(methods->methods_library_name, library_name) == 0 &&
+ strcmp(methods->methods_symbol_name, symbol_name) == 0);
+
+ /*
+ * Then, read private fields if any. Elsewhere, just construct
+ * a usual CustomScan node.
+ */
+ if (!methods->TextReadCustomScan)
+ local_node = makeNode(CustomScan);
+ else
+ {
+ local_node = methods->TextReadCustomScan();
+ Assert(IsA(local_node, CustomScan));
+ }
+ /* move the common field of CustomScan */
+ memcpy(local_node, &local_temp, offsetof(CustomScan, methods));
+ local_node->methods = methods;
+
+ READ_DONE();
+}
+
+/*
* ReadCommonJoin
* Assign the basic stuff of all nodes that inherit from Join
*/
@@ -2388,6 +2444,8 @@ parseNodeString(void)
return_value = _readWorkTableScan();
else if (MATCH("FOREIGNSCAN", 11))
return_value = _readForeignScan();
+ else if (MATCH("CUSTOMSCAN", 10))
+ return_value = _readCustomScan();
else if (MATCH("JOIN", 4))
return_value = _readJoin();
else if (MATCH("NESTLOOP", 8))
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index cd3db87..f343cfc 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -80,6 +80,8 @@ static char *find_in_dynamic_libpath(const char *basename);
/* Magic structure that module needs to match to be accepted */
static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
+/* Library filename on which PG_init() is currently processed */
+static const *current_library_filename = NULL;
/*
* Load the specified dynamic-link library file, and look for a function
@@ -277,8 +279,16 @@ internal_load_library(const char *libname)
*/
PG_init = (PG_init_t) pg_dlsym(file_scanner->handle, "_PG_init");
if (PG_init)
+ {
+ const char *saved_library_name = current_library_filename;
+
+ current_library_filename = file_scanner->filename;
+
(*PG_init) ();
+ current_library_filename = saved_library_name;
+ }
+
/* OK to link it into list */
if (file_list == NULL)
file_list = file_scanner;
@@ -291,6 +301,17 @@ internal_load_library(const char *libname)
}
/*
+ * Inform extensions their own filename, at the time of PG_init()
+ */
+const char *
+get_current_library_filename(void)
+{
+ if (!current_library_filename)
+ elog(ERROR, "Not in the context of _PG_init");
+ return current_library_filename;
+}
+
+/*
* Report a suitable error for an incompatible magic block.
*/
static void
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 808d142..116ca4d 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -645,6 +645,7 @@ extern void **find_rendezvous_variable(const char *varName);
extern Size EstimateLibraryStateSpace(void);
extern void SerializeLibraryState(Size maxsize, char *start_address);
extern void RestoreLibraryState(char *start_address);
+extern const char *get_current_library_filename(void);
/*
* Support for aggregate functions
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index cc259f1..4c64b51 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -535,24 +535,39 @@ 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;
typedef struct CustomScanMethods
{
+ /* to be set by INIT_CUSTOM_SCAN_METHODS */
const char *CustomName;
+ const char *methods_library_name;
+ const char *methods_symbol_name;
/* Create execution state (CustomScanState) from a CustomScan plan node */
Node *(*CreateCustomScanState) (struct CustomScan *cscan);
- /* Optional: print custom_xxx fields in some special way */
+ /* Optional: print private fields in some special way */
void (*TextOutCustomScan) (StringInfo str,
const struct CustomScan *node);
+ /* Optional: read and construct the private fields above */
+ struct CustomScan *(*TextReadCustomScan)(void);
+ /* Optional: copy the original including private fields */
+ struct CustomScan *(*NodeCopyCustomScan)(const struct CustomScan *from);
} CustomScanMethods;
+#define INIT_CUSTOM_SCAN_METHODS(methods, name) \
+ do { \
+ methods.CustomName = (name); \
+ methods.methods_library_name = get_current_library_filename(); \
+ methods.methods_symbol_name = #methods; \
+ } while(0)
+
typedef struct CustomScan
{
Scan scan;