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.
This commit is contained in:
Robert Haas 2016-03-29 11:00:18 -04:00
parent 534da37927
commit f9143d102f
10 changed files with 184 additions and 117 deletions

View File

@ -21,6 +21,7 @@
#include "commands/prepare.h" #include "commands/prepare.h"
#include "executor/hashjoin.h" #include "executor/hashjoin.h"
#include "foreign/fdwapi.h" #include "foreign/fdwapi.h"
#include "nodes/extensible.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"

View File

@ -24,61 +24,87 @@
#include "utils/hsearch.h" #include "utils/hsearch.h"
static HTAB *extensible_node_methods = NULL; static HTAB *extensible_node_methods = NULL;
static HTAB *custom_scan_methods = NULL;
typedef struct typedef struct
{ {
char extnodename[EXTNODENAME_MAX_LEN]; char extnodename[EXTNODENAME_MAX_LEN];
const ExtensibleNodeMethods *methods; const void *extnodemethods;
} ExtensibleNodeEntry; } 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. * Register a new type of extensible node.
*/ */
void void
RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *methods) RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *methods)
{ {
ExtensibleNodeEntry *entry; RegisterExtensibleNodeEntry(&extensible_node_methods,
bool found; "Extensible Node Methods",
methods->extnodename,
if (extensible_node_methods == NULL) methods);
{
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;
} }
/* /*
* Get the methods for a given type of extensible node. * Register a new type of custom scan node
*/ */
const ExtensibleNodeMethods * void
GetExtensibleNodeMethods(const char *extnodename, bool missing_ok) 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; ExtensibleNodeEntry *entry = NULL;
if (extensible_node_methods != NULL) if (htable != NULL)
entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods, entry = (ExtensibleNodeEntry *) hash_search(htable,
extnodename, extnodename,
HASH_FIND, NULL); HASH_FIND, NULL);
if (!entry) if (!entry)
{ {
if (missing_ok) if (missing_ok)
@ -89,5 +115,29 @@ GetExtensibleNodeMethods(const char *extnodename, bool missing_ok)
extnodename))); 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);
} }

View File

@ -632,11 +632,9 @@ _outCustomScan(StringInfo str, const CustomScan *node)
WRITE_NODE_FIELD(custom_private); WRITE_NODE_FIELD(custom_private);
WRITE_NODE_FIELD(custom_scan_tlist); WRITE_NODE_FIELD(custom_scan_tlist);
WRITE_BITMAPSET_FIELD(custom_relids); WRITE_BITMAPSET_FIELD(custom_relids);
/* Dump library and symbol name instead of raw pointer */ /* CustomName is a key to lookup CustomScanMethods */
appendStringInfoString(str, " :methods "); appendStringInfoString(str, " :methods ");
_outToken(str, node->methods->LibraryName); _outToken(str, node->methods->CustomName);
appendStringInfoChar(str, ' ');
_outToken(str, node->methods->SymbolName);
} }
static void static void

View File

@ -1827,8 +1827,7 @@ static CustomScan *
_readCustomScan(void) _readCustomScan(void)
{ {
READ_LOCALS(CustomScan); READ_LOCALS(CustomScan);
char *library_name; char *custom_name;
char *symbol_name;
const CustomScanMethods *methods; const CustomScanMethods *methods;
ReadCommonScan(&local_node->scan); ReadCommonScan(&local_node->scan);
@ -1840,19 +1839,11 @@ _readCustomScan(void)
READ_NODE_FIELD(custom_scan_tlist); READ_NODE_FIELD(custom_scan_tlist);
READ_BITMAPSET_FIELD(custom_relids); READ_BITMAPSET_FIELD(custom_relids);
/* /* Lookup CustomScanMethods by CustomName */
* Reconstruction of methods using library and symbol name
*/
token = pg_strtok(&length); /* skip methods: */ token = pg_strtok(&length); /* skip methods: */
token = pg_strtok(&length); /* LibraryName */ token = pg_strtok(&length); /* CustomName */
library_name = nullable_string(token, length); custom_name = nullable_string(token, length);
token = pg_strtok(&length); /* SymbolName */ methods = GetCustomScanMethods(custom_name, false);
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);
local_node->methods = methods; local_node->methods = methods;
READ_DONE(); READ_DONE();

View File

@ -24,6 +24,7 @@
#include "catalog/pg_class.h" #include "catalog/pg_class.h"
#include "foreign/fdwapi.h" #include "foreign/fdwapi.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/extensible.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"

View File

@ -14,6 +14,7 @@
#include "access/parallel.h" #include "access/parallel.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "nodes/extensible.h"
/* /*
* General executor code * General executor code

View File

@ -1606,38 +1606,7 @@ typedef struct ForeignScanState
* the BeginCustomScan method. * the BeginCustomScan method.
* ---------------- * ----------------
*/ */
struct ParallelContext; /* avoid including parallel.h here */ struct CustomExecMethods;
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;
typedef struct CustomScanState typedef struct CustomScanState
{ {
@ -1645,7 +1614,7 @@ typedef struct CustomScanState
uint32 flags; /* mask of CUSTOMPATH_* flags, see relation.h */ uint32 flags; /* mask of CUSTOMPATH_* flags, see relation.h */
List *custom_ps; /* list of child PlanState nodes, if any */ List *custom_ps; /* list of child PlanState nodes, if any */
Size pscan_len; /* size of parallel coordination information */ Size pscan_len; /* size of parallel coordination information */
const CustomExecMethods *methods; const struct CustomExecMethods *methods;
} CustomScanState; } CustomScanState;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------

View File

@ -1,7 +1,7 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* extensible.h * extensible.h
* Definitions for extensible node type * Definitions for extensible nodes and custom scans
* *
* *
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
@ -14,8 +14,13 @@
#ifndef EXTENSIBLE_H #ifndef EXTENSIBLE_H
#define 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 #define EXTNODENAME_MAX_LEN 64
/* /*
@ -69,4 +74,80 @@ extern void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *method);
extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name, extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name,
bool missing_ok); 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 */ #endif /* EXTENSIBLE_H */

View File

@ -555,17 +555,7 @@ typedef struct ForeignScan
* a larger struct will not work. * a larger struct will not work.
* ---------------- * ----------------
*/ */
struct CustomScan; struct CustomScanMethods;
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;
typedef struct CustomScan typedef struct CustomScan
{ {
@ -577,7 +567,7 @@ typedef struct CustomScan
List *custom_scan_tlist; /* optional tlist describing scan List *custom_scan_tlist; /* optional tlist describing scan
* tuple */ * tuple */
Bitmapset *custom_relids; /* RTIs generated by this scan */ Bitmapset *custom_relids; /* RTIs generated by this scan */
const CustomScanMethods *methods; const struct CustomScanMethods *methods;
} CustomScan; } CustomScan;
/* /*

View File

@ -1030,23 +1030,8 @@ typedef struct ForeignPath
* FDW case, we provide a "custom_private" field in CustomPath; providers * FDW case, we provide a "custom_private" field in CustomPath; providers
* may prefer to use that rather than define another struct type. * may prefer to use that rather than define another struct type.
*/ */
struct CustomPath;
#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN 0x0001 struct CustomPathMethods;
#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;
typedef struct CustomPath typedef struct CustomPath
{ {
@ -1054,7 +1039,7 @@ typedef struct CustomPath
uint32 flags; /* mask of CUSTOMPATH_* flags, see above */ uint32 flags; /* mask of CUSTOMPATH_* flags, see above */
List *custom_paths; /* list of child Path nodes, if any */ List *custom_paths; /* list of child Path nodes, if any */
List *custom_private; List *custom_private;
const CustomPathMethods *methods; const struct CustomPathMethods *methods;
} CustomPath; } CustomPath;
/* /*