From f9143d102ffd0947ca904c62b1d3d6fd587e0c80 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Tue, 29 Mar 2016 11:00:18 -0400 Subject: [PATCH] Rework custom scans to work more like the new extensible node stuff. Per discussion, the new extensible node framework is thought to be better designed than the custom path/scan/scanstate stuff we added in PostgreSQL 9.5. Rework the latter to be more like the former. This is not backward-compatible, but we generally don't promise that for C APIs, and there probably aren't many people using this yet anyway. KaiGai Kohei, reviewed by Petr Jelinek and me. Some further cosmetic changes by me. --- src/backend/commands/explain.c | 1 + src/backend/nodes/extensible.c | 120 +++++++++++++++++------- src/backend/nodes/outfuncs.c | 6 +- src/backend/nodes/readfuncs.c | 19 +--- src/backend/optimizer/plan/createplan.c | 1 + src/include/executor/nodeCustom.h | 1 + src/include/nodes/execnodes.h | 35 +------ src/include/nodes/extensible.h | 85 ++++++++++++++++- src/include/nodes/plannodes.h | 14 +-- src/include/nodes/relation.h | 19 +--- 10 files changed, 184 insertions(+), 117 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 787b0b93cc..09c230468b 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -21,6 +21,7 @@ #include "commands/prepare.h" #include "executor/hashjoin.h" #include "foreign/fdwapi.h" +#include "nodes/extensible.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" diff --git a/src/backend/nodes/extensible.c b/src/backend/nodes/extensible.c index 2473b658b1..d61be58c45 100644 --- a/src/backend/nodes/extensible.c +++ b/src/backend/nodes/extensible.c @@ -24,61 +24,87 @@ #include "utils/hsearch.h" static HTAB *extensible_node_methods = NULL; +static HTAB *custom_scan_methods = NULL; typedef struct { char extnodename[EXTNODENAME_MAX_LEN]; - const ExtensibleNodeMethods *methods; + const void *extnodemethods; } ExtensibleNodeEntry; +/* + * An internal function to register a new callback structure + */ +static void +RegisterExtensibleNodeEntry(HTAB **p_htable, const char *htable_label, + const char *extnodename, + const void *extnodemethods) +{ + ExtensibleNodeEntry *entry; + bool found; + + if (*p_htable == NULL) + { + HASHCTL ctl; + + memset(&ctl, 0, sizeof(HASHCTL)); + ctl.keysize = EXTNODENAME_MAX_LEN; + ctl.entrysize = sizeof(ExtensibleNodeEntry); + + *p_htable = hash_create(htable_label, 100, &ctl, HASH_ELEM); + } + + if (strlen(extnodename) >= EXTNODENAME_MAX_LEN) + elog(ERROR, "extensible node name is too long"); + + entry = (ExtensibleNodeEntry *) hash_search(*p_htable, + extnodename, + HASH_ENTER, &found); + if (found) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("extensible node type \"%s\" already exists", + extnodename))); + + entry->extnodemethods = extnodemethods; +} + /* * Register a new type of extensible node. */ void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *methods) { - ExtensibleNodeEntry *entry; - bool found; - - if (extensible_node_methods == NULL) - { - HASHCTL ctl; - - memset(&ctl, 0, sizeof(HASHCTL)); - ctl.keysize = EXTNODENAME_MAX_LEN; - ctl.entrysize = sizeof(ExtensibleNodeEntry); - extensible_node_methods = hash_create("Extensible Node Methods", - 100, &ctl, HASH_ELEM); - } - - if (strlen(methods->extnodename) >= EXTNODENAME_MAX_LEN) - elog(ERROR, "extensible node name is too long"); - - entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods, - methods->extnodename, - HASH_ENTER, &found); - if (found) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("extensible node type \"%s\" already exists", - methods->extnodename))); - - entry->methods = methods; + RegisterExtensibleNodeEntry(&extensible_node_methods, + "Extensible Node Methods", + methods->extnodename, + methods); } /* - * Get the methods for a given type of extensible node. + * Register a new type of custom scan node */ -const ExtensibleNodeMethods * -GetExtensibleNodeMethods(const char *extnodename, bool missing_ok) +void +RegisterCustomScanMethods(const CustomScanMethods *methods) +{ + RegisterExtensibleNodeEntry(&custom_scan_methods, + "Custom Scan Methods", + methods->CustomName, + methods); +} + +/* + * An internal routine to get an ExtensibleNodeEntry by the given identifier + */ +static const void * +GetExtensibleNodeEntry(HTAB *htable, const char *extnodename, bool missing_ok) { ExtensibleNodeEntry *entry = NULL; - if (extensible_node_methods != NULL) - entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods, + if (htable != NULL) + entry = (ExtensibleNodeEntry *) hash_search(htable, extnodename, HASH_FIND, NULL); - if (!entry) { if (missing_ok) @@ -89,5 +115,29 @@ GetExtensibleNodeMethods(const char *extnodename, bool missing_ok) extnodename))); } - return entry->methods; + return entry->extnodemethods; +} + +/* + * Get the methods for a given type of extensible node. + */ +const ExtensibleNodeMethods * +GetExtensibleNodeMethods(const char *extnodename, bool missing_ok) +{ + return (const ExtensibleNodeMethods *) + GetExtensibleNodeEntry(extensible_node_methods, + extnodename, + missing_ok); +} + +/* + * Get the methods for a given name of CustomScanMethods + */ +const CustomScanMethods * +GetCustomScanMethods(const char *CustomName, bool missing_ok) +{ + return (const CustomScanMethods *) + GetExtensibleNodeEntry(custom_scan_methods, + CustomName, + missing_ok); } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 32d03f7f25..83abaa68a3 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -632,11 +632,9 @@ _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 */ + /* CustomName is a key to lookup CustomScanMethods */ appendStringInfoString(str, " :methods "); - _outToken(str, node->methods->LibraryName); - appendStringInfoChar(str, ' '); - _outToken(str, node->methods->SymbolName); + _outToken(str, node->methods->CustomName); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 6db0492e15..cb0752a6ad 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1827,8 +1827,7 @@ static CustomScan * _readCustomScan(void) { READ_LOCALS(CustomScan); - char *library_name; - char *symbol_name; + char *custom_name; const CustomScanMethods *methods; ReadCommonScan(&local_node->scan); @@ -1840,19 +1839,11 @@ _readCustomScan(void) READ_NODE_FIELD(custom_scan_tlist); READ_BITMAPSET_FIELD(custom_relids); - /* - * Reconstruction of methods using library and symbol name - */ + /* Lookup CustomScanMethods by CustomName */ token = pg_strtok(&length); /* skip methods: */ - token = pg_strtok(&length); /* LibraryName */ - library_name = nullable_string(token, length); - token = pg_strtok(&length); /* SymbolName */ - symbol_name = nullable_string(token, length); - - methods = (const CustomScanMethods *) - load_external_function(library_name, symbol_name, true, NULL); - Assert(strcmp(methods->LibraryName, library_name) == 0 && - strcmp(methods->SymbolName, symbol_name) == 0); + token = pg_strtok(&length); /* CustomName */ + custom_name = nullable_string(token, length); + methods = GetCustomScanMethods(custom_name, false); local_node->methods = methods; READ_DONE(); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index d159a17fd2..e4bc14a151 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -24,6 +24,7 @@ #include "catalog/pg_class.h" #include "foreign/fdwapi.h" #include "miscadmin.h" +#include "nodes/extensible.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h index 410a3ad14d..9d0b393528 100644 --- a/src/include/executor/nodeCustom.h +++ b/src/include/executor/nodeCustom.h @@ -14,6 +14,7 @@ #include "access/parallel.h" #include "nodes/execnodes.h" +#include "nodes/extensible.h" /* * General executor code diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 0113e5c663..bf2a09bc39 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1606,38 +1606,7 @@ typedef struct ForeignScanState * the BeginCustomScan method. * ---------------- */ -struct ParallelContext; /* avoid including parallel.h here */ -struct shm_toc; /* avoid including shm_toc.h here */ -struct ExplainState; /* avoid including explain.h here */ -struct CustomScanState; - -typedef struct CustomExecMethods -{ - const char *CustomName; - - /* Executor methods: mark/restore are optional, the rest are required */ - void (*BeginCustomScan) (struct CustomScanState *node, - EState *estate, - int eflags); - TupleTableSlot *(*ExecCustomScan) (struct CustomScanState *node); - void (*EndCustomScan) (struct CustomScanState *node); - void (*ReScanCustomScan) (struct CustomScanState *node); - void (*MarkPosCustomScan) (struct CustomScanState *node); - void (*RestrPosCustomScan) (struct CustomScanState *node); - /* Optional: parallel execution support */ - Size (*EstimateDSMCustomScan) (struct CustomScanState *node, - struct ParallelContext *pcxt); - void (*InitializeDSMCustomScan) (struct CustomScanState *node, - struct ParallelContext *pcxt, - void *coordinate); - void (*InitializeWorkerCustomScan) (struct CustomScanState *node, - struct shm_toc *toc, - void *coordinate); - /* Optional: print additional information in EXPLAIN */ - void (*ExplainCustomScan) (struct CustomScanState *node, - List *ancestors, - struct ExplainState *es); -} CustomExecMethods; +struct CustomExecMethods; typedef struct CustomScanState { @@ -1645,7 +1614,7 @@ typedef struct CustomScanState uint32 flags; /* mask of CUSTOMPATH_* flags, see relation.h */ List *custom_ps; /* list of child PlanState nodes, if any */ Size pscan_len; /* size of parallel coordination information */ - const CustomExecMethods *methods; + const struct CustomExecMethods *methods; } CustomScanState; /* ---------------------------------------------------------------- diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h index 96ae7bc929..9df1377a8e 100644 --- a/src/include/nodes/extensible.h +++ b/src/include/nodes/extensible.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * extensible.h - * Definitions for extensible node type + * Definitions for extensible nodes and custom scans * * * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group @@ -14,8 +14,13 @@ #ifndef EXTENSIBLE_H #define EXTENSIBLE_H -#include "nodes/nodes.h" +#include "access/parallel.h" +#include "commands/explain.h" +#include "nodes/execnodes.h" +#include "nodes/plannodes.h" +#include "nodes/relation.h" +/* maximum length of an extensible node identifier */ #define EXTNODENAME_MAX_LEN 64 /* @@ -69,4 +74,80 @@ extern void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *method); extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name, bool missing_ok); +/* + * Flags for custom paths, indicating what capabilities the resulting scan + * will have. + */ +#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN 0x0001 +#define CUSTOMPATH_SUPPORT_MARK_RESTORE 0x0002 + +/* + * Custom path methods. Mostly, we just need to know how to convert a + * CustomPath to a plan. + */ +typedef struct CustomPathMethods +{ + const char *CustomName; + + /* Convert Path to a Plan */ + struct Plan *(*PlanCustomPath) (PlannerInfo *root, + RelOptInfo *rel, + struct CustomPath *best_path, + List *tlist, + List *clauses, + List *custom_plans); +} CustomPathMethods; + +/* + * Custom scan. Here again, there's not much to do: we need to be able to + * generate a ScanState corresponding to the scan. + */ +typedef struct CustomScanMethods +{ + const char *CustomName; + + /* Create execution state (CustomScanState) from a CustomScan plan node */ + Node *(*CreateCustomScanState) (CustomScan *cscan); +} CustomScanMethods; + +/* + * Execution-time methods for a CustomScanState. This is more complex than + * what we need for a custom path or scan. + */ +typedef struct CustomExecMethods +{ + const char *CustomName; + + /* Required executor methods */ + void (*BeginCustomScan) (CustomScanState *node, + EState *estate, + int eflags); + TupleTableSlot *(*ExecCustomScan) (CustomScanState *node); + void (*EndCustomScan) (CustomScanState *node); + void (*ReScanCustomScan) (CustomScanState *node); + + /* Optional methods: needed if mark/restore is supported */ + void (*MarkPosCustomScan) (CustomScanState *node); + void (*RestrPosCustomScan) (CustomScanState *node); + + /* Optional methods: needed if parallel execution is supported */ + Size (*EstimateDSMCustomScan) (CustomScanState *node, + ParallelContext *pcxt); + void (*InitializeDSMCustomScan) (CustomScanState *node, + ParallelContext *pcxt, + void *coordinate); + void (*InitializeWorkerCustomScan) (CustomScanState *node, + shm_toc *toc, + void *coordinate); + + /* Optional: print additional information in EXPLAIN */ + void (*ExplainCustomScan) (CustomScanState *node, + List *ancestors, + ExplainState *es); +} CustomExecMethods; + +extern void RegisterCustomScanMethods(const CustomScanMethods *methods); +extern const CustomScanMethods *GetCustomScanMethods(const char *CustomName, + bool missing_ok); + #endif /* EXTENSIBLE_H */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 00b1d35d75..465d72fe89 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -555,17 +555,7 @@ typedef struct ForeignScan * a larger struct will not work. * ---------------- */ -struct CustomScan; - -typedef struct CustomScanMethods -{ - const char *CustomName; - const char *LibraryName; - const char *SymbolName; - - /* Create execution state (CustomScanState) from a CustomScan plan node */ - Node *(*CreateCustomScanState) (struct CustomScan *cscan); -} CustomScanMethods; +struct CustomScanMethods; typedef struct CustomScan { @@ -577,7 +567,7 @@ typedef struct CustomScan List *custom_scan_tlist; /* optional tlist describing scan * tuple */ Bitmapset *custom_relids; /* RTIs generated by this scan */ - const CustomScanMethods *methods; + const struct CustomScanMethods *methods; } CustomScan; /* diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index ee7007aace..32f04b25dd 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -1030,23 +1030,8 @@ typedef struct ForeignPath * FDW case, we provide a "custom_private" field in CustomPath; providers * may prefer to use that rather than define another struct type. */ -struct CustomPath; -#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN 0x0001 -#define CUSTOMPATH_SUPPORT_MARK_RESTORE 0x0002 - -typedef struct CustomPathMethods -{ - const char *CustomName; - - /* Convert Path to a Plan */ - struct Plan *(*PlanCustomPath) (PlannerInfo *root, - RelOptInfo *rel, - struct CustomPath *best_path, - List *tlist, - List *clauses, - List *custom_plans); -} CustomPathMethods; +struct CustomPathMethods; typedef struct CustomPath { @@ -1054,7 +1039,7 @@ typedef struct CustomPath uint32 flags; /* mask of CUSTOMPATH_* flags, see above */ List *custom_paths; /* list of child Path nodes, if any */ List *custom_private; - const CustomPathMethods *methods; + const struct CustomPathMethods *methods; } CustomPath; /*