 src/backend/commands/explain.c          |  1 +
 src/backend/nodes/extensible.c          | 69 +++++++++++++++++++++++++
 src/backend/nodes/outfuncs.c            |  6 +--
 src/backend/nodes/readfuncs.c           | 19 ++-----
 src/backend/optimizer/plan/createplan.c |  1 +
 src/include/executor/nodeCustom.h       |  1 +
 src/include/nodes/execnodes.h           | 35 +------------
 src/include/nodes/extensible.h          | 91 ++++++++++++++++++++++++++++++++-
 src/include/nodes/plannodes.h           | 14 +----
 src/include/nodes/relation.h            | 19 +------
 10 files changed, 174 insertions(+), 82 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 787b0b9..09c2304 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/extensible.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
diff --git a/src/backend/nodes/extensible.c b/src/backend/nodes/extensible.c
index 2473b65..2931208 100644
--- a/src/backend/nodes/extensible.c
+++ b/src/backend/nodes/extensible.c
@@ -23,6 +23,7 @@
 #include "nodes/extensible.h"
 #include "utils/hsearch.h"
 
+/* for registration of extensible node */
 static HTAB *extensible_node_methods = NULL;
 
 typedef struct
@@ -31,6 +32,15 @@ typedef struct
 	const ExtensibleNodeMethods *methods;
 } ExtensibleNodeEntry;
 
+/* for registration of custom-scan methods */
+static HTAB *custom_scan_methods = NULL;
+
+typedef struct
+{
+	char		CustomName[CUSTOM_NAME_MAX_LEN];
+	const CustomScanMethods *methods;
+} CustomScanMethodsEntry;
+
 /*
  * Register a new type of extensible node.
  */
@@ -91,3 +101,62 @@ GetExtensibleNodeMethods(const char *extnodename, bool missing_ok)
 
 	return entry->methods;
 }
+
+/*
+ * 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..83abaa6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -632,11 +632,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..cb0752a 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1827,8 +1827,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 +1839,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..e4bc14a 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/extensible.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..9d0b393 100644
--- a/src/include/executor/nodeCustom.h
+++ b/src/include/executor/nodeCustom.h
@@ -14,6 +14,7 @@
 
 #include "access/parallel.h"
 #include "nodes/execnodes.h"
+#include "nodes/extensible.h"
 
 /*
  * General executor code
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/extensible.h b/src/include/nodes/extensible.h
index 96ae7bc..605833f 100644
--- a/src/include/nodes/extensible.h
+++ b/src/include/nodes/extensible.h
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * extensible.h
- *    Definitions for extensible node type
+ *    Definitions for extensible node type and custom-xxx-methods
  *
  *
  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
@@ -14,7 +14,11 @@
 #ifndef 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"
 
 #define EXTNODENAME_MAX_LEN					64
 
@@ -69,4 +73,87 @@ extern void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *method);
 extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name,
 						 bool missing_ok);
 
+/*
+ * Definition of Custom(Path|Scan|Exec)Methods.
+ *
+ * The following structures are callback definition for each relavant custom-
+ * node types.
+ * Right now, only CustomScan node can be serialized/deserialized using
+ * nodeToString/stringToNode. So, extension that performs as a custom-scan
+ * provider has to support reconstruction of 'methods' of CustomScanMethods
+ * pointer on background worker processes.
+ * As ExtensibleNode type doing above, extension registers CustomExecMethods
+ * identified by CustomName preliminary. A serialized CustomScan contains
+ * the name of CustomExecMethods, then it shall be referenced on the background
+ * worker side later.
+ *
+ * Custom-scan provider may also have to support serialization/deserialization
+ * on CustomPath and CustomScanState in the future release, but not now.
+ */
+#define CUSTOM_NAME_MAX_LEN		64
+
+/*
+ * Flags definitions
+ */
+#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;
+
+
+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 of CustomScan
+ */
+extern void RegisterCustomScanMethods(const CustomScanMethods *methods);
+extern const CustomScanMethods *GetCustomScanMethods(const char *CustomName,
+													 bool missing_ok);
+
 #endif	/* EXTENSIBLE_H */
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;
 
 /*
