doc/src/sgml/custom-scan.sgml | 55 +++++++++++++++++++++++++++++++++++++------ src/backend/nodes/copyfuncs.c | 17 ++++++++++++- src/backend/nodes/outfuncs.c | 7 ++++++ src/backend/nodes/readfuncs.c | 22 ++++++++++++++++- src/include/nodes/plannodes.h | 14 ++++++++--- 5 files changed, 103 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml index d042adb..820f8d2 100644 --- a/doc/src/sgml/custom-scan.sgml +++ b/doc/src/sgml/custom-scan.sgml @@ -185,7 +185,9 @@ typedef struct CustomScan this scan is replacing a join, it will have only one member. methods must point to a (usually statically allocated) object implementing the required custom scan methods, which are further - detailed below. + detailed below. Also note that this field shall be serialized using + a pair of library name and symbol name, thus, method variable has to + be resolvable by linker. @@ -196,12 +198,12 @@ 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. + Plan tree has to be copied using copyObject, serialized + using nodeToString and deserialized using + stringToNode. In case when custom scan provider defines + its own structure that embeds CustomScan on the head, + for better private field handling, it has to implement the relevant and + optional callbacks. @@ -220,6 +222,45 @@ 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 *cscan); + + Generate extra output when nodeToString is invoked towards + the custom scan node. This callback is optional, however, custom scan + provider has to implement if it defines larger structure that embeds + CustomScan on the head, to save own private fields. + In this case, all the callback has to dump are private fields because + the core backend dumps knows fields in CustomScan. + + + + +CustomScan *(*TextReadCustomScan) (void); + + Allocate a larger structure that embeds CustomScan then + reconstruct its private fields on the extra fields of this structure; + according to the output by TextOutCustomScan callback. + This callback is optional, however, custom scan provider has to + implement if it defines its own structure instead of bare + CustomScan. + This callback shall be invoked under stringToNode context, + so pg_strtok will give the next token. + + + + +CustomScan *(*NodeCopyCustomScan) (const CustomScan *from); + + Allocate a larger structure that embeds CustomScan then + copies its private fields from the original node. + This callback is optional, however, custom scan provider has to + implement if it defines its own structure instead of bare + CustomScan. + All this callback has to care about is its private fields. The known + fields in CustomScan shall be copied by the core backend. + diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index c176ff9..34517cd 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -661,7 +661,22 @@ _copyForeignScan(const ForeignScan *from) static CustomScan * _copyCustomScan(const CustomScan *from) { - CustomScan *newnode = makeNode(CustomScan); + const CustomScanMethods *methods = from->methods; + CustomScan *newnode; + + /* + * In case when custom scan provider defines a larger structure that + * deploys CustomScan at the head, only custom scan provider knows + * exact size of the new node to be allocated, so we ask them to + * allocate the new node and copy private fields we don't know. + */ + 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 af6674c..43fbd81 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -617,6 +617,13 @@ _outCustomScan(StringInfo str, const CustomScan *node) _outToken(str, node->methods->methodsLibraryName); appendStringInfoChar(str, ' '); _outToken(str, node->methods->methodsSymbolName); + /* + * Custom scan provider can/must dump out private fields, if it defines + * a larger structure to save its private fields. It also has to be + * reconstructable using relevant TextReadCustomScan callback. + */ + 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 d143dac..52faf9e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1812,11 +1812,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); @@ -1839,6 +1842,23 @@ _readCustomScan(void) load_external_function(library_name, symbol_name, true, NULL); Assert(strcmp(methods->methodsLibraryName, library_name) == 0 && strcmp(methods->methodsSymbolName, symbol_name) == 0); + + /* + * Then, read private fields if any. If custom scan provider defines + * a larger structure to save its private data, it should be dumpped + * out by TextOutCustomScan callback and also should be reconstructable + * by TextReadCustomScan. In this case, only custom scan provider knows + * exact size of the structure, so TextReadCustomScan callback has to + * allocate the result node not only reading private data fields. + */ + if (!methods->TextReadCustomScan) + local_node = makeNode(CustomScan); + else + { + local_node = methods->TextReadCustomScan(); + Assert(IsA(local_node, CustomScan)); + } + memcpy(local_node, &local_temp, offsetof(CustomScan, methods)); local_node->methods = methods; READ_DONE(); diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 5ecc2d1..acaf4bd 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -542,9 +542,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 tree can be copied, written and read with usual node + * functions. If custom scan provider defines a larger structure that + * embeds CustomScan at the head, it *must* provide relevant callbacks + * to support copyObject, nodeToString and stringToNode. * ---------------- */ struct CustomScan; @@ -557,6 +558,13 @@ typedef struct CustomScanMethods /* Create execution state (CustomScanState) from a CustomScan plan node */ Node *(*CreateCustomScanState) (struct CustomScan *cscan); + /* Optional: output private field if structure is extended */ + void (*TextOutCustomScan) (StringInfo str, + const struct CustomScan *cscan); + /* Optional: reconstruct the node if structure is extended */ + struct CustomScan *(*TextReadCustomScan) (void); + /* Optional: duplicate CustomScan node if any additional private fields */ + struct CustomScan *(*NodeCopyCustomScan) (const struct CustomScan *from); } CustomScanMethods; typedef struct CustomScan