 src/backend/commands/explain.c          |  1 +
 src/backend/nodes/Makefile              |  3 +-
 src/backend/nodes/copyfuncs.c           |  1 +
 src/backend/nodes/custom_node.c         | 84 +++++++++++++++++++++++++++++++
 src/backend/nodes/outfuncs.c            |  7 ++-
 src/backend/nodes/readfuncs.c           | 20 +++-----
 src/backend/optimizer/plan/createplan.c |  1 +
 src/include/executor/nodeCustom.h       |  1 +
 src/include/nodes/custom_node.h         | 87 +++++++++++++++++++++++++++++++++
 src/include/nodes/execnodes.h           | 35 +------------
 src/include/nodes/plannodes.h           | 14 +-----
 src/include/nodes/relation.h            | 19 +------
 12 files changed, 192 insertions(+), 81 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 787b0b9..8959f0f 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/custom_node.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 0b1e98c..6219d92 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS = nodeFuncs.o nodes.o list.o bitmapset.o tidbitmap.o \
        copyfuncs.o equalfuncs.o extensible.o makefuncs.o \
-       outfuncs.o readfuncs.o print.o read.o params.o value.o
+       outfuncs.o readfuncs.o print.o read.o params.o value.o custom_node.o
+
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6b5d1d6..0a734d9 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/custom_node.h"
 #include "nodes/extensible.h"
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
diff --git a/src/backend/nodes/custom_node.c b/src/backend/nodes/custom_node.c
new file mode 100644
index 0000000..5c425a2
--- /dev/null
+++ b/src/backend/nodes/custom_node.c
@@ -0,0 +1,84 @@
+/*-------------------------------------------------------------------------
+ *
+ * custom_node.c
+ *    Support for custom-path/scan/exec node types
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *    src/backend/nodes/custom_node.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/custom_node.h"
+#include "utils/hsearch.h"
+
+static HTAB *custom_scan_methods = NULL;
+
+typedef struct
+{
+	char		CustomName[CUSTOM_NAME_MAX_LEN];
+	const CustomScanMethods *methods;
+} CustomScanMethodsEntry;
+
+/*
+ * Register a new custom scan methods
+ */
+void
+RegisterCustomScanMethods(const CustomScanMethods *methods)
+{
+	CustomScanMethodsEntry *entry;
+	bool		found;
+
+	if (custom_scan_methods == NULL)
+	{
+		HASHCTL     ctl;
+
+		memset(&ctl, 0, sizeof(HASHCTL));
+		ctl.keysize = CUSTOM_NAME_MAX_LEN;
+		ctl.entrysize = sizeof(CustomScanMethodsEntry);
+		custom_scan_methods = hash_create("Custom Scan Methods",
+										  100, &ctl, HASH_ELEM);
+    }
+
+	if (strlen(methods->CustomName) >= CUSTOM_NAME_MAX_LEN)
+		elog(ERROR, "CustomScanMethods name is too long");
+
+	entry = (CustomScanMethodsEntry *) hash_search(custom_scan_methods,
+												   methods->CustomName,
+												   HASH_ENTER, &found);
+	if (found)
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("CustomScanMethods \"%s\" already exists",
+						methods->CustomName)));
+
+	entry->methods = methods;
+}
+
+/*
+ * Get the methods for a given name of CustomScanMethods
+ */
+const CustomScanMethods *
+GetCustomScanMethods(const char *CustomName, bool missing_ok)
+{
+	CustomScanMethodsEntry *entry = NULL;
+
+	if (custom_scan_methods != NULL)
+		entry = (CustomScanMethodsEntry *) hash_search(custom_scan_methods,
+													   CustomName,
+													   HASH_FIND, NULL);
+	if (!entry)
+	{
+		if (missing_ok)
+			return NULL;
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("CustomScanMethods \"%s\" was not registered",
+						CustomName)));
+	}
+	return entry->methods;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 32d03f7..67cb89e 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/custom_node.h"
 #include "nodes/extensible.h"
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
@@ -632,11 +633,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 6db0492..6b1e927 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/custom_node.h"
 #include "nodes/extensible.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
@@ -1827,8 +1828,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 +1840,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 d159a17..da295c9 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/custom_node.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 410a3ad..9bcd935 100644
--- a/src/include/executor/nodeCustom.h
+++ b/src/include/executor/nodeCustom.h
@@ -13,6 +13,7 @@
 #define NODECUSTOM_H
 
 #include "access/parallel.h"
+#include "nodes/custom_node.h"
 #include "nodes/execnodes.h"
 
 /*
diff --git a/src/include/nodes/custom_node.h b/src/include/nodes/custom_node.h
new file mode 100644
index 0000000..3b6f48c
--- /dev/null
+++ b/src/include/nodes/custom_node.h
@@ -0,0 +1,87 @@
+/*-------------------------------------------------------------------------
+ *
+ * custom-node.h
+ *    Definitions for custom-path/scan/exec-methods
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CUSTOM_APIS_H
+#define CUSTOM_APIS_H
+
+#include "access/parallel.h"
+#include "commands/explain.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+
+/*
+ * Flags definitions
+ */
+#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN	0x0001
+#define CUSTOMPATH_SUPPORT_MARK_RESTORE		0x0002
+
+
+#define CUSTOM_NAME_MAX_LEN		64
+
+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 CustomScanMethods
+{
+	const char *CustomName;
+
+	/* Create execution state (CustomScanState) from a CustomScan plan node */
+	Node	   *(*CreateCustomScanState) (CustomScan *cscan);
+} CustomScanMethods;
+
+
+typedef struct CustomExecMethods
+{
+	const char *CustomName;
+
+	/* Executor methods: mark/restore are optional, the rest are required */
+	void		(*BeginCustomScan) (CustomScanState *node,
+									EState *estate,
+									int eflags);
+	TupleTableSlot *(*ExecCustomScan) (CustomScanState *node);
+	void		(*EndCustomScan) (CustomScanState *node);
+	void		(*ReScanCustomScan) (CustomScanState *node);
+	void		(*MarkPosCustomScan) (CustomScanState *node);
+	void		(*RestrPosCustomScan) (CustomScanState *node);
+	/* Optional: parallel execution support */
+	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;
+
+/*
+ * Serialization/Deserialization Support
+ */
+extern void RegisterCustomScanMethods(const CustomScanMethods *methods);
+extern const CustomScanMethods *GetCustomScanMethods(const char *CustomName,
+													 bool missing_ok);
+
+#endif		/* CUSTOM_APIS_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 0113e5c..bf2a09b 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/plannodes.h b/src/include/nodes/plannodes.h
index 00b1d35..465d72f 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 ee7007a..32f04b2 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;
 
 /*
