 doc/src/sgml/custom-scan.sgml | 55 +++++++++++++++++++++++++++++++++++++------
 src/backend/nodes/copyfuncs.c | 17 ++++++++++++-
 src/backend/nodes/outfuncs.c  |  7 ++++++
 src/backend/nodes/readfuncs.c | 22 ++++++++++++++++-
 src/include/nodes/plannodes.h | 14 ++++++++---
 5 files changed, 103 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index d042adb..820f8d2 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -185,7 +185,9 @@ typedef struct CustomScan
     this scan is replacing a join, it will have only one member.
     <structfield>methods</> must point to a (usually statically allocated)
     object implementing the required custom scan methods, which are further
-    detailed below.
+    detailed below. Also note that this field shall be serialized using
+    a pair of library name and symbol name, thus, method variable has to
+    be resolvable by linker.
   </para>
 
   <para>
@@ -196,12 +198,12 @@ typedef struct CustomScan
   </para>
 
   <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</>.
+   Plan tree has to be copied using <function>copyObject</>, serialized
+   using <function>nodeToString</> and deserialized using
+   <function>stringToNode</>. In case when custom scan provider defines
+   its own structure that embeds <structname>CustomScan</> on the head,
+   for better private field handling, it has to implement the relevant and
+   optional callbacks.
   </para>
 
   <sect2 id="custom-scan-plan-callbacks">
@@ -220,6 +222,45 @@ Node *(*CreateCustomScanState) (CustomScan *cscan);
     the <function>BeginCustomScan</> callback will be invoked to give the
     custom scan provider a chance to do whatever else is needed.
    </para>
+
+   <para>
+<programlisting>
+void (*TextOutCustomScan) (StringInfo str, const CustomScan *cscan);
+</programlisting>
+    Generate extra output when <function>nodeToString</> is invoked towards
+    the custom scan node. This callback is optional, however, custom scan
+    provider has to implement if it defines larger structure that embeds
+    <structname>CustomScan</> on the head, to save own private fields.
+    In this case, all the callback has to dump are private fields because
+    the core backend dumps knows fields in <structname>CustomScan</>.
+   </para>
+
+   <para>
+<programlisting>
+CustomScan *(*TextReadCustomScan) (void);
+</programlisting>
+    Allocate a larger structure that embeds <structname>CustomScan</> then
+    reconstruct its private fields on the extra fields of this structure;
+    according to the output by <function>TextOutCustomScan</> callback.
+    This callback is optional, however, custom scan provider has to
+    implement if it defines its own structure instead of bare
+    <structname>CustomScan</>.
+    This callback shall be invoked under <function>stringToNode</> context,
+    so <function>pg_strtok</> will give the next token.
+   </para>
+
+   <para>
+<programlisting>
+CustomScan *(*NodeCopyCustomScan) (const CustomScan *from);
+</programlisting>
+    Allocate a larger structure that embeds <structname>CustomScan</> then
+    copies its private fields from the original node.
+    This callback is optional, however, custom scan provider has to
+    implement if it defines its own structure instead of bare
+    <structname>CustomScan</>.
+    All this callback has to care about is its private fields. The known
+    fields in <structname>CustomScan</> shall be copied by the core backend.
+   </para>
   </sect2>
  </sect1>
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c176ff9..34517cd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -661,7 +661,22 @@ _copyForeignScan(const ForeignScan *from)
 static CustomScan *
 _copyCustomScan(const CustomScan *from)
 {
-	CustomScan *newnode = makeNode(CustomScan);
+	const CustomScanMethods *methods = from->methods;
+	CustomScan	   *newnode;
+
+	/*
+	 * In case when custom scan provider defines a larger structure that
+	 * deploys CustomScan at the head, only custom scan provider knows
+	 * exact size of the new node to be allocated, so we ask them to
+	 * allocate the new node and copy private fields we don't know.
+	 */
+	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 af6674c..43fbd81 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -617,6 +617,13 @@ _outCustomScan(StringInfo str, const CustomScan *node)
 	_outToken(str, node->methods->methodsLibraryName);
 	appendStringInfoChar(str, ' ');
 	_outToken(str, node->methods->methodsSymbolName);
+	/*
+	 * Custom scan provider can/must dump out private fields, if it defines
+	 * a larger structure to save its private fields. It also has to be
+	 * reconstructable using relevant TextReadCustomScan callback.
+	 */
+	if (node->methods->TextOutCustomScan)
+		node->methods->TextOutCustomScan(str, node);
 }
 
 static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index d143dac..52faf9e 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1812,11 +1812,14 @@ _readForeignScan(void)
 static CustomScan *
 _readCustomScan(void)
 {
-	READ_LOCALS(CustomScan);
+	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);
@@ -1839,6 +1842,23 @@ _readCustomScan(void)
 		load_external_function(library_name, symbol_name, true, NULL);
 	Assert(strcmp(methods->methodsLibraryName, library_name) == 0 &&
 		   strcmp(methods->methodsSymbolName, symbol_name) == 0);
+
+	/*
+	 * Then, read private fields if any. If custom scan provider defines
+	 * a larger structure to save its private data, it should be dumpped
+	 * out by TextOutCustomScan callback and also should be reconstructable
+	 * by TextReadCustomScan. In this case, only custom scan provider knows
+	 * exact size of the structure, so TextReadCustomScan callback has to
+	 * allocate the result node not only reading private data fields.
+	 */
+	if (!methods->TextReadCustomScan)
+		local_node = makeNode(CustomScan);
+	else
+	{
+		local_node = methods->TextReadCustomScan();
+		Assert(IsA(local_node, CustomScan));
+	}
+	memcpy(local_node, &local_temp, offsetof(CustomScan, methods));
 	local_node->methods = methods;
 
 	READ_DONE();
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 5ecc2d1..acaf4bd 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -542,9 +542,10 @@ 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 tree can be copied, written and read with usual node
+ * functions. If custom scan provider defines a larger structure that
+ * embeds CustomScan at the head, it *must* provide relevant callbacks
+ * to support copyObject, nodeToString and stringToNode.
  * ----------------
  */
 struct CustomScan;
@@ -557,6 +558,13 @@ typedef struct CustomScanMethods
 
 	/* Create execution state (CustomScanState) from a CustomScan plan node */
 	Node	   *(*CreateCustomScanState) (struct CustomScan *cscan);
+	/* Optional: output private field if structure is extended */
+	void		(*TextOutCustomScan) (StringInfo str,
+									  const struct CustomScan *cscan);
+	/* Optional: reconstruct the node if structure is extended */
+	struct CustomScan *(*TextReadCustomScan) (void);
+	/* Optional: duplicate CustomScan node if any additional private fields */
+	struct CustomScan *(*NodeCopyCustomScan) (const struct CustomScan *from);
 } CustomScanMethods;
 
 typedef struct CustomScan
