diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile index fe2e46053e6..0b1e98c0190 100644 --- a/src/backend/nodes/Makefile +++ b/src/backend/nodes/Makefile @@ -13,7 +13,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = nodeFuncs.o nodes.o list.o bitmapset.o tidbitmap.o \ - copyfuncs.o equalfuncs.o makefuncs.o \ + copyfuncs.o equalfuncs.o extensible.o makefuncs.o \ outfuncs.o readfuncs.o print.o read.o params.o value.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e54d1744b0a..a9e9cc379b8 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -23,6 +23,7 @@ #include "postgres.h" #include "miscadmin.h" +#include "nodes/extensible.h" #include "nodes/plannodes.h" #include "nodes/relation.h" #include "utils/datum.h" @@ -4165,6 +4166,27 @@ _copyList(const List *from) return new; } +/* **************************************************************** + * extensible.h copy functions + * **************************************************************** + */ +static ExtensibleNode * +_copyExtensibleNode(const ExtensibleNode *from) +{ + ExtensibleNode *newnode; + const ExtensibleNodeMethods *methods; + + methods = GetExtensibleNodeMethods(from->extnodename, false); + newnode = (ExtensibleNode *) newNode(methods->node_size, + T_ExtensibleNode); + COPY_STRING_FIELD(extnodename); + + /* copy the private fields */ + methods->nodeCopy(newnode, from); + + return newnode; +} + /* **************************************************************** * value.h copy functions * **************************************************************** @@ -4544,6 +4566,13 @@ copyObject(const void *from) retval = list_copy(from); break; + /* + * EXTENSIBLE NODES + */ + case T_ExtensibleNode: + retval = _copyExtensibleNode(from); + break; + /* * PARSE NODES */ diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 08ccc0de664..b9c39594283 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -29,6 +29,7 @@ #include "postgres.h" +#include "nodes/extensible.h" #include "nodes/relation.h" #include "utils/datum.h" @@ -871,6 +872,25 @@ _equalPlaceHolderInfo(const PlaceHolderInfo *a, const PlaceHolderInfo *b) return true; } +/* + * Stuff from extensible.h + */ +static bool +_equalExtensibleNode(const ExtensibleNode *a, const ExtensibleNode *b) +{ + const ExtensibleNodeMethods *methods; + + COMPARE_STRING_FIELD(extnodename); + + /* At this point, we know extnodename is the same for both nodes. */ + methods = GetExtensibleNodeMethods(a->extnodename, false); + + /* compare the private fields */ + if (!methods->nodeEqual(a, b)) + return false; + + return true; +} /* * Stuff from parsenodes.h @@ -2873,6 +2893,13 @@ equal(const void *a, const void *b) retval = _equalValue(a, b); break; + /* + * EXTENSIBLE NODES + */ + case T_ExtensibleNode: + retval = _equalExtensibleNode(a, b); + break; + /* * PARSE NODES */ diff --git a/src/backend/nodes/extensible.c b/src/backend/nodes/extensible.c new file mode 100644 index 00000000000..8fb4767d4d2 --- /dev/null +++ b/src/backend/nodes/extensible.c @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------- + * + * extensible.c + * Support for extensible node types + * + * Loadable modules can define what are in effect new types of nodes using + * the routines in this file. All such nodes are flagged T_ExtensibleNode, + * with the extnodename field distinguishing the specific type. Use + * RegisterExtensibleNodeMethods to register a new type of extensible node, + * and GetExtensibleNodeMethods to get information about a previously + * registered type of extensible node. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/nodes/extensible.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/extensible.h" +#include "utils/hsearch.h" + +static HTAB *extensible_node_methods = NULL; + +typedef struct +{ + char extnodename[EXTNODENAME_MAX_LEN]; + const ExtensibleNodeMethods *methods; +} ExtensibleNodeEntry; + +/* + * 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 = NAMEDATALEN; + ctl.entrysize = sizeof(ExtensibleNodeEntry); + extensible_node_methods = hash_create("Extensible Node Methods", + 100, &ctl, HASH_ELEM); + } + + Assert(strlen(methods->extnodename) <= EXTNODENAME_MAX_LEN); + + 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. + */ +const ExtensibleNodeMethods * +GetExtensibleNodeMethods(const char *extnodename, bool missing_ok) +{ + ExtensibleNodeEntry *entry = NULL; + + if (extensible_node_methods != NULL) + entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods, + extnodename, + HASH_FIND, NULL); + + if (!entry) + { + if (missing_ok) + return NULL; + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("ExtensibleNodeMethods \"%s\" was not registered", + extnodename))); + } + + return entry->methods; +} diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 3e1c3e6be57..28d983c9a87 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -26,6 +26,7 @@ #include #include "lib/stringinfo.h" +#include "nodes/extensible.h" #include "nodes/plannodes.h" #include "nodes/relation.h" #include "utils/datum.h" @@ -140,6 +141,13 @@ _outToken(StringInfo str, const char *s) } } +/* for use by extensions which define extensible nodes */ +void +outToken(StringInfo str, const char *s) +{ + _outToken(str, s); +} + static void _outList(StringInfo str, const List *node) { @@ -196,6 +204,13 @@ _outBitmapset(StringInfo str, const Bitmapset *bms) appendStringInfoChar(str, ')'); } +/* for use by extensions which define extensible nodes */ +void +outBitmapset(StringInfo str, const Bitmapset *bms) +{ + _outBitmapset(str, bms); +} + /* * Print the value of a Datum given its type. */ @@ -2112,6 +2127,27 @@ _outPlannerParamItem(StringInfo str, const PlannerParamItem *node) WRITE_INT_FIELD(paramId); } +/***************************************************************************** + * + * Stuff from extensible.h + * + *****************************************************************************/ + +static void +_outExtensibleNode(StringInfo str, const ExtensibleNode *node) +{ + const ExtensibleNodeMethods *methods; + + methods = GetExtensibleNodeMethods(node->extnodename, false); + + WRITE_NODE_TYPE("EXTENSIBLENODE"); + + WRITE_STRING_FIELD(extnodename); + + /* serialize the private fields */ + methods->nodeOut(str, node); +} + /***************************************************************************** * * Stuff from parsenodes.h. @@ -3367,6 +3403,10 @@ _outNode(StringInfo str, const void *obj) _outPlannerParamItem(str, obj); break; + case T_ExtensibleNode: + _outExtensibleNode(str, obj); + break; + case T_CreateStmt: _outCreateStmt(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e4d41ee95b2..e6e6f2981c2 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -29,6 +29,7 @@ #include #include "fmgr.h" +#include "nodes/extensible.h" #include "nodes/parsenodes.h" #include "nodes/plannodes.h" #include "nodes/readfuncs.h" @@ -218,6 +219,14 @@ _readBitmapset(void) return result; } +/* + * for use by extensions which define extensible nodes + */ +Bitmapset * +readBitmapset(void) +{ + return _readBitmapset(); +} /* * _readQuery @@ -2219,6 +2228,35 @@ _readAlternativeSubPlan(void) READ_DONE(); } +/* + * _readExtensibleNode + */ +static ExtensibleNode * +_readExtensibleNode(void) +{ + const ExtensibleNodeMethods *methods; + ExtensibleNode *local_node; + const char *extnodename; + READ_TEMP_LOCALS(); + + token = pg_strtok(&length); /* skip: extnodename */ + token = pg_strtok(&length); /* get extnodename */ + + extnodename = nullable_string(token, length); + if (!extnodename) + elog(ERROR, "extnodename has to be supplied"); + methods = GetExtensibleNodeMethods(extnodename, false); + + local_node = (ExtensibleNode *) newNode(methods->node_size, + T_ExtensibleNode); + local_node->extnodename = extnodename; + + /* deserialize the private fields */ + methods->nodeRead(local_node); + + READ_DONE(); +} + /* * parseNodeString * @@ -2447,6 +2485,8 @@ parseNodeString(void) return_value = _readSubPlan(); else if (MATCH("ALTERNATIVESUBPLAN", 18)) return_value = _readAlternativeSubPlan(); + else if (MATCH("EXTENSIBLENODE", 14)) + return_value = _readExtensibleNode(); else { elog(ERROR, "badly formatted node string \"%.32s\"...", token); diff --git a/src/include/nodes/extensible.h b/src/include/nodes/extensible.h new file mode 100644 index 00000000000..96ae7bc9291 --- /dev/null +++ b/src/include/nodes/extensible.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * extensible.h + * Definitions for extensible node type + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/nodes/extensible.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXTENSIBLE_H +#define EXTENSIBLE_H + +#include "nodes/nodes.h" + +#define EXTNODENAME_MAX_LEN 64 + +/* + * An extensible node is a new type of node defined by an extension. The + * type is always T_ExtensibleNode, while the extnodename identifies the + * specific type of node. extnodename can be looked up to find the + * ExtensibleNodeMethods for this node type. + */ +typedef struct ExtensibleNode +{ + NodeTag type; + const char *extnodename; /* identifier of ExtensibleNodeMethods */ +} ExtensibleNode; + +/* + * node_size is the size of an extensible node of this type in bytes. + * + * nodeCopy is a function which performs a deep copy from oldnode to newnode. + * It does not need to copy type or extnodename, which are copied by the + * core system. + * + * nodeEqual is a function which performs a deep equality comparison between + * a and b and returns true or false accordingly. It does not need to compare + * type or extnodename, which are compared by the core system. + * + * nodeOut is a serialization function for the node type. It should use the + * output conventions typical for outfuncs.c. It does not need to output + * type or extnodename; the core system handles those. + * + * nodeRead is a deserialization function for the node type. It does not need + * to read type or extnodename; the core system handles those. It should fetch + * the next token using pg_strtok() from the current input stream, and then + * reconstruct the private fields according to the manner in readfuncs.c. + * + * All callbacks are mandatory. + */ +typedef struct ExtensibleNodeMethods +{ + const char *extnodename; + Size node_size; + void (*nodeCopy)(struct ExtensibleNode *newnode, + const struct ExtensibleNode *oldnode); + bool (*nodeEqual)(const struct ExtensibleNode *a, + const struct ExtensibleNode *b); + void (*nodeOut)(struct StringInfoData *str, + const struct ExtensibleNode *node); + void (*nodeRead)(struct ExtensibleNode *node); +} ExtensibleNodeMethods; + +extern void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *method); +extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name, + bool missing_ok); + +#endif /* EXTENSIBLE_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index cf09db4e5fe..c407fa2cd49 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -275,6 +275,11 @@ typedef enum NodeTag T_IntList, T_OidList, + /* + * TAGS FOR EXTENSIBLE NODES (extensible.h) + */ + T_ExtensibleNode, + /* * TAGS FOR STATEMENT NODES (mostly in parsenodes.h) */ @@ -527,10 +532,17 @@ extern PGDLLIMPORT Node *newNodeMacroHolder; */ extern char *nodeToString(const void *obj); +struct Bitmapset; /* not to include bitmapset.h here */ +struct StringInfoData; /* not to include stringinfo.h here */ +extern void outToken(struct StringInfoData *str, const char *s); +extern void outBitmapset(struct StringInfoData *str, + const struct Bitmapset *bms); + /* * nodes/{readfuncs.c,read.c} */ extern void *stringToNode(char *str); +extern struct Bitmapset *readBitmapset(void); /* * nodes/copyfuncs.c