From 6f7d037a03f2020166407b46a6bf6a2b8226f906 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent 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