diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index fe2e460..0b1e98c 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 e54d174..a9e9cc3 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"
@@ -4166,6 +4167,27 @@ _copyList(const List *from)
 }
 
 /* ****************************************************************
+ *					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
  * ****************************************************************
  */
@@ -4545,6 +4567,13 @@ copyObject(const void *from)
 			break;
 
 			/*
+			 * EXTENSIBLE NODES
+			 */
+		case T_ExtensibleNode:
+			retval = _copyExtensibleNode(from);
+			break;
+
+			/*
 			 * PARSE NODES
 			 */
 		case T_Query:
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 08ccc0d..b9c3959 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
@@ -2874,6 +2894,13 @@ equal(const void *a, const void *b)
 			break;
 
 			/*
+			 * EXTENSIBLE NODES
+			 */
+		case T_ExtensibleNode:
+			retval = _equalExtensibleNode(a, b);
+			break;
+
+			/*
 			 * PARSE NODES
 			 */
 		case T_Query:
diff --git a/src/backend/nodes/extensible.c b/src/backend/nodes/extensible.c
new file mode 100644
index 0000000..8fb4767
--- /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 3e1c3e6..28d983c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -26,6 +26,7 @@
 #include <ctype.h>
 
 #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.
  */
@@ -2114,6 +2129,27 @@ _outPlannerParamItem(StringInfo str, const PlannerParamItem *node)
 
 /*****************************************************************************
  *
+ *	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 e4d41ee..e6e6f29 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -29,6 +29,7 @@
 #include <math.h>
 
 #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
@@ -2220,6 +2229,35 @@ _readAlternativeSubPlan(void)
 }
 
 /*
+ * _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
  *
  * Given a character string representing a node tree, parseNodeString creates
@@ -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 0000000..96ae7bc
--- /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 cf09db4..c407fa2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -276,6 +276,11 @@ typedef enum NodeTag
 	T_OidList,
 
 	/*
+	 * TAGS FOR EXTENSIBLE NODES (extensible.h)
+	 */
+	T_ExtensibleNode,
+
+	/*
 	 * TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
 	 */
 	T_Query = 700,
@@ -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
