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;