From 6f7d037a03f2020166407b46a6bf6a2b8226f906 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Wed, 6 Dec 2023 21:49:42 +0100
Subject: [PATCH v0] Reduce the size of serialized nodes in nodeToString

Default fields don't need to be written to disk. So, add
infrastructure that handles that correctly.

This updates the rowsecurity tests which had a hard
dependency on the output format of nodeToString, and thus
broke with these changes. It is now more robust against
similar issues, as long as the textual format remains.
---
 src/backend/nodes/gen_node_support.pl     | 204 ++++++++++++++++----
 src/backend/nodes/outfuncs.c              | 151 +++++++++------
 src/backend/nodes/read.c                  |  47 +++++
 src/backend/nodes/readfuncs.c             | 215 +++++++++++++++-------
 src/backend/rewrite/rewriteDefine.c       | 102 ++++++++++
 src/include/nodes/parsenodes.h            |  11 +-
 src/include/nodes/primnodes.h             |  40 ++--
 src/include/nodes/readfuncs.h             |   1 +
 src/test/regress/expected/rowsecurity.out |   5 +-
 src/test/regress/sql/rowsecurity.sql      |   5 +-
 10 files changed, 590 insertions(+), 191 deletions(-)

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 72c7963578..2fe6547be0 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -361,6 +361,18 @@ foreach my $infile (@ARGV)
 						elsif ($attr =~ /^nodetag_number\((\d+)\)$/)
 						{
 							$manual_nodetag_number{$in_struct} = $1;
+						}
+						elsif ($attr eq 'no_default')
+						{
+							
+						}
+						elsif ($attr =~ /^default\(([\w\d."'-]+)\)$/)
+						{
+							
+						}
+						elsif ($attr =~ /^default_ref\(([\w\d."'-]+)\)$/)
+						{
+							
 						}
 						else
 						{
@@ -436,7 +448,7 @@ foreach my $infile (@ARGV)
 			}
 			# normal struct field
 			elsif ($line =~
-				/^\s*(.+)\s*\b(\w+)(\[[\w\s+]+\])?\s*(?:pg_node_attr\(([\w(), ]*)\))?;/
+				/^\s*(.+)\s*\b(\w+)(\[[\w\s+]+\])?\s*(?:pg_node_attr\(([\w\d(), ."'-]*)\))?;/
 			  )
 			{
 				if ($is_node_struct)
@@ -468,6 +480,8 @@ foreach my $infile (@ARGV)
 							if (   $attr !~ /^array_size\(\w+\)$/
 								&& $attr !~ /^copy_as\(\w+\)$/
 								&& $attr !~ /^read_as\(\w+\)$/
+								&& $attr !~ /^default\([\w\d."'-]+\)$/
+								&& $attr !~ /^default_ref\([\w\d."'-]+\)$/
 								&& !elem $attr,
 								qw(copy_as_scalar
 								equal_as_scalar
@@ -478,7 +492,8 @@ foreach my $infile (@ARGV)
 								read_write_ignore
 								write_only_relids
 								write_only_nondefault_pathtarget
-								write_only_req_outer))
+								write_only_req_outer
+								no_default))
 							{
 								die
 								  "$infile:$lineno: unrecognized attribute \"$attr\"\n";
@@ -494,7 +509,7 @@ foreach my $infile (@ARGV)
 			}
 			# function pointer field
 			elsif ($line =~
-				/^\s*([\w\s*]+)\s*\(\*(\w+)\)\s*\((.*)\)\s*(?:pg_node_attr\(([\w(), ]*)\))?;/
+				/^\s*([\w\s*]+)\s*\(\*(\w+)\)\s*\((.*)\)\s*(?:pg_node_attr\(([\w\d(), ."'-]*)\))?;/
 			  )
 			{
 				if ($is_node_struct)
@@ -969,6 +984,10 @@ _read${n}(void)
 		my $array_size_field;
 		my $read_as_field;
 		my $read_write_ignore = 0;
+		my $opt = '_OPT';
+		my $default = 0;
+		my $manual_default = 0;
+
 		foreach my $a (@a)
 		{
 			if ($a =~ /^array_size\(([\w.]+)\)$/)
@@ -987,6 +1006,111 @@ _read${n}(void)
 			{
 				$read_write_ignore = 1;
 			}
+			elsif ($a =~ /^default\(([\w\d+."'-]+)\)$/)
+			{
+				$default = $1;
+				$manual_default = 1;
+				die
+				  "default() and no_default can't both be set"
+				  if $opt eq '';
+			}
+			elsif ($a =~ /^default_ref\(([\w\d+."'-]+)\)$/)
+			{
+				$default = "NODE_FIELD($1)";
+				$manual_default = 1;
+				die
+				  "default() and no_default can't both be set"
+				  if $opt eq '';
+			}
+			elsif ($a eq 'no_default')
+			{
+				$opt = '';
+				die
+				  "default() and no_default can't both be set"
+				  if $manual_default eq 1;
+			}
+		}
+
+		my $default_clause = 'invalid';
+
+		if ($opt eq "_OPT")
+		{
+			if ($manual_default eq 1)
+			{
+				# we don't set a typed clause here
+			}
+			elsif ($t eq 'bool')
+			{
+				$default = 'false';
+			}
+			elsif ($t eq 'int' && $f =~ 'location$')
+			{
+				$default = '-1';
+			}
+			elsif ($t eq 'int'
+				|| $t eq 'int16'
+				|| $t eq 'int32'
+				|| $t eq 'uint32'
+				|| $t eq 'bits32'
+				|| $t eq 'BlockNumber'
+				|| $t eq 'Index'
+				|| $t eq 'SubTransactionId'
+				|| $t eq 'uint64'
+				|| $t eq 'AclMode'
+				|| $t eq 'Oid'
+				|| $t eq 'RelFileNumber'
+				|| $t eq 'long'
+				|| $t eq 'char')
+			{
+				$default = 0;
+			}
+			elsif ($t eq 'AttrNumber')
+			{
+				$default = 1;
+			}
+			elsif ($t eq 'StrategyNumber')
+			{
+				$default = 3; # BtEqualStrategy
+			}
+			elsif ($t eq 'double'
+				|| $t eq 'Cardinality'
+				|| $t eq 'Cost'
+				|| $t eq 'QualCost'
+				|| $t eq 'Selectivity')
+			{
+				$default = '0.0';
+			}
+			elsif ($t eq 'char*')
+			{
+				$default_clause = '';
+				$opt = '_NONNULL';
+			}
+			elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
+			{
+				$default = 'NULL'; # but not included
+			}
+			elsif (elem $t, @enum_types)
+			{
+				$default = 0;
+			}
+			elsif ($t =~ /^(\w+)(\*|\[\w+\])$/ and elem $1, @scalar_types)
+			{
+				$opt = '';
+			}
+
+			if ($opt eq '')
+			{
+				$default_clause = '';
+			}
+			elsif ($default_clause eq 'invalid')
+			{
+				$default_clause = ", $default";
+			}
+		}
+		else
+		{
+			die "invalid optionality" unless $opt eq '';
+			$default_clause = "";
 		}
 
 		if ($read_write_ignore)
@@ -1007,13 +1131,13 @@ _read${n}(void)
 		# select instructions by field type
 		if ($t eq 'bool')
 		{
-			print $off "\tWRITE_BOOL_FIELD($f);\n";
-			print $rff "\tREAD_BOOL_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_BOOL_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_BOOL_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'int' && $f =~ 'location$')
 		{
-			print $off "\tWRITE_LOCATION_FIELD($f);\n";
-			print $rff "\tREAD_LOCATION_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_LOCATION_FIELD_OPT($f);\n";
+			print $rff "\tREAD_LOCATION_FIELD_OPT($f);\n" unless $no_read;
 		}
 		elsif ($t eq 'int'
 			|| $t eq 'int16'
@@ -1021,8 +1145,8 @@ _read${n}(void)
 			|| $t eq 'AttrNumber'
 			|| $t eq 'StrategyNumber')
 		{
-			print $off "\tWRITE_INT_FIELD($f);\n";
-			print $rff "\tREAD_INT_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_INT_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_INT_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'uint32'
 			|| $t eq 'bits32'
@@ -1030,71 +1154,71 @@ _read${n}(void)
 			|| $t eq 'Index'
 			|| $t eq 'SubTransactionId')
 		{
-			print $off "\tWRITE_UINT_FIELD($f);\n";
-			print $rff "\tREAD_UINT_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_UINT_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_UINT_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'uint64'
 			|| $t eq 'AclMode')
 		{
-			print $off "\tWRITE_UINT64_FIELD($f);\n";
-			print $rff "\tREAD_UINT64_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_UINT64_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_UINT64_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'Oid' || $t eq 'RelFileNumber')
 		{
-			print $off "\tWRITE_OID_FIELD($f);\n";
-			print $rff "\tREAD_OID_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_OID_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_OID_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'long')
 		{
-			print $off "\tWRITE_LONG_FIELD($f);\n";
-			print $rff "\tREAD_LONG_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_LONG_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_LONG_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'char')
 		{
-			print $off "\tWRITE_CHAR_FIELD($f);\n";
-			print $rff "\tREAD_CHAR_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_CHAR_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_CHAR_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'double')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f);\n";
-			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_FLOAT_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_FLOAT_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'Cardinality')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f);\n";
-			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_FLOAT_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_FLOAT_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'Cost')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f);\n";
-			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_FLOAT_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_FLOAT_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'QualCost')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f.startup);\n";
-			print $off "\tWRITE_FLOAT_FIELD($f.per_tuple);\n";
-			print $rff "\tREAD_FLOAT_FIELD($f.startup);\n" unless $no_read;
-			print $rff "\tREAD_FLOAT_FIELD($f.per_tuple);\n" unless $no_read;
+			print $off "\tWRITE_FLOAT_FIELD$opt($f.startup$default_clause);\n";
+			print $off "\tWRITE_FLOAT_FIELD$opt($f.per_tuple$default_clause);\n";
+			print $rff "\tREAD_FLOAT_FIELD$opt($f.startup$default_clause);\n" unless $no_read;
+			print $rff "\tREAD_FLOAT_FIELD$opt($f.per_tuple$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'Selectivity')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f);\n";
-			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_FLOAT_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_FLOAT_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'char*')
 		{
-			print $off "\tWRITE_STRING_FIELD($f);\n";
-			print $rff "\tREAD_STRING_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_STRING_FIELD$opt($f$default_clause);\n";
+			print $rff "\tREAD_STRING_FIELD$opt($f$default_clause);\n" unless $no_read;
 		}
 		elsif ($t eq 'Bitmapset*' || $t eq 'Relids')
 		{
-			print $off "\tWRITE_BITMAPSET_FIELD($f);\n";
-			print $rff "\tREAD_BITMAPSET_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_BITMAPSET_FIELD$opt($f);\n";
+			print $rff "\tREAD_BITMAPSET_FIELD$opt($f);\n" unless $no_read;
 		}
 		elsif (elem $t, @enum_types)
 		{
-			print $off "\tWRITE_ENUM_FIELD($f, $t);\n";
-			print $rff "\tREAD_ENUM_FIELD($f, $t);\n" unless $no_read;
+			print $off "\tWRITE_ENUM_FIELD$opt($f, $t$default_clause);\n";
+			print $rff "\tREAD_ENUM_FIELD$opt($f, $t$default_clause);\n" unless $no_read;
 		}
 		# arrays of scalar types
 		elsif ($t =~ /^(\w+)(\*|\[\w+\])$/ and elem $1, @scalar_types)
@@ -1140,7 +1264,7 @@ _read${n}(void)
 		{
 			(my $f2 = $f) =~ s/pathtarget/parent/;
 			print $off "\tif (node->$f != node->$f2->reltarget)\n"
-			  . "\t\tWRITE_NODE_FIELD($f);\n";
+			  . "\t\tWRITE_NODE_FIELD$opt($f);\n";
 		}
 		elsif ($t eq 'ParamPathInfo*' && elem 'write_only_req_outer', @a)
 		{
@@ -1163,8 +1287,8 @@ _read${n}(void)
 			  if (elem $1, @no_read or elem $1, @nodetag_only)
 			  and !$no_read;
 
-			print $off "\tWRITE_NODE_FIELD($f);\n";
-			print $rff "\tREAD_NODE_FIELD($f);\n" unless $no_read;
+			print $off "\tWRITE_NODE_FIELD$opt($f);\n";
+			print $rff "\tREAD_NODE_FIELD$opt($f);\n" unless $no_read;
 		}
 		# arrays of node pointers (currently supported for write only)
 		elsif (($t =~ /^(\w+)\*\*$/ or $t =~ /^struct\s+(\w+)\*\*$/)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e66a99247e..38cb4b03f4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -43,62 +43,91 @@ static void outDouble(StringInfo str, double d);
 /* Write an integer field (anything written as ":fldname %d") */
 #define WRITE_INT_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname)
+#define WRITE_INT_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_INT_FIELD(fldname))
 
 /* Write an unsigned integer field (anything written as ":fldname %u") */
 #define WRITE_UINT_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
+#define WRITE_UINT_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_UINT_FIELD(fldname))
 
 /* Write an unsigned integer field (anything written with UINT64_FORMAT) */
 #define WRITE_UINT64_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " " UINT64_FORMAT, \
 					 node->fldname)
+#define WRITE_UINT64_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_UINT64_FIELD(fldname))
 
 /* Write an OID field (don't hard-wire assumption that OID is same as uint) */
 #define WRITE_OID_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname)
+#define WRITE_OID_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_OID_FIELD(fldname))
 
 /* Write a long-integer field */
 #define WRITE_LONG_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %ld", node->fldname)
+#define WRITE_LONG_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_LONG_FIELD(fldname))
 
 /* Write a char field (ie, one ascii character) */
 #define WRITE_CHAR_FIELD(fldname) \
 	(appendStringInfo(str, " :" CppAsString(fldname) " "), \
 	 outChar(str, node->fldname))
+#define WRITE_CHAR_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_CHAR_FIELD(fldname))
 
 /* Write an enumerated-type field as an integer code */
 #define WRITE_ENUM_FIELD(fldname, enumtype) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %d", \
 					 (int) node->fldname)
+#define WRITE_ENUM_FIELD_OPT(fldname, enumtype, default) \
+	((node->fldname == default) ? (0) : WRITE_ENUM_FIELD(fldname, enumtype))
 
 /* Write a float field (actually, they're double) */
 #define WRITE_FLOAT_FIELD(fldname) \
 	(appendStringInfo(str, " :" CppAsString(fldname) " "), \
 	 outDouble(str, node->fldname))
+#define WRITE_FLOAT_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_FLOAT_FIELD(fldname))
 
 /* Write a boolean field */
 #define WRITE_BOOL_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %s", \
 					 booltostr(node->fldname))
+#define WRITE_BOOL_FIELD_OPT(fldname, default) \
+	((node->fldname == default) ? (0) : WRITE_BOOL_FIELD(fldname))
 
 /* Write a character-string (possibly NULL) field */
 #define WRITE_STRING_FIELD(fldname) \
 	(appendStringInfoString(str, " :" CppAsString(fldname) " "), \
 	 outToken(str, node->fldname))
+#define WRITE_STRING_FIELD_OPT(fldname, default) \
+	((strcmp(node->fldname, default) == 0) ? (0) : \
+	 WRITE_STRING_FIELD(fldname))
+#define WRITE_STRING_FIELD_NONNULL(fldname) \
+	((node->fldname == NULL) ? (0) : WRITE_STRING_FIELD(fldname))
 
 /* Write a parse location field (actually same as INT case) */
 #define WRITE_LOCATION_FIELD(fldname) \
 	appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname)
+#define WRITE_LOCATION_FIELD_OPT(fldname) \
+	(node->fldname == -1 ? (0) : WRITE_LOCATION_FIELD(fldname))
 
 /* Write a Node field */
 #define WRITE_NODE_FIELD(fldname) \
 	(appendStringInfoString(str, " :" CppAsString(fldname) " "), \
 	 outNode(str, node->fldname))
+#define WRITE_NODE_FIELD_OPT(fldname) \
+	(node->fldname == NULL ? (0) : WRITE_NODE_FIELD(fldname))
 
 /* Write a bitmapset field */
 #define WRITE_BITMAPSET_FIELD(fldname) \
 	(appendStringInfoString(str, " :" CppAsString(fldname) " "), \
 	 outBitmapset(str, node->fldname))
+#define WRITE_BITMAPSET_FIELD_OPT(fldname) \
+	(node->fldname == NULL ? (0) : WRITE_BITMAPSET_FIELD(fldname))
 
 /* Write a variable-length array (not a List) of Node pointers */
 #define WRITE_NODE_ARRAY(fldname, len) \
@@ -132,6 +161,7 @@ static void outDouble(StringInfo str, double d);
 
 #define booltostr(x)  ((x) ? "true" : "false")
 
+#define NODE_FIELD(fldname) (node->fldname)
 
 /*
  * outToken
@@ -359,6 +389,11 @@ outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
 		else
 		{
 			appendStringInfo(str, "%u [ ", (unsigned int) length);
+
+			/* truncate zero postfix of the data */
+			while (length != 0 && s[length - 1] == 0)
+				length -= 1;
+
 			for (i = 0; i < length; i++)
 				appendStringInfo(str, "%d ", (int) (s[i]));
 			appendStringInfoChar(str, ']');
@@ -381,18 +416,18 @@ _outConst(StringInfo str, const Const *node)
 	WRITE_NODE_TYPE("CONST");
 
 	WRITE_OID_FIELD(consttype);
-	WRITE_INT_FIELD(consttypmod);
-	WRITE_OID_FIELD(constcollid);
+	WRITE_INT_FIELD_OPT(consttypmod, -1);
+	WRITE_OID_FIELD_OPT(constcollid, InvalidOid);
 	WRITE_INT_FIELD(constlen);
-	WRITE_BOOL_FIELD(constbyval);
-	WRITE_BOOL_FIELD(constisnull);
-	WRITE_LOCATION_FIELD(location);
+	WRITE_BOOL_FIELD_OPT(constbyval, true);
+	WRITE_BOOL_FIELD_OPT(constisnull, false);
+	WRITE_LOCATION_FIELD_OPT(location);
 
-	appendStringInfoString(str, " :constvalue ");
-	if (node->constisnull)
-		appendStringInfoString(str, "<>");
-	else
+	if (!node->constisnull)
+	{
+		appendStringInfoString(str, " :constvalue ");
 		outDatum(str, node->constvalue, node->constlen, node->constbyval);
+	}
 }
 
 static void
@@ -419,7 +454,7 @@ _outBoolExpr(StringInfo str, const BoolExpr *node)
 	outToken(str, opstr);
 
 	WRITE_NODE_FIELD(args);
-	WRITE_LOCATION_FIELD(location);
+	WRITE_LOCATION_FIELD_OPT(location);
 }
 
 static void
@@ -495,39 +530,39 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 	WRITE_NODE_TYPE("RANGETBLENTRY");
 
 	/* put alias + eref first to make dump more legible */
-	WRITE_NODE_FIELD(alias);
+	WRITE_NODE_FIELD_OPT(alias);
 	WRITE_NODE_FIELD(eref);
-	WRITE_ENUM_FIELD(rtekind, RTEKind);
+	WRITE_ENUM_FIELD_OPT(rtekind, RTEKind, 0);
 
 	switch (node->rtekind)
 	{
 		case RTE_RELATION:
 			WRITE_OID_FIELD(relid);
-			WRITE_CHAR_FIELD(relkind);
-			WRITE_INT_FIELD(rellockmode);
-			WRITE_NODE_FIELD(tablesample);
-			WRITE_UINT_FIELD(perminfoindex);
+			WRITE_CHAR_FIELD_OPT(relkind, 'r');
+			WRITE_INT_FIELD_OPT(rellockmode, 1);
+			WRITE_NODE_FIELD_OPT(tablesample);
+			WRITE_UINT_FIELD_OPT(perminfoindex, 1);
 			break;
 		case RTE_SUBQUERY:
 			WRITE_NODE_FIELD(subquery);
-			WRITE_BOOL_FIELD(security_barrier);
+			WRITE_BOOL_FIELD_OPT(security_barrier, false);
 			/* we re-use these RELATION fields, too: */
 			WRITE_OID_FIELD(relid);
-			WRITE_CHAR_FIELD(relkind);
-			WRITE_INT_FIELD(rellockmode);
-			WRITE_UINT_FIELD(perminfoindex);
+			WRITE_CHAR_FIELD_OPT(relkind, 0);
+			WRITE_INT_FIELD_OPT(rellockmode, 0);
+			WRITE_UINT_FIELD_OPT(perminfoindex, 1);
 			break;
 		case RTE_JOIN:
-			WRITE_ENUM_FIELD(jointype, JoinType);
-			WRITE_INT_FIELD(joinmergedcols);
+			WRITE_ENUM_FIELD_OPT(jointype, JoinType, 0);
+			WRITE_INT_FIELD_OPT(joinmergedcols, 0);
 			WRITE_NODE_FIELD(joinaliasvars);
 			WRITE_NODE_FIELD(joinleftcols);
 			WRITE_NODE_FIELD(joinrightcols);
-			WRITE_NODE_FIELD(join_using_alias);
+			WRITE_NODE_FIELD_OPT(join_using_alias);
 			break;
 		case RTE_FUNCTION:
 			WRITE_NODE_FIELD(functions);
-			WRITE_BOOL_FIELD(funcordinality);
+			WRITE_BOOL_FIELD_OPT(funcordinality, false);
 			break;
 		case RTE_TABLEFUNC:
 			WRITE_NODE_FIELD(tablefunc);
@@ -541,7 +576,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 		case RTE_CTE:
 			WRITE_STRING_FIELD(ctename);
 			WRITE_UINT_FIELD(ctelevelsup);
-			WRITE_BOOL_FIELD(self_reference);
+			WRITE_BOOL_FIELD_OPT(self_reference, false);
 			WRITE_NODE_FIELD(coltypes);
 			WRITE_NODE_FIELD(coltypmods);
 			WRITE_NODE_FIELD(colcollations);
@@ -563,10 +598,10 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 			break;
 	}
 
-	WRITE_BOOL_FIELD(lateral);
-	WRITE_BOOL_FIELD(inh);
-	WRITE_BOOL_FIELD(inFromCl);
-	WRITE_NODE_FIELD(securityQuals);
+	WRITE_BOOL_FIELD_OPT(lateral, false);
+	WRITE_BOOL_FIELD_OPT(inh, true);
+	WRITE_BOOL_FIELD_OPT(inFromCl, true);
+	WRITE_NODE_FIELD_OPT(securityQuals);
 }
 
 static void
@@ -638,7 +673,7 @@ _outA_Expr(StringInfo str, const A_Expr *node)
 
 	WRITE_NODE_FIELD(lexpr);
 	WRITE_NODE_FIELD(rexpr);
-	WRITE_LOCATION_FIELD(location);
+	WRITE_LOCATION_FIELD_OPT(location);
 }
 
 static void
@@ -696,7 +731,7 @@ _outA_Const(StringInfo str, const A_Const *node)
 		appendStringInfoString(str, " :val ");
 		outNode(str, &node->val);
 	}
-	WRITE_LOCATION_FIELD(location);
+	WRITE_LOCATION_FIELD_OPT(location);
 }
 
 static void
@@ -705,9 +740,9 @@ _outConstraint(StringInfo str, const Constraint *node)
 	WRITE_NODE_TYPE("CONSTRAINT");
 
 	WRITE_STRING_FIELD(conname);
-	WRITE_BOOL_FIELD(deferrable);
-	WRITE_BOOL_FIELD(initdeferred);
-	WRITE_LOCATION_FIELD(location);
+	WRITE_BOOL_FIELD_OPT(deferrable, false);
+	WRITE_BOOL_FIELD_OPT(initdeferred, false);
+	WRITE_LOCATION_FIELD_OPT(location);
 
 	appendStringInfoString(str, " :contype ");
 	switch (node->contype)
@@ -719,10 +754,10 @@ _outConstraint(StringInfo str, const Constraint *node)
 		case CONSTR_NOTNULL:
 			appendStringInfoString(str, "NOT_NULL");
 			WRITE_NODE_FIELD(keys);
-			WRITE_INT_FIELD(inhcount);
-			WRITE_BOOL_FIELD(is_no_inherit);
-			WRITE_BOOL_FIELD(skip_validation);
-			WRITE_BOOL_FIELD(initially_valid);
+			WRITE_INT_FIELD_OPT(inhcount, 0);
+			WRITE_BOOL_FIELD_OPT(is_no_inherit, false);
+			WRITE_BOOL_FIELD_OPT(skip_validation, false);
+			WRITE_BOOL_FIELD_OPT(initially_valid, true);
 			break;
 
 		case CONSTR_DEFAULT:
@@ -733,7 +768,7 @@ _outConstraint(StringInfo str, const Constraint *node)
 
 		case CONSTR_IDENTITY:
 			appendStringInfoString(str, "IDENTITY");
-			WRITE_NODE_FIELD(options);
+			WRITE_NODE_FIELD_OPT(options);
 			WRITE_CHAR_FIELD(generated_when);
 			break;
 
@@ -746,46 +781,46 @@ _outConstraint(StringInfo str, const Constraint *node)
 
 		case CONSTR_CHECK:
 			appendStringInfoString(str, "CHECK");
-			WRITE_BOOL_FIELD(is_no_inherit);
+			WRITE_BOOL_FIELD_OPT(is_no_inherit, false);
 			WRITE_NODE_FIELD(raw_expr);
 			WRITE_STRING_FIELD(cooked_expr);
-			WRITE_BOOL_FIELD(skip_validation);
-			WRITE_BOOL_FIELD(initially_valid);
+			WRITE_BOOL_FIELD_OPT(skip_validation, false);
+			WRITE_BOOL_FIELD_OPT(initially_valid, true);
 			break;
 
 		case CONSTR_PRIMARY:
 			appendStringInfoString(str, "PRIMARY_KEY");
 			WRITE_NODE_FIELD(keys);
-			WRITE_NODE_FIELD(including);
-			WRITE_NODE_FIELD(options);
+			WRITE_NODE_FIELD_OPT(including);
+			WRITE_NODE_FIELD_OPT(options);
 			WRITE_STRING_FIELD(indexname);
-			WRITE_STRING_FIELD(indexspace);
-			WRITE_BOOL_FIELD(reset_default_tblspc);
+			WRITE_STRING_FIELD_NONNULL(indexspace);
+			WRITE_BOOL_FIELD_OPT(reset_default_tblspc, false);
 			/* access_method and where_clause not currently used */
 			break;
 
 		case CONSTR_UNIQUE:
 			appendStringInfoString(str, "UNIQUE");
-			WRITE_BOOL_FIELD(nulls_not_distinct);
+			WRITE_BOOL_FIELD_OPT(nulls_not_distinct, true);
 			WRITE_NODE_FIELD(keys);
-			WRITE_NODE_FIELD(including);
-			WRITE_NODE_FIELD(options);
+			WRITE_NODE_FIELD_OPT(including);
+			WRITE_NODE_FIELD_OPT(options);
 			WRITE_STRING_FIELD(indexname);
-			WRITE_STRING_FIELD(indexspace);
-			WRITE_BOOL_FIELD(reset_default_tblspc);
+			WRITE_STRING_FIELD_NONNULL(indexspace);
+			WRITE_BOOL_FIELD_OPT(reset_default_tblspc, false);
 			/* access_method and where_clause not currently used */
 			break;
 
 		case CONSTR_EXCLUSION:
 			appendStringInfoString(str, "EXCLUSION");
 			WRITE_NODE_FIELD(exclusions);
-			WRITE_NODE_FIELD(including);
-			WRITE_NODE_FIELD(options);
+			WRITE_NODE_FIELD_OPT(including);
+			WRITE_NODE_FIELD_OPT(options);
 			WRITE_STRING_FIELD(indexname);
-			WRITE_STRING_FIELD(indexspace);
-			WRITE_BOOL_FIELD(reset_default_tblspc);
+			WRITE_STRING_FIELD_NONNULL(indexspace);
+			WRITE_BOOL_FIELD_OPT(reset_default_tblspc, false);
 			WRITE_STRING_FIELD(access_method);
-			WRITE_NODE_FIELD(where_clause);
+			WRITE_NODE_FIELD_OPT(where_clause);
 			break;
 
 		case CONSTR_FOREIGN:
@@ -799,8 +834,8 @@ _outConstraint(StringInfo str, const Constraint *node)
 			WRITE_NODE_FIELD(fk_del_set_cols);
 			WRITE_NODE_FIELD(old_conpfeqop);
 			WRITE_OID_FIELD(old_pktable_oid);
-			WRITE_BOOL_FIELD(skip_validation);
-			WRITE_BOOL_FIELD(initially_valid);
+			WRITE_BOOL_FIELD_OPT(skip_validation, false);
+			WRITE_BOOL_FIELD_OPT(initially_valid, true);
 			break;
 
 		case CONSTR_ATTR_DEFERRABLE:
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index 813eda3e73..52b71902e8 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -205,6 +205,50 @@ pg_strtok(int *length)
 	return ret_str;
 }
 
+/*
+ * Check if the next token is 'expect_token'.
+ *
+ * It handles similar to pg_strtok, except that this does not consume the
+ * next token, and has special casing for some less common tokens.
+ */
+bool
+pg_strtoken_next(const char *expect_token)
+{
+	const char *local_str;		/* working pointer to string */
+	Size expect_len = strlen(expect_token);
+	char next_char;
+
+	local_str = pg_strtok_ptr;
+
+	while (*local_str == ' ' || *local_str == '\n' || *local_str == '\t')
+		local_str++;
+
+	if (*local_str == '\0')
+		return false;			/* no more tokens */
+
+	Assert(expect_len > 0);
+
+	next_char = local_str[expect_len];
+
+	/* check if the next few bytes match the token */
+	if (strncmp(local_str, expect_token, expect_len) != 0)
+		return false;
+
+	/*
+	 * Check that the token was actually terminated at the end of the
+	 * expected token. Otherwise, we'd get positive matches for mathing the
+	 * token of "is" against a local_str of "isn't", which is clearly wrong.
+	 */
+	return (next_char == '\0' ||
+			next_char == ' ' ||
+			next_char == '\n' ||
+			next_char == '\t' ||
+			next_char == '(' ||
+			next_char == ')' ||
+			next_char == '{' ||
+			next_char == '}');
+}
+
 /*
  * debackslash -
  *	  create a palloc'd string holding the given token.
@@ -338,7 +382,10 @@ nodeRead(const char *token, int tok_len)
 			result = parseNodeString();
 			token = pg_strtok(&tok_len);
 			if (token == NULL || token[0] != '}')
+			{
+				Assert(false);
 				elog(ERROR, "did not find '}' at end of input node");
+			}
 			break;
 		case LEFT_PAREN:
 			{
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index cc2021c1f7..88699bff54 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -56,113 +56,173 @@
 	READ_LOCALS_NO_FIELDS(nodeTypeName);	\
 	READ_TEMP_LOCALS()
 
+/* */
+#define READ_OPT_SCAFFOLD(fldname, read_field_code, default_value) \
+	if (pg_strtoken_next(":" CppAsString(fldname))) \
+	{ \
+		read_field_code; \
+	} \
+	else \
+		local_node->fldname = default_value
+
+#define MY_NODE local_node
+
 /* Read an integer field (anything written as ":fldname %d") */
 #define READ_INT_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = atoi(token)
+#define READ_INT_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_INT_FIELD(fldname), default_value)
 
 /* Read an unsigned integer field (anything written as ":fldname %u") */
 #define READ_UINT_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = atoui(token)
+#define READ_UINT_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_UINT_FIELD(fldname), default_value)
 
 /* Read an unsigned integer field (anything written using UINT64_FORMAT) */
 #define READ_UINT64_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = strtou64(token, NULL, 10)
+#define READ_UINT64_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_UINT64_FIELD(fldname), default_value)
 
 /* Read a long integer field (anything written as ":fldname %ld") */
 #define READ_LONG_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = atol(token)
+#define READ_LONG_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_LONG_FIELD(fldname), default_value)
 
 /* Read an OID field (don't hard-wire assumption that OID is same as uint) */
 #define READ_OID_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = atooid(token)
+#define READ_OID_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_OID_FIELD(fldname), default_value)
 
 /* Read a char field (ie, one ascii character) */
 #define READ_CHAR_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	/* avoid overhead of calling debackslash() for one char */ \
 	local_node->fldname = (length == 0) ? '\0' : (token[0] == '\\' ? token[1] : token[0])
+#define READ_CHAR_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_CHAR_FIELD(fldname), default_value)
 
 /* Read an enumerated-type field that was written as an integer code */
 #define READ_ENUM_FIELD(fldname, enumtype) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = (enumtype) atoi(token)
+#define READ_ENUM_FIELD_OPT(fldname, enumtype, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_ENUM_FIELD(fldname, enumtype), default_value)
 
 /* Read a float field */
 #define READ_FLOAT_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = atof(token)
+#define READ_FLOAT_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_FLOAT_FIELD(fldname), default_value)
 
 /* Read a boolean field */
 #define READ_BOOL_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = strtobool(token)
+#define READ_BOOL_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_BOOL_FIELD(fldname), default_value)
 
 /* Read a character-string field */
 #define READ_STRING_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = nullable_string(token, length)
+#define READ_STRING_FIELD_OPT(fldname, default_value) \
+	READ_OPT_SCAFFOLD(fldname, READ_STRING_FIELD(fldname), default_value)
+#define READ_STRING_FIELD_NONNULL(fldname) \
+	READ_OPT_SCAFFOLD(fldname, READ_STRING_FIELD(fldname), NULL)
 
 /* Read a parse location field (and possibly throw away the value) */
 #ifdef WRITE_READ_PARSE_PLAN_TREES
 #define READ_LOCATION_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	local_node->fldname = restore_location_fields ? atoi(token) : -1
 #else
 #define READ_LOCATION_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	token = pg_strtok(&length);		/* get field value */ \
 	(void) token;				/* in case not used elsewhere */ \
 	local_node->fldname = -1	/* set field to "unknown" */
 #endif
+#define READ_LOCATION_FIELD_OPT(fldname) \
+	READ_OPT_SCAFFOLD(fldname, READ_LOCATION_FIELD(fldname), -1)
 
 /* Read a Node field */
 #define READ_NODE_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	(void) token;				/* in case not used elsewhere */ \
 	local_node->fldname = nodeRead(NULL, 0)
+#define READ_NODE_FIELD_OPT(fldname) \
+	READ_OPT_SCAFFOLD(fldname, READ_NODE_FIELD(fldname), NULL)
 
 /* Read a bitmapset field */
 #define READ_BITMAPSET_FIELD(fldname) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	(void) token;				/* in case not used elsewhere */ \
 	local_node->fldname = _readBitmapset()
+/* Read a bitmapset field */
+#define READ_BITMAPSET_FIELD_OPT(fldname) \
+	READ_OPT_SCAFFOLD(fldname, READ_BITMAPSET_FIELD(fldname), NULL)
 
 /* Read an attribute number array */
 #define READ_ATTRNUMBER_ARRAY(fldname, len) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	local_node->fldname = readAttrNumberCols(len)
 
 /* Read an oid array */
 #define READ_OID_ARRAY(fldname, len) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	local_node->fldname = readOidCols(len)
 
 /* Read an int array */
 #define READ_INT_ARRAY(fldname, len) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	local_node->fldname = readIntCols(len)
 
 /* Read a bool array */
 #define READ_BOOL_ARRAY(fldname, len) \
 	token = pg_strtok(&length);		/* skip :fldname */ \
+	Assert(strncmp(token, ":" CppAsString(fldname), length) == 0); \
 	local_node->fldname = readBoolCols(len)
 
+#define NODE_FIELD(fldname) (local_node->fldname)
+
 /* Routine exit */
 #define READ_DONE() \
 	return local_node
@@ -261,18 +321,19 @@ _readConst(void)
 	READ_LOCALS(Const);
 
 	READ_OID_FIELD(consttype);
-	READ_INT_FIELD(consttypmod);
-	READ_OID_FIELD(constcollid);
+	READ_INT_FIELD_OPT(consttypmod, -1);
+	READ_OID_FIELD_OPT(constcollid, InvalidOid);
 	READ_INT_FIELD(constlen);
-	READ_BOOL_FIELD(constbyval);
-	READ_BOOL_FIELD(constisnull);
-	READ_LOCATION_FIELD(location);
+	READ_BOOL_FIELD_OPT(constbyval, true);
+	READ_BOOL_FIELD_OPT(constisnull, false);
+	READ_LOCATION_FIELD_OPT(location);
 
-	token = pg_strtok(&length); /* skip :constvalue */
-	if (local_node->constisnull)
-		token = pg_strtok(&length); /* skip "<>" */
-	else
+	if (!local_node->constisnull)
+	{
+		token = pg_strtok(&length);
+		Assert(strncmp(token, ":constvalue", strlen(":constvalue")) == 0);
 		local_node->constvalue = readDatum(local_node->constbyval);
+	}
 
 	READ_DONE();
 }
@@ -283,19 +344,26 @@ _readBoolExpr(void)
 	READ_LOCALS(BoolExpr);
 
 	/* do-it-yourself enum representation */
-	token = pg_strtok(&length); /* skip :boolop */
-	token = pg_strtok(&length); /* get field value */
-	if (length == 3 && strncmp(token, "and", 3) == 0)
-		local_node->boolop = AND_EXPR;
-	else if (length == 2 && strncmp(token, "or", 2) == 0)
-		local_node->boolop = OR_EXPR;
-	else if (length == 3 && strncmp(token, "not", 3) == 0)
-		local_node->boolop = NOT_EXPR;
+	if (pg_strtoken_next(":boolop"))
+	{
+		token = pg_strtok(&length); /* skip :boolop */
+		token = pg_strtok(&length); /* get field value */
+		if (length == 3 && strncmp(token, "and", 3) == 0)
+			local_node->boolop = AND_EXPR;
+		else if (length == 2 && strncmp(token, "or", 2) == 0)
+			local_node->boolop = OR_EXPR;
+		else if (length == 3 && strncmp(token, "not", 3) == 0)
+			local_node->boolop = NOT_EXPR;
+		else
+			elog(ERROR, "unrecognized boolop \"%.*s\"", length, token);
+	}
 	else
-		elog(ERROR, "unrecognized boolop \"%.*s\"", length, token);
+	{
+		local_node->boolop = AND_EXPR;
+	}
 
 	READ_NODE_FIELD(args);
-	READ_LOCATION_FIELD(location);
+	READ_LOCATION_FIELD_OPT(location);
 
 	READ_DONE();
 }
@@ -338,7 +406,7 @@ _readA_Const(void)
 		}
 	}
 
-	READ_LOCATION_FIELD(location);
+	READ_LOCATION_FIELD_OPT(location);
 
 	READ_DONE();
 }
@@ -352,9 +420,9 @@ _readConstraint(void)
 	READ_LOCALS(Constraint);
 
 	READ_STRING_FIELD(conname);
-	READ_BOOL_FIELD(deferrable);
-	READ_BOOL_FIELD(initdeferred);
-	READ_LOCATION_FIELD(location);
+	READ_BOOL_FIELD_OPT(deferrable, false);
+	READ_BOOL_FIELD_OPT(initdeferred, false);
+	READ_LOCATION_FIELD_OPT(location);
 
 	token = pg_strtok(&length); /* skip :contype */
 	token = pg_strtok(&length); /* get field value */
@@ -395,10 +463,10 @@ _readConstraint(void)
 
 		case CONSTR_NOTNULL:
 			READ_NODE_FIELD(keys);
-			READ_INT_FIELD(inhcount);
-			READ_BOOL_FIELD(is_no_inherit);
-			READ_BOOL_FIELD(skip_validation);
-			READ_BOOL_FIELD(initially_valid);
+			READ_INT_FIELD_OPT(inhcount, 0);
+			READ_BOOL_FIELD_OPT(is_no_inherit, false);
+			READ_BOOL_FIELD_OPT(skip_validation, false);
+			READ_BOOL_FIELD_OPT(initially_valid, true);
 			break;
 
 		case CONSTR_DEFAULT:
@@ -407,7 +475,7 @@ _readConstraint(void)
 			break;
 
 		case CONSTR_IDENTITY:
-			READ_NODE_FIELD(options);
+			READ_NODE_FIELD_OPT(options);
 			READ_CHAR_FIELD(generated_when);
 			break;
 
@@ -418,43 +486,43 @@ _readConstraint(void)
 			break;
 
 		case CONSTR_CHECK:
-			READ_BOOL_FIELD(is_no_inherit);
+			READ_BOOL_FIELD_OPT(is_no_inherit, false);
 			READ_NODE_FIELD(raw_expr);
 			READ_STRING_FIELD(cooked_expr);
-			READ_BOOL_FIELD(skip_validation);
-			READ_BOOL_FIELD(initially_valid);
+			READ_BOOL_FIELD_OPT(skip_validation, false);
+			READ_BOOL_FIELD_OPT(initially_valid, true);
 			break;
 
 		case CONSTR_PRIMARY:
 			READ_NODE_FIELD(keys);
-			READ_NODE_FIELD(including);
-			READ_NODE_FIELD(options);
+			READ_NODE_FIELD_OPT(including);
+			READ_NODE_FIELD_OPT(options);
 			READ_STRING_FIELD(indexname);
-			READ_STRING_FIELD(indexspace);
-			READ_BOOL_FIELD(reset_default_tblspc);
+			READ_STRING_FIELD_NONNULL(indexspace);
+			READ_BOOL_FIELD_OPT(reset_default_tblspc, false);
 			/* access_method and where_clause not currently used */
 			break;
 
 		case CONSTR_UNIQUE:
-			READ_BOOL_FIELD(nulls_not_distinct);
+			READ_BOOL_FIELD_OPT(nulls_not_distinct, true);
 			READ_NODE_FIELD(keys);
-			READ_NODE_FIELD(including);
-			READ_NODE_FIELD(options);
+			READ_NODE_FIELD_OPT(including);
+			READ_NODE_FIELD_OPT(options);
 			READ_STRING_FIELD(indexname);
-			READ_STRING_FIELD(indexspace);
-			READ_BOOL_FIELD(reset_default_tblspc);
+			READ_STRING_FIELD_NONNULL(indexspace);
+			READ_BOOL_FIELD_OPT(reset_default_tblspc, false);
 			/* access_method and where_clause not currently used */
 			break;
 
 		case CONSTR_EXCLUSION:
 			READ_NODE_FIELD(exclusions);
-			READ_NODE_FIELD(including);
-			READ_NODE_FIELD(options);
+			READ_NODE_FIELD_OPT(including);
+			READ_NODE_FIELD_OPT(options);
 			READ_STRING_FIELD(indexname);
-			READ_STRING_FIELD(indexspace);
-			READ_BOOL_FIELD(reset_default_tblspc);
+			READ_STRING_FIELD_NONNULL(indexspace);
+			READ_BOOL_FIELD_OPT(reset_default_tblspc, false);
 			READ_STRING_FIELD(access_method);
-			READ_NODE_FIELD(where_clause);
+			READ_NODE_FIELD_OPT(where_clause);
 			break;
 
 		case CONSTR_FOREIGN:
@@ -467,8 +535,8 @@ _readConstraint(void)
 			READ_NODE_FIELD(fk_del_set_cols);
 			READ_NODE_FIELD(old_conpfeqop);
 			READ_OID_FIELD(old_pktable_oid);
-			READ_BOOL_FIELD(skip_validation);
-			READ_BOOL_FIELD(initially_valid);
+			READ_BOOL_FIELD_OPT(skip_validation, false);
+			READ_BOOL_FIELD_OPT(initially_valid, true);
 			break;
 
 		case CONSTR_ATTR_DEFERRABLE:
@@ -492,39 +560,39 @@ _readRangeTblEntry(void)
 	READ_LOCALS(RangeTblEntry);
 
 	/* put alias + eref first to make dump more legible */
-	READ_NODE_FIELD(alias);
+	READ_NODE_FIELD_OPT(alias);
 	READ_NODE_FIELD(eref);
-	READ_ENUM_FIELD(rtekind, RTEKind);
+	READ_ENUM_FIELD_OPT(rtekind, RTEKind, 0);
 
 	switch (local_node->rtekind)
 	{
 		case RTE_RELATION:
 			READ_OID_FIELD(relid);
-			READ_CHAR_FIELD(relkind);
-			READ_INT_FIELD(rellockmode);
-			READ_NODE_FIELD(tablesample);
-			READ_UINT_FIELD(perminfoindex);
+			READ_CHAR_FIELD_OPT(relkind, 'r');
+			READ_INT_FIELD_OPT(rellockmode, 1);
+			READ_NODE_FIELD_OPT(tablesample);
+			READ_UINT_FIELD_OPT(perminfoindex, 1);
 			break;
 		case RTE_SUBQUERY:
 			READ_NODE_FIELD(subquery);
-			READ_BOOL_FIELD(security_barrier);
+			READ_BOOL_FIELD_OPT(security_barrier, false);
 			/* we re-use these RELATION fields, too: */
 			READ_OID_FIELD(relid);
-			READ_CHAR_FIELD(relkind);
-			READ_INT_FIELD(rellockmode);
-			READ_UINT_FIELD(perminfoindex);
+			READ_CHAR_FIELD_OPT(relkind, 0);
+			READ_INT_FIELD_OPT(rellockmode, 0);
+			READ_UINT_FIELD_OPT(perminfoindex, 1);
 			break;
 		case RTE_JOIN:
-			READ_ENUM_FIELD(jointype, JoinType);
-			READ_INT_FIELD(joinmergedcols);
+			READ_ENUM_FIELD_OPT(jointype, JoinType, 0);
+			READ_INT_FIELD_OPT(joinmergedcols, 0);
 			READ_NODE_FIELD(joinaliasvars);
 			READ_NODE_FIELD(joinleftcols);
 			READ_NODE_FIELD(joinrightcols);
-			READ_NODE_FIELD(join_using_alias);
+			READ_NODE_FIELD_OPT(join_using_alias);
 			break;
 		case RTE_FUNCTION:
 			READ_NODE_FIELD(functions);
-			READ_BOOL_FIELD(funcordinality);
+			READ_BOOL_FIELD_OPT(funcordinality, false);
 			break;
 		case RTE_TABLEFUNC:
 			READ_NODE_FIELD(tablefunc);
@@ -547,7 +615,7 @@ _readRangeTblEntry(void)
 		case RTE_CTE:
 			READ_STRING_FIELD(ctename);
 			READ_UINT_FIELD(ctelevelsup);
-			READ_BOOL_FIELD(self_reference);
+			READ_BOOL_FIELD_OPT(self_reference, false);
 			READ_NODE_FIELD(coltypes);
 			READ_NODE_FIELD(coltypmods);
 			READ_NODE_FIELD(colcollations);
@@ -570,10 +638,10 @@ _readRangeTblEntry(void)
 			break;
 	}
 
-	READ_BOOL_FIELD(lateral);
-	READ_BOOL_FIELD(inh);
-	READ_BOOL_FIELD(inFromCl);
-	READ_NODE_FIELD(securityQuals);
+	READ_BOOL_FIELD_OPT(lateral, false);
+	READ_BOOL_FIELD_OPT(inh, true);
+	READ_BOOL_FIELD_OPT(inFromCl, true);
+	READ_NODE_FIELD_OPT(securityQuals);
 
 	READ_DONE();
 }
@@ -660,7 +728,7 @@ _readA_Expr(void)
 
 	READ_NODE_FIELD(lexpr);
 	READ_NODE_FIELD(rexpr);
-	READ_LOCATION_FIELD(location);
+	READ_LOCATION_FIELD_OPT(location);
 
 	READ_DONE();
 }
@@ -768,16 +836,29 @@ readDatum(bool typbyval)
 		s = (char *) palloc(length);
 		for (i = 0; i < length; i++)
 		{
+			if (pg_strtoken_next("]"))
+				break;
+
 			token = pg_strtok(&tokenLength);
+
+			Assert(token[0] != ']');
+
 			s[i] = (char) atoi(token);
 		}
+
+		for (; i < length; i++)
+			s[i] = 0;
+
 		res = PointerGetDatum(s);
 	}
 
 	token = pg_strtok(&tokenLength);	/* read the ']' */
 	if (token == NULL || token[0] != ']')
+	{
+		Assert(false);
 		elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu",
 			 token ? token : "[NULL]", length);
+	}
 
 	return res;
 }
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index e36fc72e1e..5ab0ec089e 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -219,6 +219,98 @@ DefineRule(RuleStmt *stmt, const char *queryString)
 							  actions);
 }
 
+/*
+ * Walker that clears 'location'-related fields to -1.
+ *
+ * This is used for reducing the size of pg_rewrite entries exclusively.
+ */
+static bool
+strip_location_walker(Node *node, void *context)
+{
+	const int query_recurse_flags = 0;
+	ListCell   *temp;
+
+#define RESET_FOR(_type_) \
+	case T_##_type_: \
+	{ \
+		castNode(_type_, node)->location = -1; \
+		break; \
+	}
+
+	if (node == NULL)
+		return false;
+
+	if (IsA(node, Query))
+	{
+		Query *query = castNode(Query, node);
+		query->stmt_location = -1;
+		query->stmt_len = 0;
+		
+		return query_tree_walker(query, strip_location_walker, context, query_recurse_flags);
+	}
+
+	switch (nodeTag(node))
+	{
+		RESET_FOR(RangeVar);
+		RESET_FOR(TableFunc);
+		RESET_FOR(Var);
+		RESET_FOR(Const);
+		RESET_FOR(Param);
+		RESET_FOR(Aggref);
+		RESET_FOR(GroupingFunc);
+		RESET_FOR(WindowFunc);
+		RESET_FOR(FuncExpr);
+		RESET_FOR(NamedArgExpr);
+		RESET_FOR(OpExpr);
+		RESET_FOR(DistinctExpr);
+		RESET_FOR(NullIfExpr);
+		RESET_FOR(ScalarArrayOpExpr);
+		RESET_FOR(BoolExpr);
+		RESET_FOR(SubLink);
+		RESET_FOR(RelabelType);
+		RESET_FOR(CoerceViaIO);
+		RESET_FOR(ArrayCoerceExpr);
+		RESET_FOR(ConvertRowtypeExpr);
+		RESET_FOR(CollateExpr);
+		RESET_FOR(CaseWhen);
+		RESET_FOR(ArrayExpr);
+		RESET_FOR(RowExpr);
+		RESET_FOR(CoalesceExpr);
+		RESET_FOR(MinMaxExpr);
+		RESET_FOR(SQLValueFunction);
+		RESET_FOR(XmlExpr);
+		RESET_FOR(JsonFormat);
+		RESET_FOR(JsonConstructorExpr);
+		RESET_FOR(JsonIsPredicate);
+		RESET_FOR(NullTest);
+		RESET_FOR(BooleanTest);
+		RESET_FOR(CoerceToDomain);
+		RESET_FOR(CoerceToDomainValue);
+		RESET_FOR(SetToDefault);
+	case T_CaseExpr:
+		{
+			/*
+			 * The expression_tree_walker does not call us for CaseWhen nodes,
+			 * but instead directly wires us through to its inner Nodes.
+			 * To correctly reset the location fields, we manually iterate
+			 * to get those fields reset.
+			 */
+			CaseExpr *caseExpr = castNode(CaseExpr, node);
+			caseExpr->location = -1;
+			foreach(temp, caseExpr->args)
+			{
+				CaseWhen *when = lfirst_node(CaseWhen, temp);
+				when->location = -1;
+			}
+			break;
+		}
+	default:
+		break;
+	}
+
+	return expression_tree_walker(node, strip_location_walker, (void *) context);
+#undef RESET_FOR
+}
 
 /*
  * DefineQueryRewrite
@@ -474,6 +566,16 @@ DefineQueryRewrite(const char *rulename,
 	/* discard rule if it's null action and not INSTEAD; it's a no-op */
 	if (action != NIL || is_instead)
 	{
+		/*
+		 * Clear location info from the statement. Because we don't store
+		 * the original query, maintaining this information is meaningless.
+		 */
+		foreach(l, action)
+		{
+			query = lfirst_node(Query, l);
+			strip_location_walker((Node *) query, NULL);
+		}
+		
 		ruleId = InsertRule(rulename,
 							event_type,
 							event_relid,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e494309da8..b853c78552 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -117,7 +117,8 @@ typedef struct Query
 {
 	NodeTag		type;
 
-	CmdType		commandType;	/* select|insert|update|delete|merge|utility */
+	/* select|insert|update|delete|merge|utility */
+	CmdType		commandType pg_node_attr(default(1));
 
 	/* where did I come from? */
 	QuerySource querySource pg_node_attr(query_jumble_ignore);
@@ -130,7 +131,7 @@ typedef struct Query
 	uint64		queryId pg_node_attr(equal_ignore, query_jumble_ignore, read_write_ignore, read_as(0));
 
 	/* do I set the command result tag? */
-	bool		canSetTag pg_node_attr(query_jumble_ignore);
+	bool		canSetTag pg_node_attr(query_jumble_ignore, default(true));
 
 	Node	   *utilityStmt;	/* non-null if commandType == CMD_UTILITY */
 
@@ -1234,8 +1235,10 @@ typedef struct RTEPermissionInfo
 	NodeTag		type;
 
 	Oid			relid;			/* relation OID */
-	bool		inh;			/* separately check inheritance children? */
-	AclMode		requiredPerms;	/* bitmask of required access permissions */
+	/* separately check inheritance children? */
+	bool		inh pg_node_attr(default(true));
+	/* bitmask of required access permissions */
+	AclMode		requiredPerms pg_node_attr(default(2));
 	Oid			checkAsUser;	/* if valid, check access as this role */
 	Bitmapset  *selectedCols;	/* columns needing SELECT permission */
 	Bitmapset  *insertedCols;	/* columns needing INSERT permission */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index bb930afb52..e2604edb03 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -82,10 +82,10 @@ typedef struct RangeVar
 	char	   *relname;
 
 	/* expand rel by inheritance? recursively act on children? */
-	bool		inh;
+	bool		inh pg_node_attr(default(true));
 
 	/* see RELPERSISTENCE_* in pg_class.h */
-	char		relpersistence;
+	char		relpersistence pg_node_attr(default('p'));
 
 	/* table alias & optional column aliases */
 	Alias	   *alias;
@@ -126,7 +126,7 @@ typedef struct TableFunc
 	/* nullability flag for each output column */
 	Bitmapset  *notnulls pg_node_attr(query_jumble_ignore);
 	/* counts from 0; -1 if none specified */
-	int			ordinalitycol pg_node_attr(query_jumble_ignore);
+	int			ordinalitycol pg_node_attr(query_jumble_ignore, default(-1));
 	/* token location, or -1 if unknown */
 	int			location;
 } TableFunc;
@@ -238,7 +238,7 @@ typedef struct Var
 	 * index of this var's relation in the range table, or
 	 * INNER_VAR/OUTER_VAR/etc
 	 */
-	int			varno;
+	int			varno pg_node_attr(default(1));
 
 	/*
 	 * attribute number of this var, or zero for all attrs ("whole-row Var")
@@ -248,7 +248,7 @@ typedef struct Var
 	/* pg_type OID for the type of this var */
 	Oid			vartype pg_node_attr(query_jumble_ignore);
 	/* pg_attribute typmod value */
-	int32		vartypmod pg_node_attr(query_jumble_ignore);
+	int32		vartypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* OID of collation, or InvalidOid if none */
 	Oid			varcollid pg_node_attr(query_jumble_ignore);
 
@@ -271,9 +271,9 @@ typedef struct Var
 	 * their varno/varattno match.
 	 */
 	/* syntactic relation index (0 if unknown) */
-	Index		varnosyn pg_node_attr(equal_ignore, query_jumble_ignore);
+	Index		varnosyn pg_node_attr(equal_ignore, query_jumble_ignore, default_ref(varno));
 	/* syntactic attribute number */
-	AttrNumber	varattnosyn pg_node_attr(equal_ignore, query_jumble_ignore);
+	AttrNumber	varattnosyn pg_node_attr(equal_ignore, query_jumble_ignore, default_ref(varattno));
 
 	/* token location, or -1 if unknown */
 	int			location;
@@ -297,7 +297,7 @@ typedef struct Const
 	/* pg_type OID of the constant's datatype */
 	Oid			consttype;
 	/* typmod value, if any */
-	int32		consttypmod pg_node_attr(query_jumble_ignore);
+	int32		consttypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* OID of collation, or InvalidOid if none */
 	Oid			constcollid pg_node_attr(query_jumble_ignore);
 	/* typlen of the constant's datatype */
@@ -363,7 +363,7 @@ typedef struct Param
 	int			paramid;		/* numeric ID for parameter */
 	Oid			paramtype;		/* pg_type OID of parameter's datatype */
 	/* typmod value, if known */
-	int32		paramtypmod pg_node_attr(query_jumble_ignore);
+	int32		paramtypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* OID of collation, or InvalidOid if none */
 	Oid			paramcollid pg_node_attr(query_jumble_ignore);
 	/* token location, or -1 if unknown */
@@ -621,7 +621,7 @@ typedef struct SubscriptingRef
 	/* type of the SubscriptingRef's result */
 	Oid			refrestype pg_node_attr(query_jumble_ignore);
 	/* typmod of the result */
-	int32		reftypmod pg_node_attr(query_jumble_ignore);
+	int32		reftypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* collation of result, or InvalidOid if none */
 	Oid			refcollid pg_node_attr(query_jumble_ignore);
 	/* expressions that evaluate to upper container indexes */
@@ -1065,7 +1065,7 @@ typedef struct FieldSelect
 	/* type of the field (result type of this node) */
 	Oid			resulttype pg_node_attr(query_jumble_ignore);
 	/* output typmod (usually -1) */
-	int32		resulttypmod pg_node_attr(query_jumble_ignore);
+	int32		resulttypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* OID of collation of the field */
 	Oid			resultcollid pg_node_attr(query_jumble_ignore);
 } FieldSelect;
@@ -1119,7 +1119,7 @@ typedef struct RelabelType
 	Expr	   *arg;			/* input expression */
 	Oid			resulttype;		/* output type of coercion expression */
 	/* output typmod (usually -1) */
-	int32		resulttypmod pg_node_attr(query_jumble_ignore);
+	int32		resulttypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* OID of collation, or InvalidOid if none */
 	Oid			resultcollid pg_node_attr(query_jumble_ignore);
 	/* how to display this node */
@@ -1169,7 +1169,7 @@ typedef struct ArrayCoerceExpr
 	Expr	   *elemexpr;		/* expression representing per-element work */
 	Oid			resulttype;		/* output type of coercion (an array type) */
 	/* output typmod (also element typmod) */
-	int32		resulttypmod pg_node_attr(query_jumble_ignore);
+	int32		resulttypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* OID of collation, or InvalidOid if none */
 	Oid			resultcollid pg_node_attr(query_jumble_ignore);
 	/* how to display this node */
@@ -1495,7 +1495,7 @@ typedef struct SQLValueFunction
 	 * include this Oid in the query jumbling.
 	 */
 	Oid			type pg_node_attr(query_jumble_ignore);
-	int32		typmod;
+	int32		typmod pg_node_attr(default(-1));
 	int			location;		/* token location, or -1 if unknown */
 } SQLValueFunction;
 
@@ -1547,7 +1547,7 @@ typedef struct XmlExpr
 	bool		indent;
 	/* target type/typmod for XMLSERIALIZE */
 	Oid			type pg_node_attr(query_jumble_ignore);
-	int32		typmod pg_node_attr(query_jumble_ignore);
+	int32		typmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* token location, or -1 if unknown */
 	int			location;
 } XmlExpr;
@@ -1597,7 +1597,7 @@ typedef struct JsonReturning
 	NodeTag		type;
 	JsonFormat *format;			/* output JSON format */
 	Oid			typid;			/* target type Oid */
-	int32		typmod;			/* target type modifier */
+	int32		typmod pg_node_attr(default(-1));			/* target type modifier */
 } JsonReturning;
 
 /*
@@ -1760,11 +1760,11 @@ typedef struct CoerceToDomain
 	Expr	   *arg;			/* input expression */
 	Oid			resulttype;		/* domain type ID (result type) */
 	/* output typmod (currently always -1) */
-	int32		resulttypmod pg_node_attr(query_jumble_ignore);
+	int32		resulttypmod pg_node_attr(query_jumble_ignore, default(-1));
 	/* OID of collation, or InvalidOid if none */
 	Oid			resultcollid pg_node_attr(query_jumble_ignore);
 	/* how to display this node */
-	CoercionForm coercionformat pg_node_attr(query_jumble_ignore);
+	CoercionForm coercionformat pg_node_attr(query_jumble_ignore, default(1));
 	int			location;		/* token location, or -1 if unknown */
 } CoerceToDomain;
 
@@ -1929,7 +1929,7 @@ typedef struct TargetEntry
 	/* OID of column's source table */
 	Oid			resorigtbl pg_node_attr(query_jumble_ignore);
 	/* column's number in source table */
-	AttrNumber	resorigcol pg_node_attr(query_jumble_ignore);
+	AttrNumber	resorigcol pg_node_attr(query_jumble_ignore, default(0));
 	/* set to true to eliminate the attribute from final target list */
 	bool		resjunk pg_node_attr(query_jumble_ignore);
 } TargetEntry;
@@ -1975,7 +1975,7 @@ typedef struct TargetEntry
 typedef struct RangeTblRef
 {
 	NodeTag		type;
-	int			rtindex;
+	int			rtindex pg_node_attr(default(1));
 } RangeTblRef;
 
 /*----------
diff --git a/src/include/nodes/readfuncs.h b/src/include/nodes/readfuncs.h
index cba6f0be75..e668b24ad0 100644
--- a/src/include/nodes/readfuncs.h
+++ b/src/include/nodes/readfuncs.h
@@ -27,6 +27,7 @@ extern PGDLLIMPORT bool restore_location_fields;
  * prototypes for functions in read.c (the lisp token parser)
  */
 extern const char *pg_strtok(int *length);
+extern bool pg_strtoken_next(const char *expect_token);
 extern char *debackslash(const char *token, int length);
 extern void *nodeRead(const char *token, int tok_len);
 
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 6988128aa4..942bce62a9 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -3937,7 +3937,10 @@ CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
 CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
 ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
 GRANT SELECT ON coll_t TO regress_rls_alice;
-SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass;
+SELECT split inputcollid
+FROM pg_policy,
+     lateral unnest(string_to_array(polqual, ':')) as split
+WHERE polrelid = 'coll_t'::regclass and split LIKE '%inputcollid%';
    inputcollid    
 ------------------
  inputcollid 950 
diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql
index dec7340538..9c6a6396e2 100644
--- a/src/test/regress/sql/rowsecurity.sql
+++ b/src/test/regress/sql/rowsecurity.sql
@@ -1732,7 +1732,10 @@ CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
 CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
 ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
 GRANT SELECT ON coll_t TO regress_rls_alice;
-SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass;
+SELECT split inputcollid
+FROM pg_policy,
+     lateral unnest(string_to_array(polqual, ':')) as split
+WHERE polrelid = 'coll_t'::regclass and split LIKE '%inputcollid%';
 SET SESSION AUTHORIZATION regress_rls_alice;
 SELECT * FROM coll_t;
 ROLLBACK;
-- 
2.40.1

