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;