Generate code for query jumbling through gen_node_support.pl
authorMichael Paquier <michael@paquier.xyz>
Tue, 31 Jan 2023 06:24:05 +0000 (15:24 +0900)
committerMichael Paquier <michael@paquier.xyz>
Tue, 31 Jan 2023 06:24:05 +0000 (15:24 +0900)
This commit changes the query jumbling code in queryjumblefuncs.c to be
generated automatically based on the information of the nodes in the
headers of src/include/nodes/ by using gen_node_support.pl.  This
approach offers many advantages:
- Support for query jumbling for all the utility statements, based on the
state of their parsed Nodes and not only their query string.  This will
greatly ease the switch to normalize the information of some DDLs, like
SET or CALL for example (this is left unchanged and should be part of a
separate discussion).  With this feature, the number of entries stored
for utilities in pg_stat_statements is reduced (for example now
"CHECKPOINT" and "checkpoint" mean the same thing with the same query
ID).
- Documentation of query jumbling directly in the structure definition
of the nodes.  Since this code has been introduced in pg_stat_statements
and then moved to code, the reasons behind the choices of what should be
included in the jumble are rather sparse.  Note that some explanation is
added for the most relevant parts, as a start.
- Overall code reduction and more consistency with the other parts
generating read, write and copy depending on the nodes.

The query jumbling is controlled by a couple of new node attributes,
documented in nodes/nodes.h:
- custom_query_jumble, to mark a Node as having a custom
implementation.
- no_query_jumble, to ignore entirely a Node.
- query_jumble_ignore, to ignore a field in a Node.
- query_jumble_location, to mark a location in a Node, for
normalization.  This can apply only to int fields, with "location" in
their name (only Const as of this commit).

There should be no compatibility impact on pg_stat_statements, as the
new code applies the jumbling to the same fields for each node (its
regression tests have no modification, for one).

Some benchmark of the query jumbling between HEAD and this commit for
SELECT and DMLs has proved that this new code does not cause a
performance regression, with computation times close for both methods.
For utility queries, the new method is slower than the previous method
of calculating a hash of the query string, though we are talking about
extra ns-level changes based on what I measured, which is unnoticeable
even for OLTP workloads as a query ID is calculated once per query
post-parse analysis.

Author: Michael Paquier
Reviewed-by: Peter Eisentraut
Discussion: https://postgr.es/m/Y5BHOUhX3zTH/ig6@paquier.xyz

contrib/pg_stat_statements/expected/pg_stat_statements.out
contrib/pg_stat_statements/sql/pg_stat_statements.sql
src/backend/nodes/README
src/backend/nodes/gen_node_support.pl
src/backend/nodes/meson.build
src/backend/nodes/queryjumblefuncs.c
src/include/nodes/bitmapset.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/primnodes.h

index 9ac5c87c3a263639d3778d377f2d424e4d7103d5..fb9ccd920f6459c733635fcc8ee3446c11e8ae77 100644 (file)
@@ -571,8 +571,9 @@ DROP TABLE test \;
 DROP TABLE IF EXISTS test \;
 DROP FUNCTION PLUS_ONE(INTEGER);
 NOTICE:  table "test" does not exist, skipping
+-- This DROP query uses two different strings, still they count as one entry.
 DROP TABLE IF EXISTS test \;
-DROP TABLE IF EXISTS test \;
+Drop Table If Exists test \;
 DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER);
 NOTICE:  table "test" does not exist, skipping
 NOTICE:  table "test" does not exist, skipping
index 8f5c866225bd558a0d3616ba6c8b8428cc6f7785..b82cddf16f1f2fbeaad7de2a30ecc6137811412c 100644 (file)
@@ -265,8 +265,9 @@ CREATE INDEX test_b ON test(b);
 DROP TABLE test \;
 DROP TABLE IF EXISTS test \;
 DROP FUNCTION PLUS_ONE(INTEGER);
+-- This DROP query uses two different strings, still they count as one entry.
 DROP TABLE IF EXISTS test \;
-DROP TABLE IF EXISTS test \;
+Drop Table If Exists test \;
 DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER);
 DROP FUNCTION PLUS_TWO(INTEGER);
 
index 489a67eb89972b483fd1588d774283206629a39a..7cf6e3b04102379791f37518186bf6aaf03e46f4 100644 (file)
@@ -51,6 +51,7 @@ FILES IN THIS DIRECTORY (src/backend/nodes/)
        readfuncs.c     - convert text representation back to a node tree (*)
        makefuncs.c     - creator functions for some common node types
        nodeFuncs.c     - some other general-purpose manipulation functions
+       queryjumblefuncs.c - compute a node tree for query jumbling (*)
 
     (*) - Most functions in these files are generated by
     gen_node_support.pl and #include'd there.
index b3c1ead49655e2ca8daa670f11f85c46e83f605e..19ed29657c12b330484e882a2f935bb466ff5b96 100644 (file)
@@ -121,6 +121,8 @@ my %node_type_info;
 my @no_copy;
 # node types we don't want equal support for
 my @no_equal;
+# node types we don't want jumble support for
+my @no_query_jumble;
 # node types we don't want read support for
 my @no_read;
 # node types we don't want read/write support for
@@ -155,12 +157,13 @@ my @extra_tags = qw(
 # This is a regular node, but we skip parsing it from its header file
 # since we won't use its internal structure here anyway.
 push @node_types, qw(List);
-# Lists are specially treated in all four support files, too.
+# Lists are specially treated in all five support files, too.
 # (Ideally we'd mark List as "special copy/equal" not "no copy/equal".
 # But until there's other use-cases for that, just hot-wire the tests
 # that would need to distinguish.)
 push @no_copy,            qw(List);
 push @no_equal,           qw(List);
+push @no_query_jumble,    qw(List);
 push @special_read_write, qw(List);
 
 # Nodes with custom copy/equal implementations are skipped from
@@ -170,6 +173,9 @@ my @custom_copy_equal;
 # Similarly for custom read/write implementations.
 my @custom_read_write;
 
+# Similarly for custom query jumble implementation.
+my @custom_query_jumble;
+
 # Track node types with manually assigned NodeTag numbers.
 my %manual_nodetag_number;
 
@@ -319,6 +325,10 @@ foreach my $infile (@ARGV)
                                                {
                                                        push @custom_read_write, $in_struct;
                                                }
+                                               elsif ($attr eq 'custom_query_jumble')
+                                               {
+                                                       push @custom_query_jumble, $in_struct;
+                                               }
                                                elsif ($attr eq 'no_copy')
                                                {
                                                        push @no_copy, $in_struct;
@@ -332,6 +342,10 @@ foreach my $infile (@ARGV)
                                                        push @no_copy,  $in_struct;
                                                        push @no_equal, $in_struct;
                                                }
+                                               elsif ($attr eq 'no_query_jumble')
+                                               {
+                                                       push @no_query_jumble, $in_struct;
+                                               }
                                                elsif ($attr eq 'no_read')
                                                {
                                                        push @no_read, $in_struct;
@@ -457,6 +471,8 @@ foreach my $infile (@ARGV)
                                                                equal_as_scalar
                                                                equal_ignore
                                                                equal_ignore_if_zero
+                                                               query_jumble_ignore
+                                                               query_jumble_location
                                                                read_write_ignore
                                                                write_only_relids
                                                                write_only_nondefault_pathtarget
@@ -1225,6 +1241,102 @@ close $ofs;
 close $rfs;
 
 
+# queryjumblefuncs.c
+
+push @output_files, 'queryjumblefuncs.funcs.c';
+open my $jff, '>', "$output_path/queryjumblefuncs.funcs.c$tmpext" or die $!;
+push @output_files, 'queryjumblefuncs.switch.c';
+open my $jfs, '>', "$output_path/queryjumblefuncs.switch.c$tmpext" or die $!;
+
+printf $jff $header_comment, 'queryjumblefuncs.funcs.c';
+printf $jfs $header_comment, 'queryjumblefuncs.switch.c';
+
+print $jff $node_includes;
+
+foreach my $n (@node_types)
+{
+       next if elem $n, @abstract_types;
+       next if elem $n, @nodetag_only;
+       my $struct_no_query_jumble = (elem $n, @no_query_jumble);
+
+       print $jfs "\t\t\tcase T_${n}:\n"
+         . "\t\t\t\t_jumble${n}(jstate, expr);\n"
+         . "\t\t\t\tbreak;\n"
+         unless $struct_no_query_jumble;
+
+       next if elem $n, @custom_query_jumble;
+
+       print $jff "
+static void
+_jumble${n}(JumbleState *jstate, Node *node)
+{
+\t${n} *expr = (${n} *) node;\n
+" unless $struct_no_query_jumble;
+
+       # print instructions for each field
+       foreach my $f (@{ $node_type_info{$n}->{fields} })
+       {
+               my $t                   = $node_type_info{$n}->{field_types}{$f};
+               my @a                   = @{ $node_type_info{$n}->{field_attrs}{$f} };
+               my $query_jumble_ignore = $struct_no_query_jumble;
+               my $query_jumble_location = 0;
+
+               # extract per-field attributes
+               foreach my $a (@a)
+               {
+                       if ($a eq 'query_jumble_ignore')
+                       {
+                               $query_jumble_ignore = 1;
+                       }
+                       elsif ($a eq 'query_jumble_location')
+                       {
+                               $query_jumble_location = 1;
+                       }
+               }
+
+               # node type
+               if (($t =~ /^(\w+)\*$/ or $t =~ /^struct\s+(\w+)\*$/)
+                       and elem $1, @node_types)
+               {
+                       print $jff "\tJUMBLE_NODE($f);\n"
+                         unless $query_jumble_ignore;
+               }
+               elsif ($t eq 'int' && $f =~ 'location$')
+               {
+                       # Track the node's location only if directly requested.
+                       if ($query_jumble_location)
+                       {
+                               print $jff "\tJUMBLE_LOCATION($f);\n"
+                                 unless $query_jumble_ignore;
+                       }
+               }
+               elsif ($t eq 'char*')
+               {
+                       print $jff "\tJUMBLE_STRING($f);\n"
+                         unless $query_jumble_ignore;
+               }
+               else
+               {
+                       print $jff "\tJUMBLE_FIELD($f);\n"
+                         unless $query_jumble_ignore;
+               }
+       }
+
+       # Some nodes have no attributes like CheckPointStmt,
+       # so tweak things for empty contents.
+       if (scalar(@{ $node_type_info{$n}->{fields} }) == 0)
+       {
+               print $jff "\t(void) expr;\n"
+                 unless $struct_no_query_jumble;
+       }
+
+       print $jff "}
+" unless $struct_no_query_jumble;
+}
+
+close $jff;
+close $jfs;
+
 # now rename the temporary files to their final names
 foreach my $file (@output_files)
 {
index 9230515e7fb78f1502dc80a5810687c7eb003605..31467a12d3b5925caec141e99f0191e83fed764e 100644 (file)
@@ -10,7 +10,6 @@ backend_sources += files(
   'nodes.c',
   'params.c',
   'print.c',
-  'queryjumblefuncs.c',
   'read.c',
   'tidbitmap.c',
   'value.c',
@@ -21,6 +20,7 @@ backend_sources += files(
 nodefunc_sources = files(
   'copyfuncs.c',
   'equalfuncs.c',
+  'queryjumblefuncs.c',
   'outfuncs.c',
   'readfuncs.c',
 )
index 8b101fc336cbccb7255d2552fb21434e277f4453..223d1bc826260e2eaf3eaac7d5f01e4493d6576f 100644 (file)
@@ -45,15 +45,12 @@ int                 compute_query_id = COMPUTE_QUERY_ID_AUTO;
 /* True when compute_query_id is ON, or AUTO and a module requests them */
 bool           query_id_enabled = false;
 
-static uint64 compute_utility_query_id(const char *query_text,
-                                                                          int query_location, int query_len);
 static void AppendJumble(JumbleState *jstate,
                                                 const unsigned char *item, Size size);
-static void JumbleQueryInternal(JumbleState *jstate, Query *query);
-static void JumbleRangeTable(JumbleState *jstate, List *rtable);
-static void JumbleRowMarks(JumbleState *jstate, List *rowMarks);
-static void JumbleExpr(JumbleState *jstate, Node *node);
 static void RecordConstLocation(JumbleState *jstate, int location);
+static void _jumbleNode(JumbleState *jstate, Node *node);
+static void _jumbleList(JumbleState *jstate, Node *node);
+static void _jumbleRangeTblEntry(JumbleState *jstate, Node *node);
 
 /*
  * Given a possibly multi-statement source string, confine our attention to the
@@ -105,36 +102,32 @@ JumbleQuery(Query *query, const char *querytext)
 
        Assert(IsQueryIdEnabled());
 
-       if (query->utilityStmt)
-       {
-               query->queryId = compute_utility_query_id(querytext,
-                                                                                                 query->stmt_location,
-                                                                                                 query->stmt_len);
-       }
-       else
+       jstate = (JumbleState *) palloc(sizeof(JumbleState));
+
+       /* Set up workspace for query jumbling */
+       jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
+       jstate->jumble_len = 0;
+       jstate->clocations_buf_size = 32;
+       jstate->clocations = (LocationLen *)
+               palloc(jstate->clocations_buf_size * sizeof(LocationLen));
+       jstate->clocations_count = 0;
+       jstate->highest_extern_param_id = 0;
+
+       /* Compute query ID and mark the Query node with it */
+       _jumbleNode(jstate, (Node *) query);
+       query->queryId = DatumGetUInt64(hash_any_extended(jstate->jumble,
+                                                                                                         jstate->jumble_len,
+                                                                                                         0));
+
+       /*
+        * If we are unlucky enough to get a hash of zero, use 1 instead for
+        * normal statements and 2 for utility queries.
+        */
+       if (query->queryId == UINT64CONST(0))
        {
-               jstate = (JumbleState *) palloc(sizeof(JumbleState));
-
-               /* Set up workspace for query jumbling */
-               jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
-               jstate->jumble_len = 0;
-               jstate->clocations_buf_size = 32;
-               jstate->clocations = (LocationLen *)
-                       palloc(jstate->clocations_buf_size * sizeof(LocationLen));
-               jstate->clocations_count = 0;
-               jstate->highest_extern_param_id = 0;
-
-               /* Compute query ID and mark the Query node with it */
-               JumbleQueryInternal(jstate, query);
-               query->queryId = DatumGetUInt64(hash_any_extended(jstate->jumble,
-                                                                                                                 jstate->jumble_len,
-                                                                                                                 0));
-
-               /*
-                * If we are unlucky enough to get a hash of zero, use 1 instead, to
-                * prevent confusion with the utility-statement case.
-                */
-               if (query->queryId == UINT64CONST(0))
+               if (query->utilityStmt)
+                       query->queryId = UINT64CONST(2);
+               else
                        query->queryId = UINT64CONST(1);
        }
 
@@ -154,34 +147,6 @@ EnableQueryId(void)
                query_id_enabled = true;
 }
 
-/*
- * Compute a query identifier for the given utility query string.
- */
-static uint64
-compute_utility_query_id(const char *query_text, int query_location, int query_len)
-{
-       uint64          queryId;
-       const char *sql;
-
-       /*
-        * Confine our attention to the relevant part of the string, if the query
-        * is a portion of a multi-statement source string.
-        */
-       sql = CleanQuerytext(query_text, &query_location, &query_len);
-
-       queryId = DatumGetUInt64(hash_any_extended((const unsigned char *) sql,
-                                                                                          query_len, 0));
-
-       /*
-        * If we are unlucky enough to get a hash of zero(invalid), use queryID as
-        * 2 instead, queryID 1 is already in use for normal statements.
-        */
-       if (queryId == UINT64CONST(0))
-               queryId = UINT64CONST(2);
-
-       return queryId;
-}
-
 /*
  * AppendJumble: Append a value that is substantive in a given query to
  * the current jumble.
@@ -220,149 +185,53 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
 }
 
 /*
- * Wrappers around AppendJumble to encapsulate details of serialization
- * of individual local variable elements.
- */
-#define APP_JUMB(item) \
-       AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
-#define APP_JUMB_STRING(str) \
-       AppendJumble(jstate, (const unsigned char *) (str), strlen(str) + 1)
-
-/*
- * JumbleQueryInternal: Selectively serialize the query tree, appending
- * significant data to the "query jumble" while ignoring nonsignificant data.
- *
- * Rule of thumb for what to include is that we should ignore anything not
- * semantically significant (such as alias names) as well as anything that can
- * be deduced from child nodes (else we'd just be double-hashing that piece
- * of information).
- */
-static void
-JumbleQueryInternal(JumbleState *jstate, Query *query)
-{
-       Assert(IsA(query, Query));
-       Assert(query->utilityStmt == NULL);
-
-       APP_JUMB(query->commandType);
-       /* resultRelation is usually predictable from commandType */
-       JumbleExpr(jstate, (Node *) query->cteList);
-       JumbleRangeTable(jstate, query->rtable);
-       JumbleExpr(jstate, (Node *) query->jointree);
-       JumbleExpr(jstate, (Node *) query->mergeActionList);
-       JumbleExpr(jstate, (Node *) query->targetList);
-       JumbleExpr(jstate, (Node *) query->onConflict);
-       JumbleExpr(jstate, (Node *) query->returningList);
-       JumbleExpr(jstate, (Node *) query->groupClause);
-       APP_JUMB(query->groupDistinct);
-       JumbleExpr(jstate, (Node *) query->groupingSets);
-       JumbleExpr(jstate, query->havingQual);
-       JumbleExpr(jstate, (Node *) query->windowClause);
-       JumbleExpr(jstate, (Node *) query->distinctClause);
-       JumbleExpr(jstate, (Node *) query->sortClause);
-       JumbleExpr(jstate, query->limitOffset);
-       JumbleExpr(jstate, query->limitCount);
-       APP_JUMB(query->limitOption);
-       JumbleRowMarks(jstate, query->rowMarks);
-       JumbleExpr(jstate, query->setOperations);
-}
-
-/*
- * Jumble a range table
+ * Record location of constant within query string of query tree
+ * that is currently being walked.
  */
 static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
+RecordConstLocation(JumbleState *jstate, int location)
 {
-       ListCell   *lc;
-
-       foreach(lc, rtable)
+       /* -1 indicates unknown or undefined location */
+       if (location >= 0)
        {
-               RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
-               APP_JUMB(rte->rtekind);
-               switch (rte->rtekind)
+               /* enlarge array if needed */
+               if (jstate->clocations_count >= jstate->clocations_buf_size)
                {
-                       case RTE_RELATION:
-                               APP_JUMB(rte->relid);
-                               JumbleExpr(jstate, (Node *) rte->tablesample);
-                               APP_JUMB(rte->inh);
-                               break;
-                       case RTE_SUBQUERY:
-                               JumbleQueryInternal(jstate, rte->subquery);
-                               break;
-                       case RTE_JOIN:
-                               APP_JUMB(rte->jointype);
-                               break;
-                       case RTE_FUNCTION:
-                               JumbleExpr(jstate, (Node *) rte->functions);
-                               break;
-                       case RTE_TABLEFUNC:
-                               JumbleExpr(jstate, (Node *) rte->tablefunc);
-                               break;
-                       case RTE_VALUES:
-                               JumbleExpr(jstate, (Node *) rte->values_lists);
-                               break;
-                       case RTE_CTE:
-
-                               /*
-                                * Depending on the CTE name here isn't ideal, but it's the
-                                * only info we have to identify the referenced WITH item.
-                                */
-                               APP_JUMB_STRING(rte->ctename);
-                               APP_JUMB(rte->ctelevelsup);
-                               break;
-                       case RTE_NAMEDTUPLESTORE:
-                               APP_JUMB_STRING(rte->enrname);
-                               break;
-                       case RTE_RESULT:
-                               break;
-                       default:
-                               elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
-                               break;
+                       jstate->clocations_buf_size *= 2;
+                       jstate->clocations = (LocationLen *)
+                               repalloc(jstate->clocations,
+                                                jstate->clocations_buf_size *
+                                                sizeof(LocationLen));
                }
+               jstate->clocations[jstate->clocations_count].location = location;
+               /* initialize lengths to -1 to simplify third-party module usage */
+               jstate->clocations[jstate->clocations_count].length = -1;
+               jstate->clocations_count++;
        }
 }
 
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
-       ListCell   *lc;
-
-       foreach(lc, rowMarks)
-       {
-               RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
+#define JUMBLE_NODE(item) \
+       _jumbleNode(jstate, (Node *) expr->item)
+#define JUMBLE_LOCATION(location) \
+       RecordConstLocation(jstate, expr->location)
+#define JUMBLE_FIELD(item) \
+       AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item))
+#define JUMBLE_FIELD_SINGLE(item) \
+       AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item))
+#define JUMBLE_STRING(str) \
+do { \
+       if (expr->str) \
+               AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \
+} while(0)
 
-               if (!rowmark->pushedDown)
-               {
-                       APP_JUMB(rowmark->rti);
-                       APP_JUMB(rowmark->strength);
-                       APP_JUMB(rowmark->waitPolicy);
-               }
-       }
-}
+#include "queryjumblefuncs.funcs.c"
 
-/*
- * Jumble an expression tree
- *
- * In general this function should handle all the same node types that
- * expression_tree_walker() does, and therefore it's coded to be as parallel
- * to that function as possible.  However, since we are only invoked on
- * queries immediately post-parse-analysis, we need not handle node types
- * that only appear in planning.
- *
- * Note: the reason we don't simply use expression_tree_walker() is that the
- * point of that function is to support tree walkers that don't care about
- * most tree node types, but here we care about all types.  We should complain
- * about any unrecognized node type.
- */
 static void
-JumbleExpr(JumbleState *jstate, Node *node)
+_jumbleNode(JumbleState *jstate, Node *node)
 {
-       ListCell   *temp;
+       Node       *expr = node;
 
-       if (node == NULL)
+       if (expr == NULL)
                return;
 
        /* Guard against stack overflow due to overly complex expressions */
@@ -372,495 +241,122 @@ JumbleExpr(JumbleState *jstate, Node *node)
         * We always emit the node's NodeTag, then any additional fields that are
         * considered significant, and then we recurse to any child nodes.
         */
-       APP_JUMB(node->type);
+       JUMBLE_FIELD(type);
 
-       switch (nodeTag(node))
+       switch (nodeTag(expr))
        {
-               case T_Var:
-                       {
-                               Var                *var = (Var *) node;
+#include "queryjumblefuncs.switch.c"
 
-                               APP_JUMB(var->varno);
-                               APP_JUMB(var->varattno);
-                               APP_JUMB(var->varlevelsup);
-
-                               /*
-                                * We can omit varnullingrels, because it's fully determined
-                                * by varno/varlevelsup plus the Var's query location.
-                                */
-                       }
+               case T_List:
+               case T_IntList:
+               case T_OidList:
+               case T_XidList:
+                       _jumbleList(jstate, expr);
                        break;
-               case T_Const:
-                       {
-                               Const      *c = (Const *) node;
 
-                               /* We jumble only the constant's type, not its value */
-                               APP_JUMB(c->consttype);
-                               /* Also, record its parse location for query normalization */
-                               RecordConstLocation(jstate, c->location);
-                       }
+               default:
+                       /* Only a warning, since we can stumble along anyway */
+                       elog(WARNING, "unrecognized node type: %d",
+                                (int) nodeTag(expr));
                        break;
+       }
+
+       /* Special cases to handle outside the automated code */
+       switch (nodeTag(expr))
+       {
                case T_Param:
                        {
                                Param      *p = (Param *) node;
 
-                               APP_JUMB(p->paramkind);
-                               APP_JUMB(p->paramid);
-                               APP_JUMB(p->paramtype);
-                               /* Also, track the highest external Param id */
+                               /*
+                                * Update the highest Param id seen, in order to start
+                                * normalization correctly.
+                                */
                                if (p->paramkind == PARAM_EXTERN &&
                                        p->paramid > jstate->highest_extern_param_id)
                                        jstate->highest_extern_param_id = p->paramid;
                        }
                        break;
-               case T_Aggref:
-                       {
-                               Aggref     *expr = (Aggref *) node;
-
-                               APP_JUMB(expr->aggfnoid);
-                               JumbleExpr(jstate, (Node *) expr->aggdirectargs);
-                               JumbleExpr(jstate, (Node *) expr->args);
-                               JumbleExpr(jstate, (Node *) expr->aggorder);
-                               JumbleExpr(jstate, (Node *) expr->aggdistinct);
-                               JumbleExpr(jstate, (Node *) expr->aggfilter);
-                       }
-                       break;
-               case T_GroupingFunc:
-                       {
-                               GroupingFunc *grpnode = (GroupingFunc *) node;
-
-                               JumbleExpr(jstate, (Node *) grpnode->refs);
-                               APP_JUMB(grpnode->agglevelsup);
-                       }
-                       break;
-               case T_WindowFunc:
-                       {
-                               WindowFunc *expr = (WindowFunc *) node;
-
-                               APP_JUMB(expr->winfnoid);
-                               APP_JUMB(expr->winref);
-                               JumbleExpr(jstate, (Node *) expr->args);
-                               JumbleExpr(jstate, (Node *) expr->aggfilter);
-                       }
-                       break;
-               case T_SubscriptingRef:
-                       {
-                               SubscriptingRef *sbsref = (SubscriptingRef *) node;
-
-                               JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
-                               JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
-                               JumbleExpr(jstate, (Node *) sbsref->refexpr);
-                               JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
-                       }
-                       break;
-               case T_FuncExpr:
-                       {
-                               FuncExpr   *expr = (FuncExpr *) node;
-
-                               APP_JUMB(expr->funcid);
-                               JumbleExpr(jstate, (Node *) expr->args);
-                       }
-                       break;
-               case T_NamedArgExpr:
-                       {
-                               NamedArgExpr *nae = (NamedArgExpr *) node;
-
-                               APP_JUMB(nae->argnumber);
-                               JumbleExpr(jstate, (Node *) nae->arg);
-                       }
-                       break;
-               case T_OpExpr:
-               case T_DistinctExpr:    /* struct-equivalent to OpExpr */
-               case T_NullIfExpr:              /* struct-equivalent to OpExpr */
-                       {
-                               OpExpr     *expr = (OpExpr *) node;
-
-                               APP_JUMB(expr->opno);
-                               JumbleExpr(jstate, (Node *) expr->args);
-                       }
-                       break;
-               case T_ScalarArrayOpExpr:
-                       {
-                               ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
-
-                               APP_JUMB(expr->opno);
-                               APP_JUMB(expr->useOr);
-                               JumbleExpr(jstate, (Node *) expr->args);
-                       }
-                       break;
-               case T_BoolExpr:
-                       {
-                               BoolExpr   *expr = (BoolExpr *) node;
-
-                               APP_JUMB(expr->boolop);
-                               JumbleExpr(jstate, (Node *) expr->args);
-                       }
-                       break;
-               case T_SubLink:
-                       {
-                               SubLink    *sublink = (SubLink *) node;
-
-                               APP_JUMB(sublink->subLinkType);
-                               APP_JUMB(sublink->subLinkId);
-                               JumbleExpr(jstate, (Node *) sublink->testexpr);
-                               JumbleQueryInternal(jstate, castNode(Query, sublink->subselect));
-                       }
-                       break;
-               case T_FieldSelect:
-                       {
-                               FieldSelect *fs = (FieldSelect *) node;
-
-                               APP_JUMB(fs->fieldnum);
-                               JumbleExpr(jstate, (Node *) fs->arg);
-                       }
-                       break;
-               case T_FieldStore:
-                       {
-                               FieldStore *fstore = (FieldStore *) node;
-
-                               JumbleExpr(jstate, (Node *) fstore->arg);
-                               JumbleExpr(jstate, (Node *) fstore->newvals);
-                       }
-                       break;
-               case T_RelabelType:
-                       {
-                               RelabelType *rt = (RelabelType *) node;
-
-                               APP_JUMB(rt->resulttype);
-                               JumbleExpr(jstate, (Node *) rt->arg);
-                       }
-                       break;
-               case T_CoerceViaIO:
-                       {
-                               CoerceViaIO *cio = (CoerceViaIO *) node;
-
-                               APP_JUMB(cio->resulttype);
-                               JumbleExpr(jstate, (Node *) cio->arg);
-                       }
-                       break;
-               case T_ArrayCoerceExpr:
-                       {
-                               ArrayCoerceExpr *acexpr = (ArrayCoerceExpr *) node;
-
-                               APP_JUMB(acexpr->resulttype);
-                               JumbleExpr(jstate, (Node *) acexpr->arg);
-                               JumbleExpr(jstate, (Node *) acexpr->elemexpr);
-                       }
-                       break;
-               case T_ConvertRowtypeExpr:
-                       {
-                               ConvertRowtypeExpr *crexpr = (ConvertRowtypeExpr *) node;
-
-                               APP_JUMB(crexpr->resulttype);
-                               JumbleExpr(jstate, (Node *) crexpr->arg);
-                       }
-                       break;
-               case T_CollateExpr:
-                       {
-                               CollateExpr *ce = (CollateExpr *) node;
-
-                               APP_JUMB(ce->collOid);
-                               JumbleExpr(jstate, (Node *) ce->arg);
-                       }
-                       break;
-               case T_CaseExpr:
-                       {
-                               CaseExpr   *caseexpr = (CaseExpr *) node;
-
-                               JumbleExpr(jstate, (Node *) caseexpr->arg);
-                               foreach(temp, caseexpr->args)
-                               {
-                                       CaseWhen   *when = lfirst_node(CaseWhen, temp);
-
-                                       JumbleExpr(jstate, (Node *) when->expr);
-                                       JumbleExpr(jstate, (Node *) when->result);
-                               }
-                               JumbleExpr(jstate, (Node *) caseexpr->defresult);
-                       }
-                       break;
-               case T_CaseTestExpr:
-                       {
-                               CaseTestExpr *ct = (CaseTestExpr *) node;
-
-                               APP_JUMB(ct->typeId);
-                       }
-                       break;
-               case T_ArrayExpr:
-                       JumbleExpr(jstate, (Node *) ((ArrayExpr *) node)->elements);
-                       break;
-               case T_RowExpr:
-                       JumbleExpr(jstate, (Node *) ((RowExpr *) node)->args);
-                       break;
-               case T_RowCompareExpr:
-                       {
-                               RowCompareExpr *rcexpr = (RowCompareExpr *) node;
-
-                               APP_JUMB(rcexpr->rctype);
-                               JumbleExpr(jstate, (Node *) rcexpr->largs);
-                               JumbleExpr(jstate, (Node *) rcexpr->rargs);
-                       }
-                       break;
-               case T_CoalesceExpr:
-                       JumbleExpr(jstate, (Node *) ((CoalesceExpr *) node)->args);
-                       break;
-               case T_MinMaxExpr:
-                       {
-                               MinMaxExpr *mmexpr = (MinMaxExpr *) node;
-
-                               APP_JUMB(mmexpr->op);
-                               JumbleExpr(jstate, (Node *) mmexpr->args);
-                       }
-                       break;
-               case T_XmlExpr:
-                       {
-                               XmlExpr    *xexpr = (XmlExpr *) node;
-
-                               APP_JUMB(xexpr->op);
-                               JumbleExpr(jstate, (Node *) xexpr->named_args);
-                               JumbleExpr(jstate, (Node *) xexpr->args);
-                       }
-                       break;
-               case T_NullTest:
-                       {
-                               NullTest   *nt = (NullTest *) node;
-
-                               APP_JUMB(nt->nulltesttype);
-                               JumbleExpr(jstate, (Node *) nt->arg);
-                       }
-                       break;
-               case T_BooleanTest:
-                       {
-                               BooleanTest *bt = (BooleanTest *) node;
-
-                               APP_JUMB(bt->booltesttype);
-                               JumbleExpr(jstate, (Node *) bt->arg);
-                       }
-                       break;
-               case T_CoerceToDomain:
-                       {
-                               CoerceToDomain *cd = (CoerceToDomain *) node;
-
-                               APP_JUMB(cd->resulttype);
-                               JumbleExpr(jstate, (Node *) cd->arg);
-                       }
-                       break;
-               case T_CoerceToDomainValue:
-                       {
-                               CoerceToDomainValue *cdv = (CoerceToDomainValue *) node;
-
-                               APP_JUMB(cdv->typeId);
-                       }
-                       break;
-               case T_SetToDefault:
-                       {
-                               SetToDefault *sd = (SetToDefault *) node;
-
-                               APP_JUMB(sd->typeId);
-                       }
-                       break;
-               case T_CurrentOfExpr:
-                       {
-                               CurrentOfExpr *ce = (CurrentOfExpr *) node;
-
-                               APP_JUMB(ce->cvarno);
-                               if (ce->cursor_name)
-                                       APP_JUMB_STRING(ce->cursor_name);
-                               APP_JUMB(ce->cursor_param);
-                       }
+               default:
                        break;
-               case T_NextValueExpr:
-                       {
-                               NextValueExpr *nve = (NextValueExpr *) node;
+       }
+}
 
-                               APP_JUMB(nve->seqid);
-                               APP_JUMB(nve->typeId);
-                       }
-                       break;
-               case T_InferenceElem:
-                       {
-                               InferenceElem *ie = (InferenceElem *) node;
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+       List       *expr = (List *) node;
+       ListCell   *l;
 
-                               APP_JUMB(ie->infercollid);
-                               APP_JUMB(ie->inferopclass);
-                               JumbleExpr(jstate, ie->expr);
-                       }
+       switch (expr->type)
+       {
+               case T_List:
+                       foreach(l, expr)
+                               _jumbleNode(jstate, lfirst(l));
                        break;
-               case T_TargetEntry:
-                       {
-                               TargetEntry *tle = (TargetEntry *) node;
-
-                               APP_JUMB(tle->resno);
-                               APP_JUMB(tle->ressortgroupref);
-                               JumbleExpr(jstate, (Node *) tle->expr);
-                       }
+               case T_IntList:
+                       foreach(l, expr)
+                               JUMBLE_FIELD_SINGLE(lfirst_int(l));
                        break;
-               case T_RangeTblRef:
-                       {
-                               RangeTblRef *rtr = (RangeTblRef *) node;
-
-                               APP_JUMB(rtr->rtindex);
-                       }
+               case T_OidList:
+                       foreach(l, expr)
+                               JUMBLE_FIELD_SINGLE(lfirst_oid(l));
                        break;
-               case T_JoinExpr:
-                       {
-                               JoinExpr   *join = (JoinExpr *) node;
-
-                               APP_JUMB(join->jointype);
-                               APP_JUMB(join->isNatural);
-                               APP_JUMB(join->rtindex);
-                               JumbleExpr(jstate, join->larg);
-                               JumbleExpr(jstate, join->rarg);
-                               JumbleExpr(jstate, join->quals);
-                       }
+               case T_XidList:
+                       foreach(l, expr)
+                               JUMBLE_FIELD_SINGLE(lfirst_xid(l));
                        break;
-               case T_FromExpr:
-                       {
-                               FromExpr   *from = (FromExpr *) node;
+               default:
+                       elog(ERROR, "unrecognized list node type: %d",
+                                (int) expr->type);
+                       return;
+       }
+}
 
-                               JumbleExpr(jstate, (Node *) from->fromlist);
-                               JumbleExpr(jstate, from->quals);
-                       }
-                       break;
-               case T_OnConflictExpr:
-                       {
-                               OnConflictExpr *conf = (OnConflictExpr *) node;
-
-                               APP_JUMB(conf->action);
-                               JumbleExpr(jstate, (Node *) conf->arbiterElems);
-                               JumbleExpr(jstate, conf->arbiterWhere);
-                               JumbleExpr(jstate, (Node *) conf->onConflictSet);
-                               JumbleExpr(jstate, conf->onConflictWhere);
-                               APP_JUMB(conf->constraint);
-                               APP_JUMB(conf->exclRelIndex);
-                               JumbleExpr(jstate, (Node *) conf->exclRelTlist);
-                       }
-                       break;
-               case T_MergeAction:
-                       {
-                               MergeAction *mergeaction = (MergeAction *) node;
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+       RangeTblEntry *expr = (RangeTblEntry *) node;
 
-                               APP_JUMB(mergeaction->matched);
-                               APP_JUMB(mergeaction->commandType);
-                               JumbleExpr(jstate, mergeaction->qual);
-                               JumbleExpr(jstate, (Node *) mergeaction->targetList);
-                       }
+       JUMBLE_FIELD(rtekind);
+       switch (expr->rtekind)
+       {
+               case RTE_RELATION:
+                       JUMBLE_FIELD(relid);
+                       JUMBLE_NODE(tablesample);
+                       JUMBLE_FIELD(inh);
                        break;
-               case T_List:
-                       foreach(temp, (List *) node)
-                       {
-                               JumbleExpr(jstate, (Node *) lfirst(temp));
-                       }
+               case RTE_SUBQUERY:
+                       JUMBLE_NODE(subquery);
                        break;
-               case T_IntList:
-                       foreach(temp, (List *) node)
-                       {
-                               APP_JUMB(lfirst_int(temp));
-                       }
+               case RTE_JOIN:
+                       JUMBLE_FIELD(jointype);
                        break;
-               case T_SortGroupClause:
-                       {
-                               SortGroupClause *sgc = (SortGroupClause *) node;
-
-                               APP_JUMB(sgc->tleSortGroupRef);
-                               APP_JUMB(sgc->eqop);
-                               APP_JUMB(sgc->sortop);
-                               APP_JUMB(sgc->nulls_first);
-                       }
+               case RTE_FUNCTION:
+                       JUMBLE_NODE(functions);
                        break;
-               case T_GroupingSet:
-                       {
-                               GroupingSet *gsnode = (GroupingSet *) node;
-
-                               JumbleExpr(jstate, (Node *) gsnode->content);
-                       }
+               case RTE_TABLEFUNC:
+                       JUMBLE_NODE(tablefunc);
                        break;
-               case T_WindowClause:
-                       {
-                               WindowClause *wc = (WindowClause *) node;
-
-                               APP_JUMB(wc->winref);
-                               APP_JUMB(wc->frameOptions);
-                               JumbleExpr(jstate, (Node *) wc->partitionClause);
-                               JumbleExpr(jstate, (Node *) wc->orderClause);
-                               JumbleExpr(jstate, wc->startOffset);
-                               JumbleExpr(jstate, wc->endOffset);
-                       }
+               case RTE_VALUES:
+                       JUMBLE_NODE(values_lists);
                        break;
-               case T_CommonTableExpr:
-                       {
-                               CommonTableExpr *cte = (CommonTableExpr *) node;
+               case RTE_CTE:
 
-                               /* we store the string name because RTE_CTE RTEs need it */
-                               APP_JUMB_STRING(cte->ctename);
-                               APP_JUMB(cte->ctematerialized);
-                               JumbleQueryInternal(jstate, castNode(Query, cte->ctequery));
-                       }
+                       /*
+                        * Depending on the CTE name here isn't ideal, but it's the only
+                        * info we have to identify the referenced WITH item.
+                        */
+                       JUMBLE_STRING(ctename);
+                       JUMBLE_FIELD(ctelevelsup);
                        break;
-               case T_SetOperationStmt:
-                       {
-                               SetOperationStmt *setop = (SetOperationStmt *) node;
-
-                               APP_JUMB(setop->op);
-                               APP_JUMB(setop->all);
-                               JumbleExpr(jstate, setop->larg);
-                               JumbleExpr(jstate, setop->rarg);
-                       }
+               case RTE_NAMEDTUPLESTORE:
+                       JUMBLE_STRING(enrname);
                        break;
-               case T_RangeTblFunction:
-                       {
-                               RangeTblFunction *rtfunc = (RangeTblFunction *) node;
-
-                               JumbleExpr(jstate, rtfunc->funcexpr);
-                       }
-                       break;
-               case T_TableFunc:
-                       {
-                               TableFunc  *tablefunc = (TableFunc *) node;
-
-                               JumbleExpr(jstate, tablefunc->docexpr);
-                               JumbleExpr(jstate, tablefunc->rowexpr);
-                               JumbleExpr(jstate, (Node *) tablefunc->colexprs);
-                       }
-                       break;
-               case T_TableSampleClause:
-                       {
-                               TableSampleClause *tsc = (TableSampleClause *) node;
-
-                               APP_JUMB(tsc->tsmhandler);
-                               JumbleExpr(jstate, (Node *) tsc->args);
-                               JumbleExpr(jstate, (Node *) tsc->repeatable);
-                       }
+               case RTE_RESULT:
                        break;
                default:
-                       /* Only a warning, since we can stumble along anyway */
-                       elog(WARNING, "unrecognized node type: %d",
-                                (int) nodeTag(node));
+                       elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
                        break;
        }
 }
-
-/*
- * Record location of constant within query string of query tree
- * that is currently being walked.
- */
-static void
-RecordConstLocation(JumbleState *jstate, int location)
-{
-       /* -1 indicates unknown or undefined location */
-       if (location >= 0)
-       {
-               /* enlarge array if needed */
-               if (jstate->clocations_count >= jstate->clocations_buf_size)
-               {
-                       jstate->clocations_buf_size *= 2;
-                       jstate->clocations = (LocationLen *)
-                               repalloc(jstate->clocations,
-                                                jstate->clocations_buf_size *
-                                                sizeof(LocationLen));
-               }
-               jstate->clocations[jstate->clocations_count].location = location;
-               /* initialize lengths to -1 to simplify third-party module usage */
-               jstate->clocations[jstate->clocations_count].length = -1;
-               jstate->clocations_count++;
-       }
-}
index 0dca6bc5facfd9c530fcb178c055eae42d4b6cb1..3d2225e1ae05240945ed351beb6f499b1fe06218 100644 (file)
@@ -50,7 +50,7 @@ typedef int32 signedbitmapword; /* must be the matching signed type */
 
 typedef struct Bitmapset
 {
-       pg_node_attr(custom_copy_equal, special_read_write)
+       pg_node_attr(custom_copy_equal, special_read_write, no_query_jumble)
 
        NodeTag         type;
        int                     nwords;                 /* number of words in array */
index 10752e8011c7ec4fd8957b4885258bd6cf5e9b2d..75dfe1919d75a4c91c3cc68d0f3049a80234375c 100644 (file)
@@ -53,16 +53,20 @@ typedef enum NodeTag
  * - custom_read_write: Has custom implementations in outfuncs.c and
  *   readfuncs.c.
  *
+ * - custom_query_jumble: Has custom implementation in queryjumblefuncs.c.
+ *
  * - no_copy: Does not support copyObject() at all.
  *
  * - no_equal: Does not support equal() at all.
  *
  * - no_copy_equal: Shorthand for both no_copy and no_equal.
  *
+ * - no_query_jumble: Does not support JumbleQuery() at all.
+ *
  * - no_read: Does not support nodeRead() at all.
  *
- * - nodetag_only: Does not support copyObject(), equal(), outNode(),
- *   or nodeRead().
+ * - nodetag_only: Does not support copyObject(), equal(), jumbleQuery()
+ *   outNode() or nodeRead().
  *
  * - special_read_write: Has special treatment in outNode() and nodeRead().
  *
@@ -97,6 +101,13 @@ typedef enum NodeTag
  * - equal_ignore_if_zero: Ignore the field for equality if it is zero.
  *   (Otherwise, compare normally.)
  *
+ * - query_jumble_ignore: Ignore the field for the query jumbling.  Note
+ *   that typmod and collation information are usually irrelevant for the
+ *   query jumbling.
+ *
+ * - query_jumble_location: Mark the field as a location to track.  This is
+ *   only allowed for integer fields that include "location" in their name.
+ *
  * - read_as(VALUE): In nodeRead(), replace the field's value with VALUE.
  *
  * - read_write_ignore: Ignore the field for read/write.  This is only allowed
index fbbbe647a43b97db5bf10a5235f3d43692cc6696..3d67787e7af06925d3802ec1d63c49341f38bf3b 100644 (file)
@@ -116,6 +116,11 @@ typedef uint64 AclMode;                    /* a bitmask of privilege bits */
  *
  *       Planning converts a Query tree into a Plan tree headed by a PlannedStmt
  *       node --- the Query structure is not used by the executor.
+ *
+ *       All the fields ignored for the query jumbling are not semantically
+ *       significant (such as alias names), as is ignored anything that can
+ *       be deduced from child nodes (else we'd just be double-hashing that
+ *       piece of information).
  */
 typedef struct Query
 {
@@ -124,45 +129,47 @@ typedef struct Query
        CmdType         commandType;    /* select|insert|update|delete|merge|utility */
 
        /* where did I come from? */
-       QuerySource querySource;
+       QuerySource querySource pg_node_attr(query_jumble_ignore);
 
        /*
         * query identifier (can be set by plugins); ignored for equal, as it
-        * might not be set; also not stored
+        * might not be set; also not stored.  This is the result of the query
+        * jumble, hence ignored.
         */
-       uint64          queryId pg_node_attr(equal_ignore, read_write_ignore, read_as(0));
+       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;
+       bool            canSetTag pg_node_attr(query_jumble_ignore);
 
        Node       *utilityStmt;        /* non-null if commandType == CMD_UTILITY */
 
        /*
         * rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
-        * SELECT.
+        * SELECT.  This is ignored in the query jumble as unrelated to the
+        * compilation of the query ID.
         */
-       int                     resultRelation;
+       int                     resultRelation pg_node_attr(query_jumble_ignore);
 
        /* has aggregates in tlist or havingQual */
-       bool            hasAggs;
+       bool            hasAggs pg_node_attr(query_jumble_ignore);
        /* has window functions in tlist */
-       bool            hasWindowFuncs;
+       bool            hasWindowFuncs pg_node_attr(query_jumble_ignore);
        /* has set-returning functions in tlist */
-       bool            hasTargetSRFs;
+       bool            hasTargetSRFs pg_node_attr(query_jumble_ignore);
        /* has subquery SubLink */
-       bool            hasSubLinks;
+       bool            hasSubLinks pg_node_attr(query_jumble_ignore);
        /* distinctClause is from DISTINCT ON */
-       bool            hasDistinctOn;
+       bool            hasDistinctOn pg_node_attr(query_jumble_ignore);
        /* WITH RECURSIVE was specified */
-       bool            hasRecursive;
+       bool            hasRecursive pg_node_attr(query_jumble_ignore);
        /* has INSERT/UPDATE/DELETE in WITH */
-       bool            hasModifyingCTE;
+       bool            hasModifyingCTE pg_node_attr(query_jumble_ignore);
        /* FOR [KEY] UPDATE/SHARE was specified */
-       bool            hasForUpdate;
+       bool            hasForUpdate pg_node_attr(query_jumble_ignore);
        /* rewriter has applied some RLS policy */
-       bool            hasRowSecurity;
+       bool            hasRowSecurity pg_node_attr(query_jumble_ignore);
        /* is a RETURN statement */
-       bool            isReturn;
+       bool            isReturn pg_node_attr(query_jumble_ignore);
 
        List       *cteList;            /* WITH list (of CommonTableExpr's) */
 
@@ -172,18 +179,18 @@ typedef struct Query
         * list of RTEPermissionInfo nodes for the rtable entries having
         * perminfoindex > 0
         */
-       List       *rteperminfos;
+       List       *rteperminfos pg_node_attr(query_jumble_ignore);
        FromExpr   *jointree;           /* table join tree (FROM and WHERE clauses);
                                                                 * also USING clause for MERGE */
 
        List       *mergeActionList;    /* list of actions for MERGE (only) */
        /* whether to use outer join */
-       bool            mergeUseOuterJoin;
+       bool            mergeUseOuterJoin pg_node_attr(query_jumble_ignore);
 
        List       *targetList;         /* target list (of TargetEntry) */
 
        /* OVERRIDING clause */
-       OverridingKind override;
+       OverridingKind override pg_node_attr(query_jumble_ignore);
 
        OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
 
@@ -215,10 +222,10 @@ typedef struct Query
         * A list of pg_constraint OIDs that the query depends on to be
         * semantically valid
         */
-       List       *constraintDeps;
+       List       *constraintDeps pg_node_attr(query_jumble_ignore);
 
        /* a list of WithCheckOption's (added during rewrite) */
-       List       *withCheckOptions;
+       List       *withCheckOptions pg_node_attr(query_jumble_ignore);
 
        /*
         * The following two fields identify the portion of the source text string
@@ -229,7 +236,7 @@ typedef struct Query
        /* start location, or -1 if unknown */
        int                     stmt_location;
        /* length in bytes; 0 means "rest of string" */
-       int                     stmt_len;
+       int                     stmt_len pg_node_attr(query_jumble_ignore);
 } Query;
 
 
@@ -1019,7 +1026,7 @@ typedef enum RTEKind
 
 typedef struct RangeTblEntry
 {
-       pg_node_attr(custom_read_write)
+       pg_node_attr(custom_read_write, custom_query_jumble)
 
        NodeTag         type;
 
@@ -1258,6 +1265,8 @@ typedef struct RTEPermissionInfo
  * time.  We do however remember how many columns we thought the type had
  * (including dropped columns!), so that we can successfully ignore any
  * columns added after the query was parsed.
+ *
+ * The query jumbling only needs to track the function expression.
  */
 typedef struct RangeTblFunction
 {
@@ -1265,20 +1274,20 @@ typedef struct RangeTblFunction
 
        Node       *funcexpr;           /* expression tree for func call */
        /* number of columns it contributes to RTE */
-       int                     funccolcount;
+       int                     funccolcount pg_node_attr(query_jumble_ignore);
        /* These fields record the contents of a column definition list, if any: */
        /* column names (list of String) */
-       List       *funccolnames;
+       List       *funccolnames pg_node_attr(query_jumble_ignore);
        /* OID list of column type OIDs */
-       List       *funccoltypes;
+       List       *funccoltypes pg_node_attr(query_jumble_ignore);
        /* integer list of column typmods */
-       List       *funccoltypmods;
+       List       *funccoltypmods pg_node_attr(query_jumble_ignore);
        /* OID list of column collation OIDs */
-       List       *funccolcollations;
+       List       *funccolcollations pg_node_attr(query_jumble_ignore);
 
        /* This is set during planning for use by the executor: */
        /* PARAM_EXEC Param IDs affecting this func */
-       Bitmapset  *funcparams;
+       Bitmapset  *funcparams pg_node_attr(query_jumble_ignore);
 } RangeTblFunction;
 
 /*
@@ -1386,7 +1395,7 @@ typedef struct SortGroupClause
        Oid                     sortop;                 /* the ordering operator ('<' op), or 0 */
        bool            nulls_first;    /* do NULLs come before normal values? */
        /* can eqop be implemented by hashing? */
-       bool            hashable;
+       bool            hashable pg_node_attr(query_jumble_ignore);
 } SortGroupClause;
 
 /*
@@ -1451,7 +1460,7 @@ typedef enum GroupingSetKind
 typedef struct GroupingSet
 {
        NodeTag         type;
-       GroupingSetKind kind;
+       GroupingSetKind kind pg_node_attr(query_jumble_ignore);
        List       *content;
        int                     location;
 } GroupingSet;
@@ -1472,14 +1481,17 @@ typedef struct GroupingSet
  * When refname isn't null, the partitionClause is always copied from there;
  * the orderClause might or might not be copied (see copiedOrder); the framing
  * options are never copied, per spec.
+ *
+ * The information relevant for the query jumbling is the partition clause
+ * type and its bounds.
  */
 typedef struct WindowClause
 {
        NodeTag         type;
        /* window name (NULL in an OVER clause) */
-       char       *name;
+       char       *name pg_node_attr(query_jumble_ignore);
        /* referenced window name, if any */
-       char       *refname;
+       char       *refname pg_node_attr(query_jumble_ignore);
        List       *partitionClause;    /* PARTITION BY list */
        /* ORDER BY list */
        List       *orderClause;
@@ -1487,20 +1499,20 @@ typedef struct WindowClause
        Node       *startOffset;        /* expression for starting bound, if any */
        Node       *endOffset;          /* expression for ending bound, if any */
        /* qual to help short-circuit execution */
-       List       *runCondition;
+       List       *runCondition pg_node_attr(query_jumble_ignore);
        /* in_range function for startOffset */
-       Oid                     startInRangeFunc;
+       Oid                     startInRangeFunc pg_node_attr(query_jumble_ignore);
        /* in_range function for endOffset */
-       Oid                     endInRangeFunc;
+       Oid                     endInRangeFunc pg_node_attr(query_jumble_ignore);
        /* collation for in_range tests */
-       Oid                     inRangeColl;
+       Oid                     inRangeColl pg_node_attr(query_jumble_ignore);
        /* use ASC sort order for in_range tests? */
-       bool            inRangeAsc;
+       bool            inRangeAsc pg_node_attr(query_jumble_ignore);
        /* nulls sort first for in_range tests? */
-       bool            inRangeNullsFirst;
+       bool            inRangeNullsFirst pg_node_attr(query_jumble_ignore);
        Index           winref;                 /* ID referenced by window functions */
        /* did we copy orderClause from refname? */
-       bool            copiedOrder;
+       bool            copiedOrder pg_node_attr(query_jumble_ignore);
 } WindowClause;
 
 /*
@@ -1610,31 +1622,37 @@ typedef struct CTECycleClause
 typedef struct CommonTableExpr
 {
        NodeTag         type;
-       char       *ctename;            /* query name (never qualified) */
-       List       *aliascolnames;      /* optional list of column names */
+
+       /*
+        * Query name (never qualified).  The string name is included in the query
+        * jumbling because RTE_CTE RTEs need it.
+        */
+       char       *ctename;
+       /* optional list of column names */
+       List       *aliascolnames pg_node_attr(query_jumble_ignore);
        CTEMaterialize ctematerialized; /* is this an optimization fence? */
        /* SelectStmt/InsertStmt/etc before parse analysis, Query afterwards: */
        Node       *ctequery;           /* the CTE's subquery */
-       CTESearchClause *search_clause;
-       CTECycleClause *cycle_clause;
+       CTESearchClause *search_clause pg_node_attr(query_jumble_ignore);
+       CTECycleClause *cycle_clause pg_node_attr(query_jumble_ignore);
        int                     location;               /* token location, or -1 if unknown */
        /* These fields are set during parse analysis: */
        /* is this CTE actually recursive? */
-       bool            cterecursive;
+       bool            cterecursive pg_node_attr(query_jumble_ignore);
 
        /*
         * Number of RTEs referencing this CTE (excluding internal
-        * self-references)
+        * self-references), irrelevant for query jumbling.
         */
-       int                     cterefcount;
+       int                     cterefcount pg_node_attr(query_jumble_ignore);
        /* list of output column names */
-       List       *ctecolnames;
+       List       *ctecolnames pg_node_attr(query_jumble_ignore);
        /* OID list of output column type OIDs */
-       List       *ctecoltypes;
+       List       *ctecoltypes pg_node_attr(query_jumble_ignore);
        /* integer list of output column typmods */
-       List       *ctecoltypmods;
+       List       *ctecoltypmods pg_node_attr(query_jumble_ignore);
        /* OID list of column collation OIDs */
-       List       *ctecolcollations;
+       List       *ctecolcollations pg_node_attr(query_jumble_ignore);
 } CommonTableExpr;
 
 /* Convenience macro to get the output tlist of a CTE's query */
@@ -1672,11 +1690,11 @@ typedef struct MergeAction
        bool            matched;                /* true=MATCHED, false=NOT MATCHED */
        CmdType         commandType;    /* INSERT/UPDATE/DELETE/DO NOTHING */
        /* OVERRIDING clause */
-       OverridingKind override;
+       OverridingKind override pg_node_attr(query_jumble_ignore);
        Node       *qual;                       /* transformed WHEN conditions */
        List       *targetList;         /* the target list (of TargetEntry) */
        /* target attribute numbers of an UPDATE */
-       List       *updateColnos;
+       List       *updateColnos pg_node_attr(query_jumble_ignore);
 } MergeAction;
 
 /*
@@ -1885,15 +1903,15 @@ typedef struct SetOperationStmt
        Node       *rarg;                       /* right child */
        /* Eventually add fields for CORRESPONDING spec here */
 
-       /* Fields derived during parse analysis: */
+       /* Fields derived during parse analysis (irrelevant for query jumbling): */
        /* OID list of output column type OIDs */
-       List       *colTypes;
+       List       *colTypes pg_node_attr(query_jumble_ignore);
        /* integer list of output column typmods */
-       List       *colTypmods;
+       List       *colTypmods pg_node_attr(query_jumble_ignore);
        /* OID list of output column collation OIDs */
-       List       *colCollations;
+       List       *colCollations pg_node_attr(query_jumble_ignore);
        /* a list of SortGroupClause's */
-       List       *groupClauses;
+       List       *groupClauses pg_node_attr(query_jumble_ignore);
        /* groupClauses is NIL if UNION ALL, but must be set otherwise */
 } SetOperationStmt;
 
index 6c96fa2f5165c3d8063b889bde4c30e06a62a213..6d740be5c0a47953043f66fa63f763677dc25d89 100644 (file)
@@ -96,29 +96,29 @@ typedef struct TableFunc
 {
        NodeTag         type;
        /* list of namespace URI expressions */
-       List       *ns_uris;
+       List       *ns_uris pg_node_attr(query_jumble_ignore);
        /* list of namespace names or NULL */
-       List       *ns_names;
+       List       *ns_names pg_node_attr(query_jumble_ignore);
        /* input document expression */
        Node       *docexpr;
        /* row filter expression */
        Node       *rowexpr;
        /* column names (list of String) */
-       List       *colnames;
+       List       *colnames pg_node_attr(query_jumble_ignore);
        /* OID list of column type OIDs */
-       List       *coltypes;
+       List       *coltypes pg_node_attr(query_jumble_ignore);
        /* integer list of column typmods */
-       List       *coltypmods;
+       List       *coltypmods pg_node_attr(query_jumble_ignore);
        /* OID list of column collation OIDs */
-       List       *colcollations;
+       List       *colcollations pg_node_attr(query_jumble_ignore);
        /* list of column filter expressions */
        List       *colexprs;
        /* list of column default expressions */
-       List       *coldefexprs;
+       List       *coldefexprs pg_node_attr(query_jumble_ignore);
        /* nullability flag for each output column */
-       Bitmapset  *notnulls;
+       Bitmapset  *notnulls pg_node_attr(query_jumble_ignore);
        /* counts from 0; -1 if none specified */
-       int                     ordinalitycol;
+       int                     ordinalitycol pg_node_attr(query_jumble_ignore);
        /* token location, or -1 if unknown */
        int                     location;
 } TableFunc;
@@ -235,13 +235,18 @@ typedef struct Var
        AttrNumber      varattno;
 
        /* pg_type OID for the type of this var */
-       Oid                     vartype;
+       Oid                     vartype pg_node_attr(query_jumble_ignore);
        /* pg_attribute typmod value */
-       int32           vartypmod;
+       int32           vartypmod pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     varcollid;
-       /* RT indexes of outer joins that can replace the Var's value with null */
-       Bitmapset  *varnullingrels;
+       Oid                     varcollid pg_node_attr(query_jumble_ignore);
+
+       /*
+        * RT indexes of outer joins that can replace the Var's value with null.
+        * We can omit varnullingrels in the query jumble, because it's fully
+        * determined by varno/varlevelsup plus the Var's query location.
+        */
+       Bitmapset  *varnullingrels pg_node_attr(query_jumble_ignore);
 
        /*
         * for subquery variables referencing outer relations; 0 in a normal var,
@@ -255,9 +260,9 @@ typedef struct Var
         * their varno/varattno match.
         */
        /* syntactic relation index (0 if unknown) */
-       Index           varnosyn pg_node_attr(equal_ignore);
+       Index           varnosyn pg_node_attr(equal_ignore, query_jumble_ignore);
        /* syntactic attribute number */
-       AttrNumber      varattnosyn pg_node_attr(equal_ignore);
+       AttrNumber      varattnosyn pg_node_attr(equal_ignore, query_jumble_ignore);
 
        /* token location, or -1 if unknown */
        int                     location;
@@ -270,6 +275,8 @@ typedef struct Var
  * must be in non-extended form (4-byte header, no compression or external
  * references).  This ensures that the Const node is self-contained and makes
  * it more likely that equal() will see logically identical values as equal.
+ *
+ * Only the constant type OID is relevant for the query jumbling.
  */
 typedef struct Const
 {
@@ -279,24 +286,28 @@ typedef struct Const
        /* pg_type OID of the constant's datatype */
        Oid                     consttype;
        /* typmod value, if any */
-       int32           consttypmod;
+       int32           consttypmod pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     constcollid;
+       Oid                     constcollid pg_node_attr(query_jumble_ignore);
        /* typlen of the constant's datatype */
-       int                     constlen;
+       int                     constlen pg_node_attr(query_jumble_ignore);
        /* the constant's value */
-       Datum           constvalue;
+       Datum           constvalue pg_node_attr(query_jumble_ignore);
        /* whether the constant is null (if true, constvalue is undefined) */
-       bool            constisnull;
+       bool            constisnull pg_node_attr(query_jumble_ignore);
 
        /*
         * Whether this datatype is passed by value.  If true, then all the
         * information is stored in the Datum.  If false, then the Datum contains
         * a pointer to the information.
         */
-       bool            constbyval;
-       /* token location, or -1 if unknown */
-       int                     location;
+       bool            constbyval pg_node_attr(query_jumble_ignore);
+
+       /*
+        * token location, or -1 if unknown.  All constants are tracked as
+        * locations in query jumbling, to be marked as parameters.
+        */
+       int                     location pg_node_attr(query_jumble_location);
 } Const;
 
 /*
@@ -341,9 +352,9 @@ typedef struct Param
        int                     paramid;                /* numeric ID for parameter */
        Oid                     paramtype;              /* pg_type OID of parameter's datatype */
        /* typmod value, if known */
-       int32           paramtypmod;
+       int32           paramtypmod pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     paramcollid;
+       Oid                     paramcollid pg_node_attr(query_jumble_ignore);
        /* token location, or -1 if unknown */
        int                     location;
 } Param;
@@ -396,6 +407,9 @@ typedef struct Param
  * and can share the result.  Aggregates with same 'transno' but different
  * 'aggno' can share the same transition state, only the final function needs
  * to be called separately.
+ *
+ * Information related to collations, transition types and internal states
+ * are irrelevant for the query jumbling.
  */
 typedef struct Aggref
 {
@@ -405,22 +419,22 @@ typedef struct Aggref
        Oid                     aggfnoid;
 
        /* type Oid of result of the aggregate */
-       Oid                     aggtype;
+       Oid                     aggtype pg_node_attr(query_jumble_ignore);
 
        /* OID of collation of result */
-       Oid                     aggcollid;
+       Oid                     aggcollid pg_node_attr(query_jumble_ignore);
 
        /* OID of collation that function should use */
-       Oid                     inputcollid;
+       Oid                     inputcollid pg_node_attr(query_jumble_ignore);
 
        /*
         * type Oid of aggregate's transition value; ignored for equal since it
         * might not be set yet
         */
-       Oid                     aggtranstype pg_node_attr(equal_ignore);
+       Oid                     aggtranstype pg_node_attr(equal_ignore, query_jumble_ignore);
 
        /* type Oids of direct and aggregated args */
-       List       *aggargtypes;
+       List       *aggargtypes pg_node_attr(query_jumble_ignore);
 
        /* direct arguments, if an ordered-set agg */
        List       *aggdirectargs;
@@ -438,31 +452,31 @@ typedef struct Aggref
        Expr       *aggfilter;
 
        /* true if argument list was really '*' */
-       bool            aggstar;
+       bool            aggstar pg_node_attr(query_jumble_ignore);
 
        /*
         * true if variadic arguments have been combined into an array last
         * argument
         */
-       bool            aggvariadic;
+       bool            aggvariadic pg_node_attr(query_jumble_ignore);
 
        /* aggregate kind (see pg_aggregate.h) */
-       char            aggkind;
+       char            aggkind pg_node_attr(query_jumble_ignore);
 
        /* aggregate input already sorted */
-       bool            aggpresorted pg_node_attr(equal_ignore);
+       bool            aggpresorted pg_node_attr(equal_ignore, query_jumble_ignore);
 
        /* > 0 if agg belongs to outer query */
-       Index           agglevelsup;
+       Index           agglevelsup pg_node_attr(query_jumble_ignore);
 
        /* expected agg-splitting mode of parent Agg */
-       AggSplit        aggsplit;
+       AggSplit        aggsplit pg_node_attr(query_jumble_ignore);
 
        /* unique ID within the Agg node */
-       int                     aggno;
+       int                     aggno pg_node_attr(query_jumble_ignore);
 
        /* unique ID of transition state in the Agg */
-       int                     aggtransno;
+       int                     aggtransno pg_node_attr(query_jumble_ignore);
 
        /* token location, or -1 if unknown */
        int                     location;
@@ -491,19 +505,22 @@ typedef struct Aggref
  *
  * In raw parse output we have only the args list; parse analysis fills in the
  * refs list, and the planner fills in the cols list.
+ *
+ * All the fields used as information for an internal state are irrelevant
+ * for the query jumbling.
  */
 typedef struct GroupingFunc
 {
        Expr            xpr;
 
        /* arguments, not evaluated but kept for benefit of EXPLAIN etc. */
-       List       *args;
+       List       *args pg_node_attr(query_jumble_ignore);
 
        /* ressortgrouprefs of arguments */
        List       *refs pg_node_attr(equal_ignore);
 
        /* actual column positions set by planner */
-       List       *cols pg_node_attr(equal_ignore);
+       List       *cols pg_node_attr(equal_ignore, query_jumble_ignore);
 
        /* same as Aggref.agglevelsup */
        Index           agglevelsup;
@@ -514,6 +531,9 @@ typedef struct GroupingFunc
 
 /*
  * WindowFunc
+ *
+ * Collation information is irrelevant for the query jumbling, as is the
+ * internal state information of the node like "winstar" and "winagg".
  */
 typedef struct WindowFunc
 {
@@ -521,11 +541,11 @@ typedef struct WindowFunc
        /* pg_proc Oid of the function */
        Oid                     winfnoid;
        /* type Oid of result of the window function */
-       Oid                     wintype;
+       Oid                     wintype pg_node_attr(query_jumble_ignore);
        /* OID of collation of result */
-       Oid                     wincollid;
+       Oid                     wincollid pg_node_attr(query_jumble_ignore);
        /* OID of collation that function should use */
-       Oid                     inputcollid;
+       Oid                     inputcollid pg_node_attr(query_jumble_ignore);
        /* arguments to the window function */
        List       *args;
        /* FILTER expression, if any */
@@ -533,9 +553,9 @@ typedef struct WindowFunc
        /* index of associated WindowClause */
        Index           winref;
        /* true if argument list was really '*' */
-       bool            winstar;
+       bool            winstar pg_node_attr(query_jumble_ignore);
        /* is function a simple aggregate? */
-       bool            winagg;
+       bool            winagg pg_node_attr(query_jumble_ignore);
        /* token location, or -1 if unknown */
        int                     location;
 } WindowFunc;
@@ -574,6 +594,8 @@ typedef struct WindowFunc
  * subscripting logic.  Likewise, reftypmod and refcollid will match the
  * container's properties in a store, but could be different in a fetch.
  *
+ * Any internal state data is ignored for the query jumbling.
+ *
  * Note: for the cases where a container is returned, if refexpr yields a R/W
  * expanded container, then the implementation is allowed to modify that
  * object in-place and return the same object.
@@ -582,15 +604,15 @@ typedef struct SubscriptingRef
 {
        Expr            xpr;
        /* type of the container proper */
-       Oid                     refcontainertype;
+       Oid                     refcontainertype pg_node_attr(query_jumble_ignore);
        /* the container type's pg_type.typelem */
-       Oid                     refelemtype;
+       Oid                     refelemtype pg_node_attr(query_jumble_ignore);
        /* type of the SubscriptingRef's result */
-       Oid                     refrestype;
+       Oid                     refrestype pg_node_attr(query_jumble_ignore);
        /* typmod of the result */
-       int32           reftypmod;
+       int32           reftypmod pg_node_attr(query_jumble_ignore);
        /* collation of result, or InvalidOid if none */
-       Oid                     refcollid;
+       Oid                     refcollid pg_node_attr(query_jumble_ignore);
        /* expressions that evaluate to upper container indexes */
        List       *refupperindexpr;
 
@@ -641,6 +663,9 @@ typedef enum CoercionForm
 
 /*
  * FuncExpr - expression node for a function call
+ *
+ * Collation information is irrelevant for the query jumbling, only the
+ * arguments and the function OID matter.
  */
 typedef struct FuncExpr
 {
@@ -648,21 +673,21 @@ typedef struct FuncExpr
        /* PG_PROC OID of the function */
        Oid                     funcid;
        /* PG_TYPE OID of result value */
-       Oid                     funcresulttype;
+       Oid                     funcresulttype pg_node_attr(query_jumble_ignore);
        /* true if function returns set */
-       bool            funcretset;
+       bool            funcretset pg_node_attr(query_jumble_ignore);
 
        /*
         * true if variadic arguments have been combined into an array last
         * argument
         */
-       bool            funcvariadic;
+       bool            funcvariadic pg_node_attr(query_jumble_ignore);
        /* how to display this function call */
-       CoercionForm funcformat;
+       CoercionForm funcformat pg_node_attr(query_jumble_ignore);
        /* OID of collation of result */
-       Oid                     funccollid;
+       Oid                     funccollid pg_node_attr(query_jumble_ignore);
        /* OID of collation that function should use */
-       Oid                     inputcollid;
+       Oid                     inputcollid pg_node_attr(query_jumble_ignore);
        /* arguments to the function */
        List       *args;
        /* token location, or -1 if unknown */
@@ -689,7 +714,7 @@ typedef struct NamedArgExpr
        /* the argument expression */
        Expr       *arg;
        /* the name */
-       char       *name;
+       char       *name pg_node_attr(query_jumble_ignore);
        /* argument's number in positional notation */
        int                     argnumber;
        /* argument name location, or -1 if unknown */
@@ -705,6 +730,9 @@ typedef struct NamedArgExpr
  * of the node.  The planner makes sure it is valid before passing the node
  * tree to the executor, but during parsing/planning opfuncid can be 0.
  * Therefore, equal() will accept a zero value as being equal to other values.
+ *
+ * Internal state information and collation data is irrelevant for the query
+ * jumbling.
  */
 typedef struct OpExpr
 {
@@ -714,19 +742,19 @@ typedef struct OpExpr
        Oid                     opno;
 
        /* PG_PROC OID of underlying function */
-       Oid                     opfuncid pg_node_attr(equal_ignore_if_zero);
+       Oid                     opfuncid pg_node_attr(equal_ignore_if_zero, query_jumble_ignore);
 
        /* PG_TYPE OID of result value */
-       Oid                     opresulttype;
+       Oid                     opresulttype pg_node_attr(query_jumble_ignore);
 
        /* true if operator returns set */
-       bool            opretset;
+       bool            opretset pg_node_attr(query_jumble_ignore);
 
        /* OID of collation of result */
-       Oid                     opcollid;
+       Oid                     opcollid pg_node_attr(query_jumble_ignore);
 
        /* OID of collation that operator should use */
-       Oid                     inputcollid;
+       Oid                     inputcollid pg_node_attr(query_jumble_ignore);
 
        /* arguments to the operator (1 or 2) */
        List       *args;
@@ -782,6 +810,9 @@ typedef OpExpr NullIfExpr;
  * Similar to OpExpr, opfuncid, hashfuncid, and negfuncid are not necessarily
  * filled in right away, so will be ignored for equality if they are not set
  * yet.
+ *
+ * OID entries of the internal function types are irrelevant for the query
+ * jumbling, but the operator OID and the arguments are.
  */
 typedef struct ScalarArrayOpExpr
 {
@@ -791,19 +822,19 @@ typedef struct ScalarArrayOpExpr
        Oid                     opno;
 
        /* PG_PROC OID of comparison function */
-       Oid                     opfuncid pg_node_attr(equal_ignore_if_zero);
+       Oid                     opfuncid pg_node_attr(equal_ignore_if_zero, query_jumble_ignore);
 
        /* PG_PROC OID of hash func or InvalidOid */
-       Oid                     hashfuncid pg_node_attr(equal_ignore_if_zero);
+       Oid                     hashfuncid pg_node_attr(equal_ignore_if_zero, query_jumble_ignore);
 
        /* PG_PROC OID of negator of opfuncid function or InvalidOid.  See above */
-       Oid                     negfuncid pg_node_attr(equal_ignore_if_zero);
+       Oid                     negfuncid pg_node_attr(equal_ignore_if_zero, query_jumble_ignore);
 
        /* true for ANY, false for ALL */
        bool            useOr;
 
        /* OID of collation that operator should use */
-       Oid                     inputcollid;
+       Oid                     inputcollid pg_node_attr(query_jumble_ignore);
 
        /* the scalar and array operands */
        List       *args;
@@ -905,7 +936,7 @@ typedef struct SubLink
        int                     subLinkId;              /* ID (1..n); 0 if not MULTIEXPR */
        Node       *testexpr;           /* outer-query test for ALL/ANY/ROWCOMPARE */
        /* originally specified operator name */
-       List       *operName;
+       List       *operName pg_node_attr(query_jumble_ignore);
        /* subselect as Query* or raw parsetree */
        Node       *subselect;
        int                     location;               /* token location, or -1 if unknown */
@@ -1017,11 +1048,11 @@ typedef struct FieldSelect
        Expr       *arg;                        /* input expression */
        AttrNumber      fieldnum;               /* attribute number of field to extract */
        /* type of the field (result type of this node) */
-       Oid                     resulttype;
+       Oid                     resulttype pg_node_attr(query_jumble_ignore);
        /* output typmod (usually -1) */
-       int32           resulttypmod;
+       int32           resulttypmod pg_node_attr(query_jumble_ignore);
        /* OID of collation of the field */
-       Oid                     resultcollid;
+       Oid                     resultcollid pg_node_attr(query_jumble_ignore);
 } FieldSelect;
 
 /* ----------------
@@ -1048,9 +1079,9 @@ typedef struct FieldStore
        Expr       *arg;                        /* input tuple value */
        List       *newvals;            /* new value(s) for field(s) */
        /* integer list of field attnums */
-       List       *fieldnums;
+       List       *fieldnums pg_node_attr(query_jumble_ignore);
        /* type of result (same as type of arg) */
-       Oid                     resulttype;
+       Oid                     resulttype pg_node_attr(query_jumble_ignore);
        /* Like RowExpr, we deliberately omit a typmod and collation here */
 } FieldStore;
 
@@ -1073,11 +1104,11 @@ typedef struct RelabelType
        Expr       *arg;                        /* input expression */
        Oid                     resulttype;             /* output type of coercion expression */
        /* output typmod (usually -1) */
-       int32           resulttypmod;
+       int32           resulttypmod pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     resultcollid;
+       Oid                     resultcollid pg_node_attr(query_jumble_ignore);
        /* how to display this node */
-       CoercionForm relabelformat;
+       CoercionForm relabelformat pg_node_attr(query_jumble_ignore);
        int                     location;               /* token location, or -1 if unknown */
 } RelabelType;
 
@@ -1097,9 +1128,9 @@ typedef struct CoerceViaIO
        Oid                     resulttype;             /* output type of coercion */
        /* output typmod is not stored, but is presumed -1 */
        /* OID of collation, or InvalidOid if none */
-       Oid                     resultcollid;
+       Oid                     resultcollid pg_node_attr(query_jumble_ignore);
        /* how to display this node */
-       CoercionForm coerceformat;
+       CoercionForm coerceformat pg_node_attr(query_jumble_ignore);
        int                     location;               /* token location, or -1 if unknown */
 } CoerceViaIO;
 
@@ -1123,11 +1154,11 @@ 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;
+       int32           resulttypmod pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     resultcollid;
+       Oid                     resultcollid pg_node_attr(query_jumble_ignore);
        /* how to display this node */
-       CoercionForm coerceformat;
+       CoercionForm coerceformat pg_node_attr(query_jumble_ignore);
        int                     location;               /* token location, or -1 if unknown */
 } ArrayCoerceExpr;
 
@@ -1151,7 +1182,7 @@ typedef struct ConvertRowtypeExpr
        Oid                     resulttype;             /* output type (always a composite type) */
        /* Like RowExpr, we deliberately omit a typmod and collation here */
        /* how to display this node */
-       CoercionForm convertformat;
+       CoercionForm convertformat pg_node_attr(query_jumble_ignore);
        int                     location;               /* token location, or -1 if unknown */
 } ConvertRowtypeExpr;
 
@@ -1196,9 +1227,9 @@ typedef struct CaseExpr
 {
        Expr            xpr;
        /* type of expression result */
-       Oid                     casetype;
+       Oid                     casetype pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     casecollid;
+       Oid                     casecollid pg_node_attr(query_jumble_ignore);
        Expr       *arg;                        /* implicit equality comparison argument */
        List       *args;                       /* the arguments (list of WHEN clauses) */
        Expr       *defresult;          /* the default result (ELSE clause) */
@@ -1241,9 +1272,9 @@ typedef struct CaseTestExpr
        Expr            xpr;
        Oid                     typeId;                 /* type for substituted value */
        /* typemod for substituted value */
-       int32           typeMod;
+       int32           typeMod pg_node_attr(query_jumble_ignore);
        /* collation for the substituted value */
-       Oid                     collation;
+       Oid                     collation pg_node_attr(query_jumble_ignore);
 } CaseTestExpr;
 
 /*
@@ -1258,15 +1289,15 @@ typedef struct ArrayExpr
 {
        Expr            xpr;
        /* type of expression result */
-       Oid                     array_typeid;
+       Oid                     array_typeid pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     array_collid;
+       Oid                     array_collid pg_node_attr(query_jumble_ignore);
        /* common type of array elements */
-       Oid                     element_typeid;
+       Oid                     element_typeid pg_node_attr(query_jumble_ignore);
        /* the array elements or sub-arrays */
        List       *elements;
        /* true if elements are sub-arrays */
-       bool            multidims;
+       bool            multidims pg_node_attr(query_jumble_ignore);
        /* token location, or -1 if unknown */
        int                     location;
 } ArrayExpr;
@@ -1298,7 +1329,7 @@ typedef struct RowExpr
        List       *args;                       /* the fields */
 
        /* RECORDOID or a composite type's ID */
-       Oid                     row_typeid;
+       Oid                     row_typeid pg_node_attr(query_jumble_ignore);
 
        /*
         * row_typeid cannot be a domain over composite, only plain composite.  To
@@ -1314,10 +1345,10 @@ typedef struct RowExpr
         */
 
        /* how to display this node */
-       CoercionForm row_format;
+       CoercionForm row_format pg_node_attr(query_jumble_ignore);
 
        /* list of String, or NIL */
-       List       *colnames;
+       List       *colnames pg_node_attr(query_jumble_ignore);
 
        int                     location;               /* token location, or -1 if unknown */
 } RowExpr;
@@ -1354,11 +1385,11 @@ typedef struct RowCompareExpr
        /* LT LE GE or GT, never EQ or NE */
        RowCompareType rctype;
        /* OID list of pairwise comparison ops */
-       List       *opnos;
+       List       *opnos pg_node_attr(query_jumble_ignore);
        /* OID list of containing operator families */
-       List       *opfamilies;
+       List       *opfamilies pg_node_attr(query_jumble_ignore);
        /* OID list of collations for comparisons */
-       List       *inputcollids;
+       List       *inputcollids pg_node_attr(query_jumble_ignore);
        /* the left-hand input arguments */
        List       *largs;
        /* the right-hand input arguments */
@@ -1372,9 +1403,9 @@ typedef struct CoalesceExpr
 {
        Expr            xpr;
        /* type of expression result */
-       Oid                     coalescetype;
+       Oid                     coalescetype pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     coalescecollid;
+       Oid                     coalescecollid pg_node_attr(query_jumble_ignore);
        /* the arguments */
        List       *args;
        /* token location, or -1 if unknown */
@@ -1394,11 +1425,11 @@ typedef struct MinMaxExpr
 {
        Expr            xpr;
        /* common type of arguments and result */
-       Oid                     minmaxtype;
+       Oid                     minmaxtype pg_node_attr(query_jumble_ignore);
        /* OID of collation of result */
-       Oid                     minmaxcollid;
+       Oid                     minmaxcollid pg_node_attr(query_jumble_ignore);
        /* OID of collation that function should use */
-       Oid                     inputcollid;
+       Oid                     inputcollid pg_node_attr(query_jumble_ignore);
        /* function to execute */
        MinMaxOp        op;
        /* the arguments */
@@ -1442,18 +1473,18 @@ typedef struct XmlExpr
        /* xml function ID */
        XmlExprOp       op;
        /* name in xml(NAME foo ...) syntaxes */
-       char       *name;
+       char       *name pg_node_attr(query_jumble_ignore);
        /* non-XML expressions for xml_attributes */
        List       *named_args;
        /* parallel list of String values */
-       List       *arg_names;
+       List       *arg_names pg_node_attr(query_jumble_ignore);
        /* list of expressions */
        List       *args;
        /* DOCUMENT or CONTENT */
-       XmlOptionType xmloption;
+       XmlOptionType xmloption pg_node_attr(query_jumble_ignore);
        /* target type/typmod for XMLSERIALIZE */
-       Oid                     type;
-       int32           typmod;
+       Oid                     type pg_node_attr(query_jumble_ignore);
+       int32           typmod pg_node_attr(query_jumble_ignore);
        /* token location, or -1 if unknown */
        int                     location;
 } XmlExpr;
@@ -1488,7 +1519,7 @@ typedef struct NullTest
        Expr       *arg;                        /* input expression */
        NullTestType nulltesttype;      /* IS NULL, IS NOT NULL */
        /* T to perform field-by-field null checks */
-       bool            argisrow;
+       bool            argisrow pg_node_attr(query_jumble_ignore);
        int                     location;               /* token location, or -1 if unknown */
 } NullTest;
 
@@ -1529,11 +1560,11 @@ typedef struct CoerceToDomain
        Expr       *arg;                        /* input expression */
        Oid                     resulttype;             /* domain type ID (result type) */
        /* output typmod (currently always -1) */
-       int32           resulttypmod;
+       int32           resulttypmod pg_node_attr(query_jumble_ignore);
        /* OID of collation, or InvalidOid if none */
-       Oid                     resultcollid;
+       Oid                     resultcollid pg_node_attr(query_jumble_ignore);
        /* how to display this node */
-       CoercionForm coercionformat;
+       CoercionForm coercionformat pg_node_attr(query_jumble_ignore);
        int                     location;               /* token location, or -1 if unknown */
 } CoerceToDomain;
 
@@ -1552,9 +1583,9 @@ typedef struct CoerceToDomainValue
        /* type for substituted value */
        Oid                     typeId;
        /* typemod for substituted value */
-       int32           typeMod;
+       int32           typeMod pg_node_attr(query_jumble_ignore);
        /* collation for the substituted value */
-       Oid                     collation;
+       Oid                     collation pg_node_attr(query_jumble_ignore);
        /* token location, or -1 if unknown */
        int                     location;
 } CoerceToDomainValue;
@@ -1572,9 +1603,9 @@ typedef struct SetToDefault
        /* type for substituted value */
        Oid                     typeId;
        /* typemod for substituted value */
-       int32           typeMod;
+       int32           typeMod pg_node_attr(query_jumble_ignore);
        /* collation for the substituted value */
-       Oid                     collation;
+       Oid                     collation pg_node_attr(query_jumble_ignore);
        /* token location, or -1 if unknown */
        int                     location;
 } SetToDefault;
@@ -1692,15 +1723,15 @@ typedef struct TargetEntry
        /* attribute number (see notes above) */
        AttrNumber      resno;
        /* name of the column (could be NULL) */
-       char       *resname;
+       char       *resname pg_node_attr(query_jumble_ignore);
        /* nonzero if referenced by a sort/group clause */
        Index           ressortgroupref;
        /* OID of column's source table */
-       Oid                     resorigtbl;
+       Oid                     resorigtbl pg_node_attr(query_jumble_ignore);
        /* column's number in source table */
-       AttrNumber      resorigcol;
+       AttrNumber      resorigcol pg_node_attr(query_jumble_ignore);
        /* set to true to eliminate the attribute from final target list */
-       bool            resjunk;
+       bool            resjunk pg_node_attr(query_jumble_ignore);
 } TargetEntry;
 
 
@@ -1783,13 +1814,13 @@ typedef struct JoinExpr
        Node       *larg;                       /* left subtree */
        Node       *rarg;                       /* right subtree */
        /* USING clause, if any (list of String) */
-       List       *usingClause;
+       List       *usingClause pg_node_attr(query_jumble_ignore);
        /* alias attached to USING clause, if any */
-       Alias      *join_using_alias;
+       Alias      *join_using_alias pg_node_attr(query_jumble_ignore);
        /* qualifiers on join, if any */
        Node       *quals;
        /* user-written alias clause, if any */
-       Alias      *alias;
+       Alias      *alias pg_node_attr(query_jumble_ignore);
        /* RT index assigned for join, or 0 */
        int                     rtindex;
 } JoinExpr;