diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 787b0b93cced229ab3d3ee1fde77c7784173a74d..09c230468bbced3ee108161d01fd990630466f82 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 2473b658b175bee07294533706dd4cf59b955d7d..d61be58c45dce3cc754ab734c82d0725ae32f0a8 100644
--- a/src/backend/nodes/extensible.c
+++ b/src/backend/nodes/extensible.c
@@ -24,61 +24,87 @@
 #include "utils/hsearch.h"
 
 static HTAB *extensible_node_methods = NULL;
+static HTAB *custom_scan_methods = NULL;
 
 typedef struct
 {
 	char		extnodename[EXTNODENAME_MAX_LEN];
-	const ExtensibleNodeMethods *methods;
+	const void *extnodemethods;
 } ExtensibleNodeEntry;
 
 /*
- * Register a new type of extensible node.
+ * An internal function to register a new callback structure 
  */
-void
-RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *methods)
+static void
+RegisterExtensibleNodeEntry(HTAB **p_htable, const char *htable_label,
+							const char *extnodename,
+							const void *extnodemethods)
 {
 	ExtensibleNodeEntry *entry;
 	bool		found;
 
-	if (extensible_node_methods == NULL)
+	if (*p_htable == NULL)
 	{
 		HASHCTL		ctl;
 
 		memset(&ctl, 0, sizeof(HASHCTL));
 		ctl.keysize = EXTNODENAME_MAX_LEN;
 		ctl.entrysize = sizeof(ExtensibleNodeEntry);
-		extensible_node_methods = hash_create("Extensible Node Methods",
-											  100, &ctl, HASH_ELEM);
+
+		*p_htable = hash_create(htable_label, 100, &ctl, HASH_ELEM);
 	}
 
-	if (strlen(methods->extnodename) >= EXTNODENAME_MAX_LEN)
+	if (strlen(extnodename) >= EXTNODENAME_MAX_LEN)
 		elog(ERROR, "extensible node name is too long");
 
-	entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods,
-												methods->extnodename,
+	entry = (ExtensibleNodeEntry *) hash_search(*p_htable,
+												extnodename,
 												HASH_ENTER, &found);
 	if (found)
 		ereport(ERROR,
 				(errcode(ERRCODE_DUPLICATE_OBJECT),
 				 errmsg("extensible node type \"%s\" already exists",
-						methods->extnodename)));
+						extnodename)));
 
-	entry->methods = methods;
+	entry->extnodemethods = extnodemethods;
 }
 
 /*
- * Get the methods for a given type of extensible node.
+ * Register a new type of extensible node.
  */
-const ExtensibleNodeMethods *
-GetExtensibleNodeMethods(const char *extnodename, bool missing_ok)
+void
+RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *methods)
+{
+	RegisterExtensibleNodeEntry(&extensible_node_methods,
+								"Extensible Node Methods",
+								methods->extnodename,
+								methods);
+}
+
+/*
+ * Register a new type of custom scan node
+ */
+void
+RegisterCustomScanMethods(const CustomScanMethods *methods)
+{
+	RegisterExtensibleNodeEntry(&custom_scan_methods,
+								"Custom Scan Methods",
+								methods->CustomName,
+								methods);
+}
+
+/*
+ * An internal routine to get an ExtensibleNodeEntry by the given identifier
+ */
+static const void *
+GetExtensibleNodeEntry(HTAB *htable, const char *extnodename, bool missing_ok)
 {
 	ExtensibleNodeEntry *entry = NULL;
 
-	if (extensible_node_methods != NULL)
-		entry = (ExtensibleNodeEntry *) hash_search(extensible_node_methods,
+	if (htable != NULL)
+		entry = (ExtensibleNodeEntry *) hash_search(htable,
 													extnodename,
 													HASH_FIND, NULL);
-
 	if (!entry)
 	{
 		if (missing_ok)
@@ -89,5 +115,29 @@ GetExtensibleNodeMethods(const char *extnodename, bool missing_ok)
 						extnodename)));
 	}
 
-	return entry->methods;
+	return entry->extnodemethods;
+}
+
+/*
+ * Get the methods for a given type of extensible node.
+ */
+const ExtensibleNodeMethods *
+GetExtensibleNodeMethods(const char *extnodename, bool missing_ok)
+{
+	return (const ExtensibleNodeMethods *)
+		GetExtensibleNodeEntry(extensible_node_methods,
+							   extnodename,
+							   missing_ok);
+}
+
+/*
+ * Get the methods for a given name of CustomScanMethods
+ */
+const CustomScanMethods *
+GetCustomScanMethods(const char *CustomName, bool missing_ok)
+{
+	return (const CustomScanMethods *)
+		GetExtensibleNodeEntry(custom_scan_methods,
+							   CustomName,
+							   missing_ok);
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 32d03f7f257ca483b6d340c0992bab0fe50fe6d4..83abaa68a386e2d7406fc6628faf73f3a30a3188 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 6db0492e152ada149ccf6ee9a30bba67d50dffb1..cb0752a6ad895862df25c22ef4f48ae5809d6036 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 d159a17fd264ace9478945197343e827bc04151c..e4bc14a1510e0044fc79fe6a4491a15da4240545 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 410a3ad14db8e86cdc46b4126d99abf9938aacf4..9d0b3935288506e105a510620247181dc39dcd60 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 0113e5c663cfaf4d18d6ba3a8f9d269208bfc4c7..bf2a09bc39c8085359fb140556324055b23e98af 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 96ae7bc9291b9c71faad2d571132daf5fab9e239..9df1377a8e6943077dcec18aeba9469e775f03db 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 nodes and custom scans
  *
  *
  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
@@ -14,8 +14,13 @@
 #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"
 
+/* maximum length of an extensible node identifier */
 #define EXTNODENAME_MAX_LEN					64
 
 /*
@@ -69,4 +74,80 @@ extern void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *method);
 extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name,
 						 bool missing_ok);
 
+/*
+ * Flags for custom paths, indicating what capabilities the resulting scan
+ * will have.
+ */
+#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN	0x0001
+#define CUSTOMPATH_SUPPORT_MARK_RESTORE		0x0002
+
+/*
+ * Custom path methods.  Mostly, we just need to know how to convert a
+ * CustomPath to a plan.
+ */
+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;
+
+/*
+ * Custom scan.  Here again, there's not much to do: we need to be able to
+ * generate a ScanState corresponding to the scan.
+ */
+typedef struct CustomScanMethods
+{
+	const char *CustomName;
+
+	/* Create execution state (CustomScanState) from a CustomScan plan node */
+	Node	   *(*CreateCustomScanState) (CustomScan *cscan);
+} CustomScanMethods;
+
+/*
+ * Execution-time methods for a CustomScanState.  This is more complex than
+ * what we need for a custom path or scan.
+ */
+typedef struct CustomExecMethods
+{
+	const char *CustomName;
+
+	/* Required executor methods */
+	void		(*BeginCustomScan) (CustomScanState *node,
+									EState *estate,
+									int eflags);
+	TupleTableSlot *(*ExecCustomScan) (CustomScanState *node);
+	void		(*EndCustomScan) (CustomScanState *node);
+	void		(*ReScanCustomScan) (CustomScanState *node);
+
+	/* Optional methods: needed if mark/restore is supported */
+	void		(*MarkPosCustomScan) (CustomScanState *node);
+	void		(*RestrPosCustomScan) (CustomScanState *node);
+
+	/* Optional methods: needed if parallel execution is supported */
+	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;
+
+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 00b1d35d75993b03b3184a2c4c8e18520e3aa6cb..465d72fe890d697378ec4df121d74741f4ba2b6a 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 ee7007aacec6ff1d92bb450ca84f5995481fdec2..32f04b25dde08699ac257cc3c749d31cd2d87816 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;
 
 /*