 doc/src/sgml/custom-scan.sgml     | 115 +++++++++++++++++++++++++++++---------
 src/backend/commands/explain.c    |   2 +-
 src/backend/executor/nodeCustom.c |   4 +-
 src/backend/nodes/copyfuncs.c     |  19 +++++--
 src/backend/nodes/nodes.c         |  76 +++++++++++++++++++++++++
 src/backend/nodes/outfuncs.c      |  45 ++++++++++++---
 src/backend/nodes/readfuncs.c     |  46 ++++++++++-----
 src/include/nodes/execnodes.h     |   2 +-
 src/include/nodes/nodes.h         |  24 ++++++++
 src/include/nodes/plannodes.h     |   5 +-
 src/include/nodes/relation.h      |   6 +-
 11 files changed, 283 insertions(+), 61 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index 5bba125..933a9bd 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -78,14 +78,16 @@ typedef struct CustomPath
     nodes used by this custom-path node; these will be transformed into
     <structname>Plan</> nodes by planner.
     <structfield>custom_private</> can be used to store the custom path's
-    private data.  Private data should be stored in a form that can be handled
-    by <literal>nodeToString</>, so that debugging routines that attempt to
-    print the custom path will work as designed.  <structfield>methods</> must
-    point to a (usually statically allocated) object implementing the required
-    custom path methods, of which there is currently only one.  The
-    <structfield>LibraryName</> and <structfield>SymbolName</> fields must also
-    be initialized so that the dynamic loader can resolve them to locate the
-    method table.
+    private data. Private data should be stored in a form that can be handled
+    by <literal>nodeToString</>. <structfield>methods</> must point to a
+    (usually statically allocated) object implementing the required custom
+    path methods, of which there is currently only one.
+    The first field of <structname>CustomPathMethods</> is
+    <structname>ExtensibleNodeMethods</>. If custom scan provider defines
+    own structure larger than <structname>CustomPathMethods</> to keep
+    private information on the extra area, it must support callbacks of
+    the <structname>ExtensibleNodeMethods</> to treat these private fields.
+    See <xref linkend="custom-private-structure"> for more details.
   </para>
 
   <para>
@@ -125,19 +127,6 @@ Plan *(*PlanCustomPath) (PlannerInfo *root,
     be a <literal>CustomScan</> object, which the callback must allocate and
     initialize.  See <xref linkend="custom-scan-plan"> for more details.
    </para>
-
-   <para>
-<programlisting>
-void (*TextOutCustomPath) (StringInfo str,
-                           const CustomPath *node);
-</programlisting>
-    Generate additional output when <function>nodeToString</> is invoked on
-    this custom path.  This callback is optional.  Since
-    <function>nodeToString</> will automatically dump all fields in the
-    structure that it can see, including <structfield>custom_private</>, this
-    is only useful if the <structname>CustomPath</> is actually embedded in a
-    larger struct containing additional fields.
-   </para>
   </sect2>
  </sect1>
 
@@ -198,10 +187,15 @@ typedef struct CustomScan
   <para>
    Plan trees must be able to be duplicated using <function>copyObject</>,
    so all the data stored within the <quote>custom</> fields must consist of
-   nodes that that function can handle.  Furthermore, custom scan providers
-   cannot substitute a larger structure that embeds
-   a <structname>CustomScan</> for the structure itself, as would be possible
-   for a <structname>CustomPath</> or <structname>CustomScanState</>.
+   nodes that that function can handle. In case when custom scan providers
+   can substitute a larger structure that embeds a <structname>CustomScan</>
+   for the structure itself, as would be possible for <structname>CustomPath</>
+   or <structname>CustomScanState</>.
+   If custom scan provider keeps its private fields in the extra area of the
+   above own defined structure, it also have to implement each callbacks of
+   <structname>ExtensibleNodeMethods</> to support serialization and
+   deserialization.
+   See <xref linkend="custom-scan-plan"> for more details.
   </para>
 
   <sect2 id="custom-scan-plan-callbacks">
@@ -328,4 +322,75 @@ void (*ExplainCustomScan) (CustomScanState *node,
    </para>
   </sect2>
  </sect1>
+
+ <sect1 id="custom-private-structure">
+  <title>Private fields with own structure</title>
+  <para>
+   Some node types, like <structname>CustomScan</> or
+   <structname>ForeignScan</>, allows extension to have itself as a part of
+   larger structure for their private fields with arbitrary forms.
+   In this case, extension has to ensure its private fields are kept
+   correctly on node operations, like copy, serialization and deserialization.
+  </para>
+  <para>
+   <structname>ExtensibleNodeMethods</> is a collection of callbacks that
+   enable extension to get control on node operations, to process its private
+   fields.
+   All the <structname>ExtensibleNodeMethods</> implementation has to be
+   registered with a unique name. It is key to lookup proper collection of
+   the callbacks later. Usually, it shall be done on <funcation>_PG_init</>.
+  </para>
+  <para>
+   Specification of the individual callbacks are below.
+  </para>
+  <para>
+<programlisting>
+const char *extnodename;
+</programlisting>
+   A unique name of this collection of the node operations callback.
+   It should not be duplicated with one preliminary registered.
+  </para>
+  <para>
+<programlisting>
+Size    node_size;
+</programlisting>
+   Size of the self define structure. Of course, it has to be larger
+   than or equal to the structure to be embedded in.
+  </para>
+  <para>
+<programlisting>
+void    nodeCopy(Node *newnode, const Node *oldnode);
+</programlisting>
+   This callback copies the private fields on <varname>oldnode</> to
+   the <varname>newnode</>. Extension does not need to copy the known
+   fields in the structure that is embedded in; it is the responsibility
+   of the core backend.
+  </para>
+  <para>
+<programlisting>
+Size    nodeEqual(const Node *a, const Node *b);
+</programlisting>
+   This callback compares private fields on <varname>a</> and <varname>b</>,
+   then returns <literal>true</> if equivalent. Elsewhere, <literal>false</>.
+  </para>
+  <para>
+<programlisting>
+Size    nodeOut(StringInfo str, const Node *node);
+</programlisting>
+   This callback serializes the private fields in text form, then put this
+   string on the supplied <structname>StringInfo</>. All extension has to
+   write out is its own private fields. The known fields on the structure
+   embedded in are written out by the core backend.
+  </para>
+  <para>
+<programlisting>
+Size    nodeRead(Node *node);
+</programlisting>
+   This callback deserializes the private fields, written by the above
+   <function>nodeOut</>, on the supplied <varname>node</> object.
+   So, these callbacks have to be always symmetric.
+   It shall be invoked in the context of <function>stringToNode</>, thus
+   <function>pg_strtok</> will fetch next token to read.
+  </para>
+ </sect1>
 </chapter>
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 25d8ca0..2fc16de 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -892,7 +892,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			break;
 		case T_CustomScan:
 			sname = "Custom Scan";
-			custom_name = ((CustomScan *) plan)->methods->CustomName;
+			custom_name = ((CustomScan *) plan)->methods->xnode.extnodename;
 			if (custom_name)
 				pname = psprintf("Custom Scan (%s)", custom_name);
 			else
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 640289e..55d5e1f 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -145,7 +145,7 @@ ExecCustomMarkPos(CustomScanState *node)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("custom scan \"%s\" does not support MarkPos",
-						node->methods->CustomName)));
+						node->methods->xnode.extnodename)));
 	node->methods->MarkPosCustomScan(node);
 }
 
@@ -156,6 +156,6 @@ ExecCustomRestrPos(CustomScanState *node)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("custom scan \"%s\" does not support MarkPos",
-						node->methods->CustomName)));
+						node->methods->xnode.extnodename)));
 	node->methods->RestrPosCustomScan(node);
 }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5877037..a98bd88 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -635,8 +635,12 @@ _copyWorkTableScan(const WorkTableScan *from)
 static ForeignScan *
 _copyForeignScan(const ForeignScan *from)
 {
-	ForeignScan *newnode = makeNode(ForeignScan);
-
+	const ExtensibleNodeMethods *methods
+		= GetExtensibleNodeMethods(from->extnodename, true);
+	ForeignScan *newnode = (ForeignScan *) newNode(!methods
+												   ? sizeof(ForeignScan)
+												   : methods->node_size,
+												   T_ForeignScan);
 	/*
 	 * copy node superclass fields
 	 */
@@ -645,6 +649,7 @@ _copyForeignScan(const ForeignScan *from)
 	/*
 	 * copy remainder of node
 	 */
+	COPY_STRING_FIELD(extnodename);
 	COPY_SCALAR_FIELD(fs_server);
 	COPY_NODE_FIELD(fdw_exprs);
 	COPY_NODE_FIELD(fdw_private);
@@ -652,6 +657,8 @@ _copyForeignScan(const ForeignScan *from)
 	COPY_NODE_FIELD(fdw_recheck_quals);
 	COPY_BITMAPSET_FIELD(fs_relids);
 	COPY_SCALAR_FIELD(fsSystemCol);
+	if (methods && methods->nodeCopy)
+		methods->nodeCopy((Node *) newnode, (const Node *) from);
 
 	return newnode;
 }
@@ -662,8 +669,9 @@ _copyForeignScan(const ForeignScan *from)
 static CustomScan *
 _copyCustomScan(const CustomScan *from)
 {
-	CustomScan *newnode = makeNode(CustomScan);
-
+	const ExtensibleNodeMethods *methods = &from->methods->xnode;
+	CustomScan *newnode = (CustomScan *) newNode(methods->node_size,
+												 T_CustomScan);
 	/*
 	 * copy node superclass fields
 	 */
@@ -686,6 +694,9 @@ _copyCustomScan(const CustomScan *from)
 	 */
 	COPY_SCALAR_FIELD(methods);
 
+	if (methods->nodeCopy)
+		methods->nodeCopy((Node *) newnode, (const Node *) from);
+
 	return newnode;
 }
 
diff --git a/src/backend/nodes/nodes.c b/src/backend/nodes/nodes.c
index aea3880..d31b2d7 100644
--- a/src/backend/nodes/nodes.c
+++ b/src/backend/nodes/nodes.c
@@ -19,6 +19,9 @@
 #include "postgres.h"
 
 #include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
 
 /*
  * Support for newNode() macro
@@ -29,3 +32,76 @@
  */
 
 Node	   *newNodeMacroHolder;
+
+/*
+ * Extensible node support - We allow extension to have own structure that
+ * contains well known structure (like ForeignScan or CustomScan), to keep
+ * their private data fields. These structure also have to support
+ * serialization / deserialization using node functions. A collection of
+ * callback enables to handle private fields also, not only well known
+ * portion.
+ */
+static HTAB	   *extensible_node_methods = NULL;
+
+typedef struct
+{
+	char		extnodename[NAMEDATALEN];
+	const ExtensibleNodeMethods *methods;
+} ExtensibleNodeEntry;
+
+void
+RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *methods)
+{
+	ExtensibleNodeEntry *entry;
+	bool	found;
+
+	if (!extensible_node_methods)
+	{
+		HASHCTL		ctl;
+
+		memset(&ctl, 0, sizeof(HASHCTL));
+		ctl.keysize = NAMEDATALEN;
+		ctl.entrysize = sizeof(const ExtensibleNodeEntry);
+		extensible_node_methods = hash_create("Extensible Node Methods",
+											  100, &ctl, HASH_ELEM);
+	}
+
+	if (strlen(methods->extnodename) >= NAMEDATALEN)
+		ereport(ERROR,
+				(errcode(ERRCODE_NAME_TOO_LONG),
+				 errmsg("ExtensibleNodeMethods \"%s\" name too long",
+						methods->extnodename)));
+
+	entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods,
+												methods->extnodename,
+												HASH_ENTER, &found);
+	if (found)
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("ExtensibleNodeMethods \"%s\" already exists",
+						methods->extnodename)));
+	/* setup entry */
+	entry->methods = methods;
+}
+
+const ExtensibleNodeMethods *
+GetExtensibleNodeMethods(const char *extnodename, bool missing_ok)
+{
+	ExtensibleNodeEntry *entry = NULL;
+
+	if (!extnodename)
+		return NULL;
+	if (extensible_node_methods)
+	{
+		entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods,
+													extnodename,
+													HASH_FIND, NULL);
+	}
+
+	if (!entry && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("ExtensibleNodeMethods \"%s\" was not registered",
+						extnodename)));
+	return entry ? entry->methods : NULL;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b5e0b55..90800c8 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -140,6 +140,13 @@ _outToken(StringInfo str, const char *s)
 	}
 }
 
+/* outToken - entrypoint for extensions */
+void
+outToken(StringInfo str, const char *s)
+{
+	_outToken(str, s);
+}
+
 static void
 _outList(StringInfo str, const List *node)
 {
@@ -196,6 +203,13 @@ _outBitmapset(StringInfo str, const Bitmapset *bms)
 	appendStringInfoChar(str, ')');
 }
 
+/* outBitmapset - entrypoint for extensions */
+void
+outBitmapset(StringInfo str, const Bitmapset *bms)
+{
+	_outBitmapset(str, bms);
+}
+
 /*
  * Print the value of a Datum given its type.
  */
@@ -231,7 +245,6 @@ _outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
 	}
 }
 
-
 /*
  *	Stuff from plannodes.h
  */
@@ -587,6 +600,9 @@ _outWorkTableScan(StringInfo str, const WorkTableScan *node)
 static void
 _outForeignScan(StringInfo str, const ForeignScan *node)
 {
+	const ExtensibleNodeMethods *methods
+		= GetExtensibleNodeMethods(node->extnodename, true);
+
 	WRITE_NODE_TYPE("FOREIGNSCAN");
 
 	_outScanInfo(str, (const Scan *) node);
@@ -598,11 +614,15 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
 	WRITE_NODE_FIELD(fdw_recheck_quals);
 	WRITE_BITMAPSET_FIELD(fs_relids);
 	WRITE_BOOL_FIELD(fsSystemCol);
+	if (methods && methods->nodeOut)
+		methods->nodeOut(str, (const Node *) node);
 }
 
 static void
 _outCustomScan(StringInfo str, const CustomScan *node)
 {
+	const ExtensibleNodeMethods *methods = &node->methods->xnode;
+
 	WRITE_NODE_TYPE("CUSTOMSCAN");
 
 	_outScanInfo(str, (const Scan *) node);
@@ -613,11 +633,12 @@ _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 */
+	/* Dump CustomName; alias of extnodename */
 	appendStringInfoString(str, " :methods ");
-	_outToken(str, node->methods->LibraryName);
-	appendStringInfoChar(str, ' ');
-	_outToken(str, node->methods->SymbolName);
+	_outToken(str, methods->extnodename);
+	/* Dump private fields if any */
+	if (methods && methods->nodeOut)
+		methods->nodeOut(str, (const Node *) node);
 }
 
 static void
@@ -1684,17 +1705,25 @@ _outTidPath(StringInfo str, const TidPath *node)
 static void
 _outForeignPath(StringInfo str, const ForeignPath *node)
 {
+	const ExtensibleNodeMethods *methods
+		= GetExtensibleNodeMethods(node->extnodename, true);
+
 	WRITE_NODE_TYPE("FOREIGNPATH");
 
 	_outPathInfo(str, (const Path *) node);
 
+	WRITE_STRING_FIELD(extnodename);
 	WRITE_NODE_FIELD(fdw_outerpath);
 	WRITE_NODE_FIELD(fdw_private);
+	if (methods && methods->nodeOut)
+		methods->nodeOut(str, (Node *) node);
 }
 
 static void
 _outCustomPath(StringInfo str, const CustomPath *node)
 {
+	const ExtensibleNodeMethods *methods = &node->methods->xnode;
+
 	WRITE_NODE_TYPE("CUSTOMPATH");
 
 	_outPathInfo(str, (const Path *) node);
@@ -1703,9 +1732,9 @@ _outCustomPath(StringInfo str, const CustomPath *node)
 	WRITE_NODE_FIELD(custom_paths);
 	WRITE_NODE_FIELD(custom_private);
 	appendStringInfoString(str, " :methods ");
-	_outToken(str, node->methods->CustomName);
-	if (node->methods->TextOutCustomPath)
-		node->methods->TextOutCustomPath(str, node);
+	_outToken(str, methods->extnodename);
+	if (methods && methods->nodeOut)
+		methods->nodeOut(str, (Node *)node);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index a67b337..49030ea 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1792,10 +1792,12 @@ _readWorkTableScan(void)
 static ForeignScan *
 _readForeignScan(void)
 {
+	const ExtensibleNodeMethods *methods;
 	READ_LOCALS(ForeignScan);
 
 	ReadCommonScan(&local_node->scan);
 
+	READ_STRING_FIELD(extnodename);
 	READ_OID_FIELD(fs_server);
 	READ_NODE_FIELD(fdw_exprs);
 	READ_NODE_FIELD(fdw_private);
@@ -1804,6 +1806,19 @@ _readForeignScan(void)
 	READ_BITMAPSET_FIELD(fs_relids);
 	READ_BOOL_FIELD(fsSystemCol);
 
+	methods = GetExtensibleNodeMethods(local_node->extnodename, true);
+	if (methods && methods->nodeRead)
+	{
+		ForeignScan	   *local_temp = palloc0(methods->node_size);
+
+		Assert(methods->node_size >= sizeof(ForeignScan));
+
+		memcpy(local_temp, local_node, sizeof(ForeignScan));
+		methods->nodeRead((Node *) local_temp);
+
+		pfree(local_node);
+		local_node = local_temp;
+	}
 	READ_DONE();
 }
 
@@ -1813,10 +1828,9 @@ _readForeignScan(void)
 static CustomScan *
 _readCustomScan(void)
 {
+	const ExtensibleNodeMethods *methods;
+	const char	   *extnodename;
 	READ_LOCALS(CustomScan);
-	char	   *library_name;
-	char	   *symbol_name;
-	const CustomScanMethods *methods;
 
 	ReadCommonScan(&local_node->scan);
 
@@ -1831,18 +1845,24 @@ _readCustomScan(void)
 	 * Reconstruction of methods using library and symbol name
 	 */
 	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);
+	token = pg_strtok(&length);		/* CustomName (alias of extnodename) */
+	extnodename = nullable_string(token, length);
+	methods = GetExtensibleNodeMethods(extnodename, false);
 
-	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;
+	if (methods->nodeRead)
+	{
+		CustomScan	   *local_temp = palloc0(methods->node_size);
 
-	READ_DONE();
+		Assert(methods->node_size >= sizeof(CustomScan));
+		memcpy(local_temp, local_node, sizeof(CustomScan));
+		methods->nodeRead((Node *) local_temp);
+
+		pfree(local_node);
+		local_node = local_temp;
+	}
+	local_node->methods = (const CustomScanMethods *) methods;
+
+    READ_DONE();
 }
 
 /*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 07cd20a..d8693b2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1608,7 +1608,7 @@ struct CustomScanState;
 
 typedef struct CustomExecMethods
 {
-	const char *CustomName;
+	ExtensibleNodeMethods xnode;	/* collection of node operations */
 
 	/* Executor methods: mark/restore are optional, the rest are required */
 	void		(*BeginCustomScan) (struct CustomScanState *node,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index cf09db4..9957e0a 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -527,6 +527,12 @@ 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}
  */
@@ -542,6 +548,24 @@ extern void *copyObject(const void *obj);
  */
 extern bool equal(const void *a, const void *b);
 
+/*
+ * ExtensibleNodeMethods - a collection of node operation callbacks.
+ */
+struct StringInfoData;		/* not to include stringinfo.h here */
+
+typedef struct ExtensibleNodeMethods
+{
+	const char *extnodename;
+	Size		node_size;
+	void	  (*nodeCopy)(Node *newnode, const Node *oldnode);
+	bool	  (*nodeEqual)(const Node *a, const Node *b);
+	void	  (*nodeOut)(struct StringInfoData *str, const Node *node);
+	void	  (*nodeRead)(Node *node);
+} ExtensibleNodeMethods;
+
+extern void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *method);
+extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name,
+															 bool missing_ok);
 
 /*
  * Typedefs for identifying qualifier selectivities and plan costs as such.
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index e823c83..b0cbe1a 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -530,6 +530,7 @@ typedef struct WorkTableScan
 typedef struct ForeignScan
 {
 	Scan		scan;
+	const char *extnodename;	/* collection of node operation callbacks */
 	Oid			fs_server;		/* OID of foreign server */
 	List	   *fdw_exprs;		/* expressions that FDW may evaluate */
 	List	   *fdw_private;	/* private data for FDW */
@@ -556,9 +557,7 @@ struct CustomScan;
 
 typedef struct CustomScanMethods
 {
-	const char *CustomName;
-	const char *LibraryName;
-	const char *SymbolName;
+	ExtensibleNodeMethods xnode;	/* collection of node operations */
 
 	/* Create execution state (CustomScanState) from a CustomScan plan node */
 	Node	   *(*CreateCustomScanState) (struct CustomScan *cscan);
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index b233b62..3fa6ed0 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -918,6 +918,7 @@ typedef struct TidPath
 typedef struct ForeignPath
 {
 	Path		path;
+	const char *extnodename;
 	Path	   *fdw_outerpath;
 	List	   *fdw_private;
 } ForeignPath;
@@ -947,7 +948,7 @@ struct CustomPath;
 
 typedef struct CustomPathMethods
 {
-	const char *CustomName;
+	ExtensibleNodeMethods xnode;	/* collection of node operations */
 
 	/* Convert Path to a Plan */
 	struct Plan *(*PlanCustomPath) (PlannerInfo *root,
@@ -956,9 +957,6 @@ typedef struct CustomPathMethods
 												List *tlist,
 												List *clauses,
 												List *custom_plans);
-	/* Optional: print additional fields besides "private" */
-	void		(*TextOutCustomPath) (StringInfo str,
-											  const struct CustomPath *node);
 } CustomPathMethods;
 
 typedef struct CustomPath
