doc/src/sgml/custom-scan.sgml | 25 +++++++++++++++++++++---- src/backend/nodes/copyfuncs.c | 16 +++++++++++++++- src/backend/nodes/readfuncs.c | 22 +++++++++++++++++----- src/include/nodes/plannodes.h | 11 +++++++---- 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml index af0dd56..21a9ace 100644 --- a/doc/src/sgml/custom-scan.sgml +++ b/doc/src/sgml/custom-scan.sgml @@ -197,9 +197,11 @@ typedef struct CustomScan Plan trees must be able to be duplicated using copyObject, serialized using nodeToString and deserialized using stringToNode. + CustomScan can be located on head of larger structure + to keep private fields of custom scan provider. 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. + TextReadCustomScan to reconstruct the private structure. Like any other fields, methods pointer of the @@ -251,16 +253,31 @@ void (*TextOutCustomScan) (StringInfo str, -void (*TextReadCustomScan)(CustomScan *node); +CustomScan *(*TextReadCustomScan)(void); - Reads the private fields of supplied CustomScan node - generated by TextOutCustomScan callback. + 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. + + + +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 c176ff9..ba58f3f 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -661,7 +661,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/readfuncs.c b/src/backend/nodes/readfuncs.c index 8e6e3d6..8e99703 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); @@ -1842,11 +1845,20 @@ _readCustomScan(void) local_node->methods = methods; /* - * Read custom fields if any. Number of tokens has to be equivalent - * to the output of TextOutCustomScan + * Then, read private data fields and reconstruct a struct that + * contains CustomScan on its head, if any. Elsewhere, construct + * usual CustomScan node. */ - if (methods->TextReadCustomScan) - methods->TextReadCustomScan(local_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(); } diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index b3f90fb..908c029 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 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; @@ -562,7 +563,9 @@ typedef struct CustomScanMethods void (*TextOutCustomScan) (StringInfo str, const struct CustomScan *node); /* Optional: read the private fields printed in special way */ - void (*TextReadCustomScan) (struct CustomScan *cscan); + 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) \