doc/src/sgml/custom-scan.sgml | 70 +++++++++++++++++++++++++++++++++++++++---- doc/src/sgml/fdwhandler.sgml | 64 +++++++++++++++++++++++++++++++++++++++ src/backend/nodes/copyfuncs.c | 33 ++++++++++++++++++-- src/backend/nodes/outfuncs.c | 15 ++++++++++ src/backend/nodes/readfuncs.c | 38 +++++++++++++++++++++-- src/include/foreign/fdwapi.h | 10 +++++++ src/include/nodes/plannodes.h | 15 ++++++++-- 7 files changed, 233 insertions(+), 12 deletions(-) diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml index 5bba125..03f1d87 100644 --- a/doc/src/sgml/custom-scan.sgml +++ b/doc/src/sgml/custom-scan.sgml @@ -197,11 +197,31 @@ 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. + 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 reconstruct the private structure. + + + 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. @@ -220,6 +240,46 @@ 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 *node); + + 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. + + + + +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. + + + + +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/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 1533a6b..bbe798c 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -195,6 +195,70 @@ GetForeignPlan (PlannerInfo *root, void +TextOutForeignScan(StringInfo str, const ForeignScan *node); + + Write out private fields if FDW-driver defines a larger structure that + contains a ForeignScan plan node, and wants to save its + private data on the extra area rather than fdw_private. + + + The core backend already wrote out the known fields of + ForeignScan on the supplied StringInfo, + then, this callback enables to append some extra private information. + + + This callback is optional, however, it is required to define the + relevant TextReadForeignScan callback also, to match + serialization with deserialization. + + + + +ForeignScan * +TextReadForeignScan(const ForeignScan *node); + + Allocates a node then read in private fields if FDW-driver defines + a larger structure that contains a ForeignScan plan + node, and wants to save its private data on the extra area rather + than fdw_private. + + + The core backend already read in the known fields of + ForeignScan from input stream; we can pick up tokens + using pg_strtok because this callback shall be invoked + under the stringToNode context. + + + This callback is optional, however, it is required to define the + relevant TextOutForeignScan callback also, to match + serialization with deserialization. + + + + +ForeignScan * +NodeCopyForeignScan(const ForeignScan *from); + + Allocates a node object then duplicate private fields if FDW-driver + defines a larger structure that contains a ForeignScan + plan node, and wants to save its private data on the extra area rather + than fdw_private. + + + The core backend will copy the known fields of ForeignScan, + thus, all this function needs to do is allocation of the own larger + structure and fill up the private fields. + + + This callback is optional, but should be implemented if FDW-driver + wants to save its private fields on the extra field in a larger + structure, like TextOutForeignScan or + TextReadForeignScan. + + + + +void BeginForeignScan (ForeignScanState *node, int eflags); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 26264cb..9eb1fd6 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -22,6 +22,7 @@ #include "postgres.h" +#include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/plannodes.h" #include "nodes/relation.h" @@ -635,7 +636,21 @@ _copyWorkTableScan(const WorkTableScan *from) static ForeignScan * _copyForeignScan(const ForeignScan *from) { - ForeignScan *newnode = makeNode(ForeignScan); + FdwRoutine *fdwroutine = GetFdwRoutineByServerId(from->fs_server); + ForeignScan *newnode; + + /* + * If FDW driver extends ForeignScan node to save extra private fields, + * it is role of the FDW driver to allocate a new node that includes + * the private fields. + */ + if (!fdwroutine->NodeCopyForeignScan) + newnode = makeNode(ForeignScan); + else + { + newnode = fdwroutine->NodeCopyForeignScan(from); + Assert(IsA(newnode, ForeignScan)); + } /* * copy node superclass fields @@ -662,7 +677,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 012c14b..937dc95 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -25,6 +25,7 @@ #include +#include "foreign/fdwapi.h" #include "lib/stringinfo.h" #include "nodes/plannodes.h" #include "nodes/relation.h" @@ -587,6 +588,8 @@ _outWorkTableScan(StringInfo str, const WorkTableScan *node) static void _outForeignScan(StringInfo str, const ForeignScan *node) { + FdwRoutine *routines = GetFdwRoutineByServerId(node->fs_server); + WRITE_NODE_TYPE("FOREIGNSCAN"); _outScanInfo(str, (const Scan *) node); @@ -598,6 +601,14 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_NODE_FIELD(fdw_recheck_quals); WRITE_BITMAPSET_FIELD(fs_relids); WRITE_BOOL_FIELD(fsSystemCol); + + /* + * Optional: it allows FDW driver to dump extra private field, if supplied + * ForeignScan node is located within a larger structure that contains + * various private fields of individual FDW. + */ + if (routines->TextOutForeignScan) + routines->TextOutForeignScan(str, node); } static void @@ -618,6 +629,10 @@ _outCustomScan(StringInfo str, const CustomScan *node) _outToken(str, node->methods->LibraryName); appendStringInfoChar(str, ' '); _outToken(str, node->methods->SymbolName); + + /* Also, private fields if any */ + 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 222e2ed..f9a19d8 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -29,6 +29,7 @@ #include #include "fmgr.h" +#include "foreign/fdwapi.h" #include "nodes/parsenodes.h" #include "nodes/plannodes.h" #include "nodes/readfuncs.h" @@ -1792,8 +1793,12 @@ _readWorkTableScan(void) static ForeignScan * _readForeignScan(void) { - READ_LOCALS(ForeignScan); + READ_TEMP_LOCALS(); + ForeignScan local_temp; + ForeignScan *local_node; + FdwRoutine *fdwroutine; + NodeSetTag(local_node, T_ForeignScan); ReadCommonScan(&local_node->scan); READ_OID_FIELD(fs_server); @@ -1804,6 +1809,17 @@ _readForeignScan(void) READ_BITMAPSET_FIELD(fs_relids); READ_BOOL_FIELD(fsSystemCol); + fdwroutine = GetFdwRoutineByServerId(local_node->fs_server); + if (!fdwroutine->TextReadForeignScan) + local_node = makeNode(ForeignScan); + else + { + local_node = fdwroutine->TextReadForeignScan(&local_temp); + Assert(IsA(local_node, ForeignScan)); + } + /* move the common fields of ForeignScan */ + memcpy(local_node, &local_temp, sizeof(ForeignScan)); + READ_DONE(); } @@ -1813,11 +1829,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,6 +1861,21 @@ _readCustomScan(void) strcmp(methods->SymbolName, symbol_name) == 0); local_node->methods = methods; + /* + * 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) + local_node = makeNode(CustomScan); + else + { + local_node = methods->TextReadCustomScan(&local_temp); + Assert(IsA(local_node, CustomScan)); + } + /* move the common field of CustomScan */ + memcpy(local_node, &local_temp, sizeof(CustomScan)); + READ_DONE(); } diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 69b48b4..224d44c 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -38,6 +38,13 @@ typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root, List *tlist, List *scan_clauses); +typedef void (*TextOutForeignScan_function) (StringInfo str, + const ForeignScan *node); + +typedef ForeignScan *(*TextReadForeignScan_function) (const ForeignScan *node); + +typedef ForeignScan *(*NodeCopyForeignScan_function) (const ForeignScan *from); + typedef void (*BeginForeignScan_function) (ForeignScanState *node, int eflags); @@ -136,6 +143,9 @@ typedef struct FdwRoutine GetForeignRelSize_function GetForeignRelSize; GetForeignPaths_function GetForeignPaths; GetForeignPlan_function GetForeignPlan; + TextOutForeignScan_function TextOutForeignScan; + TextReadForeignScan_function TextReadForeignScan; + NodeCopyForeignScan_function NodeCopyForeignScan; BeginForeignScan_function BeginForeignScan; IterateForeignScan_function IterateForeignScan; ReScanForeignScan_function ReScanForeignScan; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 37086c6..18639e6 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -547,9 +547,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,6 +563,14 @@ typedef struct CustomScanMethods /* Create execution state (CustomScanState) from a CustomScan plan node */ Node *(*CreateCustomScanState) (struct CustomScan *cscan); + + /* 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 */ + struct CustomScan *(*TextReadCustomScan)(const struct CustomScan *node); + /* Optional: copy the original including private fields */ + struct CustomScan *(*NodeCopyCustomScan)(const struct CustomScan *from); } CustomScanMethods; typedef struct CustomScan