 doc/src/sgml/custom-scan.sgml  | 41 +++++++++++++++++++++++++++++++-----
 src/backend/nodes/outfuncs.c   |  8 ++++++-
 src/backend/nodes/readfuncs.c  | 48 ++++++++++++++++++++++++++++++++++++++++++
 src/backend/utils/fmgr/dfmgr.c | 21 ++++++++++++++++++
 src/include/fmgr.h             |  1 +
 src/include/nodes/plannodes.h  | 14 +++++++++++-
 6 files changed, 126 insertions(+), 7 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index a229326..af0dd56 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -195,11 +195,29 @@ 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</>.
+   serialized using <function>nodeToString</> and deserialized using
+   <function>stringToNode</>.
+   Thus, if custom scan provider implements <function>TextOutCustomScan</>
+   to dump its private fields its own way, it also has responsibility of
+   <function>TextReadCustomScan</> to clean out these tokens.
+  </para>
+  <para>
+   Like any other fields, <structfield>methods</> pointer of the
+   <structname>CustomScanMethods</> also has to be restored, however,
+   nobody can ensure a particular shared library that contains the custom
+   scan provider is loaded exactly same address, for example, when
+   <structname>CustomScan</> node is deserialized on the background worker
+   under the control of <structname>Gather</>.
+
+   Thus, here is a few additional interface contract. The first one is
+   symbol name of the <structname>CustomScanMethods</> table has to be
+   visible to linker; that means the variable should not be declared as
+   static, because <function>stringToNode</> reconstructs the
+   <structfield>methods</> pointer using a pair of library and symbol
+   name. The second one is, <structname>CustomScanMethods</> table also
+   has to be initialized using <function>INIT_CUSTOM_SCAN_METHODS</>
+   macro on <function>_PG_init</>, to assign exact library and symbol
+   name to be referenced.
   </para>
 
   <sect2 id="custom-scan-plan-callbacks">
@@ -230,6 +248,19 @@ void (*TextOutCustomScan) (StringInfo str,
     structure, including the substructure of the <quote>custom</> fields,
     there is usually not much need for this callback.
    </para>
+
+   <para>
+<programlisting>
+void (*TextReadCustomScan)(CustomScan *node);
+</programlisting>
+    Reads the private fields of supplied <structname>CustomScan</> node
+    generated by <function>TextOutCustomScan</> callback.
+    This callback is optional, however, must be implemented if custom
+    scan provider makes additional output for support of plan-tree
+    serialization and deserialization.
+    This callback shall be invoked under <function>stringToNode</>
+    context, so <function>pg_strtok</> will give the next token.
+   </para>
   </sect2>
  </sect1>
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3e75cd1..775ac95 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -612,8 +612,14 @@ _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 */
 	appendStringInfoString(str, " :methods ");
-	_outToken(str, node->methods->CustomName);
+	_outToken(str, node->methods->methods_library_name);
+	appendStringInfoChar(str, ' ');
+	_outToken(str, node->methods->methods_symbol_name);
+
+	/* Also, private fields if any */
 	if (node->methods->TextOutCustomScan)
 		node->methods->TextOutCustomScan(str, node);
 }
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 94ba6dc..8e6e3d6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -28,6 +28,7 @@
 
 #include <math.h>
 
+#include "fmgr.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
 #include "nodes/readfuncs.h"
@@ -1806,6 +1807,51 @@ _readForeignScan(void)
 }
 
 /*
+ * _readCustomScan
+ */
+static CustomScan *
+_readCustomScan(void)
+{
+	READ_LOCALS(CustomScan);
+	char	   *library_name;
+	char	   *symbol_name;
+	const CustomScanMethods *methods;
+
+	ReadCommonScan(&local_node->scan);
+
+	READ_UINT_FIELD(flags);
+	READ_NODE_FIELD(custom_plans);
+	READ_NODE_FIELD(custom_exprs);
+	READ_NODE_FIELD(custom_private);
+	READ_NODE_FIELD(custom_scan_tlist);
+	READ_BITMAPSET_FIELD(custom_relids);
+
+	/*
+	 * Reconstruction of methods using library and symbol name
+	 */
+	token = pg_strtok(&length);		/* skip methods: */
+	token = pg_strtok(&length);		/* methods_library_name */
+	library_name = nullable_string(token, length);
+	token = pg_strtok(&length);		/* methods_symbol_name */
+	symbol_name = nullable_string(token, length);
+
+	methods = (const CustomScanMethods *)
+		load_external_function(library_name, symbol_name, true, NULL);
+	Assert(strcmp(methods->methods_library_name, library_name) == 0 &&
+		   strcmp(methods->methods_symbol_name, symbol_name) == 0);
+	local_node->methods = methods;
+
+	/*
+	 * Read custom fields if any. Number of tokens has to be equivalent
+	 * to the output of TextOutCustomScan
+	 */
+	if (methods->TextReadCustomScan)
+		methods->TextReadCustomScan(local_node);
+
+	READ_DONE();
+}
+
+/*
  * ReadCommonJoin
  *	Assign the basic stuff of all nodes that inherit from Join
  */
@@ -2361,6 +2407,8 @@ parseNodeString(void)
 		return_value = _readWorkTableScan();
 	else if (MATCH("FOREIGNSCAN", 11))
 		return_value = _readForeignScan();
+	else if (MATCH("CUSTOMSCAN", 10))
+		return_value = _readCustomScan();
 	else if (MATCH("JOIN", 4))
 		return_value = _readJoin();
 	else if (MATCH("NESTLOOP", 8))
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index cd3db87..f343cfc 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -80,6 +80,8 @@ static char *find_in_dynamic_libpath(const char *basename);
 /* Magic structure that module needs to match to be accepted */
 static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
 
+/* Library filename on which PG_init() is currently processed */
+static const *current_library_filename = NULL;
 
 /*
  * Load the specified dynamic-link library file, and look for a function
@@ -277,8 +279,16 @@ internal_load_library(const char *libname)
 		 */
 		PG_init = (PG_init_t) pg_dlsym(file_scanner->handle, "_PG_init");
 		if (PG_init)
+		{
+			const char *saved_library_name = current_library_filename;
+
+			current_library_filename = file_scanner->filename;
+
 			(*PG_init) ();
 
+			current_library_filename = saved_library_name;
+		}
+
 		/* OK to link it into list */
 		if (file_list == NULL)
 			file_list = file_scanner;
@@ -291,6 +301,17 @@ internal_load_library(const char *libname)
 }
 
 /*
+ * Inform extensions their own filename, at the time of PG_init()
+ */
+const char *
+get_current_library_filename(void)
+{
+	if (!current_library_filename)
+		elog(ERROR, "Not in the context of _PG_init");
+	return current_library_filename;
+}
+
+/*
  * Report a suitable error for an incompatible magic block.
  */
 static void
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 808d142..116ca4d 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -645,6 +645,7 @@ extern void **find_rendezvous_variable(const char *varName);
 extern Size EstimateLibraryStateSpace(void);
 extern void SerializeLibraryState(Size maxsize, char *start_address);
 extern void RestoreLibraryState(char *start_address);
+extern const char *get_current_library_filename(void);
 
 /*
  * Support for aggregate functions
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6b28c8e..b3f90fb 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -551,15 +551,27 @@ struct CustomScan;
 
 typedef struct CustomScanMethods
 {
+	/* to be set by INIT_CUSTOM_SCAN_METHODS */
 	const char *CustomName;
+	const char *methods_library_name;
+	const char *methods_symbol_name;
 
 	/* Create execution state (CustomScanState) from a CustomScan plan node */
 	Node	   *(*CreateCustomScanState) (struct CustomScan *cscan);
-	/* Optional: print custom_xxx fields in some special way */
+	/* Optional: print private fields in some special way */
 	void		(*TextOutCustomScan) (StringInfo str,
 											  const struct CustomScan *node);
+	/* Optional: read the private fields printed in special way */
+	void		(*TextReadCustomScan) (struct CustomScan *cscan);
 } CustomScanMethods;
 
+#define INIT_CUSTOM_SCAN_METHODS(methods, name)							\
+	do {																\
+		methods.CustomName = (name);									\
+		methods.methods_library_name = get_current_library_filename();	\
+		methods.methods_symbol_name = #methods;							\
+	} while(0)
+
 typedef struct CustomScan
 {
 	Scan		scan;
