doc/src/sgml/custom-scan.sgml | 42 ++++++++++++++++++++++++++++-------------- src/backend/nodes/copyfuncs.c | 17 ++++++++++++++++- src/backend/nodes/outfuncs.c | 5 +++++ src/include/nodes/plannodes.h | 13 ++++++++----- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml index a229326..507586b 100644 --- a/doc/src/sgml/custom-scan.sgml +++ b/doc/src/sgml/custom-scan.sgml @@ -183,7 +183,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. @@ -194,12 +196,11 @@ 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 and written + using nodeToString. 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. @@ -221,14 +222,27 @@ Node *(*CreateCustomScanState) (CustomScan *cscan); -void (*TextOutCustomScan) (StringInfo str, - const CustomScan *node); +void (*TextOutCustomScan) (StringInfo str, const CustomScan *cscan); - Generate additional output when nodeToString is invoked on - 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. + 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 *(*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 e19fee4..53c7d90 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -637,7 +637,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 b39c772..4877ccf 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -599,6 +599,11 @@ _outCustomScan(StringInfo str, const CustomScan *node) WRITE_BITMAPSET_FIELD(custom_relids); appendStringInfoString(str, " :methods "); _outToken(str, node->methods->CustomName); + /* + * 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); } diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 7a6a3fe..27ad64b 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -539,9 +539,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 and written 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 + * and nodeToString. * ---------------- */ struct CustomScan; @@ -552,9 +553,11 @@ typedef struct CustomScanMethods /* Create execution state (CustomScanState) from a CustomScan plan node */ Node *(*CreateCustomScanState) (struct CustomScan *cscan); - /* Optional: print custom_xxx fields in some special way */ + /* Optional: output private field if structure is extended */ void (*TextOutCustomScan) (StringInfo str, - const struct CustomScan *node); + const struct CustomScan *cscan); + /* Optional: duplicate CustomScan node if any additional private fields */ + struct CustomScan *(*NodeCopyCustomScan) (const struct CustomScan *from); } CustomScanMethods; typedef struct CustomScan