 doc/src/sgml/custom-scan.sgml  | 53 +++++++++++++++++++++++++++++++++-----
 src/backend/nodes/copyfuncs.c  | 16 +++++++++++-
 src/backend/nodes/outfuncs.c   |  8 +++++-
 src/backend/nodes/readfuncs.c  | 58 ++++++++++++++++++++++++++++++++++++++++++
 src/backend/utils/fmgr/dfmgr.c | 21 +++++++++++++++
 src/include/fmgr.h             |  1 +
 src/include/nodes/plannodes.h  | 23 ++++++++++++++---
 7 files changed, 168 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index a229326..867b47e 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -195,11 +195,22 @@ 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</>, so so all the data stored within
+   the <quote>custom</> fields must consist of nodes that that function
+   can handle.
+   Furthermore, custom scan providers have to implement <quote>optional</>
+   callbacks if it defines substitute a larger structure that embeds
+   a <structname>CustomScan</> for the structure itself.
+  </para>
+
+  <para>
+   Also note that symbol name of the <structname>CustomScanMethods</> table
+   has to be visible to linker, because <function>stringToNode</>
+   reconstructs the <structfield>methods</> pointer using a pair of library
+   and symbol name. <function>INIT_CUSTOM_SCAN_METHODS</> initializes the
+   relevant fields, and expects custom-scan provider uses this macro on
+   <function>_PG_init</> timing.
   </para>
 
   <sect2 id="custom-scan-plan-callbacks">
@@ -228,8 +239,38 @@ void (*TextOutCustomScan) (StringInfo str,
     this custom plan node.  This callback is optional.  Since
     <function>nodeToString</> will automatically dump all fields in the
     structure, including the substructure of the <quote>custom</> fields,
-    there is usually not much need for this callback.
+    there is usually not much need for this callback, unless custom-scan
+    provider does not embed its 
+   </para>
+
+   <para>
+<programlisting>
+struct CustomScan *(*TextReadCustomScan)(void);
+</programlisting>
+    Allocate a <structname>CustomScan</> or larger structure embedding
+    <structname>CustomScan</>, and read its private fields 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>
+
+   <para>
+<programlisting>
+struct CustomScan *(*NodeCopyCustomScan)(const struct CustomScan *from);
+</programlisting>
+    Allocate a <structname>CustomScan</> or larger structure embedding
+    <structname>CustomScan</>, and copies its private fields from the
+    original node.
+    This callback is optional, however, must be implemented if custom
+    scan provider extends <structname>CustomScan</> structure to save
+    its private fields for safety of <function>copyObject</>.
+    The common fields are duplicated by the backend, so all extension
+    needs to focus on is its private fields, if exists.
+   </para>
+
   </sect2>
  </sect1>
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 62355aa..05d23aa 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -637,7 +637,21 @@ _copyForeignScan(const ForeignScan *from)
 static CustomScan *
 _copyCustomScan(const CustomScan *from)
 {
-	CustomScan *newnode = makeNode(CustomScan);
+	const CustomScanMethods *methods = from->methods;
+	CustomScan *newnode;
+
+	/*
+	 * If custom-scan provider extends CustomScan node to save its private
+	 * data, it is role of the provider to allocate a new node that includes
+	 * the private fields.
+	 */
+	if (!methods->NodeCopyCustomScan)
+		newnode = makeNode(CustomScan);
+	else
+	{
+		newnode = methods->NodeCopyCustomScan(from);
+		Assert(IsA(newnode, CustomScan));
+	}
 
 	/*
 	 * copy node superclass fields
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c91273c..7ed8118 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -599,8 +599,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 ef88209..81449a2 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"
@@ -1849,6 +1850,61 @@ _readForeignScan(void)
 }
 
 /*
+ * _readCustomScan
+ */
+static CustomScan *
+_readCustomScan(void)
+{
+	READ_TEMP_LOCALS();
+	CustomScan		local_temp;
+	CustomScan	   *local_node = &local_temp;
+	char		   *library_name;
+	char		   *symbol_name;
+	const CustomScanMethods *methods;
+
+	NodeSetTag(local_node, T_CustomScan);
+	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);
+
+	/*
+	 * Then, read private fields if any. Elsewhere, just construct
+	 * a usual CustomScan node.
+	 */
+	if (!methods->TextReadCustomScan)
+		local_node = makeNode(CustomScan);
+	else
+	{
+		local_node = methods->TextReadCustomScan();
+		Assert(IsA(local_node, CustomScan));
+	}
+	/* move the common field of CustomScan */
+	memcpy(local_node, &local_temp, offsetof(CustomScan, methods));
+	local_node->methods = methods;
+
+	READ_DONE();
+}
+
+/*
  * ReadCommonJoin
  *	Assign the basic stuff of all nodes that inherit from Join
  */
@@ -2388,6 +2444,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 cc259f1..4c64b51 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -535,24 +535,39 @@ typedef struct ForeignScan
  * custom_private, custom_scan_tlist, and custom_relids fields.  The
  * convention of setting scan.scanrelid to zero for joins applies as well.
  *
- * Note that since Plan trees can be copied, custom scan providers *must*
- * fit all plan data they need into those fields; embedding CustomScan in
- * a larger struct will not work.
+ * Note that Plan trees can be copied, displayed and read using node
+ * functions, thus, custom scan provider *must* support relevant callbacks
+ * if provider wants to embed CustomScan in a larger struct to save private
+ * fields.
  * ----------------
  */
 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 and construct the private fields above */
+	struct CustomScan *(*TextReadCustomScan)(void);
+	/* Optional: copy the original including private fields */
+	struct CustomScan *(*NodeCopyCustomScan)(const struct CustomScan *from);
 } 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;
