doc/src/sgml/custom-scan.sgml | 41 +++++++++++++++++++++++++++++++-----
src/backend/nodes/outfuncs.c | 8 ++++++-
src/backend/nodes/readfuncs.c | 48 ++++++++++++++++++++++++++++++++++++++++++
src/backend/utils/fmgr/dfmgr.c | 21 ++++++++++++++++++
src/include/fmgr.h | 1 +
src/include/nodes/plannodes.h | 14 +++++++++++-
6 files changed, 126 insertions(+), 7 deletions(-)
diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index a229326..af0dd56 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -195,11 +195,29 @@ 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>.
+ Thus, if custom scan provider implements TextOutCustomScan>
+ to dump its private fields its own way, it also has responsibility of
+ TextReadCustomScan> to clean out these tokens.
+
+
+ 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.
@@ -230,6 +248,19 @@ void (*TextOutCustomScan) (StringInfo str,
structure, including the substructure of the custom> fields,
there is usually not much need for this callback.
+
+
+
+void (*TextReadCustomScan)(CustomScan *node);
+
+ Reads the private fields of supplied CustomScan> node
+ 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.
+
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3e75cd1..775ac95 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -612,8 +612,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 94ba6dc..8e6e3d6 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"
@@ -1806,6 +1807,51 @@ _readForeignScan(void)
}
/*
+ * _readCustomScan
+ */
+static CustomScan *
+_readCustomScan(void)
+{
+ READ_LOCALS(CustomScan);
+ char *library_name;
+ char *symbol_name;
+ const CustomScanMethods *methods;
+
+ 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);
+ local_node->methods = methods;
+
+ /*
+ * Read custom fields if any. Number of tokens has to be equivalent
+ * to the output of TextOutCustomScan
+ */
+ if (methods->TextReadCustomScan)
+ methods->TextReadCustomScan(local_node);
+
+ READ_DONE();
+}
+
+/*
* ReadCommonJoin
* Assign the basic stuff of all nodes that inherit from Join
*/
@@ -2361,6 +2407,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 6b28c8e..b3f90fb 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -551,15 +551,27 @@ 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 the private fields printed in special way */
+ void (*TextReadCustomScan) (struct CustomScan *cscan);
} 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;