Generating code for query jumbling through gen_node_support.pl
Hi all,
This thread is a follow-up of the recent discussion about query
jumbling with DDL statements, where the conclusion was that we'd want
to generate all this code automatically for all the nodes:
/messages/by-id/36e5bffe-e989-194f-85c8-06e7bc88e6f7@amazon.com
What this patch allows to do it to compute the same query ID for
utility statements using their parsed Node state instead of their
string, meaning that things like "BEGIN", "bEGIN" or "begin" would be
treated the same, for example. But the main idea is not only that.
I have implemented that as of the attached, where the following things
are done:
- queryjumble.c is moved to src/backend/nodes/, to stick with the
other things for node equal/read/write/copy, renamed to
jumblefuncs.c.
- gen_node_support.c is extended to generate the functions and the
switch for the jumbling. There are a few exceptions, as of the Lists
and RangeTblEntry to do the jumbling consistently.
- Two pg_node_attr() are added in consistency with the existing ones:
no_jumble to discard completely a node from the the query jumbling
and jumble_ignore to discard one field from the jumble.
The patch is in a rather good shape, passes check-world and the CI,
but there are a few things that need to be discussed IMO. Things
could be perhaps divided in more patches, now the areas touched are
quite different so it did not look like a big deal to me as the
changes touch different areas and are straight-forward.
The location of the Nodes is quite invasive because we only care about
that for T_Const now in the query jumbling, and this could be
compensated with a third pg_node_attr() that tracks for the "int
location" of a Node whether it should participate in the jumbling or
not. There is also an argument where we would want to not include by
default new fields added to a Node, but that would require added more
pg_node_attr() than what's done here.
Note that the plan is to extend the normalization to some other parts
of the Nodes, like CALL and SET as mentioned on the other thread. I
have done nothing about that yet but doing so can be done in a few
lines with the facility presented here (aka just add a location
field). Hence, the normalization is consistent with the existing
queryjumble.c for the fields and the nodes processed.
In this patch, things are done so as the query ID is not computed
anymore from the query string but from the Query. I still need to
study the performance impact of that with short queries. If things
prove to be noticeable in some cases, this stuff could be switched to
use a new GUC where we could have a code path for the computation of
utilityStmt using its string as a fallback. I am not sure that I want
to enter in this level of complications, though, to keep things
simple, but that's yet to be done.
A bit more could be cut but pg_ident got in the way.. There are also
a few things for pg_stat_statements where a query ID of 0 can be
implied for utility statements in some cases.
Generating this code leads to an overall removal of code as what
queryjumble.c is generated automatically:
13 files changed, 901 insertions(+), 1113 deletions(-)
I am adding that to the next commit fest.
Thoughts?
--
Michael
Attachments:
0001-Support-for-automated-query-jumble-with-all-Nodes.patchtext/x-diff; charset=us-asciiDownload
From 55b6d9154a5524e67865299b9a73ef074bd12adf Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 7 Dec 2022 16:36:47 +0900
Subject: [PATCH] Support for automated query jumble with all Nodes
This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
src/include/nodes/bitmapset.h | 2 +-
src/include/nodes/nodes.h | 4 +
src/include/nodes/parsenodes.h | 267 +++++---
src/include/nodes/plannodes.h | 3 +-
src/include/nodes/primnodes.h | 419 ++++++++-----
src/backend/nodes/Makefile | 1 +
src/backend/nodes/README | 1 +
src/backend/nodes/gen_node_support.pl | 95 ++-
src/backend/nodes/jumblefuncs.c | 358 +++++++++++
src/backend/nodes/meson.build | 1 +
src/backend/utils/misc/Makefile | 1 -
src/backend/utils/misc/meson.build | 1 -
src/backend/utils/misc/queryjumble.c | 861 --------------------------
13 files changed, 901 insertions(+), 1113 deletions(-)
create mode 100644 src/backend/nodes/jumblefuncs.c
delete mode 100644 src/backend/utils/misc/queryjumble.c
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 2792281658..29d531224c 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -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_jumble)
NodeTag type;
int nwords; /* number of words in array */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1f33902947..afaeeaf2c9 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -59,6 +59,8 @@ typedef enum NodeTag
*
* - no_copy_equal: Shorthand for both no_copy and no_equal.
*
+ * - no_jumble: Does not support jumble() at all.
+ *
* - no_read: Does not support nodeRead() at all.
*
* - nodetag_only: Does not support copyObject(), equal(), outNode(),
@@ -97,6 +99,8 @@ typedef enum NodeTag
* - equal_ignore_if_zero: Ignore the field for equality if it is zero.
* (Otherwise, compare normally.)
*
+ * - jumble_ignore: Ignore the field for the query jumbling.
+ *
* - 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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6a6d3293e4..16b6591c00 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -124,47 +124,61 @@ typedef struct Query
CmdType commandType; /* select|insert|update|delete|merge|utility */
- QuerySource querySource; /* where did I come from? */
+ /* where did I come from? */
+ QuerySource querySource pg_node_attr(jumble_ignore);
/*
* query identifier (can be set by plugins); ignored for equal, as it
* might not be set; also not stored
*/
- uint64 queryId pg_node_attr(equal_ignore, read_write_ignore, read_as(0));
+ uint64 queryId pg_node_attr(equal_ignore, jumble_ignore, read_write_ignore, read_as(0));
- bool canSetTag; /* do I set the command result tag? */
+ /* do I set the command result tag? */
+ bool canSetTag pg_node_attr(jumble_ignore);
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
- int resultRelation; /* rtable index of target relation for
- * INSERT/UPDATE/DELETE/MERGE; 0 for SELECT */
+ /*
+ * rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
+ * SELECT.
+ */
+ int resultRelation pg_node_attr(jumble_ignore);
- bool hasAggs; /* has aggregates in tlist or havingQual */
- bool hasWindowFuncs; /* has window functions in tlist */
- bool hasTargetSRFs; /* has set-returning functions in tlist */
- bool hasSubLinks; /* has subquery SubLink */
- bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
- bool hasRecursive; /* WITH RECURSIVE was specified */
- bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
- bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
- bool hasRowSecurity; /* rewriter has applied some RLS policy */
+ /* has aggregates in tlist or havingQual */
+ bool hasAggs pg_node_attr(jumble_ignore);
+ /* has window functions in tlist */
+ bool hasWindowFuncs pg_node_attr(jumble_ignore);
+ /* has set-returning functions in tlist */
+ bool hasTargetSRFs pg_node_attr(jumble_ignore);
+ bool hasSubLinks pg_node_attr(jumble_ignore); /* has subquery SubLink */
+ /* distinctClause is from DISTINCT ON */
+ bool hasDistinctOn pg_node_attr(jumble_ignore);
+ /* WITH RECURSIVE was specified */
+ bool hasRecursive pg_node_attr(jumble_ignore);
+ /* has INSERT/UPDATE/DELETE in WITH */
+ bool hasModifyingCTE pg_node_attr(jumble_ignore);
+ /* FOR [KEY] UPDATE/SHARE was specified */
+ bool hasForUpdate pg_node_attr(jumble_ignore);
+ /* rewriter has applied some RLS policy */
+ bool hasRowSecurity pg_node_attr(jumble_ignore);
- bool isReturn; /* is a RETURN statement */
+ bool isReturn pg_node_attr(jumble_ignore); /* is a RETURN statement */
List *cteList; /* WITH list (of CommonTableExpr's) */
List *rtable; /* list of range table entries */
- List *rteperminfos; /* list of RTEPermissionInfo nodes for the
- * rtable entries having perminfoindex > 0 */
+ /* list of RTEPermissionInfo nodes for the rtable entries having perminfoindex > 0 */
+ List *rteperminfos pg_node_attr(jumble_ignore);
FromExpr *jointree; /* table join tree (FROM and WHERE clauses);
* also USING clause for MERGE */
List *mergeActionList; /* list of actions for MERGE (only) */
- bool mergeUseOuterJoin; /* whether to use outer join */
+ /* whether to use outer join */
+ bool mergeUseOuterJoin pg_node_attr(jumble_ignore);
List *targetList; /* target list (of TargetEntry) */
- OverridingKind override; /* OVERRIDING clause */
+ OverridingKind override pg_node_attr(jumble_ignore); /* OVERRIDING clause */
OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
@@ -192,11 +206,14 @@ typedef struct Query
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
- List *constraintDeps; /* a list of pg_constraint OIDs that the query
- * depends on to be semantically valid */
+ /*
+ * A list of pg_constraint OIDs that the query depends on to be
+ * semantically valid
+ */
+ List *constraintDeps pg_node_attr(jumble_ignore);
- List *withCheckOptions; /* a list of WithCheckOption's (added
- * during rewrite) */
+ /* a list of WithCheckOption's (added during rewrite) */
+ List *withCheckOptions pg_node_attr(jumble_ignore);
/*
* The following two fields identify the portion of the source text string
@@ -204,8 +221,10 @@ typedef struct Query
* Queries, not in sub-queries. When not set, they might both be zero, or
* both be -1 meaning "unknown".
*/
- int stmt_location; /* start location, or -1 if unknown */
- int stmt_len; /* length in bytes; 0 means "rest of string" */
+ /* start location, or -1 if unknown */
+ int stmt_location pg_node_attr(jumble_ignore);
+ /* length in bytes; 0 means "rest of string" */
+ int stmt_len pg_node_attr(jumble_ignore);
} Query;
@@ -240,7 +259,8 @@ typedef struct TypeName
List *typmods; /* type modifier expression(s) */
int32 typemod; /* prespecified type modifier */
List *arrayBounds; /* array bounds */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} TypeName;
/*
@@ -260,7 +280,8 @@ typedef struct ColumnRef
{
NodeTag type;
List *fields; /* field names (String nodes) or A_Star */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} ColumnRef;
/*
@@ -270,7 +291,8 @@ typedef struct ParamRef
{
NodeTag type;
int number; /* the number of the parameter */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} ParamRef;
/*
@@ -303,7 +325,8 @@ typedef struct A_Expr
List *name; /* possibly-qualified name of operator */
Node *lexpr; /* left argument, or NULL if none */
Node *rexpr; /* right argument, or NULL if none */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} A_Expr;
/*
@@ -329,7 +352,8 @@ typedef struct A_Const
NodeTag type;
union ValUnion val;
bool isnull; /* SQL NULL constant */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} A_Const;
/*
@@ -340,7 +364,8 @@ typedef struct TypeCast
NodeTag type;
Node *arg; /* the expression being casted */
TypeName *typeName; /* the target type */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} TypeCast;
/*
@@ -351,7 +376,8 @@ typedef struct CollateClause
NodeTag type;
Node *arg; /* input expression */
List *collname; /* possibly-qualified collation name */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CollateClause;
/*
@@ -371,7 +397,8 @@ typedef struct RoleSpec
NodeTag type;
RoleSpecType roletype; /* Type of this rolespec */
char *rolename; /* filled only for ROLESPEC_CSTRING */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} RoleSpec;
/*
@@ -401,7 +428,8 @@ typedef struct FuncCall
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
CoercionForm funcformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} FuncCall;
/*
@@ -458,7 +486,8 @@ typedef struct A_ArrayExpr
{
NodeTag type;
List *elements; /* array element expressions */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} A_ArrayExpr;
/*
@@ -485,7 +514,8 @@ typedef struct ResTarget
char *name; /* column name or NULL */
List *indirection; /* subscripts, field names, and '*', or NIL */
Node *val; /* the value expression to compute or assign */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} ResTarget;
/*
@@ -515,7 +545,8 @@ typedef struct SortBy
SortByDir sortby_dir; /* ASC/DESC/USING/default */
SortByNulls sortby_nulls; /* NULLS FIRST/LAST */
List *useOp; /* name of op to use, if SORTBY_USING */
- int location; /* operator location, or -1 if none/unknown */
+ int location pg_node_attr(jumble_ignore); /* operator location, or
+ * -1 if none/unknown */
} SortBy;
/*
@@ -536,7 +567,8 @@ typedef struct WindowDef
int frameOptions; /* frame_clause options, see below */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- int location; /* parse location, or -1 if none/unknown */
+ int location pg_node_attr(jumble_ignore); /* parse location, or -1
+ * if none/unknown */
} WindowDef;
/*
@@ -626,7 +658,8 @@ typedef struct RangeTableFunc
List *namespaces; /* list of namespaces as ResTarget */
List *columns; /* list of RangeTableFuncCol */
Alias *alias; /* table alias & optional column aliases */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} RangeTableFunc;
/*
@@ -644,7 +677,8 @@ typedef struct RangeTableFuncCol
bool is_not_null; /* does it have NOT NULL? */
Node *colexpr; /* column filter expression */
Node *coldefexpr; /* column default value expression */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} RangeTableFuncCol;
/*
@@ -664,7 +698,8 @@ typedef struct RangeTableSample
List *method; /* sampling method name (possibly qualified) */
List *args; /* argument(s) for sampling method */
Node *repeatable; /* REPEATABLE expression, or NULL if none */
- int location; /* method name location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* method name location,
+ * or -1 if unknown */
} RangeTableSample;
/*
@@ -707,7 +742,8 @@ typedef struct ColumnDef
Oid collOid; /* collation OID (InvalidOid if not set) */
List *constraints; /* other constraints on column */
List *fdwoptions; /* per-column FDW options */
- int location; /* parse location, or -1 if none/unknown */
+ int location pg_node_attr(jumble_ignore); /* parse location, or -1
+ * if none/unknown */
} ColumnDef;
/*
@@ -781,7 +817,8 @@ typedef struct DefElem
Node *arg; /* typically Integer, Float, String, or
* TypeName */
DefElemAction defaction; /* unspecified action, or SET/ADD/DROP */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} DefElem;
/*
@@ -810,7 +847,8 @@ typedef struct XmlSerialize
XmlOptionType xmloption; /* DOCUMENT or CONTENT */
Node *expr;
TypeName *typeName;
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} XmlSerialize;
/* Partitioning related definitions */
@@ -828,7 +866,8 @@ typedef struct PartitionElem
Node *expr; /* expression to partition on, or NULL */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} PartitionElem;
typedef enum PartitionStrategy
@@ -848,7 +887,8 @@ typedef struct PartitionSpec
NodeTag type;
PartitionStrategy strategy;
List *partParams; /* List of PartitionElems */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} PartitionSpec;
/*
@@ -875,7 +915,8 @@ struct PartitionBoundSpec
List *lowerdatums; /* List of PartitionRangeDatums */
List *upperdatums; /* List of PartitionRangeDatums */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
};
/*
@@ -898,7 +939,8 @@ typedef struct PartitionRangeDatum
Node *value; /* Const (or A_Const in raw tree), if kind is
* PARTITION_RANGE_DATUM_VALUE, else NULL */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} PartitionRangeDatum;
/*
@@ -995,7 +1037,7 @@ typedef enum RTEKind
typedef struct RangeTblEntry
{
- pg_node_attr(custom_read_write)
+ pg_node_attr(custom_read_write, no_jumble)
NodeTag type;
@@ -1234,14 +1276,20 @@ typedef struct RangeTblFunction
NodeTag type;
Node *funcexpr; /* expression tree for func call */
- int funccolcount; /* number of columns it contributes to RTE */
+ int funccolcount pg_node_attr(jumble_ignore); /* number of columns it
+ * contributes to RTE */
/* These fields record the contents of a column definition list, if any: */
- List *funccolnames; /* column names (list of String) */
- List *funccoltypes; /* OID list of column type OIDs */
- List *funccoltypmods; /* integer list of column typmods */
- List *funccolcollations; /* OID list of column collation OIDs */
+ List *funccolnames pg_node_attr(jumble_ignore); /* column names (list of
+ * String) */
+ List *funccoltypes pg_node_attr(jumble_ignore); /* OID list of column
+ * type OIDs */
+ List *funccoltypmods pg_node_attr(jumble_ignore); /* integer list of
+ * column typmods */
+ List *funccolcollations pg_node_attr(jumble_ignore); /* OID list of column
+ * collation OIDs */
/* This is set during planning for use by the executor: */
- Bitmapset *funcparams; /* PARAM_EXEC Param IDs affecting this func */
+ Bitmapset *funcparams pg_node_attr(jumble_ignore); /* PARAM_EXEC Param IDs
+ * affecting this func */
} RangeTblFunction;
/*
@@ -1348,7 +1396,9 @@ typedef struct SortGroupClause
Oid eqop; /* the equality operator ('=' op) */
Oid sortop; /* the ordering operator ('<' op), or 0 */
bool nulls_first; /* do NULLs come before normal values? */
- bool hashable; /* can eqop be implemented by hashing? */
+ bool hashable pg_node_attr(jumble_ignore); /* can eqop be
+ * implemented by
+ * hashing? */
} SortGroupClause;
/*
@@ -1413,9 +1463,9 @@ typedef enum GroupingSetKind
typedef struct GroupingSet
{
NodeTag type;
- GroupingSetKind kind;
+ GroupingSetKind kind pg_node_attr(jumble_ignore);
List *content;
- int location;
+ int location pg_node_attr(jumble_ignore);
} GroupingSet;
/*
@@ -1438,21 +1488,30 @@ typedef struct GroupingSet
typedef struct WindowClause
{
NodeTag type;
- char *name; /* window name (NULL in an OVER clause) */
- char *refname; /* referenced window name, if any */
+ char *name pg_node_attr(jumble_ignore); /* window name (NULL in an
+ * OVER clause) */
+ char *refname pg_node_attr(jumble_ignore); /* referenced window
+ * name, if any */
List *partitionClause; /* PARTITION BY list */
- List *orderClause; /* ORDER BY list */
+ List *orderClause pg_node_attr(jumble_ignore); /* ORDER BY list */
int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- List *runCondition; /* qual to help short-circuit execution */
- Oid startInRangeFunc; /* in_range function for startOffset */
- Oid endInRangeFunc; /* in_range function for endOffset */
- Oid inRangeColl; /* collation for in_range tests */
- bool inRangeAsc; /* use ASC sort order for in_range tests? */
- bool inRangeNullsFirst; /* nulls sort first for in_range tests? */
+ /* qual to help short-circuit execution */
+ List *runCondition pg_node_attr(jumble_ignore);
+ /* in_range function for startOffset */
+ Oid startInRangeFunc pg_node_attr(jumble_ignore);
+ /* in_range function for endOffset */
+ Oid endInRangeFunc pg_node_attr(jumble_ignore);
+ /* collation for in_range tests */
+ Oid inRangeColl pg_node_attr(jumble_ignore);
+ /* use ASC sort order for in_range tests? */
+ bool inRangeAsc pg_node_attr(jumble_ignore);
+ /* nulls sort first for in_range tests? */
+ bool inRangeNullsFirst pg_node_attr(jumble_ignore);
Index winref; /* ID referenced by window functions */
- bool copiedOrder; /* did we copy orderClause from refname? */
+ /* did we copy orderClause from refname? */
+ bool copiedOrder pg_node_attr(jumble_ignore);
} WindowClause;
/*
@@ -1488,7 +1547,8 @@ typedef struct WithClause
NodeTag type;
List *ctes; /* list of CommonTableExprs */
bool recursive; /* true = WITH RECURSIVE */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} WithClause;
/*
@@ -1503,7 +1563,8 @@ typedef struct InferClause
List *indexElems; /* IndexElems to infer unique index */
Node *whereClause; /* qualification (partial-index predicate) */
char *conname; /* Constraint name, or NULL if unnamed */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} InferClause;
/*
@@ -1519,7 +1580,8 @@ typedef struct OnConflictClause
InferClause *infer; /* Optional index inference clause */
List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} OnConflictClause;
/*
@@ -1540,7 +1602,7 @@ typedef struct CTESearchClause
List *search_col_list;
bool search_breadth_first;
char *search_seq_column;
- int location;
+ int location pg_node_attr(jumble_ignore);
} CTESearchClause;
typedef struct CTECycleClause
@@ -1551,7 +1613,7 @@ typedef struct CTECycleClause
Node *cycle_mark_value;
Node *cycle_mark_default;
char *cycle_path_column;
- int location;
+ int location pg_node_attr(jumble_ignore);
/* These fields are set during parse analysis: */
Oid cycle_mark_type; /* common type of _value and _default */
int cycle_mark_typmod;
@@ -1567,17 +1629,26 @@ typedef struct CommonTableExpr
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;
- int location; /* token location, or -1 if unknown */
+ CTESearchClause *search_clause pg_node_attr(jumble_ignore);
+ CTECycleClause *cycle_clause pg_node_attr(jumble_ignore);
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
/* These fields are set during parse analysis: */
- bool cterecursive; /* is this CTE actually recursive? */
- int cterefcount; /* number of RTEs referencing this CTE
- * (excluding internal self-references) */
- List *ctecolnames; /* list of output column names */
- List *ctecoltypes; /* OID list of output column type OIDs */
- List *ctecoltypmods; /* integer list of output column typmods */
- List *ctecolcollations; /* OID list of column collation OIDs */
+ /* is this CTE actually recursive? */
+ bool cterecursive pg_node_attr(jumble_ignore);
+ /*
+ * Number of RTEs referencing this CTE (excluding internal
+ * self-references)
+ */
+ int cterefcount pg_node_attr(jumble_ignore);
+ List *ctecolnames pg_node_attr(jumble_ignore); /* list of output column
+ * names */
+ List *ctecoltypes pg_node_attr(jumble_ignore); /* OID list of output
+ * column type OIDs */
+ List *ctecoltypmods pg_node_attr(jumble_ignore); /* integer list of
+ * output column typmods */
+ List *ctecolcollations pg_node_attr(jumble_ignore); /* OID list of column
+ * collation OIDs */
} CommonTableExpr;
/* Convenience macro to get the output tlist of a CTE's query */
@@ -1614,10 +1685,11 @@ typedef struct MergeAction
NodeTag type;
bool matched; /* true=MATCHED, false=NOT MATCHED */
CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
- OverridingKind override; /* OVERRIDING clause */
+ OverridingKind override pg_node_attr(jumble_ignore); /* OVERRIDING clause */
Node *qual; /* transformed WHEN conditions */
List *targetList; /* the target list (of TargetEntry) */
- List *updateColnos; /* target attribute numbers of an UPDATE */
+ List *updateColnos pg_node_attr(jumble_ignore); /* target attribute
+ * numbers of an UPDATE */
} MergeAction;
/*
@@ -1656,7 +1728,8 @@ typedef struct RawStmt
{
NodeTag type;
Node *stmt; /* raw parse tree */
- int stmt_location; /* start location, or -1 if unknown */
+ int stmt_location pg_node_attr(jumble_ignore); /* start location, or -1
+ * if unknown */
int stmt_len; /* length in bytes; 0 means "rest of string" */
} RawStmt;
@@ -1827,10 +1900,14 @@ typedef struct SetOperationStmt
/* Eventually add fields for CORRESPONDING spec here */
/* Fields derived during parse analysis: */
- List *colTypes; /* OID list of output column type OIDs */
- List *colTypmods; /* integer list of output column typmods */
- List *colCollations; /* OID list of output column collation OIDs */
- List *groupClauses; /* a list of SortGroupClause's */
+ List *colTypes pg_node_attr(jumble_ignore); /* OID list of output
+ * column type OIDs */
+ List *colTypmods pg_node_attr(jumble_ignore); /* integer list of
+ * output column typmods */
+ List *colCollations pg_node_attr(jumble_ignore); /* OID list of output
+ * column collation OIDs */
+ List *groupClauses pg_node_attr(jumble_ignore); /* a list of
+ * SortGroupClause's */
/* groupClauses is NIL if UNION ALL, but must be set otherwise */
} SetOperationStmt;
@@ -1860,7 +1937,9 @@ typedef struct PLAssignStmt
List *indirection; /* subscripts and field names, if any */
int nnames; /* number of names to use in ColumnRef */
SelectStmt *val; /* the PL/pgSQL expression to assign */
- int location; /* name's token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* name's token
+ * location, or -1 if
+ * unknown */
} PLAssignStmt;
@@ -2371,7 +2450,8 @@ typedef struct Constraint
char *conname; /* Constraint name, or NULL if unnamed */
bool deferrable; /* DEFERRABLE? */
bool initdeferred; /* INITIALLY DEFERRED? */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
/* Fields used for constraints with expressions (CHECK and DEFAULT): */
bool is_no_inherit; /* is constraint non-inheritable? */
@@ -3780,7 +3860,8 @@ typedef struct PublicationObjSpec
PublicationObjSpecType pubobjtype; /* type of this publication object */
char *name;
PublicationTable *pubtable;
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} PublicationObjSpec;
typedef struct CreatePublicationStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index bddfe86191..3d7e2942b1 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -99,7 +99,8 @@ typedef struct PlannedStmt
Node *utilityStmt; /* non-null if this is utility stmt */
/* statement location in source string (copied from Query) */
- int stmt_location; /* start location, or -1 if unknown */
+ int stmt_location pg_node_attr(jumble_ignore); /* start location, or -1
+ * if unknown */
int stmt_len; /* length in bytes; 0 means "rest of string" */
} PlannedStmt;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 74f228d959..8a0458c3d3 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -86,7 +86,7 @@ typedef struct RangeVar
Alias *alias;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(jumble_ignore);
} RangeVar;
/*
@@ -98,19 +98,29 @@ typedef struct RangeVar
typedef struct TableFunc
{
NodeTag type;
- List *ns_uris; /* list of namespace URI expressions */
- List *ns_names; /* list of namespace names or NULL */
+ List *ns_uris pg_node_attr(jumble_ignore); /* list of namespace URI
+ * expressions */
+ List *ns_names pg_node_attr(jumble_ignore); /* list of namespace
+ * names or NULL */
Node *docexpr; /* input document expression */
Node *rowexpr; /* row filter expression */
- List *colnames; /* column names (list of String) */
- List *coltypes; /* OID list of column type OIDs */
- List *coltypmods; /* integer list of column typmods */
- List *colcollations; /* OID list of column collation OIDs */
+ List *colnames pg_node_attr(jumble_ignore); /* column names (list of
+ * String) */
+ List *coltypes pg_node_attr(jumble_ignore); /* OID list of column
+ * type OIDs */
+ List *coltypmods pg_node_attr(jumble_ignore); /* integer list of
+ * column typmods */
+ List *colcollations pg_node_attr(jumble_ignore); /* OID list of column
+ * collation OIDs */
List *colexprs; /* list of column filter expressions */
- List *coldefexprs; /* list of column default expressions */
- Bitmapset *notnulls; /* nullability flag for each output column */
- int ordinalitycol; /* counts from 0; -1 if none specified */
- int location; /* token location, or -1 if unknown */
+ List *coldefexprs pg_node_attr(jumble_ignore); /* list of column
+ * default expressions */
+ Bitmapset *notnulls pg_node_attr(jumble_ignore); /* nullability flag for
+ * each output column */
+ int ordinalitycol pg_node_attr(jumble_ignore); /* counts from 0; -1 if
+ * none specified */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} TableFunc;
/*
@@ -217,11 +227,11 @@ typedef struct Var
AttrNumber varattno;
/* pg_type OID for the type of this var */
- Oid vartype;
+ Oid vartype pg_node_attr(jumble_ignore);
/* pg_attribute typmod value */
- int32 vartypmod;
+ int32 vartypmod pg_node_attr(jumble_ignore);
/* OID of collation, or InvalidOid if none */
- Oid varcollid;
+ Oid varcollid pg_node_attr(jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
@@ -235,12 +245,12 @@ 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, jumble_ignore);
/* syntactic attribute number */
- AttrNumber varattnosyn pg_node_attr(equal_ignore);
+ AttrNumber varattnosyn pg_node_attr(equal_ignore, jumble_ignore);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(jumble_ignore);
} Var;
/*
@@ -257,16 +267,20 @@ typedef struct Const
Expr xpr;
Oid consttype; /* pg_type OID of the constant's datatype */
- int32 consttypmod; /* typmod value, if any */
- Oid constcollid; /* OID of collation, or InvalidOid if none */
- int constlen; /* typlen of the constant's datatype */
- Datum constvalue; /* the constant's value */
- bool constisnull; /* whether the constant is null (if true,
- * constvalue is undefined) */
- bool constbyval; /* 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. */
+ int32 consttypmod pg_node_attr(jumble_ignore); /* typmod value, if any */
+ Oid constcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ int constlen pg_node_attr(jumble_ignore); /* typlen of the
+ * constant's datatype */
+ Datum constvalue pg_node_attr(jumble_ignore); /* the constant's value */
+ /* whether the constant is null (if true, constvalue is undefined) */
+ bool constisnull pg_node_attr(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 pg_node_attr(jumble_ignore);
int location; /* token location, or -1 if unknown */
} Const;
@@ -311,9 +325,12 @@ typedef struct Param
ParamKind paramkind; /* kind of parameter. See above */
int paramid; /* numeric ID for parameter */
Oid paramtype; /* pg_type OID of parameter's datatype */
- int32 paramtypmod; /* typmod value, if known */
- Oid paramcollid; /* OID of collation, or InvalidOid if none */
- int location; /* token location, or -1 if unknown */
+ int32 paramtypmod pg_node_attr(jumble_ignore); /* typmod value, if
+ * known */
+ Oid paramcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} Param;
/*
@@ -373,22 +390,22 @@ typedef struct Aggref
Oid aggfnoid;
/* type Oid of result of the aggregate */
- Oid aggtype;
+ Oid aggtype pg_node_attr(jumble_ignore);
/* OID of collation of result */
- Oid aggcollid;
+ Oid aggcollid pg_node_attr(jumble_ignore);
/* OID of collation that function should use */
- Oid inputcollid;
+ Oid inputcollid pg_node_attr(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, jumble_ignore);
/* type Oids of direct and aggregated args */
- List *aggargtypes;
+ List *aggargtypes pg_node_attr(jumble_ignore);
/* direct arguments, if an ordered-set agg */
List *aggdirectargs;
@@ -406,34 +423,34 @@ typedef struct Aggref
Expr *aggfilter;
/* true if argument list was really '*' */
- bool aggstar;
+ bool aggstar pg_node_attr(jumble_ignore);
/*
* true if variadic arguments have been combined into an array last
* argument
*/
- bool aggvariadic;
+ bool aggvariadic pg_node_attr(jumble_ignore);
/* aggregate kind (see pg_aggregate.h) */
- char aggkind;
+ char aggkind pg_node_attr(jumble_ignore);
/* aggregate input already sorted */
- bool aggpresorted pg_node_attr(equal_ignore);
+ bool aggpresorted pg_node_attr(equal_ignore, jumble_ignore);
/* > 0 if agg belongs to outer query */
- Index agglevelsup;
+ Index agglevelsup pg_node_attr(jumble_ignore);
/* expected agg-splitting mode of parent Agg */
- AggSplit aggsplit;
+ AggSplit aggsplit pg_node_attr(jumble_ignore);
/* unique ID within the Agg node */
- int aggno;
+ int aggno pg_node_attr(jumble_ignore);
/* unique ID of transition state in the Agg */
- int aggtransno;
+ int aggtransno pg_node_attr(jumble_ignore);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(jumble_ignore);
} Aggref;
/*
@@ -465,19 +482,19 @@ typedef struct GroupingFunc
Expr xpr;
/* arguments, not evaluated but kept for benefit of EXPLAIN etc. */
- List *args;
+ List *args pg_node_attr(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, jumble_ignore);
/* same as Aggref.agglevelsup */
Index agglevelsup;
/* token location */
- int location;
+ int location pg_node_attr(jumble_ignore);
} GroupingFunc;
/*
@@ -487,15 +504,21 @@ typedef struct WindowFunc
{
Expr xpr;
Oid winfnoid; /* pg_proc Oid of the function */
- Oid wintype; /* type Oid of result of the window function */
- Oid wincollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
+ Oid wintype pg_node_attr(jumble_ignore); /* type Oid of result of
+ * the window function */
+ Oid wincollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * result */
+ Oid inputcollid pg_node_attr(jumble_ignore); /* OID of collation that
+ * function should use */
List *args; /* arguments to the window function */
Expr *aggfilter; /* FILTER expression, if any */
Index winref; /* index of associated WindowClause */
- bool winstar; /* true if argument list was really '*' */
- bool winagg; /* is function a simple aggregate? */
- int location; /* token location, or -1 if unknown */
+ bool winstar pg_node_attr(jumble_ignore); /* true if argument list
+ * was really '*' */
+ bool winagg pg_node_attr(jumble_ignore); /* is function a simple
+ * aggregate? */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} WindowFunc;
/*
@@ -539,11 +562,16 @@ typedef struct WindowFunc
typedef struct SubscriptingRef
{
Expr xpr;
- Oid refcontainertype; /* type of the container proper */
- Oid refelemtype; /* the container type's pg_type.typelem */
- Oid refrestype; /* type of the SubscriptingRef's result */
- int32 reftypmod; /* typmod of the result */
- Oid refcollid; /* collation of result, or InvalidOid if none */
+ Oid refcontainertype pg_node_attr(jumble_ignore); /* type of the container
+ * proper */
+ Oid refelemtype pg_node_attr(jumble_ignore); /* the container type's
+ * pg_type.typelem */
+ Oid refrestype pg_node_attr(jumble_ignore); /* type of the
+ * SubscriptingRef's
+ * result */
+ int32 reftypmod pg_node_attr(jumble_ignore); /* typmod of the result */
+ Oid refcollid pg_node_attr(jumble_ignore); /* collation of result,
+ * or InvalidOid if none */
List *refupperindexpr; /* expressions that evaluate to upper
* container indexes */
List *reflowerindexpr; /* expressions that evaluate to lower
@@ -596,15 +624,23 @@ typedef struct FuncExpr
{
Expr xpr;
Oid funcid; /* PG_PROC OID of the function */
- Oid funcresulttype; /* PG_TYPE OID of result value */
- bool funcretset; /* true if function returns set */
- bool funcvariadic; /* true if variadic arguments have been
- * combined into an array last argument */
- CoercionForm funcformat; /* how to display this function call */
- Oid funccollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
+ Oid funcresulttype pg_node_attr(jumble_ignore); /* PG_TYPE OID of result
+ * value */
+ bool funcretset pg_node_attr(jumble_ignore); /* true if function
+ * returns set */
+ bool funcvariadic pg_node_attr(jumble_ignore); /* true if variadic
+ * arguments have been
+ * combined into an
+ * array last argument */
+ CoercionForm funcformat pg_node_attr(jumble_ignore); /* how to display this
+ * function call */
+ Oid funccollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * result */
+ Oid inputcollid pg_node_attr(jumble_ignore); /* OID of collation that
+ * function should use */
List *args; /* arguments to the function */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} FuncExpr;
/*
@@ -625,9 +661,11 @@ typedef struct NamedArgExpr
{
Expr xpr;
Expr *arg; /* the argument expression */
- char *name; /* the name */
+ char *name pg_node_attr(jumble_ignore); /* the name */
int argnumber; /* argument's number in positional notation */
- int location; /* argument name location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* argument name
+ * location, or -1 if
+ * unknown */
} NamedArgExpr;
/*
@@ -648,25 +686,25 @@ 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, jumble_ignore);
/* PG_TYPE OID of result value */
- Oid opresulttype;
+ Oid opresulttype pg_node_attr(jumble_ignore);
/* true if operator returns set */
- bool opretset;
+ bool opretset pg_node_attr(jumble_ignore);
/* OID of collation of result */
- Oid opcollid;
+ Oid opcollid pg_node_attr(jumble_ignore);
/* OID of collation that operator should use */
- Oid inputcollid;
+ Oid inputcollid pg_node_attr(jumble_ignore);
/* arguments to the operator (1 or 2) */
List *args;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(jumble_ignore);
} OpExpr;
/*
@@ -725,25 +763,25 @@ 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, 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, 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, jumble_ignore);
/* true for ANY, false for ALL */
bool useOr;
/* OID of collation that operator should use */
- Oid inputcollid;
+ Oid inputcollid pg_node_attr(jumble_ignore);
/* the scalar and array operands */
List *args;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(jumble_ignore);
} ScalarArrayOpExpr;
/*
@@ -765,7 +803,8 @@ typedef struct BoolExpr
Expr xpr;
BoolExprType boolop;
List *args; /* arguments to this expression */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} BoolExpr;
/*
@@ -838,9 +877,11 @@ typedef struct SubLink
SubLinkType subLinkType; /* see above */
int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */
Node *testexpr; /* outer-query test for ALL/ANY/ROWCOMPARE */
- List *operName; /* originally specified operator name */
+ List *operName pg_node_attr(jumble_ignore); /* originally specified
+ * operator name */
Node *subselect; /* subselect as Query* or raw parsetree */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} SubLink;
/*
@@ -948,10 +989,13 @@ typedef struct FieldSelect
Expr xpr;
Expr *arg; /* input expression */
AttrNumber fieldnum; /* attribute number of field to extract */
- Oid resulttype; /* type of the field (result type of this
- * node) */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation of the field */
+ Oid resulttype pg_node_attr(jumble_ignore); /* type of the field
+ * (result type of this
+ * node) */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod
+ * (usually -1) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * the field */
} FieldSelect;
/* ----------------
@@ -977,8 +1021,10 @@ typedef struct FieldStore
Expr xpr;
Expr *arg; /* input tuple value */
List *newvals; /* new value(s) for field(s) */
- List *fieldnums; /* integer list of field attnums */
- Oid resulttype; /* type of result (same as type of arg) */
+ List *fieldnums pg_node_attr(jumble_ignore); /* integer list of field
+ * attnums */
+ Oid resulttype pg_node_attr(jumble_ignore); /* type of result (same
+ * as type of arg) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
} FieldStore;
@@ -1000,10 +1046,14 @@ typedef struct RelabelType
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion expression */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm relabelformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod
+ * (usually -1) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm relabelformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} RelabelType;
/* ----------------
@@ -1021,9 +1071,12 @@ typedef struct CoerceViaIO
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion */
/* output typmod is not stored, but is presumed -1 */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm coerceformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CoerceViaIO;
/* ----------------
@@ -1045,10 +1098,14 @@ typedef struct ArrayCoerceExpr
Expr *arg; /* input expression (yields an array) */
Expr *elemexpr; /* expression representing per-element work */
Oid resulttype; /* output type of coercion (an array type) */
- int32 resulttypmod; /* output typmod (also element typmod) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod (also
+ * element typmod) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm coerceformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} ArrayCoerceExpr;
/* ----------------
@@ -1070,8 +1127,10 @@ typedef struct ConvertRowtypeExpr
Expr *arg; /* input expression */
Oid resulttype; /* output type (always a composite type) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
- CoercionForm convertformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ CoercionForm convertformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} ConvertRowtypeExpr;
/*----------
@@ -1086,7 +1145,8 @@ typedef struct CollateExpr
Expr xpr;
Expr *arg; /* input expression */
Oid collOid; /* collation's OID */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CollateExpr;
/*----------
@@ -1114,12 +1174,15 @@ typedef struct CollateExpr
typedef struct CaseExpr
{
Expr xpr;
- Oid casetype; /* type of expression result */
- Oid casecollid; /* OID of collation, or InvalidOid if none */
+ Oid casetype pg_node_attr(jumble_ignore); /* type of expression
+ * result */
+ Oid casecollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
Expr *arg; /* implicit equality comparison argument */
List *args; /* the arguments (list of WHEN clauses) */
Expr *defresult; /* the default result (ELSE clause) */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CaseExpr;
/*
@@ -1130,7 +1193,8 @@ typedef struct CaseWhen
Expr xpr;
Expr *expr; /* condition expression */
Expr *result; /* substitution result */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CaseWhen;
/*
@@ -1157,8 +1221,10 @@ typedef struct CaseTestExpr
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
+ int32 typeMod pg_node_attr(jumble_ignore); /* typemod for
+ * substituted value */
+ Oid collation pg_node_attr(jumble_ignore); /* collation for the
+ * substituted value */
} CaseTestExpr;
/*
@@ -1172,12 +1238,17 @@ typedef struct CaseTestExpr
typedef struct ArrayExpr
{
Expr xpr;
- Oid array_typeid; /* type of expression result */
- Oid array_collid; /* OID of collation, or InvalidOid if none */
- Oid element_typeid; /* common type of array elements */
+ Oid array_typeid pg_node_attr(jumble_ignore); /* type of expression
+ * result */
+ Oid array_collid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ Oid element_typeid pg_node_attr(jumble_ignore); /* common type of array
+ * elements */
List *elements; /* the array elements or sub-arrays */
- bool multidims; /* true if elements are sub-arrays */
- int location; /* token location, or -1 if unknown */
+ bool multidims pg_node_attr(jumble_ignore); /* true if elements are
+ * sub-arrays */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} ArrayExpr;
/*
@@ -1205,7 +1276,8 @@ typedef struct RowExpr
{
Expr xpr;
List *args; /* the fields */
- Oid row_typeid; /* RECORDOID or a composite type's ID */
+ Oid row_typeid pg_node_attr(jumble_ignore); /* RECORDOID or a
+ * composite type's ID */
/*
* row_typeid cannot be a domain over composite, only plain composite. To
@@ -1219,9 +1291,12 @@ typedef struct RowExpr
* We don't need to store a collation either. The result type is
* necessarily composite, and composite types never have a collation.
*/
- CoercionForm row_format; /* how to display this node */
- List *colnames; /* list of String, or NIL */
- int location; /* token location, or -1 if unknown */
+ CoercionForm row_format pg_node_attr(jumble_ignore); /* how to display this
+ * node */
+ List *colnames pg_node_attr(jumble_ignore); /* list of String, or
+ * NIL */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} RowExpr;
/*
@@ -1253,9 +1328,14 @@ typedef struct RowCompareExpr
{
Expr xpr;
RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
- List *opnos; /* OID list of pairwise comparison ops */
- List *opfamilies; /* OID list of containing operator families */
- List *inputcollids; /* OID list of collations for comparisons */
+ List *opnos pg_node_attr(jumble_ignore); /* OID list of pairwise
+ * comparison ops */
+ List *opfamilies pg_node_attr(jumble_ignore); /* OID list of
+ * containing operator
+ * families */
+ List *inputcollids pg_node_attr(jumble_ignore); /* OID list of
+ * collations for
+ * comparisons */
List *largs; /* the left-hand input arguments */
List *rargs; /* the right-hand input arguments */
} RowCompareExpr;
@@ -1266,10 +1346,13 @@ typedef struct RowCompareExpr
typedef struct CoalesceExpr
{
Expr xpr;
- Oid coalescetype; /* type of expression result */
- Oid coalescecollid; /* OID of collation, or InvalidOid if none */
+ Oid coalescetype pg_node_attr(jumble_ignore); /* type of expression
+ * result */
+ Oid coalescecollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
List *args; /* the arguments */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CoalesceExpr;
/*
@@ -1284,12 +1367,16 @@ typedef enum MinMaxOp
typedef struct MinMaxExpr
{
Expr xpr;
- Oid minmaxtype; /* common type of arguments and result */
- Oid minmaxcollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
+ Oid minmaxtype pg_node_attr(jumble_ignore); /* common type of
+ * arguments and result */
+ Oid minmaxcollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * result */
+ Oid inputcollid pg_node_attr(jumble_ignore); /* OID of collation that
+ * function should use */
MinMaxOp op; /* function to execute */
List *args; /* the arguments */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} MinMaxExpr;
/*
@@ -1325,14 +1412,18 @@ typedef struct XmlExpr
{
Expr xpr;
XmlExprOp op; /* xml function ID */
- char *name; /* name in xml(NAME foo ...) syntaxes */
+ char *name pg_node_attr(jumble_ignore); /* name in xml(NAME foo
+ * ...) syntaxes */
List *named_args; /* non-XML expressions for xml_attributes */
- List *arg_names; /* parallel list of String values */
+ List *arg_names pg_node_attr(jumble_ignore); /* parallel list of
+ * String values */
List *args; /* list of expressions */
- XmlOptionType xmloption; /* DOCUMENT or CONTENT */
- Oid type; /* target type/typmod for XMLSERIALIZE */
- int32 typmod;
- int location; /* token location, or -1 if unknown */
+ XmlOptionType xmloption pg_node_attr(jumble_ignore); /* DOCUMENT or CONTENT */
+ Oid type pg_node_attr(jumble_ignore); /* target type/typmod for
+ * XMLSERIALIZE */
+ int32 typmod pg_node_attr(jumble_ignore);
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} XmlExpr;
/* ----------------
@@ -1364,8 +1455,11 @@ typedef struct NullTest
Expr xpr;
Expr *arg; /* input expression */
NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
- bool argisrow; /* T to perform field-by-field null checks */
- int location; /* token location, or -1 if unknown */
+ bool argisrow pg_node_attr(jumble_ignore); /* T to perform
+ * field-by-field null
+ * checks */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} NullTest;
/*
@@ -1387,7 +1481,8 @@ typedef struct BooleanTest
Expr xpr;
Expr *arg; /* input expression */
BoolTestType booltesttype; /* test type */
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} BooleanTest;
/*
@@ -1404,10 +1499,14 @@ typedef struct CoerceToDomain
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* domain type ID (result type) */
- int32 resulttypmod; /* output typmod (currently always -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coercionformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod
+ * (currently always -1) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm coercionformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CoerceToDomain;
/*
@@ -1423,9 +1522,12 @@ typedef struct CoerceToDomainValue
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
- int location; /* token location, or -1 if unknown */
+ int32 typeMod pg_node_attr(jumble_ignore); /* typemod for
+ * substituted value */
+ Oid collation pg_node_attr(jumble_ignore); /* collation for the
+ * substituted value */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} CoerceToDomainValue;
/*
@@ -1439,9 +1541,12 @@ typedef struct SetToDefault
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
- int location; /* token location, or -1 if unknown */
+ int32 typeMod pg_node_attr(jumble_ignore); /* typemod for
+ * substituted value */
+ Oid collation pg_node_attr(jumble_ignore); /* collation for the
+ * substituted value */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
} SetToDefault;
/*
@@ -1554,13 +1659,16 @@ typedef struct TargetEntry
Expr xpr;
Expr *expr; /* expression to evaluate */
AttrNumber resno; /* attribute number (see notes above) */
- char *resname; /* name of the column (could be NULL) */
+ char *resname pg_node_attr(jumble_ignore); /* name of the column
+ * (could be NULL) */
Index ressortgroupref; /* nonzero if referenced by a sort/group
* clause */
- Oid resorigtbl; /* OID of column's source table */
- AttrNumber resorigcol; /* column's number in source table */
- bool resjunk; /* set to true to eliminate the attribute from
- * final target list */
+ Oid resorigtbl pg_node_attr(jumble_ignore); /* OID of column's
+ * source table */
+ AttrNumber resorigcol pg_node_attr(jumble_ignore); /* column's number in
+ * source table */
+ /* set to true to eliminate the attribute from final target list */
+ bool resjunk pg_node_attr(jumble_ignore);
} TargetEntry;
@@ -1642,10 +1750,13 @@ typedef struct JoinExpr
bool isNatural; /* Natural join? Will need to shape table */
Node *larg; /* left subtree */
Node *rarg; /* right subtree */
- List *usingClause; /* USING clause, if any (list of String) */
- Alias *join_using_alias; /* alias attached to USING clause, if any */
+ List *usingClause pg_node_attr(jumble_ignore); /* USING clause, if any
+ * (list of String) */
+ Alias *join_using_alias pg_node_attr(jumble_ignore); /* alias attached to
+ * USING clause, if any */
Node *quals; /* qualifiers on join, if any */
- Alias *alias; /* user-written alias clause, if any */
+ Alias *alias pg_node_attr(jumble_ignore); /* user-written alias
+ * clause, if any */
int rtindex; /* RT index assigned for join, or 0 */
} JoinExpr;
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 4368c30fdb..aac5f59feb 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -19,6 +19,7 @@ OBJS = \
copyfuncs.o \
equalfuncs.o \
extensible.o \
+ jumblefuncs.o \
list.o \
makefuncs.o \
multibitmapset.o \
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..e97a918e95 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -47,6 +47,7 @@ FILES IN THIS DIRECTORY (src/backend/nodes/)
General-purpose node manipulation functions:
copyfuncs.c - copy a node tree (*)
equalfuncs.c - compare two node trees (*)
+ jumblefuncs.c - compute a node tree for query jumbling (*)
outfuncs.c - convert a node tree to text representation (*)
readfuncs.c - convert text representation back to a node tree (*)
makefuncs.c - creator functions for some common node types
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 7212bc486f..4d9cf83230 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -119,6 +119,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_jumble;
# node types we don't want read support for
my @no_read;
# node types we don't want read/write support for
@@ -153,12 +155,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_jumble, qw(List);
push @special_read_write, qw(List);
# Nodes with custom copy/equal implementations are skipped from
@@ -330,6 +333,10 @@ foreach my $infile (@ARGV)
push @no_copy, $in_struct;
push @no_equal, $in_struct;
}
+ elsif ($attr eq 'no_jumble')
+ {
+ push @no_jumble, $in_struct;
+ }
elsif ($attr eq 'no_read')
{
push @no_read, $in_struct;
@@ -455,6 +462,7 @@ foreach my $infile (@ARGV)
equal_as_scalar
equal_ignore
equal_ignore_if_zero
+ jumble_ignore
read_write_ignore
write_only_relids
write_only_nondefault_pathtarget
@@ -1223,6 +1231,91 @@ close $ofs;
close $rfs;
+# jumblefuncs.c
+
+push @output_files, 'jumblefuncs.funcs.c';
+open my $jff, '>', "$output_path/jumblefuncs.funcs.c$tmpext" or die $!;
+push @output_files, 'jumblefuncs.switch.c';
+open my $jfs, '>', "$output_path/jumblefuncs.switch.c$tmpext" or die $!;
+
+printf $jff $header_comment, 'jumblefuncs.funcs.c';
+printf $jfs $header_comment, 'jumblefuncs.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_jumble = (elem $n, @no_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_jumble;
+
+ print $jff "
+static void
+_jumble${n}(JumbleState *jstate, Node *node)
+{
+\t${n} *expr = (${n} *) node;\n
+" unless $struct_no_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 $jumble_ignore = $struct_no_jumble;
+
+ # extract per-field attributes
+ foreach my $a (@a)
+ {
+ if ($a eq 'jumble_ignore')
+ {
+ $jumble_ignore = 1;
+ }
+ }
+
+ # node type
+ if (($t =~ /^(\w+)\*$/ or $t =~ /^struct\s+(\w+)\*$/)
+ and elem $1, @node_types)
+ {
+ print $jff "\tJUMBLE_NODE($f);\n"
+ unless $jumble_ignore;
+ }
+ elsif ($t eq 'int' && $f =~ 'location$')
+ {
+ print $jff "\tJUMBLE_LOCATION($f);\n"
+ unless $jumble_ignore;
+ }
+ elsif ($t eq 'char*')
+ {
+ print $jff "\tJUMBLE_STRING($f);\n"
+ unless $jumble_ignore;
+ }
+ else
+ {
+ print $jff "\tJUMBLE_FIELD($f);\n"
+ unless $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_jumble;
+ }
+
+ print $jff "}
+" unless $struct_no_jumble;
+}
+
+close $jff;
+close $jfs;
+
# now rename the temporary files to their final names
foreach my $file (@output_files)
{
diff --git a/src/backend/nodes/jumblefuncs.c b/src/backend/nodes/jumblefuncs.c
new file mode 100644
index 0000000000..b65e022d17
--- /dev/null
+++ b/src/backend/nodes/jumblefuncs.c
@@ -0,0 +1,358 @@
+/*-------------------------------------------------------------------------
+ *
+ * jumblefuncs.c
+ * Query normalization and fingerprinting.
+ *
+ * Normalization is a process whereby similar queries, typically differing only
+ * in their constants (though the exact rules are somewhat more subtle than
+ * that) are recognized as equivalent, and are tracked as a single entry. This
+ * is particularly useful for non-prepared queries.
+ *
+ * Normalization is implemented by fingerprinting queries, selectively
+ * serializing those fields of each query tree's nodes that are judged to be
+ * essential to the query. This is referred to as a query jumble. This is
+ * distinct from a regular serialization in that various extraneous
+ * information is ignored as irrelevant or not essential to the query, such
+ * as the collations of Vars and, most notably, the values of constants.
+ *
+ * This jumble is acquired at the end of parse analysis of each query, and
+ * a 64-bit hash of it is stored into the query's Query.queryId field.
+ * The server then copies this value around, making it available in plan
+ * tree(s) generated from the query. The executor can then use this value
+ * to blame query costs on the proper queryId.
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/nodes/jumblefuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "common/hashfn.h"
+#include "miscadmin.h"
+#include "parser/scansup.h"
+#include "utils/queryjumble.h"
+
+#define JUMBLE_SIZE 1024 /* query serialization buffer size */
+
+/* GUC parameters */
+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 void AppendJumble(JumbleState *jstate,
+ const unsigned char *item, Size size);
+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
+ * relevant part of the string.
+ */
+const char *
+CleanQuerytext(const char *query, int *location, int *len)
+{
+ int query_location = *location;
+ int query_len = *len;
+
+ /* First apply starting offset, unless it's -1 (unknown). */
+ if (query_location >= 0)
+ {
+ Assert(query_location <= strlen(query));
+ query += query_location;
+ /* Length of 0 (or -1) means "rest of string" */
+ if (query_len <= 0)
+ query_len = strlen(query);
+ else
+ Assert(query_len <= strlen(query));
+ }
+ else
+ {
+ /* If query location is unknown, distrust query_len as well */
+ query_location = 0;
+ query_len = strlen(query);
+ }
+
+ /*
+ * Discard leading and trailing whitespace, too. Use scanner_isspace()
+ * not libc's isspace(), because we want to match the lexer's behavior.
+ */
+ while (query_len > 0 && scanner_isspace(query[0]))
+ query++, query_location++, query_len--;
+ while (query_len > 0 && scanner_isspace(query[query_len - 1]))
+ query_len--;
+
+ *location = query_location;
+ *len = query_len;
+
+ return query;
+}
+
+JumbleState *
+JumbleQuery(Query *query, const char *querytext)
+{
+ JumbleState *jstate = NULL;
+
+ Assert(IsQueryIdEnabled());
+
+ 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, to
+ * prevent confusion with the utility-statement case.
+ */
+ if (query->queryId == UINT64CONST(0))
+ query->queryId = UINT64CONST(1);
+
+ return jstate;
+}
+
+/*
+ * Enables query identifier computation.
+ *
+ * Third-party plugins can use this function to inform core that they require
+ * a query identifier to be computed.
+ */
+void
+EnableQueryId(void)
+{
+ if (compute_query_id != COMPUTE_QUERY_ID_OFF)
+ query_id_enabled = true;
+}
+
+/*
+ * AppendJumble: Append a value that is substantive in a given query to
+ * the current jumble.
+ */
+static void
+AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
+{
+ unsigned char *jumble = jstate->jumble;
+ Size jumble_len = jstate->jumble_len;
+
+ /*
+ * Whenever the jumble buffer is full, we hash the current contents and
+ * reset the buffer to contain just that hash value, thus relying on the
+ * hash to summarize everything so far.
+ */
+ while (size > 0)
+ {
+ Size part_size;
+
+ if (jumble_len >= JUMBLE_SIZE)
+ {
+ uint64 start_hash;
+
+ start_hash = DatumGetUInt64(hash_any_extended(jumble,
+ JUMBLE_SIZE, 0));
+ memcpy(jumble, &start_hash, sizeof(start_hash));
+ jumble_len = sizeof(start_hash);
+ }
+ part_size = Min(size, JUMBLE_SIZE - jumble_len);
+ memcpy(jumble + jumble_len, item, part_size);
+ jumble_len += part_size;
+ item += part_size;
+ size -= part_size;
+ }
+ jstate->jumble_len = jumble_len;
+}
+
+/*
+ * 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++;
+ }
+}
+
+#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)
+
+#include "jumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+ Node *expr = node;
+
+ if (expr == NULL)
+ return;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /*
+ * We always emit the node's NodeTag, then any additional fields that are
+ * considered significant, and then we recurse to any child nodes.
+ */
+ JUMBLE_FIELD(type);
+
+ switch (nodeTag(expr))
+ {
+#include "jumblefuncs.switch.c"
+
+ case T_List:
+ case T_IntList:
+ case T_OidList:
+ case T_XidList:
+ _jumbleList(jstate, expr);
+ break;
+
+ case T_RangeTblEntry:
+ _jumbleRangeTblEntry(jstate, expr);
+ break;
+
+ default:
+ /* Only a warning, since we can stumble along anyway */
+ elog(WARNING, "unrecognized node type: %d",
+ (int) nodeTag(expr));
+ break;
+ }
+
+ /* Special cases */
+ switch (nodeTag(expr))
+ {
+ case T_Param:
+ {
+ Param *p = (Param *) node;
+
+ /* Also, track the highest external Param id */
+ if (p->paramkind == PARAM_EXTERN &&
+ p->paramid > jstate->highest_extern_param_id)
+ jstate->highest_extern_param_id = p->paramid;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+ List *expr = (List *) node;
+ ListCell *l;
+
+ switch (expr->type)
+ {
+ case T_List:
+ foreach(l, expr)
+ _jumbleNode(jstate, lfirst(l));
+ break;
+ case T_IntList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_int(l));
+ break;
+ case T_OidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+ break;
+ case T_XidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+ break;
+ default:
+ elog(ERROR, "unrecognized list node type: %d",
+ (int) expr->type);
+ return;
+ }
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+ RangeTblEntry *expr = (RangeTblEntry *) node;
+
+ JUMBLE_FIELD(rtekind);
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(tablesample);
+ JUMBLE_FIELD(inh);
+ break;
+ case RTE_SUBQUERY:
+ JUMBLE_NODE(subquery);
+ break;
+ case RTE_JOIN:
+ JUMBLE_FIELD(jointype);
+ break;
+ case RTE_FUNCTION:
+ JUMBLE_NODE(functions);
+ break;
+ case RTE_TABLEFUNC:
+ JUMBLE_NODE(tablefunc);
+ break;
+ case RTE_VALUES:
+ JUMBLE_NODE(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.
+ */
+ JUMBLE_STRING(ctename);
+ JUMBLE_FIELD(ctelevelsup);
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ JUMBLE_STRING(enrname);
+ break;
+ case RTE_RESULT:
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+ break;
+ }
+}
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index c4f3897ef2..323a1f07d0 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -18,6 +18,7 @@ backend_sources += files(
nodefunc_sources = files(
'copyfuncs.c',
'equalfuncs.c',
+ 'jumblefuncs.c',
'outfuncs.c',
'readfuncs.c',
)
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index b9ee4eb48a..2910032930 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -26,7 +26,6 @@ OBJS = \
pg_rusage.o \
ps_status.o \
queryenvironment.o \
- queryjumble.o \
rls.o \
sampling.o \
superuser.o \
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index e7a9730229..6caa2f5a2c 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -9,7 +9,6 @@ backend_sources += files(
'pg_rusage.c',
'ps_status.c',
'queryenvironment.c',
- 'queryjumble.c',
'rls.c',
'sampling.c',
'superuser.c',
diff --git a/src/backend/utils/misc/queryjumble.c b/src/backend/utils/misc/queryjumble.c
deleted file mode 100644
index 0ace74de78..0000000000
--- a/src/backend/utils/misc/queryjumble.c
+++ /dev/null
@@ -1,861 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * queryjumble.c
- * Query normalization and fingerprinting.
- *
- * Normalization is a process whereby similar queries, typically differing only
- * in their constants (though the exact rules are somewhat more subtle than
- * that) are recognized as equivalent, and are tracked as a single entry. This
- * is particularly useful for non-prepared queries.
- *
- * Normalization is implemented by fingerprinting queries, selectively
- * serializing those fields of each query tree's nodes that are judged to be
- * essential to the query. This is referred to as a query jumble. This is
- * distinct from a regular serialization in that various extraneous
- * information is ignored as irrelevant or not essential to the query, such
- * as the collations of Vars and, most notably, the values of constants.
- *
- * This jumble is acquired at the end of parse analysis of each query, and
- * a 64-bit hash of it is stored into the query's Query.queryId field.
- * The server then copies this value around, making it available in plan
- * tree(s) generated from the query. The executor can then use this value
- * to blame query costs on the proper queryId.
- *
- * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/utils/misc/queryjumble.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "common/hashfn.h"
-#include "miscadmin.h"
-#include "parser/scansup.h"
-#include "utils/queryjumble.h"
-
-#define JUMBLE_SIZE 1024 /* query serialization buffer size */
-
-/* GUC parameters */
-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);
-
-/*
- * Given a possibly multi-statement source string, confine our attention to the
- * relevant part of the string.
- */
-const char *
-CleanQuerytext(const char *query, int *location, int *len)
-{
- int query_location = *location;
- int query_len = *len;
-
- /* First apply starting offset, unless it's -1 (unknown). */
- if (query_location >= 0)
- {
- Assert(query_location <= strlen(query));
- query += query_location;
- /* Length of 0 (or -1) means "rest of string" */
- if (query_len <= 0)
- query_len = strlen(query);
- else
- Assert(query_len <= strlen(query));
- }
- else
- {
- /* If query location is unknown, distrust query_len as well */
- query_location = 0;
- query_len = strlen(query);
- }
-
- /*
- * Discard leading and trailing whitespace, too. Use scanner_isspace()
- * not libc's isspace(), because we want to match the lexer's behavior.
- */
- while (query_len > 0 && scanner_isspace(query[0]))
- query++, query_location++, query_len--;
- while (query_len > 0 && scanner_isspace(query[query_len - 1]))
- query_len--;
-
- *location = query_location;
- *len = query_len;
-
- return query;
-}
-
-JumbleState *
-JumbleQuery(Query *query, const char *querytext)
-{
- JumbleState *jstate = NULL;
-
- 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 */
- 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))
- query->queryId = UINT64CONST(1);
- }
-
- return jstate;
-}
-
-/*
- * Enables query identifier computation.
- *
- * Third-party plugins can use this function to inform core that they require
- * a query identifier to be computed.
- */
-void
-EnableQueryId(void)
-{
- if (compute_query_id != COMPUTE_QUERY_ID_OFF)
- 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.
- */
-static void
-AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
-{
- unsigned char *jumble = jstate->jumble;
- Size jumble_len = jstate->jumble_len;
-
- /*
- * Whenever the jumble buffer is full, we hash the current contents and
- * reset the buffer to contain just that hash value, thus relying on the
- * hash to summarize everything so far.
- */
- while (size > 0)
- {
- Size part_size;
-
- if (jumble_len >= JUMBLE_SIZE)
- {
- uint64 start_hash;
-
- start_hash = DatumGetUInt64(hash_any_extended(jumble,
- JUMBLE_SIZE, 0));
- memcpy(jumble, &start_hash, sizeof(start_hash));
- jumble_len = sizeof(start_hash);
- }
- part_size = Min(size, JUMBLE_SIZE - jumble_len);
- memcpy(jumble + jumble_len, item, part_size);
- jumble_len += part_size;
- item += part_size;
- size -= part_size;
- }
- jstate->jumble_len = jumble_len;
-}
-
-/*
- * 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
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- 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;
- }
- }
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
- ListCell *lc;
-
- foreach(lc, rowMarks)
- {
- RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
- if (!rowmark->pushedDown)
- {
- APP_JUMB(rowmark->rti);
- APP_JUMB(rowmark->strength);
- APP_JUMB(rowmark->waitPolicy);
- }
- }
-}
-
-/*
- * 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)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * 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);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- 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);
- }
- break;
- 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 */
- 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);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- 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);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- 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;
-
- APP_JUMB(mergeaction->matched);
- APP_JUMB(mergeaction->commandType);
- JumbleExpr(jstate, mergeaction->qual);
- JumbleExpr(jstate, (Node *) mergeaction->targetList);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- 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);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- 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);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* 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));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- 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);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- 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++;
- }
-}
--
2.38.1
On Wed, 7 Dec 2022 at 13:27, Michael Paquier <michael@paquier.xyz> wrote:
Hi all,
This thread is a follow-up of the recent discussion about query
jumbling with DDL statements, where the conclusion was that we'd want
to generate all this code automatically for all the nodes:
/messages/by-id/36e5bffe-e989-194f-85c8-06e7bc88e6f7@amazon.comWhat this patch allows to do it to compute the same query ID for
utility statements using their parsed Node state instead of their
string, meaning that things like "BEGIN", "bEGIN" or "begin" would be
treated the same, for example. But the main idea is not only that.I have implemented that as of the attached, where the following things
are done:
- queryjumble.c is moved to src/backend/nodes/, to stick with the
other things for node equal/read/write/copy, renamed to
jumblefuncs.c.
- gen_node_support.c is extended to generate the functions and the
switch for the jumbling. There are a few exceptions, as of the Lists
and RangeTblEntry to do the jumbling consistently.
- Two pg_node_attr() are added in consistency with the existing ones:
no_jumble to discard completely a node from the the query jumbling
and jumble_ignore to discard one field from the jumble.The patch is in a rather good shape, passes check-world and the CI,
but there are a few things that need to be discussed IMO. Things
could be perhaps divided in more patches, now the areas touched are
quite different so it did not look like a big deal to me as the
changes touch different areas and are straight-forward.The location of the Nodes is quite invasive because we only care about
that for T_Const now in the query jumbling, and this could be
compensated with a third pg_node_attr() that tracks for the "int
location" of a Node whether it should participate in the jumbling or
not. There is also an argument where we would want to not include by
default new fields added to a Node, but that would require added more
pg_node_attr() than what's done here.Note that the plan is to extend the normalization to some other parts
of the Nodes, like CALL and SET as mentioned on the other thread. I
have done nothing about that yet but doing so can be done in a few
lines with the facility presented here (aka just add a location
field). Hence, the normalization is consistent with the existing
queryjumble.c for the fields and the nodes processed.In this patch, things are done so as the query ID is not computed
anymore from the query string but from the Query. I still need to
study the performance impact of that with short queries. If things
prove to be noticeable in some cases, this stuff could be switched to
use a new GUC where we could have a code path for the computation of
utilityStmt using its string as a fallback. I am not sure that I want
to enter in this level of complications, though, to keep things
simple, but that's yet to be done.A bit more could be cut but pg_ident got in the way.. There are also
a few things for pg_stat_statements where a query ID of 0 can be
implied for utility statements in some cases.Generating this code leads to an overall removal of code as what
queryjumble.c is generated automatically:
13 files changed, 901 insertions(+), 1113 deletions(-)I am adding that to the next commit fest.
The patch does not apply on top of HEAD as in [1]http://cfbot.cputube.org/patch_41_4047.log, please post a rebased patch:
=== Applying patches on top of PostgreSQL commit ID
b82557ecc2ebbf649142740a1c5ce8d19089f620 ===
=== applying patch
./0001-Support-for-automated-query-jumble-with-all-Nodes.patch
...
patching file src/backend/utils/misc/queryjumble.c
Hunk #1 FAILED at 1.
Not deleting file src/backend/utils/misc/queryjumble.c as content
differs from patch
1 out of 1 hunk FAILED -- saving rejects to file
src/backend/utils/misc/queryjumble.c.rej
[1]: http://cfbot.cputube.org/patch_41_4047.log
Regards,
Vignesh
On 07.12.22 08:56, Michael Paquier wrote:
The location of the Nodes is quite invasive because we only care about
that for T_Const now in the query jumbling, and this could be
compensated with a third pg_node_attr() that tracks for the "int
location" of a Node whether it should participate in the jumbling or
not.
The generation script already has a way to identify location fields, by
($t eq 'int' && $f =~ 'location$'), so you could use that as well.
There is also an argument where we would want to not include by
default new fields added to a Node, but that would require added more
pg_node_attr() than what's done here.
I'm concerned about the large number of additional field annotations
this adds. We have been careful so far to document the use of each
attribute, e.g., *why* does a field not need to be copied etc. This
patch adds dozens and dozens of annotations without any explanation at
all. Now, the code this replaces also has no documentation, but maybe
this is the time to add some.
On Sat, Jan 07, 2023 at 07:37:49AM +0100, Peter Eisentraut wrote:
The generation script already has a way to identify location fields, by ($t
eq 'int' && $f =~ 'location$'), so you could use that as well.
I recall that some of the nodes may need renames to map with this
choice. That could be just one patch on top of the actual feature.
I'm concerned about the large number of additional field annotations this
adds. We have been careful so far to document the use of each attribute,
e.g., *why* does a field not need to be copied etc. This patch adds dozens
and dozens of annotations without any explanation at all. Now, the code
this replaces also has no documentation, but maybe this is the time to add
some.
In most cases, the addition of the node marker would be enough to
self-explain why they are included, but there is a trend for a lot of
the nodes when it comes to collations and typmods where we don't want
to see these in the jumbling calculations. I'll look at providing
more info for all that. (Note: I'm out for now.)
--
Michael
On Sat, Jan 07, 2023 at 07:37:49AM +0100, Peter Eisentraut wrote:
On 07.12.22 08:56, Michael Paquier wrote:
The location of the Nodes is quite invasive because we only care about
that for T_Const now in the query jumbling, and this could be
compensated with a third pg_node_attr() that tracks for the "int
location" of a Node whether it should participate in the jumbling or
not.The generation script already has a way to identify location fields, by ($t
eq 'int' && $f =~ 'location$'), so you could use that as well.
I did not recall exactly everything here, but there are two parts to
the logic:
- gen_node_support.pl uses exactly this condition when scanning the
nodes to put the correct macro to mark a location to track, calling
down RecordConstLocation().
- Marking a bunch of nodes as jumble_ignore is actually necessary, or
we may finish by silencing parts of queries that should be
semantically unrelevant to the queries jumbled (ColumnRef is one).
Using a "jumble_ignore" flag is equally invasive to using an
"jumble_include" flag for each field, I am afraid, as the number of
fields in the nodes included in jumbles is pretty equivalent to the
number of fields ignored. I tend to prefer the approach of ignoring
things though, which is more consistent with the practive for node
read, write and copy.
Anyway, when it comes to the location, another thing that can be
considered here would be to require a node-level flag for the nodes on
which we want to track the location. This overlaps a bit with the
fields satisfying "($t eq 'int' && $f =~ 'location$')", but it removes
most of the code changes like this one as at the end we only care
about the location for Const nodes:
- int location; /* token location, or -1 if unknown */
+ int location pg_node_attr(jumble_ignore); /* token location, or -1
+ * if unknown */
I have taken this approach in v2 of the patch, shaving ~100 lines of
more code as there is no need to mark all these location fields with
"jumble_ignore" anymore, except for Const, of course.
There is also an argument where we would want to not include by
default new fields added to a Node, but that would require added more
pg_node_attr() than what's done here.I'm concerned about the large number of additional field annotations this
adds. We have been careful so far to document the use of each attribute,
e.g., *why* does a field not need to be copied etc. This patch adds dozens
and dozens of annotations without any explanation at all. Now, the code
this replaces also has no documentation, but maybe this is the time to add
some.
That's fair, though it is not doing to buy us much to update all the
nodes with similar small comments, as well. As far as I know, there
are basiscally three things here: typmods, collation information, and
internal data of the nodes stored during parse-analyze. I have added
more documentation to track what looks like the most relevant areas.
I have begun running some performance tests with this stuff and HEAD
to see if this leads to any difference in the query ID compilation
(compute_query_id = on, on scissors roughly) with a simple set of
short commands (like BEGIN/COMMIT) or longer ones, and I am seeing a
speedup trend actually (?). I still need to think more about a set of
tests here, but I think that micro-benchmarking of JumbleQuery() is
the most adapted approach to minimize the noise, with a few nodes of
various sizes (Const, Query, ColumnRef, anything..).
Thoughts?
--
Michael
Attachments:
v2-0001-Support-for-automated-query-jumble-with-all-Nodes.patchtext/x-diff; charset=us-asciiDownload
From 28d8b2ede0c7cfe25a4efa2d49f05106d61b55d1 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 13 Jan 2023 16:53:16 +0900
Subject: [PATCH v2] Support for automated query jumble with all Nodes
This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
src/include/nodes/bitmapset.h | 2 +-
src/include/nodes/nodes.h | 7 +
src/include/nodes/parsenodes.h | 184 ++++--
src/include/nodes/primnodes.h | 371 +++++++----
src/backend/nodes/Makefile | 1 +
src/backend/nodes/README | 1 +
src/backend/nodes/gen_node_support.pl | 105 +++-
src/backend/nodes/jumblefuncs.c | 358 +++++++++++
src/backend/nodes/meson.build | 1 +
src/backend/utils/misc/Makefile | 1 -
src/backend/utils/misc/meson.build | 1 -
src/backend/utils/misc/queryjumble.c | 861 --------------------------
12 files changed, 843 insertions(+), 1050 deletions(-)
create mode 100644 src/backend/nodes/jumblefuncs.c
delete mode 100644 src/backend/utils/misc/queryjumble.c
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 0dca6bc5fa..618c967a2b 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -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_jumble)
NodeTag type;
int nwords; /* number of words in array */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 10752e8011..52e219f838 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -59,6 +59,8 @@ typedef enum NodeTag
*
* - no_copy_equal: Shorthand for both no_copy and no_equal.
*
+ * - no_jumble: Does not support jumble() at all.
+ *
* - no_read: Does not support nodeRead() at all.
*
* - nodetag_only: Does not support copyObject(), equal(), outNode(),
@@ -97,6 +99,11 @@ typedef enum NodeTag
* - equal_ignore_if_zero: Ignore the field for equality if it is zero.
* (Otherwise, compare normally.)
*
+ * - jumble_ignore: Ignore the field for the query jumbling.
+ *
+ * - 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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cfeca96d53..6d7c23dfec 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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
{
@@ -123,47 +128,67 @@ typedef struct Query
CmdType commandType; /* select|insert|update|delete|merge|utility */
- QuerySource querySource; /* where did I come from? */
+ /* where did I come from? */
+ QuerySource querySource pg_node_attr(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, jumble_ignore, read_write_ignore, read_as(0));
- bool canSetTag; /* do I set the command result tag? */
+ /* do I set the command result tag? */
+ bool canSetTag pg_node_attr(jumble_ignore);
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
- int resultRelation; /* rtable index of target relation for
- * INSERT/UPDATE/DELETE/MERGE; 0 for SELECT */
+ /*
+ * rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
+ * SELECT. This is ignored in the query jumble as unrelated to the
+ * compilation of the query ID.
+ */
+ int resultRelation pg_node_attr(jumble_ignore);
- bool hasAggs; /* has aggregates in tlist or havingQual */
- bool hasWindowFuncs; /* has window functions in tlist */
- bool hasTargetSRFs; /* has set-returning functions in tlist */
- bool hasSubLinks; /* has subquery SubLink */
- bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
- bool hasRecursive; /* WITH RECURSIVE was specified */
- bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
- bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
- bool hasRowSecurity; /* rewriter has applied some RLS policy */
+ /* has aggregates in tlist or havingQual */
+ bool hasAggs pg_node_attr(jumble_ignore);
+ /* has window functions in tlist */
+ bool hasWindowFuncs pg_node_attr(jumble_ignore);
+ /* has set-returning functions in tlist */
+ bool hasTargetSRFs pg_node_attr(jumble_ignore);
+ bool hasSubLinks pg_node_attr(jumble_ignore); /* has subquery SubLink */
+ /* distinctClause is from DISTINCT ON */
+ bool hasDistinctOn pg_node_attr(jumble_ignore);
+ /* WITH RECURSIVE was specified */
+ bool hasRecursive pg_node_attr(jumble_ignore);
+ /* has INSERT/UPDATE/DELETE in WITH */
+ bool hasModifyingCTE pg_node_attr(jumble_ignore);
+ /* FOR [KEY] UPDATE/SHARE was specified */
+ bool hasForUpdate pg_node_attr(jumble_ignore);
+ /* rewriter has applied some RLS policy */
+ bool hasRowSecurity pg_node_attr(jumble_ignore);
- bool isReturn; /* is a RETURN statement */
+ bool isReturn pg_node_attr(jumble_ignore); /* is a RETURN statement */
List *cteList; /* WITH list (of CommonTableExpr's) */
List *rtable; /* list of range table entries */
- List *rteperminfos; /* list of RTEPermissionInfo nodes for the
- * rtable entries having perminfoindex > 0 */
+
+ /*
+ * list of RTEPermissionInfo nodes for the rtable entries having
+ * perminfoindex > 0
+ */
+ List *rteperminfos pg_node_attr(jumble_ignore);
FromExpr *jointree; /* table join tree (FROM and WHERE clauses);
* also USING clause for MERGE */
List *mergeActionList; /* list of actions for MERGE (only) */
- bool mergeUseOuterJoin; /* whether to use outer join */
+ /* whether to use outer join */
+ bool mergeUseOuterJoin pg_node_attr(jumble_ignore);
List *targetList; /* target list (of TargetEntry) */
- OverridingKind override; /* OVERRIDING clause */
+ OverridingKind override pg_node_attr(jumble_ignore); /* OVERRIDING clause */
OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
@@ -191,11 +216,14 @@ typedef struct Query
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
- List *constraintDeps; /* a list of pg_constraint OIDs that the query
- * depends on to be semantically valid */
+ /*
+ * A list of pg_constraint OIDs that the query depends on to be
+ * semantically valid
+ */
+ List *constraintDeps pg_node_attr(jumble_ignore);
- List *withCheckOptions; /* a list of WithCheckOption's (added
- * during rewrite) */
+ /* a list of WithCheckOption's (added during rewrite) */
+ List *withCheckOptions pg_node_attr(jumble_ignore);
/*
* The following two fields identify the portion of the source text string
@@ -204,7 +232,8 @@ typedef struct Query
* both be -1 meaning "unknown".
*/
int stmt_location; /* start location, or -1 if unknown */
- int stmt_len; /* length in bytes; 0 means "rest of string" */
+ /* length in bytes; 0 means "rest of string" */
+ int stmt_len pg_node_attr(jumble_ignore);
} Query;
@@ -994,7 +1023,7 @@ typedef enum RTEKind
typedef struct RangeTblEntry
{
- pg_node_attr(custom_read_write)
+ pg_node_attr(custom_read_write, no_jumble)
NodeTag type;
@@ -1217,20 +1246,28 @@ 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 needs only to track the function expression.
*/
typedef struct RangeTblFunction
{
NodeTag type;
Node *funcexpr; /* expression tree for func call */
- int funccolcount; /* number of columns it contributes to RTE */
+ int funccolcount pg_node_attr(jumble_ignore); /* number of columns it
+ * contributes to RTE */
/* These fields record the contents of a column definition list, if any: */
- List *funccolnames; /* column names (list of String) */
- List *funccoltypes; /* OID list of column type OIDs */
- List *funccoltypmods; /* integer list of column typmods */
- List *funccolcollations; /* OID list of column collation OIDs */
+ List *funccolnames pg_node_attr(jumble_ignore); /* column names (list of
+ * String) */
+ List *funccoltypes pg_node_attr(jumble_ignore); /* OID list of column
+ * type OIDs */
+ List *funccoltypmods pg_node_attr(jumble_ignore); /* integer list of
+ * column typmods */
+ List *funccolcollations pg_node_attr(jumble_ignore); /* OID list of column
+ * collation OIDs */
/* This is set during planning for use by the executor: */
- Bitmapset *funcparams; /* PARAM_EXEC Param IDs affecting this func */
+ Bitmapset *funcparams pg_node_attr(jumble_ignore); /* PARAM_EXEC Param IDs
+ * affecting this func */
} RangeTblFunction;
/*
@@ -1337,7 +1374,8 @@ typedef struct SortGroupClause
Oid eqop; /* the equality operator ('=' op) */
Oid sortop; /* the ordering operator ('<' op), or 0 */
bool nulls_first; /* do NULLs come before normal values? */
- bool hashable; /* can eqop be implemented by hashing? */
+ /* can eqop be implemented by hashing? */
+ bool hashable pg_node_attr(jumble_ignore);
} SortGroupClause;
/*
@@ -1402,7 +1440,7 @@ typedef enum GroupingSetKind
typedef struct GroupingSet
{
NodeTag type;
- GroupingSetKind kind;
+ GroupingSetKind kind pg_node_attr(jumble_ignore);
List *content;
int location;
} GroupingSet;
@@ -1423,25 +1461,37 @@ 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;
- char *name; /* window name (NULL in an OVER clause) */
- char *refname; /* referenced window name, if any */
+ char *name pg_node_attr(jumble_ignore); /* window name (NULL in an
+ * OVER clause) */
+ char *refname pg_node_attr(jumble_ignore); /* referenced window
+ * name, if any */
List *partitionClause; /* PARTITION BY list */
- List *orderClause; /* ORDER BY list */
+ List *orderClause pg_node_attr(jumble_ignore); /* ORDER BY list */
int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- List *runCondition; /* qual to help short-circuit execution */
- Oid startInRangeFunc; /* in_range function for startOffset */
- Oid endInRangeFunc; /* in_range function for endOffset */
- Oid inRangeColl; /* collation for in_range tests */
- bool inRangeAsc; /* use ASC sort order for in_range tests? */
- bool inRangeNullsFirst; /* nulls sort first for in_range tests? */
+ /* qual to help short-circuit execution */
+ List *runCondition pg_node_attr(jumble_ignore);
+ /* in_range function for startOffset */
+ Oid startInRangeFunc pg_node_attr(jumble_ignore);
+ /* in_range function for endOffset */
+ Oid endInRangeFunc pg_node_attr(jumble_ignore);
+ /* collation for in_range tests */
+ Oid inRangeColl pg_node_attr(jumble_ignore);
+ /* use ASC sort order for in_range tests? */
+ bool inRangeAsc pg_node_attr(jumble_ignore);
+ /* nulls sort first for in_range tests? */
+ bool inRangeNullsFirst pg_node_attr(jumble_ignore);
Index winref; /* ID referenced by window functions */
- bool copiedOrder; /* did we copy orderClause from refname? */
+ /* did we copy orderClause from refname? */
+ bool copiedOrder pg_node_attr(jumble_ignore);
} WindowClause;
/*
@@ -1556,17 +1606,26 @@ typedef struct CommonTableExpr
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(jumble_ignore);
+ CTECycleClause *cycle_clause pg_node_attr(jumble_ignore);
int location; /* token location, or -1 if unknown */
/* These fields are set during parse analysis: */
- bool cterecursive; /* is this CTE actually recursive? */
- int cterefcount; /* number of RTEs referencing this CTE
- * (excluding internal self-references) */
- List *ctecolnames; /* list of output column names */
- List *ctecoltypes; /* OID list of output column type OIDs */
- List *ctecoltypmods; /* integer list of output column typmods */
- List *ctecolcollations; /* OID list of column collation OIDs */
+ /* is this CTE actually recursive? */
+ bool cterecursive pg_node_attr(jumble_ignore);
+
+ /*
+ * Number of RTEs referencing this CTE (excluding internal
+ * self-references), irrelevant for query jumbling.
+ */
+ int cterefcount pg_node_attr(jumble_ignore);
+ List *ctecolnames pg_node_attr(jumble_ignore); /* list of output column
+ * names */
+ List *ctecoltypes pg_node_attr(jumble_ignore); /* OID list of output
+ * column type OIDs */
+ List *ctecoltypmods pg_node_attr(jumble_ignore); /* integer list of
+ * output column typmods */
+ List *ctecolcollations pg_node_attr(jumble_ignore); /* OID list of column
+ * collation OIDs */
} CommonTableExpr;
/* Convenience macro to get the output tlist of a CTE's query */
@@ -1603,10 +1662,11 @@ typedef struct MergeAction
NodeTag type;
bool matched; /* true=MATCHED, false=NOT MATCHED */
CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
- OverridingKind override; /* OVERRIDING clause */
+ OverridingKind override pg_node_attr(jumble_ignore); /* OVERRIDING clause */
Node *qual; /* transformed WHEN conditions */
List *targetList; /* the target list (of TargetEntry) */
- List *updateColnos; /* target attribute numbers of an UPDATE */
+ List *updateColnos pg_node_attr(jumble_ignore); /* target attribute
+ * numbers of an UPDATE */
} MergeAction;
/*
@@ -1815,11 +1875,15 @@ typedef struct SetOperationStmt
Node *rarg; /* right child */
/* Eventually add fields for CORRESPONDING spec here */
- /* Fields derived during parse analysis: */
- List *colTypes; /* OID list of output column type OIDs */
- List *colTypmods; /* integer list of output column typmods */
- List *colCollations; /* OID list of output column collation OIDs */
- List *groupClauses; /* a list of SortGroupClause's */
+ /* Fields derived during parse analysis, irrelevant for query jumbling */
+ List *colTypes pg_node_attr(jumble_ignore); /* OID list of output
+ * column type OIDs */
+ List *colTypmods pg_node_attr(jumble_ignore); /* integer list of
+ * output column typmods */
+ List *colCollations pg_node_attr(jumble_ignore); /* OID list of output
+ * column collation OIDs */
+ List *groupClauses pg_node_attr(jumble_ignore); /* a list of
+ * SortGroupClause's */
/* groupClauses is NIL if UNION ALL, but must be set otherwise */
} SetOperationStmt;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 83e40e56d3..3681587ca2 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -98,18 +98,27 @@ typedef struct RangeVar
typedef struct TableFunc
{
NodeTag type;
- List *ns_uris; /* list of namespace URI expressions */
- List *ns_names; /* list of namespace names or NULL */
+ List *ns_uris pg_node_attr(jumble_ignore); /* list of namespace URI
+ * expressions */
+ List *ns_names pg_node_attr(jumble_ignore); /* list of namespace
+ * names or NULL */
Node *docexpr; /* input document expression */
Node *rowexpr; /* row filter expression */
- List *colnames; /* column names (list of String) */
- List *coltypes; /* OID list of column type OIDs */
- List *coltypmods; /* integer list of column typmods */
- List *colcollations; /* OID list of column collation OIDs */
+ List *colnames pg_node_attr(jumble_ignore); /* column names (list of
+ * String) */
+ List *coltypes pg_node_attr(jumble_ignore); /* OID list of column
+ * type OIDs */
+ List *coltypmods pg_node_attr(jumble_ignore); /* integer list of
+ * column typmods */
+ List *colcollations pg_node_attr(jumble_ignore); /* OID list of column
+ * collation OIDs */
List *colexprs; /* list of column filter expressions */
- List *coldefexprs; /* list of column default expressions */
- Bitmapset *notnulls; /* nullability flag for each output column */
- int ordinalitycol; /* counts from 0; -1 if none specified */
+ List *coldefexprs pg_node_attr(jumble_ignore); /* list of column
+ * default expressions */
+ Bitmapset *notnulls pg_node_attr(jumble_ignore); /* nullability flag for
+ * each output column */
+ int ordinalitycol pg_node_attr(jumble_ignore); /* counts from 0; -1 if
+ * none specified */
int location; /* token location, or -1 if unknown */
} TableFunc;
@@ -217,11 +226,11 @@ typedef struct Var
AttrNumber varattno;
/* pg_type OID for the type of this var */
- Oid vartype;
+ Oid vartype pg_node_attr(jumble_ignore);
/* pg_attribute typmod value */
- int32 vartypmod;
+ int32 vartypmod pg_node_attr(jumble_ignore);
/* OID of collation, or InvalidOid if none */
- Oid varcollid;
+ Oid varcollid pg_node_attr(jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
@@ -235,9 +244,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, jumble_ignore);
/* syntactic attribute number */
- AttrNumber varattnosyn pg_node_attr(equal_ignore);
+ AttrNumber varattnosyn pg_node_attr(equal_ignore, jumble_ignore);
/* token location, or -1 if unknown */
int location;
@@ -250,6 +259,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
{
@@ -257,17 +268,27 @@ typedef struct Const
Expr xpr;
Oid consttype; /* pg_type OID of the constant's datatype */
- int32 consttypmod; /* typmod value, if any */
- Oid constcollid; /* OID of collation, or InvalidOid if none */
- int constlen; /* typlen of the constant's datatype */
- Datum constvalue; /* the constant's value */
- bool constisnull; /* whether the constant is null (if true,
- * constvalue is undefined) */
- bool constbyval; /* 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. */
- int location; /* token location, or -1 if unknown */
+ int32 consttypmod pg_node_attr(jumble_ignore); /* typmod value, if any */
+ Oid constcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ int constlen pg_node_attr(jumble_ignore); /* typlen of the
+ * constant's datatype */
+ Datum constvalue pg_node_attr(jumble_ignore); /* the constant's value */
+ /* whether the constant is null (if true, constvalue is undefined) */
+ bool constisnull pg_node_attr(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 pg_node_attr(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(jumble_location);
} Const;
/*
@@ -305,14 +326,17 @@ typedef enum ParamKind
PARAM_MULTIEXPR
} ParamKind;
+/* typmod and collation information are irrelevant for the query jumbling. */
typedef struct Param
{
Expr xpr;
ParamKind paramkind; /* kind of parameter. See above */
int paramid; /* numeric ID for parameter */
Oid paramtype; /* pg_type OID of parameter's datatype */
- int32 paramtypmod; /* typmod value, if known */
- Oid paramcollid; /* OID of collation, or InvalidOid if none */
+ int32 paramtypmod pg_node_attr(jumble_ignore); /* typmod value, if
+ * known */
+ Oid paramcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
int location; /* token location, or -1 if unknown */
} Param;
@@ -364,6 +388,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
{
@@ -373,22 +400,22 @@ typedef struct Aggref
Oid aggfnoid;
/* type Oid of result of the aggregate */
- Oid aggtype;
+ Oid aggtype pg_node_attr(jumble_ignore);
/* OID of collation of result */
- Oid aggcollid;
+ Oid aggcollid pg_node_attr(jumble_ignore);
/* OID of collation that function should use */
- Oid inputcollid;
+ Oid inputcollid pg_node_attr(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, jumble_ignore);
/* type Oids of direct and aggregated args */
- List *aggargtypes;
+ List *aggargtypes pg_node_attr(jumble_ignore);
/* direct arguments, if an ordered-set agg */
List *aggdirectargs;
@@ -406,31 +433,31 @@ typedef struct Aggref
Expr *aggfilter;
/* true if argument list was really '*' */
- bool aggstar;
+ bool aggstar pg_node_attr(jumble_ignore);
/*
* true if variadic arguments have been combined into an array last
* argument
*/
- bool aggvariadic;
+ bool aggvariadic pg_node_attr(jumble_ignore);
/* aggregate kind (see pg_aggregate.h) */
- char aggkind;
+ char aggkind pg_node_attr(jumble_ignore);
/* aggregate input already sorted */
- bool aggpresorted pg_node_attr(equal_ignore);
+ bool aggpresorted pg_node_attr(equal_ignore, jumble_ignore);
/* > 0 if agg belongs to outer query */
- Index agglevelsup;
+ Index agglevelsup pg_node_attr(jumble_ignore);
/* expected agg-splitting mode of parent Agg */
- AggSplit aggsplit;
+ AggSplit aggsplit pg_node_attr(jumble_ignore);
/* unique ID within the Agg node */
- int aggno;
+ int aggno pg_node_attr(jumble_ignore);
/* unique ID of transition state in the Agg */
- int aggtransno;
+ int aggtransno pg_node_attr(jumble_ignore);
/* token location, or -1 if unknown */
int location;
@@ -459,19 +486,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(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, jumble_ignore);
/* same as Aggref.agglevelsup */
Index agglevelsup;
@@ -482,19 +512,27 @@ 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
{
Expr xpr;
Oid winfnoid; /* pg_proc Oid of the function */
- Oid wintype; /* type Oid of result of the window function */
- Oid wincollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
+ Oid wintype pg_node_attr(jumble_ignore); /* type Oid of result of
+ * the window function */
+ Oid wincollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * result */
+ Oid inputcollid pg_node_attr(jumble_ignore); /* OID of collation that
+ * function should use */
List *args; /* arguments to the window function */
Expr *aggfilter; /* FILTER expression, if any */
Index winref; /* index of associated WindowClause */
- bool winstar; /* true if argument list was really '*' */
- bool winagg; /* is function a simple aggregate? */
+ bool winstar pg_node_attr(jumble_ignore); /* true if argument list
+ * was really '*' */
+ bool winagg pg_node_attr(jumble_ignore); /* is function a simple
+ * aggregate? */
int location; /* token location, or -1 if unknown */
} WindowFunc;
@@ -532,6 +570,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.
@@ -539,11 +579,16 @@ typedef struct WindowFunc
typedef struct SubscriptingRef
{
Expr xpr;
- Oid refcontainertype; /* type of the container proper */
- Oid refelemtype; /* the container type's pg_type.typelem */
- Oid refrestype; /* type of the SubscriptingRef's result */
- int32 reftypmod; /* typmod of the result */
- Oid refcollid; /* collation of result, or InvalidOid if none */
+ Oid refcontainertype pg_node_attr(jumble_ignore); /* type of the container
+ * proper */
+ Oid refelemtype pg_node_attr(jumble_ignore); /* the container type's
+ * pg_type.typelem */
+ Oid refrestype pg_node_attr(jumble_ignore); /* type of the
+ * SubscriptingRef's
+ * result */
+ int32 reftypmod pg_node_attr(jumble_ignore); /* typmod of the result */
+ Oid refcollid pg_node_attr(jumble_ignore); /* collation of result,
+ * or InvalidOid if none */
List *refupperindexpr; /* expressions that evaluate to upper
* container indexes */
List *reflowerindexpr; /* expressions that evaluate to lower
@@ -591,18 +636,30 @@ 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
{
Expr xpr;
Oid funcid; /* PG_PROC OID of the function */
- Oid funcresulttype; /* PG_TYPE OID of result value */
- bool funcretset; /* true if function returns set */
- bool funcvariadic; /* true if variadic arguments have been
- * combined into an array last argument */
- CoercionForm funcformat; /* how to display this function call */
- Oid funccollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
+ /* PG_TYPE OID of result value */
+ Oid funcresulttype pg_node_attr(jumble_ignore);
+ /* true if function returns set */
+ bool funcretset pg_node_attr(jumble_ignore);
+
+ /*
+ * true if variadic arguments have been combined into an array last
+ * argument
+ */
+ bool funcvariadic pg_node_attr(jumble_ignore);
+ CoercionForm funcformat pg_node_attr(jumble_ignore); /* how to display this
+ * function call */
+ Oid funccollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * result */
+ Oid inputcollid pg_node_attr(jumble_ignore); /* OID of collation that
+ * function should use */
List *args; /* arguments to the function */
int location; /* token location, or -1 if unknown */
} FuncExpr;
@@ -625,7 +682,7 @@ typedef struct NamedArgExpr
{
Expr xpr;
Expr *arg; /* the argument expression */
- char *name; /* the name */
+ char *name pg_node_attr(jumble_ignore); /* the name */
int argnumber; /* argument's number in positional notation */
int location; /* argument name location, or -1 if unknown */
} NamedArgExpr;
@@ -639,6 +696,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
{
@@ -648,19 +708,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, jumble_ignore);
/* PG_TYPE OID of result value */
- Oid opresulttype;
+ Oid opresulttype pg_node_attr(jumble_ignore);
/* true if operator returns set */
- bool opretset;
+ bool opretset pg_node_attr(jumble_ignore);
/* OID of collation of result */
- Oid opcollid;
+ Oid opcollid pg_node_attr(jumble_ignore);
/* OID of collation that operator should use */
- Oid inputcollid;
+ Oid inputcollid pg_node_attr(jumble_ignore);
/* arguments to the operator (1 or 2) */
List *args;
@@ -716,6 +776,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 entruues of the internal function types are irrelevant for the query
+ * jumbling, but the operator OID and the arguments are.
*/
typedef struct ScalarArrayOpExpr
{
@@ -725,19 +788,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, 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, 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, jumble_ignore);
/* true for ANY, false for ALL */
bool useOr;
/* OID of collation that operator should use */
- Oid inputcollid;
+ Oid inputcollid pg_node_attr(jumble_ignore);
/* the scalar and array operands */
List *args;
@@ -838,7 +901,8 @@ typedef struct SubLink
SubLinkType subLinkType; /* see above */
int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */
Node *testexpr; /* outer-query test for ALL/ANY/ROWCOMPARE */
- List *operName; /* originally specified operator name */
+ List *operName pg_node_attr(jumble_ignore); /* originally specified
+ * operator name */
Node *subselect; /* subselect as Query* or raw parsetree */
int location; /* token location, or -1 if unknown */
} SubLink;
@@ -948,10 +1012,13 @@ typedef struct FieldSelect
Expr xpr;
Expr *arg; /* input expression */
AttrNumber fieldnum; /* attribute number of field to extract */
- Oid resulttype; /* type of the field (result type of this
- * node) */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation of the field */
+ Oid resulttype pg_node_attr(jumble_ignore); /* type of the field
+ * (result type of this
+ * node) */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod
+ * (usually -1) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * the field */
} FieldSelect;
/* ----------------
@@ -977,8 +1044,10 @@ typedef struct FieldStore
Expr xpr;
Expr *arg; /* input tuple value */
List *newvals; /* new value(s) for field(s) */
- List *fieldnums; /* integer list of field attnums */
- Oid resulttype; /* type of result (same as type of arg) */
+ List *fieldnums pg_node_attr(jumble_ignore); /* integer list of field
+ * attnums */
+ Oid resulttype pg_node_attr(jumble_ignore); /* type of result (same
+ * as type of arg) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
} FieldStore;
@@ -1000,9 +1069,12 @@ typedef struct RelabelType
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion expression */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm relabelformat; /* how to display this node */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod
+ * (usually -1) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm relabelformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
int location; /* token location, or -1 if unknown */
} RelabelType;
@@ -1021,8 +1093,10 @@ typedef struct CoerceViaIO
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion */
/* output typmod is not stored, but is presumed -1 */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm coerceformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
int location; /* token location, or -1 if unknown */
} CoerceViaIO;
@@ -1045,9 +1119,12 @@ typedef struct ArrayCoerceExpr
Expr *arg; /* input expression (yields an array) */
Expr *elemexpr; /* expression representing per-element work */
Oid resulttype; /* output type of coercion (an array type) */
- int32 resulttypmod; /* output typmod (also element typmod) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod (also
+ * element typmod) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm coerceformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
int location; /* token location, or -1 if unknown */
} ArrayCoerceExpr;
@@ -1070,7 +1147,8 @@ typedef struct ConvertRowtypeExpr
Expr *arg; /* input expression */
Oid resulttype; /* output type (always a composite type) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
- CoercionForm convertformat; /* how to display this node */
+ CoercionForm convertformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
int location; /* token location, or -1 if unknown */
} ConvertRowtypeExpr;
@@ -1114,8 +1192,10 @@ typedef struct CollateExpr
typedef struct CaseExpr
{
Expr xpr;
- Oid casetype; /* type of expression result */
- Oid casecollid; /* OID of collation, or InvalidOid if none */
+ Oid casetype pg_node_attr(jumble_ignore); /* type of expression
+ * result */
+ Oid casecollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
Expr *arg; /* implicit equality comparison argument */
List *args; /* the arguments (list of WHEN clauses) */
Expr *defresult; /* the default result (ELSE clause) */
@@ -1157,8 +1237,10 @@ typedef struct CaseTestExpr
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
+ int32 typeMod pg_node_attr(jumble_ignore); /* typemod for
+ * substituted value */
+ Oid collation pg_node_attr(jumble_ignore); /* collation for the
+ * substituted value */
} CaseTestExpr;
/*
@@ -1172,11 +1254,15 @@ typedef struct CaseTestExpr
typedef struct ArrayExpr
{
Expr xpr;
- Oid array_typeid; /* type of expression result */
- Oid array_collid; /* OID of collation, or InvalidOid if none */
- Oid element_typeid; /* common type of array elements */
+ Oid array_typeid pg_node_attr(jumble_ignore); /* type of expression
+ * result */
+ Oid array_collid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ Oid element_typeid pg_node_attr(jumble_ignore); /* common type of array
+ * elements */
List *elements; /* the array elements or sub-arrays */
- bool multidims; /* true if elements are sub-arrays */
+ bool multidims pg_node_attr(jumble_ignore); /* true if elements are
+ * sub-arrays */
int location; /* token location, or -1 if unknown */
} ArrayExpr;
@@ -1205,7 +1291,8 @@ typedef struct RowExpr
{
Expr xpr;
List *args; /* the fields */
- Oid row_typeid; /* RECORDOID or a composite type's ID */
+ Oid row_typeid pg_node_attr(jumble_ignore); /* RECORDOID or a
+ * composite type's ID */
/*
* row_typeid cannot be a domain over composite, only plain composite. To
@@ -1219,8 +1306,10 @@ typedef struct RowExpr
* We don't need to store a collation either. The result type is
* necessarily composite, and composite types never have a collation.
*/
- CoercionForm row_format; /* how to display this node */
- List *colnames; /* list of String, or NIL */
+ CoercionForm row_format pg_node_attr(jumble_ignore); /* how to display this
+ * node */
+ List *colnames pg_node_attr(jumble_ignore); /* list of String, or
+ * NIL */
int location; /* token location, or -1 if unknown */
} RowExpr;
@@ -1253,9 +1342,14 @@ typedef struct RowCompareExpr
{
Expr xpr;
RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
- List *opnos; /* OID list of pairwise comparison ops */
- List *opfamilies; /* OID list of containing operator families */
- List *inputcollids; /* OID list of collations for comparisons */
+ List *opnos pg_node_attr(jumble_ignore); /* OID list of pairwise
+ * comparison ops */
+ List *opfamilies pg_node_attr(jumble_ignore); /* OID list of
+ * containing operator
+ * families */
+ List *inputcollids pg_node_attr(jumble_ignore); /* OID list of
+ * collations for
+ * comparisons */
List *largs; /* the left-hand input arguments */
List *rargs; /* the right-hand input arguments */
} RowCompareExpr;
@@ -1266,8 +1360,10 @@ typedef struct RowCompareExpr
typedef struct CoalesceExpr
{
Expr xpr;
- Oid coalescetype; /* type of expression result */
- Oid coalescecollid; /* OID of collation, or InvalidOid if none */
+ Oid coalescetype pg_node_attr(jumble_ignore); /* type of expression
+ * result */
+ Oid coalescecollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
List *args; /* the arguments */
int location; /* token location, or -1 if unknown */
} CoalesceExpr;
@@ -1284,9 +1380,12 @@ typedef enum MinMaxOp
typedef struct MinMaxExpr
{
Expr xpr;
- Oid minmaxtype; /* common type of arguments and result */
- Oid minmaxcollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
+ Oid minmaxtype pg_node_attr(jumble_ignore); /* common type of
+ * arguments and result */
+ Oid minmaxcollid pg_node_attr(jumble_ignore); /* OID of collation of
+ * result */
+ Oid inputcollid pg_node_attr(jumble_ignore); /* OID of collation that
+ * function should use */
MinMaxOp op; /* function to execute */
List *args; /* the arguments */
int location; /* token location, or -1 if unknown */
@@ -1325,13 +1424,16 @@ typedef struct XmlExpr
{
Expr xpr;
XmlExprOp op; /* xml function ID */
- char *name; /* name in xml(NAME foo ...) syntaxes */
+ char *name pg_node_attr(jumble_ignore); /* name in xml(NAME foo
+ * ...) syntaxes */
List *named_args; /* non-XML expressions for xml_attributes */
- List *arg_names; /* parallel list of String values */
+ List *arg_names pg_node_attr(jumble_ignore); /* parallel list of
+ * String values */
List *args; /* list of expressions */
- XmlOptionType xmloption; /* DOCUMENT or CONTENT */
- Oid type; /* target type/typmod for XMLSERIALIZE */
- int32 typmod;
+ XmlOptionType xmloption pg_node_attr(jumble_ignore); /* DOCUMENT or CONTENT */
+ Oid type pg_node_attr(jumble_ignore); /* target type/typmod for
+ * XMLSERIALIZE */
+ int32 typmod pg_node_attr(jumble_ignore);
int location; /* token location, or -1 if unknown */
} XmlExpr;
@@ -1364,7 +1466,9 @@ typedef struct NullTest
Expr xpr;
Expr *arg; /* input expression */
NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
- bool argisrow; /* T to perform field-by-field null checks */
+ bool argisrow pg_node_attr(jumble_ignore); /* T to perform
+ * field-by-field null
+ * checks */
int location; /* token location, or -1 if unknown */
} NullTest;
@@ -1404,9 +1508,12 @@ typedef struct CoerceToDomain
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* domain type ID (result type) */
- int32 resulttypmod; /* output typmod (currently always -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coercionformat; /* how to display this node */
+ int32 resulttypmod pg_node_attr(jumble_ignore); /* output typmod
+ * (currently always -1) */
+ Oid resultcollid pg_node_attr(jumble_ignore); /* OID of collation, or
+ * InvalidOid if none */
+ CoercionForm coercionformat pg_node_attr(jumble_ignore); /* how to display this
+ * node */
int location; /* token location, or -1 if unknown */
} CoerceToDomain;
@@ -1418,13 +1525,17 @@ typedef struct CoerceToDomain
* Note: the typeId/typeMod/collation will be set from the domain's base type,
* not the domain itself. This is because we shouldn't consider the value
* to be a member of the domain if we haven't yet checked its constraints.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct CoerceToDomainValue
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
+ int32 typeMod pg_node_attr(jumble_ignore); /* typemod for
+ * substituted value */
+ Oid collation pg_node_attr(jumble_ignore); /* collation for the
+ * substituted value */
int location; /* token location, or -1 if unknown */
} CoerceToDomainValue;
@@ -1434,13 +1545,17 @@ typedef struct CoerceToDomainValue
* This is not an executable expression: it must be replaced by the actual
* column default expression during rewriting. But it is convenient to
* treat it as an expression node during parsing and rewriting.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct SetToDefault
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
+ int32 typeMod pg_node_attr(jumble_ignore); /* typemod for
+ * substituted value */
+ Oid collation pg_node_attr(jumble_ignore); /* collation for the
+ * substituted value */
int location; /* token location, or -1 if unknown */
} SetToDefault;
@@ -1554,13 +1669,16 @@ typedef struct TargetEntry
Expr xpr;
Expr *expr; /* expression to evaluate */
AttrNumber resno; /* attribute number (see notes above) */
- char *resname; /* name of the column (could be NULL) */
+ char *resname pg_node_attr(jumble_ignore); /* name of the column
+ * (could be NULL) */
Index ressortgroupref; /* nonzero if referenced by a sort/group
* clause */
- Oid resorigtbl; /* OID of column's source table */
- AttrNumber resorigcol; /* column's number in source table */
- bool resjunk; /* set to true to eliminate the attribute from
- * final target list */
+ Oid resorigtbl pg_node_attr(jumble_ignore); /* OID of column's
+ * source table */
+ AttrNumber resorigcol pg_node_attr(jumble_ignore); /* column's number in
+ * source table */
+ /* set to true to eliminate the attribute from final target list */
+ bool resjunk pg_node_attr(jumble_ignore);
} TargetEntry;
@@ -1642,10 +1760,13 @@ typedef struct JoinExpr
bool isNatural; /* Natural join? Will need to shape table */
Node *larg; /* left subtree */
Node *rarg; /* right subtree */
- List *usingClause; /* USING clause, if any (list of String) */
- Alias *join_using_alias; /* alias attached to USING clause, if any */
+ List *usingClause pg_node_attr(jumble_ignore); /* USING clause, if any
+ * (list of String) */
+ Alias *join_using_alias pg_node_attr(jumble_ignore); /* alias attached to
+ * USING clause, if any */
Node *quals; /* qualifiers on join, if any */
- Alias *alias; /* user-written alias clause, if any */
+ Alias *alias pg_node_attr(jumble_ignore); /* user-written alias
+ * clause, if any */
int rtindex; /* RT index assigned for join, or 0 */
} JoinExpr;
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 7c594be583..cd5731a2ca 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -19,6 +19,7 @@ OBJS = \
copyfuncs.o \
equalfuncs.o \
extensible.o \
+ jumblefuncs.o \
list.o \
makefuncs.o \
multibitmapset.o \
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..e97a918e95 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -47,6 +47,7 @@ FILES IN THIS DIRECTORY (src/backend/nodes/)
General-purpose node manipulation functions:
copyfuncs.c - copy a node tree (*)
equalfuncs.c - compare two node trees (*)
+ jumblefuncs.c - compute a node tree for query jumbling (*)
outfuncs.c - convert a node tree to text representation (*)
readfuncs.c - convert text representation back to a node tree (*)
makefuncs.c - creator functions for some common node types
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b3c1ead496..89357c1ce6 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -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_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_jumble, qw(List);
push @special_read_write, qw(List);
# Nodes with custom copy/equal implementations are skipped from
@@ -332,6 +335,10 @@ foreach my $infile (@ARGV)
push @no_copy, $in_struct;
push @no_equal, $in_struct;
}
+ elsif ($attr eq 'no_jumble')
+ {
+ push @no_jumble, $in_struct;
+ }
elsif ($attr eq 'no_read')
{
push @no_read, $in_struct;
@@ -457,6 +464,8 @@ foreach my $infile (@ARGV)
equal_as_scalar
equal_ignore
equal_ignore_if_zero
+ jumble_ignore
+ jumble_location
read_write_ignore
write_only_relids
write_only_nondefault_pathtarget
@@ -1225,6 +1234,100 @@ close $ofs;
close $rfs;
+# jumblefuncs.c
+
+push @output_files, 'jumblefuncs.funcs.c';
+open my $jff, '>', "$output_path/jumblefuncs.funcs.c$tmpext" or die $!;
+push @output_files, 'jumblefuncs.switch.c';
+open my $jfs, '>', "$output_path/jumblefuncs.switch.c$tmpext" or die $!;
+
+printf $jff $header_comment, 'jumblefuncs.funcs.c';
+printf $jfs $header_comment, 'jumblefuncs.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_jumble = (elem $n, @no_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_jumble;
+
+ print $jff "
+static void
+_jumble${n}(JumbleState *jstate, Node *node)
+{
+\t${n} *expr = (${n} *) node;\n
+" unless $struct_no_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 $jumble_ignore = $struct_no_jumble;
+ my $jumble_location = 0;
+
+ # extract per-field attributes
+ foreach my $a (@a)
+ {
+ if ($a eq 'jumble_ignore')
+ {
+ $jumble_ignore = 1;
+ }
+ elsif ($a eq 'jumble_location')
+ {
+ $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 $jumble_ignore;
+ }
+ elsif ($t eq 'int' && $f =~ 'location$')
+ {
+ # Track the node's location only if directly requested.
+ if ($jumble_location)
+ {
+ print $jff "\tJUMBLE_LOCATION($f);\n"
+ unless $jumble_ignore;
+ }
+ }
+ elsif ($t eq 'char*')
+ {
+ print $jff "\tJUMBLE_STRING($f);\n"
+ unless $jumble_ignore;
+ }
+ else
+ {
+ print $jff "\tJUMBLE_FIELD($f);\n"
+ unless $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_jumble;
+ }
+
+ print $jff "}
+" unless $struct_no_jumble;
+}
+
+close $jff;
+close $jfs;
+
# now rename the temporary files to their final names
foreach my $file (@output_files)
{
diff --git a/src/backend/nodes/jumblefuncs.c b/src/backend/nodes/jumblefuncs.c
new file mode 100644
index 0000000000..b65e022d17
--- /dev/null
+++ b/src/backend/nodes/jumblefuncs.c
@@ -0,0 +1,358 @@
+/*-------------------------------------------------------------------------
+ *
+ * jumblefuncs.c
+ * Query normalization and fingerprinting.
+ *
+ * Normalization is a process whereby similar queries, typically differing only
+ * in their constants (though the exact rules are somewhat more subtle than
+ * that) are recognized as equivalent, and are tracked as a single entry. This
+ * is particularly useful for non-prepared queries.
+ *
+ * Normalization is implemented by fingerprinting queries, selectively
+ * serializing those fields of each query tree's nodes that are judged to be
+ * essential to the query. This is referred to as a query jumble. This is
+ * distinct from a regular serialization in that various extraneous
+ * information is ignored as irrelevant or not essential to the query, such
+ * as the collations of Vars and, most notably, the values of constants.
+ *
+ * This jumble is acquired at the end of parse analysis of each query, and
+ * a 64-bit hash of it is stored into the query's Query.queryId field.
+ * The server then copies this value around, making it available in plan
+ * tree(s) generated from the query. The executor can then use this value
+ * to blame query costs on the proper queryId.
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/nodes/jumblefuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "common/hashfn.h"
+#include "miscadmin.h"
+#include "parser/scansup.h"
+#include "utils/queryjumble.h"
+
+#define JUMBLE_SIZE 1024 /* query serialization buffer size */
+
+/* GUC parameters */
+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 void AppendJumble(JumbleState *jstate,
+ const unsigned char *item, Size size);
+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
+ * relevant part of the string.
+ */
+const char *
+CleanQuerytext(const char *query, int *location, int *len)
+{
+ int query_location = *location;
+ int query_len = *len;
+
+ /* First apply starting offset, unless it's -1 (unknown). */
+ if (query_location >= 0)
+ {
+ Assert(query_location <= strlen(query));
+ query += query_location;
+ /* Length of 0 (or -1) means "rest of string" */
+ if (query_len <= 0)
+ query_len = strlen(query);
+ else
+ Assert(query_len <= strlen(query));
+ }
+ else
+ {
+ /* If query location is unknown, distrust query_len as well */
+ query_location = 0;
+ query_len = strlen(query);
+ }
+
+ /*
+ * Discard leading and trailing whitespace, too. Use scanner_isspace()
+ * not libc's isspace(), because we want to match the lexer's behavior.
+ */
+ while (query_len > 0 && scanner_isspace(query[0]))
+ query++, query_location++, query_len--;
+ while (query_len > 0 && scanner_isspace(query[query_len - 1]))
+ query_len--;
+
+ *location = query_location;
+ *len = query_len;
+
+ return query;
+}
+
+JumbleState *
+JumbleQuery(Query *query, const char *querytext)
+{
+ JumbleState *jstate = NULL;
+
+ Assert(IsQueryIdEnabled());
+
+ 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, to
+ * prevent confusion with the utility-statement case.
+ */
+ if (query->queryId == UINT64CONST(0))
+ query->queryId = UINT64CONST(1);
+
+ return jstate;
+}
+
+/*
+ * Enables query identifier computation.
+ *
+ * Third-party plugins can use this function to inform core that they require
+ * a query identifier to be computed.
+ */
+void
+EnableQueryId(void)
+{
+ if (compute_query_id != COMPUTE_QUERY_ID_OFF)
+ query_id_enabled = true;
+}
+
+/*
+ * AppendJumble: Append a value that is substantive in a given query to
+ * the current jumble.
+ */
+static void
+AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
+{
+ unsigned char *jumble = jstate->jumble;
+ Size jumble_len = jstate->jumble_len;
+
+ /*
+ * Whenever the jumble buffer is full, we hash the current contents and
+ * reset the buffer to contain just that hash value, thus relying on the
+ * hash to summarize everything so far.
+ */
+ while (size > 0)
+ {
+ Size part_size;
+
+ if (jumble_len >= JUMBLE_SIZE)
+ {
+ uint64 start_hash;
+
+ start_hash = DatumGetUInt64(hash_any_extended(jumble,
+ JUMBLE_SIZE, 0));
+ memcpy(jumble, &start_hash, sizeof(start_hash));
+ jumble_len = sizeof(start_hash);
+ }
+ part_size = Min(size, JUMBLE_SIZE - jumble_len);
+ memcpy(jumble + jumble_len, item, part_size);
+ jumble_len += part_size;
+ item += part_size;
+ size -= part_size;
+ }
+ jstate->jumble_len = jumble_len;
+}
+
+/*
+ * 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++;
+ }
+}
+
+#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)
+
+#include "jumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+ Node *expr = node;
+
+ if (expr == NULL)
+ return;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /*
+ * We always emit the node's NodeTag, then any additional fields that are
+ * considered significant, and then we recurse to any child nodes.
+ */
+ JUMBLE_FIELD(type);
+
+ switch (nodeTag(expr))
+ {
+#include "jumblefuncs.switch.c"
+
+ case T_List:
+ case T_IntList:
+ case T_OidList:
+ case T_XidList:
+ _jumbleList(jstate, expr);
+ break;
+
+ case T_RangeTblEntry:
+ _jumbleRangeTblEntry(jstate, expr);
+ break;
+
+ default:
+ /* Only a warning, since we can stumble along anyway */
+ elog(WARNING, "unrecognized node type: %d",
+ (int) nodeTag(expr));
+ break;
+ }
+
+ /* Special cases */
+ switch (nodeTag(expr))
+ {
+ case T_Param:
+ {
+ Param *p = (Param *) node;
+
+ /* Also, track the highest external Param id */
+ if (p->paramkind == PARAM_EXTERN &&
+ p->paramid > jstate->highest_extern_param_id)
+ jstate->highest_extern_param_id = p->paramid;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+ List *expr = (List *) node;
+ ListCell *l;
+
+ switch (expr->type)
+ {
+ case T_List:
+ foreach(l, expr)
+ _jumbleNode(jstate, lfirst(l));
+ break;
+ case T_IntList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_int(l));
+ break;
+ case T_OidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+ break;
+ case T_XidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+ break;
+ default:
+ elog(ERROR, "unrecognized list node type: %d",
+ (int) expr->type);
+ return;
+ }
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+ RangeTblEntry *expr = (RangeTblEntry *) node;
+
+ JUMBLE_FIELD(rtekind);
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(tablesample);
+ JUMBLE_FIELD(inh);
+ break;
+ case RTE_SUBQUERY:
+ JUMBLE_NODE(subquery);
+ break;
+ case RTE_JOIN:
+ JUMBLE_FIELD(jointype);
+ break;
+ case RTE_FUNCTION:
+ JUMBLE_NODE(functions);
+ break;
+ case RTE_TABLEFUNC:
+ JUMBLE_NODE(tablefunc);
+ break;
+ case RTE_VALUES:
+ JUMBLE_NODE(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.
+ */
+ JUMBLE_STRING(ctename);
+ JUMBLE_FIELD(ctelevelsup);
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ JUMBLE_STRING(enrname);
+ break;
+ case RTE_RESULT:
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+ break;
+ }
+}
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 2ff7dbac1d..58d774478d 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -20,6 +20,7 @@ backend_sources += files(
nodefunc_sources = files(
'copyfuncs.c',
'equalfuncs.c',
+ 'jumblefuncs.c',
'outfuncs.c',
'readfuncs.c',
)
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index b9ee4eb48a..2910032930 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -26,7 +26,6 @@ OBJS = \
pg_rusage.o \
ps_status.o \
queryenvironment.o \
- queryjumble.o \
rls.o \
sampling.o \
superuser.o \
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index e3e99ec5cb..f719c97c05 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -11,7 +11,6 @@ backend_sources += files(
'pg_rusage.c',
'ps_status.c',
'queryenvironment.c',
- 'queryjumble.c',
'rls.c',
'sampling.c',
'superuser.c',
diff --git a/src/backend/utils/misc/queryjumble.c b/src/backend/utils/misc/queryjumble.c
deleted file mode 100644
index 328995a7dc..0000000000
--- a/src/backend/utils/misc/queryjumble.c
+++ /dev/null
@@ -1,861 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * queryjumble.c
- * Query normalization and fingerprinting.
- *
- * Normalization is a process whereby similar queries, typically differing only
- * in their constants (though the exact rules are somewhat more subtle than
- * that) are recognized as equivalent, and are tracked as a single entry. This
- * is particularly useful for non-prepared queries.
- *
- * Normalization is implemented by fingerprinting queries, selectively
- * serializing those fields of each query tree's nodes that are judged to be
- * essential to the query. This is referred to as a query jumble. This is
- * distinct from a regular serialization in that various extraneous
- * information is ignored as irrelevant or not essential to the query, such
- * as the collations of Vars and, most notably, the values of constants.
- *
- * This jumble is acquired at the end of parse analysis of each query, and
- * a 64-bit hash of it is stored into the query's Query.queryId field.
- * The server then copies this value around, making it available in plan
- * tree(s) generated from the query. The executor can then use this value
- * to blame query costs on the proper queryId.
- *
- * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/utils/misc/queryjumble.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "common/hashfn.h"
-#include "miscadmin.h"
-#include "parser/scansup.h"
-#include "utils/queryjumble.h"
-
-#define JUMBLE_SIZE 1024 /* query serialization buffer size */
-
-/* GUC parameters */
-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);
-
-/*
- * Given a possibly multi-statement source string, confine our attention to the
- * relevant part of the string.
- */
-const char *
-CleanQuerytext(const char *query, int *location, int *len)
-{
- int query_location = *location;
- int query_len = *len;
-
- /* First apply starting offset, unless it's -1 (unknown). */
- if (query_location >= 0)
- {
- Assert(query_location <= strlen(query));
- query += query_location;
- /* Length of 0 (or -1) means "rest of string" */
- if (query_len <= 0)
- query_len = strlen(query);
- else
- Assert(query_len <= strlen(query));
- }
- else
- {
- /* If query location is unknown, distrust query_len as well */
- query_location = 0;
- query_len = strlen(query);
- }
-
- /*
- * Discard leading and trailing whitespace, too. Use scanner_isspace()
- * not libc's isspace(), because we want to match the lexer's behavior.
- */
- while (query_len > 0 && scanner_isspace(query[0]))
- query++, query_location++, query_len--;
- while (query_len > 0 && scanner_isspace(query[query_len - 1]))
- query_len--;
-
- *location = query_location;
- *len = query_len;
-
- return query;
-}
-
-JumbleState *
-JumbleQuery(Query *query, const char *querytext)
-{
- JumbleState *jstate = NULL;
-
- 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 */
- 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))
- query->queryId = UINT64CONST(1);
- }
-
- return jstate;
-}
-
-/*
- * Enables query identifier computation.
- *
- * Third-party plugins can use this function to inform core that they require
- * a query identifier to be computed.
- */
-void
-EnableQueryId(void)
-{
- if (compute_query_id != COMPUTE_QUERY_ID_OFF)
- 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.
- */
-static void
-AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
-{
- unsigned char *jumble = jstate->jumble;
- Size jumble_len = jstate->jumble_len;
-
- /*
- * Whenever the jumble buffer is full, we hash the current contents and
- * reset the buffer to contain just that hash value, thus relying on the
- * hash to summarize everything so far.
- */
- while (size > 0)
- {
- Size part_size;
-
- if (jumble_len >= JUMBLE_SIZE)
- {
- uint64 start_hash;
-
- start_hash = DatumGetUInt64(hash_any_extended(jumble,
- JUMBLE_SIZE, 0));
- memcpy(jumble, &start_hash, sizeof(start_hash));
- jumble_len = sizeof(start_hash);
- }
- part_size = Min(size, JUMBLE_SIZE - jumble_len);
- memcpy(jumble + jumble_len, item, part_size);
- jumble_len += part_size;
- item += part_size;
- size -= part_size;
- }
- jstate->jumble_len = jumble_len;
-}
-
-/*
- * 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
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- 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;
- }
- }
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
- ListCell *lc;
-
- foreach(lc, rowMarks)
- {
- RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
- if (!rowmark->pushedDown)
- {
- APP_JUMB(rowmark->rti);
- APP_JUMB(rowmark->strength);
- APP_JUMB(rowmark->waitPolicy);
- }
- }
-}
-
-/*
- * 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)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * 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);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- 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);
- }
- break;
- 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 */
- 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);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- 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);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- 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;
-
- APP_JUMB(mergeaction->matched);
- APP_JUMB(mergeaction->commandType);
- JumbleExpr(jstate, mergeaction->qual);
- JumbleExpr(jstate, (Node *) mergeaction->targetList);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- 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);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- 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);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* 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));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- 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);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- 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++;
- }
-}
--
2.39.0
On 13.01.23 08:54, Michael Paquier wrote:
Using a "jumble_ignore" flag is equally invasive to using an
"jumble_include" flag for each field, I am afraid, as the number of
fields in the nodes included in jumbles is pretty equivalent to the
number of fields ignored. I tend to prefer the approach of ignoring
things though, which is more consistent with the practive for node
read, write and copy.
I concur that jumble_ignore is better. I suppose you placed the
jumble_ignore markers to maintain parity with the existing code, but I
think that some the markers are actually wrong and are just errors of
omission in the existing code (such as Query.override, to pick a random
example). The way you have structured this would allow us to find and
analyze such errors better.
Anyway, when it comes to the location, another thing that can be considered here would be to require a node-level flag for the nodes on which we want to track the location. This overlaps a bit with the fields satisfying "($t eq 'int' && $f =~ 'location$')", but it removes most of the code changes like this one as at the end we only care about the location for Const nodes: - int location; /* token location, or -1 if unknown */ + int location pg_node_attr(jumble_ignore); /* token location, or -1 + * if unknown */I have taken this approach in v2 of the patch, shaving ~100 lines of
more code as there is no need to mark all these location fields with
"jumble_ignore" anymore, except for Const, of course.
I don't understand why you chose that when we already have an
established way. This would just make the jumble annotations
inconsistent with the other annotations.
I also have two suggestions to make this patch more palatable:
1. Make a separate patch to reformat long comments, like
835d476fd21bcfb60b055941dee8c3d9559af14c.
2. Make a separate patch to move the jumble support to
src/backend/nodes/jumblefuncs.c. (It was probably a mistake that it
wasn't there to begin with, and some of the errors of omission alluded
to above are probably caused by it having been hidden away in the wrong
place.)
On Mon, Jan 16, 2023 at 03:13:35PM +0100, Peter Eisentraut wrote:
On 13.01.23 08:54, Michael Paquier wrote:
Using a "jumble_ignore" flag is equally invasive to using an
"jumble_include" flag for each field, I am afraid, as the number of
fields in the nodes included in jumbles is pretty equivalent to the
number of fields ignored. I tend to prefer the approach of ignoring
things though, which is more consistent with the practive for node
read, write and copy.I concur that jumble_ignore is better. I suppose you placed the
jumble_ignore markers to maintain parity with the existing code, but I think
that some the markers are actually wrong and are just errors of omission in
the existing code (such as Query.override, to pick a random example). The
way you have structured this would allow us to find and analyze such errors
better.
Thanks. Yes, I have made an effort of going down to maintain an exact
compatibility with the existing code for now. My take is that
removing or adding more things into the jumble deserves its own
discussion. I think that's a bit better once this code is automated
with the nodes, now it would not be difficult either to adjust HEAD
depending on that. Only the analysis effort is different.
Anyway, when it comes to the location, another thing that can be considered here would be to require a node-level flag for the nodes on which we want to track the location. This overlaps a bit with the fields satisfying "($t eq 'int' && $f =~ 'location$')", but it removes most of the code changes like this one as at the end we only care about the location for Const nodes: - int location; /* token location, or -1 if unknown */ + int location pg_node_attr(jumble_ignore); /* token location, or -1 + * if unknown */I have taken this approach in v2 of the patch, shaving ~100 lines of
more code as there is no need to mark all these location fields with
"jumble_ignore" anymore, except for Const, of course.I don't understand why you chose that when we already have an established
way. This would just make the jumble annotations inconsistent with the
other annotations.
Because we don't want to track the location of all the nodes! If we
do that, pg_stat_statements would begin to parameterize a lot more
things, from column aliases to full contents of IN or WITH clauses.
The root point is that we only want to track the jumble location for
Const nodes now. In order to do that, there are two approaches:
- Mark all the locations with jumble_ignore: more invasive, but
it requires only one per-field attribute with "jumble_ignore". This
is what v1 did.
- Mark only the fields where we want to track the location with a
second node type, like "jumble_location". We could restrict that
depending on the field type or its name on top of checking the field
attribute, whatever. This is what v2 did.
Which approach do you prefer? Marking all the locations we don't want
with jumble_ignore, or introduce a second attribute with
jumble_location. I'd tend to prefer the approach of minimizing the
number of node and field attributes, FWIW. Now you have worked on
this area previously, so your view may be more adapted than my
thinking process
The long-term perspective is that I'd like to extend the tracking of
the locations to more DDL nodes, like parameters of SET statements or
parts of CALL statements. Not to mention that this makes the work of
forks easier. This is a separate discussion.
I also have two suggestions to make this patch more palatable:
1. Make a separate patch to reformat long comments, like
835d476fd21bcfb60b055941dee8c3d9559af14c.2. Make a separate patch to move the jumble support to
src/backend/nodes/jumblefuncs.c. (It was probably a mistake that it wasn't
there to begin with, and some of the errors of omission alluded to above are
probably caused by it having been hidden away in the wrong place.)
Both suggestions make sense. I'll shape the next series of the patch
to do something among these lines.
My question about the location tracking greatly influences the first
patch (comment reformating) and third patch (automated generation of
the code) of the series, so do you have a preference about that?
--
Michael
On 17.01.23 04:48, Michael Paquier wrote:
Anyway, when it comes to the location, another thing that can be considered here would be to require a node-level flag for the nodes on which we want to track the location. This overlaps a bit with the fields satisfying "($t eq 'int' && $f =~ 'location$')", but it removes most of the code changes like this one as at the end we only care about the location for Const nodes: - int location; /* token location, or -1 if unknown */ + int location pg_node_attr(jumble_ignore); /* token location, or -1 + * if unknown */I have taken this approach in v2 of the patch, shaving ~100 lines of
more code as there is no need to mark all these location fields with
"jumble_ignore" anymore, except for Const, of course.I don't understand why you chose that when we already have an established
way. This would just make the jumble annotations inconsistent with the
other annotations.Because we don't want to track the location of all the nodes! If we
do that, pg_stat_statements would begin to parameterize a lot more
things, from column aliases to full contents of IN or WITH clauses.
The root point is that we only want to track the jumble location for
Const nodes now. In order to do that, there are two approaches:
- Mark all the locations with jumble_ignore: more invasive, but
it requires only one per-field attribute with "jumble_ignore". This
is what v1 did.
Ok, I understand now, and I agree with this approach over the opposite.
I was confused because the snippet you showed above used
"jumble_ignore", but your patch is correct as it uses "jumble_location".
That said, the term "jumble" is really weird, because in the sense that
we are using it here it means, approximately, "to mix together", "to
unify". So what we are doing with the Const nodes is really to *not*
jumble the location, but for all other node types we are jumbling the
location. At least that is my understanding.
On Tue, Jan 17, 2023 at 08:43:44AM +0100, Peter Eisentraut wrote:
Ok, I understand now, and I agree with this approach over the opposite. I
was confused because the snippet you showed above used "jumble_ignore", but
your patch is correct as it uses "jumble_location".
Okay. I'll refresh the patch set so as we have only "jumble_ignore",
then, like v1, with preparatory patches for what you mentioned and
anything that comes into mind.
That said, the term "jumble" is really weird, because in the sense that we
are using it here it means, approximately, "to mix together", "to unify".
So what we are doing with the Const nodes is really to *not* jumble the
location, but for all other node types we are jumbling the location. At
least that is my understanding.
I am quite familiar with this term, FWIW. That's what we've inherited
from the days where this has been introduced in pg_stat_statements.
--
Michael
On Tue, Jan 17, 2023 at 04:52:28PM +0900, Michael Paquier wrote:
On Tue, Jan 17, 2023 at 08:43:44AM +0100, Peter Eisentraut wrote:
Ok, I understand now, and I agree with this approach over the opposite. I
was confused because the snippet you showed above used "jumble_ignore", but
your patch is correct as it uses "jumble_location".Okay. I'll refresh the patch set so as we have only "jumble_ignore",
then, like v1, with preparatory patches for what you mentioned and
anything that comes into mind.
This is done as of the patch series v3 attached:
- 0001 reformats all the comments of the nodes.
- 0002 moves the current files for query jumble as of queryjumble.c ->
queryjumblefuncs.c and utils/queryjumble.h -> nodes/queryjumble.h.
- 0003 is the core feature, where I have done a second pass over the
nodes to make sure that things map with HEAD, incorporating the extra
docs coming from v2, adding a bit more.
That said, the term "jumble" is really weird, because in the sense that we
are using it here it means, approximately, "to mix together", "to unify".
So what we are doing with the Const nodes is really to *not* jumble the
location, but for all other node types we are jumbling the location. At
least that is my understanding.I am quite familiar with this term, FWIW. That's what we've inherited
from the days where this has been introduced in pg_stat_statements.
I have renamed the node attributes to query_jumble_ignore and
no_query_jumble at the end, after considering Peter's point that only
"jumble" could be fuzzy here. The file names are changed in
consequence.
While doing all that, I have done some micro-benchmarking of
JumbleQuery(), making it loop 50M times on my laptop each time a query
ID is computed (hideous hack with a loop in queryjumble.c):
- For non-utility queries, aka queries that go through
JumbleQueryInternal(), I am measuring a repeatable ~10% improvement
with the generated code over HEAD, which is kind of nice. I have
tested a few DMLs and simple SELECTs, still it looks like a trend.
- For utility queries, the new code is competing against
hash_any_extended() with the query string, which is going to be hard
to beat.. I am measuring what looks like a 5 times slowdown, at
least, and more depending on the depth of the query tree. That's not
surprising. On a 50M loop, this comes down to compare a computation
of 100ns to 5ns for a 20-time slowdown for example, still this could
justify the addition of a GUC to control whether utility queries have
their query ID compiled depending on their nodes or their string
hash, as this could become noticeable in OLTP workloads with loads
of short statements and BEGIN/COMMIT queries?
Thoughts or comments?
--
Michael
Attachments:
v3-0001-Rework-format-of-comments-for-nodes.patchtext/x-diff; charset=us-asciiDownload
From 43a5bdffdb9dbe3ecb40e9bf8a5f0e9f12ceff32 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 18 Jan 2023 14:26:12 +0900
Subject: [PATCH v3 1/3] Rework format of comments for nodes
This is similar to 835d476, except that this one is to add node
properties related to query jumbling.
---
src/include/nodes/parsenodes.h | 261 ++++++++++++------
src/include/nodes/plannodes.h | 3 +-
src/include/nodes/primnodes.h | 469 ++++++++++++++++++++++-----------
3 files changed, 487 insertions(+), 246 deletions(-)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cfeca96d53..eec51e3ee2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -123,7 +123,8 @@ typedef struct Query
CmdType commandType; /* select|insert|update|delete|merge|utility */
- QuerySource querySource; /* where did I come from? */
+ /* where did I come from? */
+ QuerySource querySource;
/*
* query identifier (can be set by plugins); ignored for equal, as it
@@ -131,39 +132,58 @@ typedef struct Query
*/
uint64 queryId pg_node_attr(equal_ignore, read_write_ignore, read_as(0));
- bool canSetTag; /* do I set the command result tag? */
+ /* do I set the command result tag? */
+ bool canSetTag;
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
- int resultRelation; /* rtable index of target relation for
- * INSERT/UPDATE/DELETE/MERGE; 0 for SELECT */
+ /*
+ * rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
+ * SELECT.
+ */
+ int resultRelation;
- bool hasAggs; /* has aggregates in tlist or havingQual */
- bool hasWindowFuncs; /* has window functions in tlist */
- bool hasTargetSRFs; /* has set-returning functions in tlist */
- bool hasSubLinks; /* has subquery SubLink */
- bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
- bool hasRecursive; /* WITH RECURSIVE was specified */
- bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
- bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
- bool hasRowSecurity; /* rewriter has applied some RLS policy */
-
- bool isReturn; /* is a RETURN statement */
+ /* has aggregates in tlist or havingQual */
+ bool hasAggs;
+ /* has window functions in tlist */
+ bool hasWindowFuncs;
+ /* has set-returning functions in tlist */
+ bool hasTargetSRFs;
+ /* has subquery SubLink */
+ bool hasSubLinks;
+ /* distinctClause is from DISTINCT ON */
+ bool hasDistinctOn;
+ /* WITH RECURSIVE was specified */
+ bool hasRecursive;
+ /* has INSERT/UPDATE/DELETE in WITH */
+ bool hasModifyingCTE;
+ /* FOR [KEY] UPDATE/SHARE was specified */
+ bool hasForUpdate;
+ /* rewriter has applied some RLS policy */
+ bool hasRowSecurity;
+ /* is a RETURN statement */
+ bool isReturn;
List *cteList; /* WITH list (of CommonTableExpr's) */
List *rtable; /* list of range table entries */
- List *rteperminfos; /* list of RTEPermissionInfo nodes for the
- * rtable entries having perminfoindex > 0 */
+
+ /*
+ * list of RTEPermissionInfo nodes for the rtable entries having
+ * perminfoindex > 0
+ */
+ List *rteperminfos;
FromExpr *jointree; /* table join tree (FROM and WHERE clauses);
* also USING clause for MERGE */
List *mergeActionList; /* list of actions for MERGE (only) */
- bool mergeUseOuterJoin; /* whether to use outer join */
+ /* whether to use outer join */
+ bool mergeUseOuterJoin;
List *targetList; /* target list (of TargetEntry) */
- OverridingKind override; /* OVERRIDING clause */
+ /* OVERRIDING clause */
+ OverridingKind override;
OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
@@ -191,11 +211,14 @@ typedef struct Query
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
- List *constraintDeps; /* a list of pg_constraint OIDs that the query
- * depends on to be semantically valid */
+ /*
+ * A list of pg_constraint OIDs that the query depends on to be
+ * semantically valid
+ */
+ List *constraintDeps;
- List *withCheckOptions; /* a list of WithCheckOption's (added
- * during rewrite) */
+ /* a list of WithCheckOption's (added during rewrite) */
+ List *withCheckOptions;
/*
* The following two fields identify the portion of the source text string
@@ -203,8 +226,10 @@ typedef struct Query
* Queries, not in sub-queries. When not set, they might both be zero, or
* both be -1 meaning "unknown".
*/
- int stmt_location; /* start location, or -1 if unknown */
- int stmt_len; /* length in bytes; 0 means "rest of string" */
+ /* start location, or -1 if unknown */
+ int stmt_location;
+ /* length in bytes; 0 means "rest of string" */
+ int stmt_len;
} Query;
@@ -239,7 +264,8 @@ typedef struct TypeName
List *typmods; /* type modifier expression(s) */
int32 typemod; /* prespecified type modifier */
List *arrayBounds; /* array bounds */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} TypeName;
/*
@@ -259,7 +285,8 @@ typedef struct ColumnRef
{
NodeTag type;
List *fields; /* field names (String nodes) or A_Star */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} ColumnRef;
/*
@@ -269,7 +296,8 @@ typedef struct ParamRef
{
NodeTag type;
int number; /* the number of the parameter */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} ParamRef;
/*
@@ -302,7 +330,8 @@ typedef struct A_Expr
List *name; /* possibly-qualified name of operator */
Node *lexpr; /* left argument, or NULL if none */
Node *rexpr; /* right argument, or NULL if none */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} A_Expr;
/*
@@ -328,7 +357,8 @@ typedef struct A_Const
NodeTag type;
union ValUnion val;
bool isnull; /* SQL NULL constant */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} A_Const;
/*
@@ -339,7 +369,8 @@ typedef struct TypeCast
NodeTag type;
Node *arg; /* the expression being casted */
TypeName *typeName; /* the target type */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} TypeCast;
/*
@@ -350,7 +381,8 @@ typedef struct CollateClause
NodeTag type;
Node *arg; /* input expression */
List *collname; /* possibly-qualified collation name */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CollateClause;
/*
@@ -370,7 +402,8 @@ typedef struct RoleSpec
NodeTag type;
RoleSpecType roletype; /* Type of this rolespec */
char *rolename; /* filled only for ROLESPEC_CSTRING */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} RoleSpec;
/*
@@ -400,7 +433,8 @@ typedef struct FuncCall
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
CoercionForm funcformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} FuncCall;
/*
@@ -457,7 +491,8 @@ typedef struct A_ArrayExpr
{
NodeTag type;
List *elements; /* array element expressions */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} A_ArrayExpr;
/*
@@ -484,7 +519,8 @@ typedef struct ResTarget
char *name; /* column name or NULL */
List *indirection; /* subscripts, field names, and '*', or NIL */
Node *val; /* the value expression to compute or assign */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} ResTarget;
/*
@@ -514,7 +550,8 @@ typedef struct SortBy
SortByDir sortby_dir; /* ASC/DESC/USING/default */
SortByNulls sortby_nulls; /* NULLS FIRST/LAST */
List *useOp; /* name of op to use, if SORTBY_USING */
- int location; /* operator location, or -1 if none/unknown */
+ /* token location, or -1 if unknown */
+ int location;
} SortBy;
/*
@@ -535,7 +572,8 @@ typedef struct WindowDef
int frameOptions; /* frame_clause options, see below */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- int location; /* parse location, or -1 if none/unknown */
+ /* token location, or -1 if unknown */
+ int location;
} WindowDef;
/*
@@ -625,7 +663,8 @@ typedef struct RangeTableFunc
List *namespaces; /* list of namespaces as ResTarget */
List *columns; /* list of RangeTableFuncCol */
Alias *alias; /* table alias & optional column aliases */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} RangeTableFunc;
/*
@@ -643,7 +682,8 @@ typedef struct RangeTableFuncCol
bool is_not_null; /* does it have NOT NULL? */
Node *colexpr; /* column filter expression */
Node *coldefexpr; /* column default value expression */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} RangeTableFuncCol;
/*
@@ -663,7 +703,8 @@ typedef struct RangeTableSample
List *method; /* sampling method name (possibly qualified) */
List *args; /* argument(s) for sampling method */
Node *repeatable; /* REPEATABLE expression, or NULL if none */
- int location; /* method name location, or -1 if unknown */
+ /* method name location, or -1 if unknown */
+ int location;
} RangeTableSample;
/*
@@ -706,7 +747,8 @@ typedef struct ColumnDef
Oid collOid; /* collation OID (InvalidOid if not set) */
List *constraints; /* other constraints on column */
List *fdwoptions; /* per-column FDW options */
- int location; /* parse location, or -1 if none/unknown */
+ /* parse location, or -1 if unknown */
+ int location;
} ColumnDef;
/*
@@ -780,7 +822,8 @@ typedef struct DefElem
Node *arg; /* typically Integer, Float, String, or
* TypeName */
DefElemAction defaction; /* unspecified action, or SET/ADD/DROP */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} DefElem;
/*
@@ -809,7 +852,8 @@ typedef struct XmlSerialize
XmlOptionType xmloption; /* DOCUMENT or CONTENT */
Node *expr;
TypeName *typeName;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} XmlSerialize;
/* Partitioning related definitions */
@@ -827,7 +871,8 @@ typedef struct PartitionElem
Node *expr; /* expression to partition on, or NULL */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PartitionElem;
typedef enum PartitionStrategy
@@ -847,7 +892,8 @@ typedef struct PartitionSpec
NodeTag type;
PartitionStrategy strategy;
List *partParams; /* List of PartitionElems */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PartitionSpec;
/*
@@ -874,7 +920,8 @@ struct PartitionBoundSpec
List *lowerdatums; /* List of PartitionRangeDatums */
List *upperdatums; /* List of PartitionRangeDatums */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
};
/*
@@ -897,7 +944,8 @@ typedef struct PartitionRangeDatum
Node *value; /* Const (or A_Const in raw tree), if kind is
* PARTITION_RANGE_DATUM_VALUE, else NULL */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PartitionRangeDatum;
/*
@@ -1223,14 +1271,21 @@ typedef struct RangeTblFunction
NodeTag type;
Node *funcexpr; /* expression tree for func call */
- int funccolcount; /* number of columns it contributes to RTE */
+ /* number of columns it contributes to RTE */
+ int funccolcount;
/* These fields record the contents of a column definition list, if any: */
- List *funccolnames; /* column names (list of String) */
- List *funccoltypes; /* OID list of column type OIDs */
- List *funccoltypmods; /* integer list of column typmods */
- List *funccolcollations; /* OID list of column collation OIDs */
+ /* column names (list of String) */
+ List *funccolnames;
+ /* OID list of column type OIDs */
+ List *funccoltypes;
+ /* integer list of column typmods */
+ List *funccoltypmods;
+ /* OID list of column collation OIDs */
+ List *funccolcollations;
+
/* This is set during planning for use by the executor: */
- Bitmapset *funcparams; /* PARAM_EXEC Param IDs affecting this func */
+ /* PARAM_EXEC Param IDs affecting this func */
+ Bitmapset *funcparams;
} RangeTblFunction;
/*
@@ -1337,7 +1392,8 @@ typedef struct SortGroupClause
Oid eqop; /* the equality operator ('=' op) */
Oid sortop; /* the ordering operator ('<' op), or 0 */
bool nulls_first; /* do NULLs come before normal values? */
- bool hashable; /* can eqop be implemented by hashing? */
+ /* can eqop be implemented by hashing? */
+ bool hashable;
} SortGroupClause;
/*
@@ -1427,21 +1483,31 @@ typedef struct GroupingSet
typedef struct WindowClause
{
NodeTag type;
- char *name; /* window name (NULL in an OVER clause) */
- char *refname; /* referenced window name, if any */
+ /* window name (NULL in an OVER clause) */
+ char *name;
+ /* referenced window name, if any */
+ char *refname;
List *partitionClause; /* PARTITION BY list */
- List *orderClause; /* ORDER BY list */
+ /* ORDER BY list */
+ List *orderClause;
int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- List *runCondition; /* qual to help short-circuit execution */
- Oid startInRangeFunc; /* in_range function for startOffset */
- Oid endInRangeFunc; /* in_range function for endOffset */
- Oid inRangeColl; /* collation for in_range tests */
- bool inRangeAsc; /* use ASC sort order for in_range tests? */
- bool inRangeNullsFirst; /* nulls sort first for in_range tests? */
+ /* qual to help short-circuit execution */
+ List *runCondition;
+ /* in_range function for startOffset */
+ Oid startInRangeFunc;
+ /* in_range function for endOffset */
+ Oid endInRangeFunc;
+ /* collation for in_range tests */
+ Oid inRangeColl;
+ /* use ASC sort order for in_range tests? */
+ bool inRangeAsc;
+ /* nulls sort first for in_range tests? */
+ bool inRangeNullsFirst;
Index winref; /* ID referenced by window functions */
- bool copiedOrder; /* did we copy orderClause from refname? */
+ /* did we copy orderClause from refname? */
+ bool copiedOrder;
} WindowClause;
/*
@@ -1477,7 +1543,8 @@ typedef struct WithClause
NodeTag type;
List *ctes; /* list of CommonTableExprs */
bool recursive; /* true = WITH RECURSIVE */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} WithClause;
/*
@@ -1492,7 +1559,8 @@ typedef struct InferClause
List *indexElems; /* IndexElems to infer unique index */
Node *whereClause; /* qualification (partial-index predicate) */
char *conname; /* Constraint name, or NULL if unnamed */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} InferClause;
/*
@@ -1508,7 +1576,8 @@ typedef struct OnConflictClause
InferClause *infer; /* Optional index inference clause */
List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} OnConflictClause;
/*
@@ -1558,15 +1627,25 @@ typedef struct CommonTableExpr
Node *ctequery; /* the CTE's subquery */
CTESearchClause *search_clause;
CTECycleClause *cycle_clause;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
/* These fields are set during parse analysis: */
- bool cterecursive; /* is this CTE actually recursive? */
- int cterefcount; /* number of RTEs referencing this CTE
- * (excluding internal self-references) */
- List *ctecolnames; /* list of output column names */
- List *ctecoltypes; /* OID list of output column type OIDs */
- List *ctecoltypmods; /* integer list of output column typmods */
- List *ctecolcollations; /* OID list of column collation OIDs */
+ /* is this CTE actually recursive? */
+ bool cterecursive;
+
+ /*
+ * Number of RTEs referencing this CTE (excluding internal
+ * self-references)
+ */
+ int cterefcount;
+ /* list of output column names */
+ List *ctecolnames;
+ /* OID list of output column type OIDs */
+ List *ctecoltypes;
+ /* integer list of output column typmods */
+ List *ctecoltypmods;
+ /* OID list of column collation OIDs */
+ List *ctecolcollations;
} CommonTableExpr;
/* Convenience macro to get the output tlist of a CTE's query */
@@ -1603,10 +1682,12 @@ typedef struct MergeAction
NodeTag type;
bool matched; /* true=MATCHED, false=NOT MATCHED */
CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
- OverridingKind override; /* OVERRIDING clause */
+ /* OVERRIDING clause */
+ OverridingKind override;
Node *qual; /* transformed WHEN conditions */
List *targetList; /* the target list (of TargetEntry) */
- List *updateColnos; /* target attribute numbers of an UPDATE */
+ /* target attribute numbers of an UPDATE */
+ List *updateColnos;
} MergeAction;
/*
@@ -1645,7 +1726,8 @@ typedef struct RawStmt
{
NodeTag type;
Node *stmt; /* raw parse tree */
- int stmt_location; /* start location, or -1 if unknown */
+ /* start location, or -1 if unknown */
+ int stmt_location;
int stmt_len; /* length in bytes; 0 means "rest of string" */
} RawStmt;
@@ -1816,10 +1898,14 @@ typedef struct SetOperationStmt
/* Eventually add fields for CORRESPONDING spec here */
/* Fields derived during parse analysis: */
- List *colTypes; /* OID list of output column type OIDs */
- List *colTypmods; /* integer list of output column typmods */
- List *colCollations; /* OID list of output column collation OIDs */
- List *groupClauses; /* a list of SortGroupClause's */
+ /* OID list of output column type OIDs */
+ List *colTypes;
+ /* integer list of output column typmods */
+ List *colTypmods;
+ /* OID list of output column collation OIDs */
+ List *colCollations;
+ /* a list of SortGroupClause's */
+ List *groupClauses;
/* groupClauses is NIL if UNION ALL, but must be set otherwise */
} SetOperationStmt;
@@ -1849,7 +1935,8 @@ typedef struct PLAssignStmt
List *indirection; /* subscripts and field names, if any */
int nnames; /* number of names to use in ColumnRef */
SelectStmt *val; /* the PL/pgSQL expression to assign */
- int location; /* name's token location, or -1 if unknown */
+ /* name's token location, or -1 if unknown */
+ int location;
} PLAssignStmt;
@@ -2356,7 +2443,8 @@ typedef struct Constraint
char *conname; /* Constraint name, or NULL if unnamed */
bool deferrable; /* DEFERRABLE? */
bool initdeferred; /* INITIALLY DEFERRED? */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
/* Fields used for constraints with expressions (CHECK and DEFAULT): */
bool is_no_inherit; /* is constraint non-inheritable? */
@@ -3765,7 +3853,8 @@ typedef struct PublicationObjSpec
PublicationObjSpecType pubobjtype; /* type of this publication object */
char *name;
PublicationTable *pubtable;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PublicationObjSpec;
typedef struct CreatePublicationStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index c1234fcf36..f33d7fe167 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -99,7 +99,8 @@ typedef struct PlannedStmt
Node *utilityStmt; /* non-null if this is utility stmt */
/* statement location in source string (copied from Query) */
- int stmt_location; /* start location, or -1 if unknown */
+ /* start location, or -1 if unknown */
+ int stmt_location;
int stmt_len; /* length in bytes; 0 means "rest of string" */
} PlannedStmt;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 83e40e56d3..8d80c90ce7 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -98,19 +98,32 @@ typedef struct RangeVar
typedef struct TableFunc
{
NodeTag type;
- List *ns_uris; /* list of namespace URI expressions */
- List *ns_names; /* list of namespace names or NULL */
- Node *docexpr; /* input document expression */
- Node *rowexpr; /* row filter expression */
- List *colnames; /* column names (list of String) */
- List *coltypes; /* OID list of column type OIDs */
- List *coltypmods; /* integer list of column typmods */
- List *colcollations; /* OID list of column collation OIDs */
- List *colexprs; /* list of column filter expressions */
- List *coldefexprs; /* list of column default expressions */
- Bitmapset *notnulls; /* nullability flag for each output column */
- int ordinalitycol; /* counts from 0; -1 if none specified */
- int location; /* token location, or -1 if unknown */
+ /* list of namespace URI expressions */
+ List *ns_uris;
+ /* list of namespace names or NULL */
+ List *ns_names;
+ /* input document expression */
+ Node *docexpr;
+ /* row filter expression */
+ Node *rowexpr;
+ /* column names (list of String) */
+ List *colnames;
+ /* OID list of column type OIDs */
+ List *coltypes;
+ /* integer list of column typmods */
+ List *coltypmods;
+ /* OID list of column collation OIDs */
+ List *colcollations;
+ /* list of column filter expressions */
+ List *colexprs;
+ /* list of column default expressions */
+ List *coldefexprs;
+ /* nullability flag for each output column */
+ Bitmapset *notnulls;
+ /* counts from 0; -1 if none specified */
+ int ordinalitycol;
+ /* token location, or -1 if unknown */
+ int location;
} TableFunc;
/*
@@ -256,18 +269,27 @@ typedef struct Const
pg_node_attr(custom_copy_equal, custom_read_write)
Expr xpr;
- Oid consttype; /* pg_type OID of the constant's datatype */
- int32 consttypmod; /* typmod value, if any */
- Oid constcollid; /* OID of collation, or InvalidOid if none */
- int constlen; /* typlen of the constant's datatype */
- Datum constvalue; /* the constant's value */
- bool constisnull; /* whether the constant is null (if true,
- * constvalue is undefined) */
- bool constbyval; /* 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. */
- int location; /* token location, or -1 if unknown */
+ /* pg_type OID of the constant's datatype */
+ Oid consttype;
+ /* typmod value, if any */
+ int32 consttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid constcollid;
+ /* typlen of the constant's datatype */
+ int constlen;
+ /* the constant's value */
+ Datum constvalue;
+ /* whether the constant is null (if true, constvalue is undefined) */
+ bool constisnull;
+
+ /*
+ * 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;
} Const;
/*
@@ -311,9 +333,12 @@ typedef struct Param
ParamKind paramkind; /* kind of parameter. See above */
int paramid; /* numeric ID for parameter */
Oid paramtype; /* pg_type OID of parameter's datatype */
- int32 paramtypmod; /* typmod value, if known */
- Oid paramcollid; /* OID of collation, or InvalidOid if none */
- int location; /* token location, or -1 if unknown */
+ /* typmod value, if known */
+ int32 paramtypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid paramcollid;
+ /* token location, or -1 if unknown */
+ int location;
} Param;
/*
@@ -486,16 +511,26 @@ typedef struct GroupingFunc
typedef struct WindowFunc
{
Expr xpr;
- Oid winfnoid; /* pg_proc Oid of the function */
- Oid wintype; /* type Oid of result of the window function */
- Oid wincollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
- List *args; /* arguments to the window function */
- Expr *aggfilter; /* FILTER expression, if any */
- Index winref; /* index of associated WindowClause */
- bool winstar; /* true if argument list was really '*' */
- bool winagg; /* is function a simple aggregate? */
- int location; /* token location, or -1 if unknown */
+ /* pg_proc Oid of the function */
+ Oid winfnoid;
+ /* type Oid of result of the window function */
+ Oid wintype;
+ /* OID of collation of result */
+ Oid wincollid;
+ /* OID of collation that function should use */
+ Oid inputcollid;
+ /* arguments to the window function */
+ List *args;
+ /* FILTER expression, if any */
+ Expr *aggfilter;
+ /* index of associated WindowClause */
+ Index winref;
+ /* true if argument list was really '*' */
+ bool winstar;
+ /* is function a simple aggregate? */
+ bool winagg;
+ /* token location, or -1 if unknown */
+ int location;
} WindowFunc;
/*
@@ -539,20 +574,28 @@ typedef struct WindowFunc
typedef struct SubscriptingRef
{
Expr xpr;
- Oid refcontainertype; /* type of the container proper */
- Oid refelemtype; /* the container type's pg_type.typelem */
- Oid refrestype; /* type of the SubscriptingRef's result */
- int32 reftypmod; /* typmod of the result */
- Oid refcollid; /* collation of result, or InvalidOid if none */
- List *refupperindexpr; /* expressions that evaluate to upper
- * container indexes */
- List *reflowerindexpr; /* expressions that evaluate to lower
- * container indexes, or NIL for single
- * container element */
- Expr *refexpr; /* the expression that evaluates to a
- * container value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
+ /* type of the container proper */
+ Oid refcontainertype;
+ /* the container type's pg_type.typelem */
+ Oid refelemtype;
+ /* type of the SubscriptingRef's result */
+ Oid refrestype;
+ /* typmod of the result */
+ int32 reftypmod;
+ /* collation of result, or InvalidOid if none */
+ Oid refcollid;
+ /* expressions that evaluate to upper container indexes */
+ List *refupperindexpr;
+
+ /*
+ * expressions that evaluate to lower container indexes, or NIL for single
+ * container element.
+ */
+ List *reflowerindexpr;
+ /* the expression that evaluates to a container value */
+ Expr *refexpr;
+ /* expression for the source value, or NULL if fetch */
+ Expr *refassgnexpr;
} SubscriptingRef;
/*
@@ -595,16 +638,28 @@ typedef enum CoercionForm
typedef struct FuncExpr
{
Expr xpr;
- Oid funcid; /* PG_PROC OID of the function */
- Oid funcresulttype; /* PG_TYPE OID of result value */
- bool funcretset; /* true if function returns set */
- bool funcvariadic; /* true if variadic arguments have been
- * combined into an array last argument */
- CoercionForm funcformat; /* how to display this function call */
- Oid funccollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
- List *args; /* arguments to the function */
- int location; /* token location, or -1 if unknown */
+ /* PG_PROC OID of the function */
+ Oid funcid;
+ /* PG_TYPE OID of result value */
+ Oid funcresulttype;
+ /* true if function returns set */
+ bool funcretset;
+
+ /*
+ * true if variadic arguments have been combined into an array last
+ * argument
+ */
+ bool funcvariadic;
+ /* how to display this function call */
+ CoercionForm funcformat;
+ /* OID of collation of result */
+ Oid funccollid;
+ /* OID of collation that function should use */
+ Oid inputcollid;
+ /* arguments to the function */
+ List *args;
+ /* token location, or -1 if unknown */
+ int location;
} FuncExpr;
/*
@@ -624,10 +679,14 @@ typedef struct FuncExpr
typedef struct NamedArgExpr
{
Expr xpr;
- Expr *arg; /* the argument expression */
- char *name; /* the name */
- int argnumber; /* argument's number in positional notation */
- int location; /* argument name location, or -1 if unknown */
+ /* the argument expression */
+ Expr *arg;
+ /* the name */
+ char *name;
+ /* argument's number in positional notation */
+ int argnumber;
+ /* argument name location, or -1 if unknown */
+ int location;
} NamedArgExpr;
/*
@@ -765,7 +824,8 @@ typedef struct BoolExpr
Expr xpr;
BoolExprType boolop;
List *args; /* arguments to this expression */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} BoolExpr;
/*
@@ -838,9 +898,12 @@ typedef struct SubLink
SubLinkType subLinkType; /* see above */
int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */
Node *testexpr; /* outer-query test for ALL/ANY/ROWCOMPARE */
- List *operName; /* originally specified operator name */
- Node *subselect; /* subselect as Query* or raw parsetree */
- int location; /* token location, or -1 if unknown */
+ /* originally specified operator name */
+ List *operName;
+ /* subselect as Query* or raw parsetree */
+ Node *subselect;
+ /* token location, or -1 if unknown */
+ int location;
} SubLink;
/*
@@ -948,10 +1011,12 @@ typedef struct FieldSelect
Expr xpr;
Expr *arg; /* input expression */
AttrNumber fieldnum; /* attribute number of field to extract */
- Oid resulttype; /* type of the field (result type of this
- * node) */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation of the field */
+ /* type of the field (result type of this node) */
+ Oid resulttype;
+ /* output typmod (usually -1) */
+ int32 resulttypmod;
+ /* OID of collation of the field */
+ Oid resultcollid;
} FieldSelect;
/* ----------------
@@ -977,8 +1042,10 @@ typedef struct FieldStore
Expr xpr;
Expr *arg; /* input tuple value */
List *newvals; /* new value(s) for field(s) */
- List *fieldnums; /* integer list of field attnums */
- Oid resulttype; /* type of result (same as type of arg) */
+ /* integer list of field attnums */
+ List *fieldnums;
+ /* type of result (same as type of arg) */
+ Oid resulttype;
/* Like RowExpr, we deliberately omit a typmod and collation here */
} FieldStore;
@@ -1000,10 +1067,14 @@ typedef struct RelabelType
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion expression */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm relabelformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* output typmod (usually -1) */
+ int32 resulttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm relabelformat;
+ /* token location, or -1 if unknown */
+ int location;
} RelabelType;
/* ----------------
@@ -1021,9 +1092,12 @@ typedef struct CoerceViaIO
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion */
/* output typmod is not stored, but is presumed -1 */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm coerceformat;
+ /* token location, or -1 if unknown */
+ int location;
} CoerceViaIO;
/* ----------------
@@ -1045,10 +1119,14 @@ typedef struct ArrayCoerceExpr
Expr *arg; /* input expression (yields an array) */
Expr *elemexpr; /* expression representing per-element work */
Oid resulttype; /* output type of coercion (an array type) */
- int32 resulttypmod; /* output typmod (also element typmod) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* output typmod (also element typmod) */
+ int32 resulttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm coerceformat;
+ /* token location, or -1 if unknown */
+ int location;
} ArrayCoerceExpr;
/* ----------------
@@ -1070,8 +1148,10 @@ typedef struct ConvertRowtypeExpr
Expr *arg; /* input expression */
Oid resulttype; /* output type (always a composite type) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
- CoercionForm convertformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* how to display this node */
+ CoercionForm convertformat;
+ /* token location, or -1 if unknown */
+ int location;
} ConvertRowtypeExpr;
/*----------
@@ -1086,7 +1166,8 @@ typedef struct CollateExpr
Expr xpr;
Expr *arg; /* input expression */
Oid collOid; /* collation's OID */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CollateExpr;
/*----------
@@ -1114,12 +1195,15 @@ typedef struct CollateExpr
typedef struct CaseExpr
{
Expr xpr;
- Oid casetype; /* type of expression result */
- Oid casecollid; /* OID of collation, or InvalidOid if none */
+ /* type of expression result */
+ Oid casetype;
+ /* OID of collation, or InvalidOid if none */
+ Oid casecollid;
Expr *arg; /* implicit equality comparison argument */
List *args; /* the arguments (list of WHEN clauses) */
Expr *defresult; /* the default result (ELSE clause) */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CaseExpr;
/*
@@ -1130,7 +1214,8 @@ typedef struct CaseWhen
Expr xpr;
Expr *expr; /* condition expression */
Expr *result; /* substitution result */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CaseWhen;
/*
@@ -1157,8 +1242,10 @@ typedef struct CaseTestExpr
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
+ /* typemod for substituted value */
+ int32 typeMod;
+ /* collation for the substituted value */
+ Oid collation;
} CaseTestExpr;
/*
@@ -1172,12 +1259,18 @@ typedef struct CaseTestExpr
typedef struct ArrayExpr
{
Expr xpr;
- Oid array_typeid; /* type of expression result */
- Oid array_collid; /* OID of collation, or InvalidOid if none */
- Oid element_typeid; /* common type of array elements */
- List *elements; /* the array elements or sub-arrays */
- bool multidims; /* true if elements are sub-arrays */
- int location; /* token location, or -1 if unknown */
+ /* type of expression result */
+ Oid array_typeid;
+ /* OID of collation, or InvalidOid if none */
+ Oid array_collid;
+ /* common type of array elements */
+ Oid element_typeid;
+ /* the array elements or sub-arrays */
+ List *elements;
+ /* true if elements are sub-arrays */
+ bool multidims;
+ /* token location, or -1 if unknown */
+ int location;
} ArrayExpr;
/*
@@ -1205,7 +1298,9 @@ typedef struct RowExpr
{
Expr xpr;
List *args; /* the fields */
- Oid row_typeid; /* RECORDOID or a composite type's ID */
+
+ /* RECORDOID or a composite type's ID */
+ Oid row_typeid;
/*
* row_typeid cannot be a domain over composite, only plain composite. To
@@ -1219,9 +1314,15 @@ typedef struct RowExpr
* We don't need to store a collation either. The result type is
* necessarily composite, and composite types never have a collation.
*/
- CoercionForm row_format; /* how to display this node */
- List *colnames; /* list of String, or NIL */
- int location; /* token location, or -1 if unknown */
+
+ /* how to display this node */
+ CoercionForm row_format;
+
+ /* list of String, or NIL */
+ List *colnames;
+
+ /* token location, or -1 if unknown */
+ int location;
} RowExpr;
/*
@@ -1252,12 +1353,19 @@ typedef enum RowCompareType
typedef struct RowCompareExpr
{
Expr xpr;
- RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
- List *opnos; /* OID list of pairwise comparison ops */
- List *opfamilies; /* OID list of containing operator families */
- List *inputcollids; /* OID list of collations for comparisons */
- List *largs; /* the left-hand input arguments */
- List *rargs; /* the right-hand input arguments */
+
+ /* LT LE GE or GT, never EQ or NE */
+ RowCompareType rctype;
+ /* OID list of pairwise comparison ops */
+ List *opnos;
+ /* OID list of containing operator families */
+ List *opfamilies;
+ /* OID list of collations for comparisons */
+ List *inputcollids;
+ /* the left-hand input arguments */
+ List *largs;
+ /* the right-hand input arguments */
+ List *rargs;
} RowCompareExpr;
/*
@@ -1266,10 +1374,14 @@ typedef struct RowCompareExpr
typedef struct CoalesceExpr
{
Expr xpr;
- Oid coalescetype; /* type of expression result */
- Oid coalescecollid; /* OID of collation, or InvalidOid if none */
- List *args; /* the arguments */
- int location; /* token location, or -1 if unknown */
+ /* type of expression result */
+ Oid coalescetype;
+ /* OID of collation, or InvalidOid if none */
+ Oid coalescecollid;
+ /* the arguments */
+ List *args;
+ /* token location, or -1 if unknown */
+ int location;
} CoalesceExpr;
/*
@@ -1284,12 +1396,18 @@ typedef enum MinMaxOp
typedef struct MinMaxExpr
{
Expr xpr;
- Oid minmaxtype; /* common type of arguments and result */
- Oid minmaxcollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
- MinMaxOp op; /* function to execute */
- List *args; /* the arguments */
- int location; /* token location, or -1 if unknown */
+ /* common type of arguments and result */
+ Oid minmaxtype;
+ /* OID of collation of result */
+ Oid minmaxcollid;
+ /* OID of collation that function should use */
+ Oid inputcollid;
+ /* function to execute */
+ MinMaxOp op;
+ /* the arguments */
+ List *args;
+ /* token location, or -1 if unknown */
+ int location;
} MinMaxExpr;
/*
@@ -1324,15 +1442,23 @@ typedef enum XmlOptionType
typedef struct XmlExpr
{
Expr xpr;
- XmlExprOp op; /* xml function ID */
- char *name; /* name in xml(NAME foo ...) syntaxes */
- List *named_args; /* non-XML expressions for xml_attributes */
- List *arg_names; /* parallel list of String values */
- List *args; /* list of expressions */
- XmlOptionType xmloption; /* DOCUMENT or CONTENT */
- Oid type; /* target type/typmod for XMLSERIALIZE */
+ /* xml function ID */
+ XmlExprOp op;
+ /* name in xml(NAME foo ...) syntaxes */
+ char *name;
+ /* non-XML expressions for xml_attributes */
+ List *named_args;
+ /* parallel list of String values */
+ List *arg_names;
+ /* list of expressions */
+ List *args;
+ /* DOCUMENT or CONTENT */
+ XmlOptionType xmloption;
+ /* target type/typmod for XMLSERIALIZE */
+ Oid type;
int32 typmod;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} XmlExpr;
/* ----------------
@@ -1364,8 +1490,10 @@ typedef struct NullTest
Expr xpr;
Expr *arg; /* input expression */
NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
- bool argisrow; /* T to perform field-by-field null checks */
- int location; /* token location, or -1 if unknown */
+ /* T to perform field-by-field null checks */
+ bool argisrow;
+ /* token location, or -1 if unknown */
+ int location;
} NullTest;
/*
@@ -1387,7 +1515,8 @@ typedef struct BooleanTest
Expr xpr;
Expr *arg; /* input expression */
BoolTestType booltesttype; /* test type */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} BooleanTest;
/*
@@ -1404,10 +1533,14 @@ typedef struct CoerceToDomain
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* domain type ID (result type) */
- int32 resulttypmod; /* output typmod (currently always -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coercionformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* output typmod (currently always -1) */
+ int32 resulttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm coercionformat;
+ /* token location, or -1 if unknown */
+ int location;
} CoerceToDomain;
/*
@@ -1422,10 +1555,14 @@ typedef struct CoerceToDomain
typedef struct CoerceToDomainValue
{
Expr xpr;
- Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
- int location; /* token location, or -1 if unknown */
+ /* type for substituted value */
+ Oid typeId;
+ /* typemod for substituted value */
+ int32 typeMod;
+ /* collation for the substituted value */
+ Oid collation;
+ /* token location, or -1 if unknown */
+ int location;
} CoerceToDomainValue;
/*
@@ -1438,10 +1575,14 @@ typedef struct CoerceToDomainValue
typedef struct SetToDefault
{
Expr xpr;
- Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
- int location; /* token location, or -1 if unknown */
+ /* type for substituted value */
+ Oid typeId;
+ /* typemod for substituted value */
+ int32 typeMod;
+ /* collation for the substituted value */
+ Oid collation;
+ /* token location, or -1 if unknown */
+ int location;
} SetToDefault;
/*
@@ -1552,15 +1693,20 @@ typedef struct InferenceElem
typedef struct TargetEntry
{
Expr xpr;
- Expr *expr; /* expression to evaluate */
- AttrNumber resno; /* attribute number (see notes above) */
- char *resname; /* name of the column (could be NULL) */
- Index ressortgroupref; /* nonzero if referenced by a sort/group
- * clause */
- Oid resorigtbl; /* OID of column's source table */
- AttrNumber resorigcol; /* column's number in source table */
- bool resjunk; /* set to true to eliminate the attribute from
- * final target list */
+ /* expression to evaluate */
+ Expr *expr;
+ /* attribute number (see notes above) */
+ AttrNumber resno;
+ /* name of the column (could be NULL) */
+ char *resname;
+ /* nonzero if referenced by a sort/group clause */
+ Index ressortgroupref;
+ /* OID of column's source table */
+ Oid resorigtbl;
+ /* column's number in source table */
+ AttrNumber resorigcol;
+ /* set to true to eliminate the attribute from final target list */
+ bool resjunk;
} TargetEntry;
@@ -1642,11 +1788,16 @@ typedef struct JoinExpr
bool isNatural; /* Natural join? Will need to shape table */
Node *larg; /* left subtree */
Node *rarg; /* right subtree */
- List *usingClause; /* USING clause, if any (list of String) */
- Alias *join_using_alias; /* alias attached to USING clause, if any */
- Node *quals; /* qualifiers on join, if any */
- Alias *alias; /* user-written alias clause, if any */
- int rtindex; /* RT index assigned for join, or 0 */
+ /* USING clause, if any (list of String) */
+ List *usingClause;
+ /* alias attached to USING clause, if any */
+ Alias *join_using_alias;
+ /* qualifiers on join, if any */
+ Node *quals;
+ /* user-written alias clause, if any */
+ Alias *alias;
+ /* RT index assigned for join, or 0 */
+ int rtindex;
} JoinExpr;
/*----------
--
2.39.0
v3-0002-Move-query-jumble-code-to-src-backend-nodes.patchtext/x-diff; charset=us-asciiDownload
From cd672f8feb4c533c2ef1bbfb31088efc64ce7160 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 18 Jan 2023 14:37:07 +0900
Subject: [PATCH v3 2/3] Move query jumble code to src/backend/nodes/
This will ease a follow-up move that will generate automatically this
code:
- queryjumble.c -> queryjumblefuncs.c
- utils/queryjumble.h -> nodes/queryjumble.h
---
src/include/{utils => nodes}/queryjumble.h | 2 +-
src/include/parser/analyze.h | 2 +-
src/backend/nodes/Makefile | 1 +
src/backend/nodes/meson.build | 1 +
.../{utils/misc/queryjumble.c => nodes/queryjumblefuncs.c} | 6 +++---
src/backend/parser/analyze.c | 2 +-
src/backend/postmaster/postmaster.c | 2 +-
src/backend/utils/misc/Makefile | 1 -
src/backend/utils/misc/guc_tables.c | 2 +-
src/backend/utils/misc/meson.build | 1 -
contrib/pg_stat_statements/pg_stat_statements.c | 2 +-
11 files changed, 11 insertions(+), 11 deletions(-)
rename src/include/{utils => nodes}/queryjumble.h (98%)
rename src/backend/{utils/misc/queryjumble.c => nodes/queryjumblefuncs.c} (99%)
diff --git a/src/include/utils/queryjumble.h b/src/include/nodes/queryjumble.h
similarity index 98%
rename from src/include/utils/queryjumble.h
rename to src/include/nodes/queryjumble.h
index d372801410..204b8f74fd 100644
--- a/src/include/utils/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * src/include/utils/queryjumble.h
+ * src/include/nodes/queryjumble.h
*
*-------------------------------------------------------------------------
*/
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index c97be6efcf..1cef1833a6 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -15,8 +15,8 @@
#define ANALYZE_H
#include "nodes/params.h"
+#include "nodes/queryjumble.h"
#include "parser/parse_node.h"
-#include "utils/queryjumble.h"
/* Hook for plugins to get control at end of parse analysis */
typedef void (*post_parse_analyze_hook_type) (ParseState *pstate,
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 7c594be583..af12c64878 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -27,6 +27,7 @@ OBJS = \
outfuncs.o \
params.o \
print.o \
+ queryjumblefuncs.o \
read.o \
readfuncs.o \
tidbitmap.o \
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 2ff7dbac1d..9230515e7f 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -10,6 +10,7 @@ backend_sources += files(
'nodes.c',
'params.c',
'print.c',
+ 'queryjumblefuncs.c',
'read.c',
'tidbitmap.c',
'value.c',
diff --git a/src/backend/utils/misc/queryjumble.c b/src/backend/nodes/queryjumblefuncs.c
similarity index 99%
rename from src/backend/utils/misc/queryjumble.c
rename to src/backend/nodes/queryjumblefuncs.c
index 328995a7dc..16084842a3 100644
--- a/src/backend/utils/misc/queryjumble.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * queryjumble.c
+ * queryjumblefuncs.c
* Query normalization and fingerprinting.
*
* Normalization is a process whereby similar queries, typically differing only
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * src/backend/utils/misc/queryjumble.c
+ * src/backend/nodes/queryjumblefuncs.c
*
*-------------------------------------------------------------------------
*/
@@ -34,8 +34,8 @@
#include "common/hashfn.h"
#include "miscadmin.h"
+#include "nodes/queryjumble.h"
#include "parser/scansup.h"
-#include "utils/queryjumble.h"
#define JUMBLE_SIZE 1024 /* query serialization buffer size */
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5b90974e83..4a817b75ad 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -30,6 +30,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "nodes/queryjumble.h"
#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "parser/parse_agg.h"
@@ -50,7 +51,6 @@
#include "utils/backend_status.h"
#include "utils/builtins.h"
#include "utils/guc.h"
-#include "utils/queryjumble.h"
#include "utils/rel.h"
#include "utils/syscache.h"
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 9cedc1b9f0..f05a26d255 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -102,6 +102,7 @@
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
+#include "nodes/queryjumble.h"
#include "pg_getopt.h"
#include "pgstat.h"
#include "port/pg_bswap.h"
@@ -126,7 +127,6 @@
#include "utils/memutils.h"
#include "utils/pidfile.h"
#include "utils/ps_status.h"
-#include "utils/queryjumble.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index b9ee4eb48a..2910032930 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -26,7 +26,6 @@ OBJS = \
pg_rusage.o \
ps_status.o \
queryenvironment.o \
- queryjumble.o \
rls.o \
sampling.o \
superuser.o \
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 5025e80f89..f9bfbbbd95 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -43,6 +43,7 @@
#include "jit/jit.h"
#include "libpq/auth.h"
#include "libpq/libpq.h"
+#include "nodes/queryjumble.h"
#include "optimizer/cost.h"
#include "optimizer/geqo.h"
#include "optimizer/optimizer.h"
@@ -77,7 +78,6 @@
#include "utils/pg_locale.h"
#include "utils/portal.h"
#include "utils/ps_status.h"
-#include "utils/queryjumble.h"
#include "utils/inval.h"
#include "utils/xml.h"
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index e3e99ec5cb..f719c97c05 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -11,7 +11,6 @@ backend_sources += files(
'pg_rusage.c',
'ps_status.c',
'queryenvironment.c',
- 'queryjumble.c',
'rls.c',
'sampling.c',
'superuser.c',
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index a7a72783e5..ad1fe44496 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -55,6 +55,7 @@
#include "jit/jit.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/queryjumble.h"
#include "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
@@ -69,7 +70,6 @@
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
-#include "utils/queryjumble.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
--
2.39.0
v3-0003-Support-for-automated-query-jumble-with-all-Nodes.patchtext/x-diff; charset=us-asciiDownload
From 95ca73dbb1a316778f93eb52968b5a91bee55443 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 18 Jan 2023 15:21:36 +0900
Subject: [PATCH v3 3/3] Support for automated query jumble with all Nodes
This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
src/include/nodes/bitmapset.h | 2 +-
src/include/nodes/nodes.h | 4 +
src/include/nodes/parsenodes.h | 196 +++---
src/include/nodes/plannodes.h | 2 +-
src/include/nodes/primnodes.h | 327 +++++-----
src/backend/nodes/README | 1 +
src/backend/nodes/gen_node_support.pl | 95 ++-
src/backend/nodes/meson.build | 2 +-
src/backend/nodes/queryjumblefuncs.c | 855 ++++++--------------------
9 files changed, 561 insertions(+), 923 deletions(-)
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 0dca6bc5fa..3d2225e1ae 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 10752e8011..bcd0931add 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -59,6 +59,8 @@ typedef enum NodeTag
*
* - no_copy_equal: Shorthand for both no_copy and no_equal.
*
+ * - no_query_jumble: Does not support jumble() at all.
+ *
* - no_read: Does not support nodeRead() at all.
*
* - nodetag_only: Does not support copyObject(), equal(), outNode(),
@@ -97,6 +99,8 @@ 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.
+ *
* - 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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index eec51e3ee2..a4b06db293 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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
@@ -227,9 +234,9 @@ typedef struct Query
* both be -1 meaning "unknown".
*/
/* start location, or -1 if unknown */
- int stmt_location;
+ int stmt_location pg_node_attr(query_jumble_ignore);
/* length in bytes; 0 means "rest of string" */
- int stmt_len;
+ int stmt_len pg_node_attr(query_jumble_ignore);
} Query;
@@ -265,7 +272,7 @@ typedef struct TypeName
int32 typemod; /* prespecified type modifier */
List *arrayBounds; /* array bounds */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} TypeName;
/*
@@ -286,7 +293,7 @@ typedef struct ColumnRef
NodeTag type;
List *fields; /* field names (String nodes) or A_Star */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} ColumnRef;
/*
@@ -297,7 +304,7 @@ typedef struct ParamRef
NodeTag type;
int number; /* the number of the parameter */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} ParamRef;
/*
@@ -331,7 +338,7 @@ typedef struct A_Expr
Node *lexpr; /* left argument, or NULL if none */
Node *rexpr; /* right argument, or NULL if none */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} A_Expr;
/*
@@ -358,7 +365,7 @@ typedef struct A_Const
union ValUnion val;
bool isnull; /* SQL NULL constant */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} A_Const;
/*
@@ -370,7 +377,7 @@ typedef struct TypeCast
Node *arg; /* the expression being casted */
TypeName *typeName; /* the target type */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} TypeCast;
/*
@@ -382,7 +389,7 @@ typedef struct CollateClause
Node *arg; /* input expression */
List *collname; /* possibly-qualified collation name */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CollateClause;
/*
@@ -403,7 +410,7 @@ typedef struct RoleSpec
RoleSpecType roletype; /* Type of this rolespec */
char *rolename; /* filled only for ROLESPEC_CSTRING */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} RoleSpec;
/*
@@ -434,7 +441,7 @@ typedef struct FuncCall
bool func_variadic; /* last argument was labeled VARIADIC */
CoercionForm funcformat; /* how to display this node */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} FuncCall;
/*
@@ -492,7 +499,7 @@ typedef struct A_ArrayExpr
NodeTag type;
List *elements; /* array element expressions */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} A_ArrayExpr;
/*
@@ -520,7 +527,7 @@ typedef struct ResTarget
List *indirection; /* subscripts, field names, and '*', or NIL */
Node *val; /* the value expression to compute or assign */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} ResTarget;
/*
@@ -551,7 +558,7 @@ typedef struct SortBy
SortByNulls sortby_nulls; /* NULLS FIRST/LAST */
List *useOp; /* name of op to use, if SORTBY_USING */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} SortBy;
/*
@@ -573,7 +580,7 @@ typedef struct WindowDef
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} WindowDef;
/*
@@ -664,7 +671,7 @@ typedef struct RangeTableFunc
List *columns; /* list of RangeTableFuncCol */
Alias *alias; /* table alias & optional column aliases */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} RangeTableFunc;
/*
@@ -683,7 +690,7 @@ typedef struct RangeTableFuncCol
Node *colexpr; /* column filter expression */
Node *coldefexpr; /* column default value expression */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} RangeTableFuncCol;
/*
@@ -704,7 +711,7 @@ typedef struct RangeTableSample
List *args; /* argument(s) for sampling method */
Node *repeatable; /* REPEATABLE expression, or NULL if none */
/* method name location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} RangeTableSample;
/*
@@ -748,7 +755,7 @@ typedef struct ColumnDef
List *constraints; /* other constraints on column */
List *fdwoptions; /* per-column FDW options */
/* parse location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} ColumnDef;
/*
@@ -823,7 +830,7 @@ typedef struct DefElem
* TypeName */
DefElemAction defaction; /* unspecified action, or SET/ADD/DROP */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} DefElem;
/*
@@ -853,7 +860,7 @@ typedef struct XmlSerialize
Node *expr;
TypeName *typeName;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} XmlSerialize;
/* Partitioning related definitions */
@@ -872,7 +879,7 @@ typedef struct PartitionElem
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} PartitionElem;
typedef enum PartitionStrategy
@@ -893,7 +900,7 @@ typedef struct PartitionSpec
PartitionStrategy strategy;
List *partParams; /* List of PartitionElems */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} PartitionSpec;
/*
@@ -921,7 +928,7 @@ struct PartitionBoundSpec
List *upperdatums; /* List of PartitionRangeDatums */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
};
/*
@@ -945,7 +952,7 @@ typedef struct PartitionRangeDatum
* PARTITION_RANGE_DATUM_VALUE, else NULL */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} PartitionRangeDatum;
/*
@@ -1042,7 +1049,7 @@ typedef enum RTEKind
typedef struct RangeTblEntry
{
- pg_node_attr(custom_read_write)
+ pg_node_attr(custom_read_write, no_query_jumble)
NodeTag type;
@@ -1265,6 +1272,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 needs only to track the function expression.
*/
typedef struct RangeTblFunction
{
@@ -1272,20 +1281,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;
/*
@@ -1393,7 +1402,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;
/*
@@ -1458,9 +1467,9 @@ typedef enum GroupingSetKind
typedef struct GroupingSet
{
NodeTag type;
- GroupingSetKind kind;
+ GroupingSetKind kind pg_node_attr(query_jumble_ignore);
List *content;
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} GroupingSet;
/*
@@ -1479,35 +1488,38 @@ 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;
+ List *orderClause pg_node_attr(query_jumble_ignore);
int frameOptions; /* frame_clause options, see WindowDef */
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;
/*
@@ -1544,7 +1556,7 @@ typedef struct WithClause
List *ctes; /* list of CommonTableExprs */
bool recursive; /* true = WITH RECURSIVE */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} WithClause;
/*
@@ -1560,7 +1572,7 @@ typedef struct InferClause
Node *whereClause; /* qualification (partial-index predicate) */
char *conname; /* Constraint name, or NULL if unnamed */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} InferClause;
/*
@@ -1577,7 +1589,7 @@ typedef struct OnConflictClause
List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} OnConflictClause;
/*
@@ -1598,7 +1610,7 @@ typedef struct CTESearchClause
List *search_col_list;
bool search_breadth_first;
char *search_seq_column;
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CTESearchClause;
typedef struct CTECycleClause
@@ -1609,7 +1621,7 @@ typedef struct CTECycleClause
Node *cycle_mark_value;
Node *cycle_mark_default;
char *cycle_path_column;
- int location;
+ int location pg_node_attr(query_jumble_ignore);
/* These fields are set during parse analysis: */
Oid cycle_mark_type; /* common type of _value and _default */
int cycle_mark_typmod;
@@ -1625,27 +1637,27 @@ typedef struct CommonTableExpr
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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
/* 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 */
@@ -1683,11 +1695,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;
/*
@@ -1727,7 +1739,7 @@ typedef struct RawStmt
NodeTag type;
Node *stmt; /* raw parse tree */
/* start location, or -1 if unknown */
- int stmt_location;
+ int stmt_location pg_node_attr(query_jumble_ignore);
int stmt_len; /* length in bytes; 0 means "rest of string" */
} RawStmt;
@@ -1897,15 +1909,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;
@@ -1936,7 +1948,7 @@ typedef struct PLAssignStmt
int nnames; /* number of names to use in ColumnRef */
SelectStmt *val; /* the PL/pgSQL expression to assign */
/* name's token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} PLAssignStmt;
@@ -2444,7 +2456,7 @@ typedef struct Constraint
bool deferrable; /* DEFERRABLE? */
bool initdeferred; /* INITIALLY DEFERRED? */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
/* Fields used for constraints with expressions (CHECK and DEFAULT): */
bool is_no_inherit; /* is constraint non-inheritable? */
@@ -3854,7 +3866,7 @@ typedef struct PublicationObjSpec
char *name;
PublicationTable *pubtable;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} PublicationObjSpec;
typedef struct CreatePublicationStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index f33d7fe167..5f105c48c3 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -100,7 +100,7 @@ typedef struct PlannedStmt
/* statement location in source string (copied from Query) */
/* start location, or -1 if unknown */
- int stmt_location;
+ int stmt_location pg_node_attr(query_jumble_ignore);
int stmt_len; /* length in bytes; 0 means "rest of string" */
} PlannedStmt;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 8d80c90ce7..d20142bd0e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -86,7 +86,7 @@ typedef struct RangeVar
Alias *alias;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} RangeVar;
/*
@@ -99,31 +99,31 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} TableFunc;
/*
@@ -230,11 +230,11 @@ 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;
+ Oid varcollid pg_node_attr(query_jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
@@ -248,12 +248,12 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} Var;
/*
@@ -263,6 +263,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
{
@@ -272,23 +274,26 @@ 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 */
+ bool constbyval pg_node_attr(query_jumble_ignore);
+ /*
+ * token location, or -1 if unknown. Note that this is used in the
+ * query jumbling.
+ */
int location;
} Const;
@@ -327,6 +332,7 @@ typedef enum ParamKind
PARAM_MULTIEXPR
} ParamKind;
+/* typmod and collation information are irrelevant for the query jumbling. */
typedef struct Param
{
Expr xpr;
@@ -334,11 +340,11 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} Param;
/*
@@ -389,6 +395,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
{
@@ -398,22 +407,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;
@@ -431,34 +440,34 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} Aggref;
/*
@@ -484,29 +493,35 @@ 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;
/* token location */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} 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
{
@@ -514,11 +529,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 */
@@ -526,11 +541,11 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} WindowFunc;
/*
@@ -567,6 +582,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.
@@ -575,15 +592,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;
@@ -634,6 +651,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
{
@@ -641,25 +661,25 @@ 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 */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} FuncExpr;
/*
@@ -682,11 +702,11 @@ 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 */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} NamedArgExpr;
/*
@@ -698,6 +718,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
{
@@ -707,25 +730,25 @@ 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;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} OpExpr;
/*
@@ -775,6 +798,10 @@ 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
{
@@ -784,25 +811,25 @@ 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;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} ScalarArrayOpExpr;
/*
@@ -825,7 +852,7 @@ typedef struct BoolExpr
BoolExprType boolop;
List *args; /* arguments to this expression */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} BoolExpr;
/*
@@ -899,11 +926,11 @@ 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;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} SubLink;
/*
@@ -1012,11 +1039,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;
/* ----------------
@@ -1043,9 +1070,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;
@@ -1068,13 +1095,13 @@ 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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} RelabelType;
/* ----------------
@@ -1093,11 +1120,11 @@ 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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CoerceViaIO;
/* ----------------
@@ -1120,13 +1147,13 @@ 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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} ArrayCoerceExpr;
/* ----------------
@@ -1149,9 +1176,9 @@ 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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} ConvertRowtypeExpr;
/*----------
@@ -1167,7 +1194,7 @@ typedef struct CollateExpr
Expr *arg; /* input expression */
Oid collOid; /* collation's OID */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CollateExpr;
/*----------
@@ -1196,14 +1223,14 @@ 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) */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CaseExpr;
/*
@@ -1215,7 +1242,7 @@ typedef struct CaseWhen
Expr *expr; /* condition expression */
Expr *result; /* substitution result */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CaseWhen;
/*
@@ -1243,9 +1270,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;
/*
@@ -1260,17 +1287,17 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} ArrayExpr;
/*
@@ -1300,7 +1327,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
@@ -1316,13 +1343,13 @@ 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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} RowExpr;
/*
@@ -1357,11 +1384,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 */
@@ -1375,13 +1402,13 @@ 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 */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CoalesceExpr;
/*
@@ -1397,17 +1424,17 @@ 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 */
List *args;
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} MinMaxExpr;
/*
@@ -1445,20 +1472,20 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} XmlExpr;
/* ----------------
@@ -1491,9 +1518,9 @@ 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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} NullTest;
/*
@@ -1516,7 +1543,7 @@ typedef struct BooleanTest
Expr *arg; /* input expression */
BoolTestType booltesttype; /* test type */
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} BooleanTest;
/*
@@ -1527,6 +1554,8 @@ typedef struct BooleanTest
* checked will be determined. If the value passes, it is returned as the
* result; if not, an error is raised. Note that this is equivalent to
* RelabelType in the scenario where no constraints are applied.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct CoerceToDomain
{
@@ -1534,13 +1563,13 @@ 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);
/* token location, or -1 if unknown */
- int location;
+ int location pg_node_attr(query_jumble_ignore);
} CoerceToDomain;
/*
@@ -1558,11 +1587,11 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} CoerceToDomainValue;
/*
@@ -1571,6 +1600,8 @@ typedef struct CoerceToDomainValue
* This is not an executable expression: it must be replaced by the actual
* column default expression during rewriting. But it is convenient to
* treat it as an expression node during parsing and rewriting.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct SetToDefault
{
@@ -1578,11 +1609,11 @@ 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;
+ int location pg_node_attr(query_jumble_ignore);
} SetToDefault;
/*
@@ -1698,15 +1729,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;
@@ -1789,13 +1820,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;
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..7cf6e3b041 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -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.
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b3c1ead496..13e2043d55 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -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
@@ -332,6 +335,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 +464,7 @@ foreach my $infile (@ARGV)
equal_as_scalar
equal_ignore
equal_ignore_if_zero
+ query_jumble_ignore
read_write_ignore
write_only_relids
write_only_nondefault_pathtarget
@@ -1225,6 +1233,91 @@ 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;
+
+ 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;
+
+ # extract per-field attributes
+ foreach my $a (@a)
+ {
+ if ($a eq 'query_jumble_ignore')
+ {
+ $query_jumble_ignore = 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$')
+ {
+ 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)
{
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 9230515e7f..31467a12d3 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -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',
)
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16084842a3..278150fba0 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -21,7 +21,7 @@
* tree(s) generated from the query. The executor can then use this value
* to blame query costs on the proper queryId.
*
- * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -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,38 +102,29 @@ 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));
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
- }
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
return jstate;
}
@@ -154,34 +142,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.
@@ -219,621 +179,6 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
jstate->jumble_len = jumble_len;
}
-/*
- * 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
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- 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;
- }
- }
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
- ListCell *lc;
-
- foreach(lc, rowMarks)
- {
- RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
- if (!rowmark->pushedDown)
- {
- APP_JUMB(rowmark->rti);
- APP_JUMB(rowmark->strength);
- APP_JUMB(rowmark->waitPolicy);
- }
- }
-}
-
-/*
- * 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)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * 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);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- 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);
- }
- break;
- 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 */
- 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);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- 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);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- 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;
-
- APP_JUMB(mergeaction->matched);
- APP_JUMB(mergeaction->commandType);
- JumbleExpr(jstate, mergeaction->qual);
- JumbleExpr(jstate, (Node *) mergeaction->targetList);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- 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);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- 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);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* 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));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- 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);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- break;
- }
-}
-
/*
* Record location of constant within query string of query tree
* that is currently being walked.
@@ -859,3 +204,155 @@ RecordConstLocation(JumbleState *jstate, int location)
jstate->clocations_count++;
}
}
+
+#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)
+
+#include "queryjumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+ Node *expr = node;
+
+ if (expr == NULL)
+ return;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /*
+ * We always emit the node's NodeTag, then any additional fields that are
+ * considered significant, and then we recurse to any child nodes.
+ */
+ JUMBLE_FIELD(type);
+
+ switch (nodeTag(expr))
+ {
+#include "queryjumblefuncs.switch.c"
+
+ case T_List:
+ case T_IntList:
+ case T_OidList:
+ case T_XidList:
+ _jumbleList(jstate, expr);
+ break;
+
+ case T_RangeTblEntry:
+ _jumbleRangeTblEntry(jstate, expr);
+ break;
+
+ default:
+ /* Only a warning, since we can stumble along anyway */
+ elog(WARNING, "unrecognized node type: %d",
+ (int) nodeTag(expr));
+ break;
+ }
+
+ /* Special cases */
+ switch (nodeTag(expr))
+ {
+ case T_Param:
+ {
+ Param *p = (Param *) node;
+
+ /* Also, track the highest external Param id */
+ if (p->paramkind == PARAM_EXTERN &&
+ p->paramid > jstate->highest_extern_param_id)
+ jstate->highest_extern_param_id = p->paramid;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+ List *expr = (List *) node;
+ ListCell *l;
+
+ switch (expr->type)
+ {
+ case T_List:
+ foreach(l, expr)
+ _jumbleNode(jstate, lfirst(l));
+ break;
+ case T_IntList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_int(l));
+ break;
+ case T_OidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+ break;
+ case T_XidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+ break;
+ default:
+ elog(ERROR, "unrecognized list node type: %d",
+ (int) expr->type);
+ return;
+ }
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+ RangeTblEntry *expr = (RangeTblEntry *) node;
+
+ JUMBLE_FIELD(rtekind);
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(tablesample);
+ JUMBLE_FIELD(inh);
+ break;
+ case RTE_SUBQUERY:
+ JUMBLE_NODE(subquery);
+ break;
+ case RTE_JOIN:
+ JUMBLE_FIELD(jointype);
+ break;
+ case RTE_FUNCTION:
+ JUMBLE_NODE(functions);
+ break;
+ case RTE_TABLEFUNC:
+ JUMBLE_NODE(tablefunc);
+ break;
+ case RTE_VALUES:
+ JUMBLE_NODE(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.
+ */
+ JUMBLE_STRING(ctename);
+ JUMBLE_FIELD(ctelevelsup);
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ JUMBLE_STRING(enrname);
+ break;
+ case RTE_RESULT:
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+ break;
+ }
+}
--
2.39.0
On 18.01.23 08:04, Michael Paquier wrote:
On Tue, Jan 17, 2023 at 04:52:28PM +0900, Michael Paquier wrote:
On Tue, Jan 17, 2023 at 08:43:44AM +0100, Peter Eisentraut wrote:
Ok, I understand now, and I agree with this approach over the opposite. I
was confused because the snippet you showed above used "jumble_ignore", but
your patch is correct as it uses "jumble_location".Okay. I'll refresh the patch set so as we have only "jumble_ignore",
then, like v1, with preparatory patches for what you mentioned and
anything that comes into mind.This is done as of the patch series v3 attached:
- 0001 reformats all the comments of the nodes.
- 0002 moves the current files for query jumble as of queryjumble.c ->
queryjumblefuncs.c and utils/queryjumble.h -> nodes/queryjumble.h.
- 0003 is the core feature, where I have done a second pass over the
nodes to make sure that things map with HEAD, incorporating the extra
docs coming from v2, adding a bit more.
This patch structure looks good.
That said, the term "jumble" is really weird, because in the sense that we
are using it here it means, approximately, "to mix together", "to unify".
So what we are doing with the Const nodes is really to *not* jumble the
location, but for all other node types we are jumbling the location. At
least that is my understanding.I am quite familiar with this term, FWIW. That's what we've inherited
from the days where this has been introduced in pg_stat_statements.I have renamed the node attributes to query_jumble_ignore and
no_query_jumble at the end, after considering Peter's point that only
"jumble" could be fuzzy here. The file names are changed in
consequence.
I see that in the 0003 patch, most location fields now have an explicit
markup with query_jumble_ignore. I thought we had previously resolved
to consider location fields to be automatically ignored unless
explicitly included (like for the Const node). This appears to invert
that? Am I missing something?
On Thu, Jan 19, 2023 at 09:42:03AM +0100, Peter Eisentraut wrote:
I see that in the 0003 patch, most location fields now have an explicit
markup with query_jumble_ignore. I thought we had previously resolved to
consider location fields to be automatically ignored unless explicitly
included (like for the Const node). This appears to invert that? Am I
missing something?
My misunderstanding then, I thought that you were OK with what was
part of v1, where all these fields was marked as "ignore". But you
actually prefer v2, with the second field "location" on top of
"ignore". I can update 0003 to refresh that.
Would you be OK if I apply 0001 (with the comments of the locations
still reshaped to ease future property additions) and 0002?
--
Michael
On Thu, Jan 19, 2023 at 09:42:03AM +0100, Peter Eisentraut wrote:
I see that in the 0003 patch, most location fields now have an explicit
markup with query_jumble_ignore. I thought we had previously resolved to
consider location fields to be automatically ignored unless explicitly
included (like for the Const node). This appears to invert that? Am I
missing something?
As a result, I have rebased the patch set to use the two-attribute
approach: query_jumble_ignore and query_jumble_location.
On top of the three previous patches, I am adding 0004 to implement a
GUC able to switch the computation of the utility statements between
what I am calling "string" to compute the query IDs based on the hash
of the query string and the previous default, or "jumble", to use the
parsed tree, with a few more tests to see the difference. Perhaps it
is not worth bothering, but it could be possible that some users don't
want to pay the penalty of doing the query jumbling with the parsed
tree for utilities, as well..
--
Michael
Attachments:
v4-0001-Rework-format-of-comments-for-nodes.patchtext/x-diff; charset=us-asciiDownload
From 43a5bdffdb9dbe3ecb40e9bf8a5f0e9f12ceff32 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 18 Jan 2023 14:26:12 +0900
Subject: [PATCH v4 1/4] Rework format of comments for nodes
This is similar to 835d476, except that this one is to add node
properties related to query jumbling.
---
src/include/nodes/parsenodes.h | 261 ++++++++++++------
src/include/nodes/plannodes.h | 3 +-
src/include/nodes/primnodes.h | 469 ++++++++++++++++++++++-----------
3 files changed, 487 insertions(+), 246 deletions(-)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cfeca96d53..eec51e3ee2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -123,7 +123,8 @@ typedef struct Query
CmdType commandType; /* select|insert|update|delete|merge|utility */
- QuerySource querySource; /* where did I come from? */
+ /* where did I come from? */
+ QuerySource querySource;
/*
* query identifier (can be set by plugins); ignored for equal, as it
@@ -131,39 +132,58 @@ typedef struct Query
*/
uint64 queryId pg_node_attr(equal_ignore, read_write_ignore, read_as(0));
- bool canSetTag; /* do I set the command result tag? */
+ /* do I set the command result tag? */
+ bool canSetTag;
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
- int resultRelation; /* rtable index of target relation for
- * INSERT/UPDATE/DELETE/MERGE; 0 for SELECT */
+ /*
+ * rtable index of target relation for INSERT/UPDATE/DELETE/MERGE; 0 for
+ * SELECT.
+ */
+ int resultRelation;
- bool hasAggs; /* has aggregates in tlist or havingQual */
- bool hasWindowFuncs; /* has window functions in tlist */
- bool hasTargetSRFs; /* has set-returning functions in tlist */
- bool hasSubLinks; /* has subquery SubLink */
- bool hasDistinctOn; /* distinctClause is from DISTINCT ON */
- bool hasRecursive; /* WITH RECURSIVE was specified */
- bool hasModifyingCTE; /* has INSERT/UPDATE/DELETE in WITH */
- bool hasForUpdate; /* FOR [KEY] UPDATE/SHARE was specified */
- bool hasRowSecurity; /* rewriter has applied some RLS policy */
-
- bool isReturn; /* is a RETURN statement */
+ /* has aggregates in tlist or havingQual */
+ bool hasAggs;
+ /* has window functions in tlist */
+ bool hasWindowFuncs;
+ /* has set-returning functions in tlist */
+ bool hasTargetSRFs;
+ /* has subquery SubLink */
+ bool hasSubLinks;
+ /* distinctClause is from DISTINCT ON */
+ bool hasDistinctOn;
+ /* WITH RECURSIVE was specified */
+ bool hasRecursive;
+ /* has INSERT/UPDATE/DELETE in WITH */
+ bool hasModifyingCTE;
+ /* FOR [KEY] UPDATE/SHARE was specified */
+ bool hasForUpdate;
+ /* rewriter has applied some RLS policy */
+ bool hasRowSecurity;
+ /* is a RETURN statement */
+ bool isReturn;
List *cteList; /* WITH list (of CommonTableExpr's) */
List *rtable; /* list of range table entries */
- List *rteperminfos; /* list of RTEPermissionInfo nodes for the
- * rtable entries having perminfoindex > 0 */
+
+ /*
+ * list of RTEPermissionInfo nodes for the rtable entries having
+ * perminfoindex > 0
+ */
+ List *rteperminfos;
FromExpr *jointree; /* table join tree (FROM and WHERE clauses);
* also USING clause for MERGE */
List *mergeActionList; /* list of actions for MERGE (only) */
- bool mergeUseOuterJoin; /* whether to use outer join */
+ /* whether to use outer join */
+ bool mergeUseOuterJoin;
List *targetList; /* target list (of TargetEntry) */
- OverridingKind override; /* OVERRIDING clause */
+ /* OVERRIDING clause */
+ OverridingKind override;
OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
@@ -191,11 +211,14 @@ typedef struct Query
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
- List *constraintDeps; /* a list of pg_constraint OIDs that the query
- * depends on to be semantically valid */
+ /*
+ * A list of pg_constraint OIDs that the query depends on to be
+ * semantically valid
+ */
+ List *constraintDeps;
- List *withCheckOptions; /* a list of WithCheckOption's (added
- * during rewrite) */
+ /* a list of WithCheckOption's (added during rewrite) */
+ List *withCheckOptions;
/*
* The following two fields identify the portion of the source text string
@@ -203,8 +226,10 @@ typedef struct Query
* Queries, not in sub-queries. When not set, they might both be zero, or
* both be -1 meaning "unknown".
*/
- int stmt_location; /* start location, or -1 if unknown */
- int stmt_len; /* length in bytes; 0 means "rest of string" */
+ /* start location, or -1 if unknown */
+ int stmt_location;
+ /* length in bytes; 0 means "rest of string" */
+ int stmt_len;
} Query;
@@ -239,7 +264,8 @@ typedef struct TypeName
List *typmods; /* type modifier expression(s) */
int32 typemod; /* prespecified type modifier */
List *arrayBounds; /* array bounds */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} TypeName;
/*
@@ -259,7 +285,8 @@ typedef struct ColumnRef
{
NodeTag type;
List *fields; /* field names (String nodes) or A_Star */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} ColumnRef;
/*
@@ -269,7 +296,8 @@ typedef struct ParamRef
{
NodeTag type;
int number; /* the number of the parameter */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} ParamRef;
/*
@@ -302,7 +330,8 @@ typedef struct A_Expr
List *name; /* possibly-qualified name of operator */
Node *lexpr; /* left argument, or NULL if none */
Node *rexpr; /* right argument, or NULL if none */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} A_Expr;
/*
@@ -328,7 +357,8 @@ typedef struct A_Const
NodeTag type;
union ValUnion val;
bool isnull; /* SQL NULL constant */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} A_Const;
/*
@@ -339,7 +369,8 @@ typedef struct TypeCast
NodeTag type;
Node *arg; /* the expression being casted */
TypeName *typeName; /* the target type */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} TypeCast;
/*
@@ -350,7 +381,8 @@ typedef struct CollateClause
NodeTag type;
Node *arg; /* input expression */
List *collname; /* possibly-qualified collation name */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CollateClause;
/*
@@ -370,7 +402,8 @@ typedef struct RoleSpec
NodeTag type;
RoleSpecType roletype; /* Type of this rolespec */
char *rolename; /* filled only for ROLESPEC_CSTRING */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} RoleSpec;
/*
@@ -400,7 +433,8 @@ typedef struct FuncCall
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
CoercionForm funcformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} FuncCall;
/*
@@ -457,7 +491,8 @@ typedef struct A_ArrayExpr
{
NodeTag type;
List *elements; /* array element expressions */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} A_ArrayExpr;
/*
@@ -484,7 +519,8 @@ typedef struct ResTarget
char *name; /* column name or NULL */
List *indirection; /* subscripts, field names, and '*', or NIL */
Node *val; /* the value expression to compute or assign */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} ResTarget;
/*
@@ -514,7 +550,8 @@ typedef struct SortBy
SortByDir sortby_dir; /* ASC/DESC/USING/default */
SortByNulls sortby_nulls; /* NULLS FIRST/LAST */
List *useOp; /* name of op to use, if SORTBY_USING */
- int location; /* operator location, or -1 if none/unknown */
+ /* token location, or -1 if unknown */
+ int location;
} SortBy;
/*
@@ -535,7 +572,8 @@ typedef struct WindowDef
int frameOptions; /* frame_clause options, see below */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- int location; /* parse location, or -1 if none/unknown */
+ /* token location, or -1 if unknown */
+ int location;
} WindowDef;
/*
@@ -625,7 +663,8 @@ typedef struct RangeTableFunc
List *namespaces; /* list of namespaces as ResTarget */
List *columns; /* list of RangeTableFuncCol */
Alias *alias; /* table alias & optional column aliases */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} RangeTableFunc;
/*
@@ -643,7 +682,8 @@ typedef struct RangeTableFuncCol
bool is_not_null; /* does it have NOT NULL? */
Node *colexpr; /* column filter expression */
Node *coldefexpr; /* column default value expression */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} RangeTableFuncCol;
/*
@@ -663,7 +703,8 @@ typedef struct RangeTableSample
List *method; /* sampling method name (possibly qualified) */
List *args; /* argument(s) for sampling method */
Node *repeatable; /* REPEATABLE expression, or NULL if none */
- int location; /* method name location, or -1 if unknown */
+ /* method name location, or -1 if unknown */
+ int location;
} RangeTableSample;
/*
@@ -706,7 +747,8 @@ typedef struct ColumnDef
Oid collOid; /* collation OID (InvalidOid if not set) */
List *constraints; /* other constraints on column */
List *fdwoptions; /* per-column FDW options */
- int location; /* parse location, or -1 if none/unknown */
+ /* parse location, or -1 if unknown */
+ int location;
} ColumnDef;
/*
@@ -780,7 +822,8 @@ typedef struct DefElem
Node *arg; /* typically Integer, Float, String, or
* TypeName */
DefElemAction defaction; /* unspecified action, or SET/ADD/DROP */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} DefElem;
/*
@@ -809,7 +852,8 @@ typedef struct XmlSerialize
XmlOptionType xmloption; /* DOCUMENT or CONTENT */
Node *expr;
TypeName *typeName;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} XmlSerialize;
/* Partitioning related definitions */
@@ -827,7 +871,8 @@ typedef struct PartitionElem
Node *expr; /* expression to partition on, or NULL */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PartitionElem;
typedef enum PartitionStrategy
@@ -847,7 +892,8 @@ typedef struct PartitionSpec
NodeTag type;
PartitionStrategy strategy;
List *partParams; /* List of PartitionElems */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PartitionSpec;
/*
@@ -874,7 +920,8 @@ struct PartitionBoundSpec
List *lowerdatums; /* List of PartitionRangeDatums */
List *upperdatums; /* List of PartitionRangeDatums */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
};
/*
@@ -897,7 +944,8 @@ typedef struct PartitionRangeDatum
Node *value; /* Const (or A_Const in raw tree), if kind is
* PARTITION_RANGE_DATUM_VALUE, else NULL */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PartitionRangeDatum;
/*
@@ -1223,14 +1271,21 @@ typedef struct RangeTblFunction
NodeTag type;
Node *funcexpr; /* expression tree for func call */
- int funccolcount; /* number of columns it contributes to RTE */
+ /* number of columns it contributes to RTE */
+ int funccolcount;
/* These fields record the contents of a column definition list, if any: */
- List *funccolnames; /* column names (list of String) */
- List *funccoltypes; /* OID list of column type OIDs */
- List *funccoltypmods; /* integer list of column typmods */
- List *funccolcollations; /* OID list of column collation OIDs */
+ /* column names (list of String) */
+ List *funccolnames;
+ /* OID list of column type OIDs */
+ List *funccoltypes;
+ /* integer list of column typmods */
+ List *funccoltypmods;
+ /* OID list of column collation OIDs */
+ List *funccolcollations;
+
/* This is set during planning for use by the executor: */
- Bitmapset *funcparams; /* PARAM_EXEC Param IDs affecting this func */
+ /* PARAM_EXEC Param IDs affecting this func */
+ Bitmapset *funcparams;
} RangeTblFunction;
/*
@@ -1337,7 +1392,8 @@ typedef struct SortGroupClause
Oid eqop; /* the equality operator ('=' op) */
Oid sortop; /* the ordering operator ('<' op), or 0 */
bool nulls_first; /* do NULLs come before normal values? */
- bool hashable; /* can eqop be implemented by hashing? */
+ /* can eqop be implemented by hashing? */
+ bool hashable;
} SortGroupClause;
/*
@@ -1427,21 +1483,31 @@ typedef struct GroupingSet
typedef struct WindowClause
{
NodeTag type;
- char *name; /* window name (NULL in an OVER clause) */
- char *refname; /* referenced window name, if any */
+ /* window name (NULL in an OVER clause) */
+ char *name;
+ /* referenced window name, if any */
+ char *refname;
List *partitionClause; /* PARTITION BY list */
- List *orderClause; /* ORDER BY list */
+ /* ORDER BY list */
+ List *orderClause;
int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
- List *runCondition; /* qual to help short-circuit execution */
- Oid startInRangeFunc; /* in_range function for startOffset */
- Oid endInRangeFunc; /* in_range function for endOffset */
- Oid inRangeColl; /* collation for in_range tests */
- bool inRangeAsc; /* use ASC sort order for in_range tests? */
- bool inRangeNullsFirst; /* nulls sort first for in_range tests? */
+ /* qual to help short-circuit execution */
+ List *runCondition;
+ /* in_range function for startOffset */
+ Oid startInRangeFunc;
+ /* in_range function for endOffset */
+ Oid endInRangeFunc;
+ /* collation for in_range tests */
+ Oid inRangeColl;
+ /* use ASC sort order for in_range tests? */
+ bool inRangeAsc;
+ /* nulls sort first for in_range tests? */
+ bool inRangeNullsFirst;
Index winref; /* ID referenced by window functions */
- bool copiedOrder; /* did we copy orderClause from refname? */
+ /* did we copy orderClause from refname? */
+ bool copiedOrder;
} WindowClause;
/*
@@ -1477,7 +1543,8 @@ typedef struct WithClause
NodeTag type;
List *ctes; /* list of CommonTableExprs */
bool recursive; /* true = WITH RECURSIVE */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} WithClause;
/*
@@ -1492,7 +1559,8 @@ typedef struct InferClause
List *indexElems; /* IndexElems to infer unique index */
Node *whereClause; /* qualification (partial-index predicate) */
char *conname; /* Constraint name, or NULL if unnamed */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} InferClause;
/*
@@ -1508,7 +1576,8 @@ typedef struct OnConflictClause
InferClause *infer; /* Optional index inference clause */
List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} OnConflictClause;
/*
@@ -1558,15 +1627,25 @@ typedef struct CommonTableExpr
Node *ctequery; /* the CTE's subquery */
CTESearchClause *search_clause;
CTECycleClause *cycle_clause;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
/* These fields are set during parse analysis: */
- bool cterecursive; /* is this CTE actually recursive? */
- int cterefcount; /* number of RTEs referencing this CTE
- * (excluding internal self-references) */
- List *ctecolnames; /* list of output column names */
- List *ctecoltypes; /* OID list of output column type OIDs */
- List *ctecoltypmods; /* integer list of output column typmods */
- List *ctecolcollations; /* OID list of column collation OIDs */
+ /* is this CTE actually recursive? */
+ bool cterecursive;
+
+ /*
+ * Number of RTEs referencing this CTE (excluding internal
+ * self-references)
+ */
+ int cterefcount;
+ /* list of output column names */
+ List *ctecolnames;
+ /* OID list of output column type OIDs */
+ List *ctecoltypes;
+ /* integer list of output column typmods */
+ List *ctecoltypmods;
+ /* OID list of column collation OIDs */
+ List *ctecolcollations;
} CommonTableExpr;
/* Convenience macro to get the output tlist of a CTE's query */
@@ -1603,10 +1682,12 @@ typedef struct MergeAction
NodeTag type;
bool matched; /* true=MATCHED, false=NOT MATCHED */
CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
- OverridingKind override; /* OVERRIDING clause */
+ /* OVERRIDING clause */
+ OverridingKind override;
Node *qual; /* transformed WHEN conditions */
List *targetList; /* the target list (of TargetEntry) */
- List *updateColnos; /* target attribute numbers of an UPDATE */
+ /* target attribute numbers of an UPDATE */
+ List *updateColnos;
} MergeAction;
/*
@@ -1645,7 +1726,8 @@ typedef struct RawStmt
{
NodeTag type;
Node *stmt; /* raw parse tree */
- int stmt_location; /* start location, or -1 if unknown */
+ /* start location, or -1 if unknown */
+ int stmt_location;
int stmt_len; /* length in bytes; 0 means "rest of string" */
} RawStmt;
@@ -1816,10 +1898,14 @@ typedef struct SetOperationStmt
/* Eventually add fields for CORRESPONDING spec here */
/* Fields derived during parse analysis: */
- List *colTypes; /* OID list of output column type OIDs */
- List *colTypmods; /* integer list of output column typmods */
- List *colCollations; /* OID list of output column collation OIDs */
- List *groupClauses; /* a list of SortGroupClause's */
+ /* OID list of output column type OIDs */
+ List *colTypes;
+ /* integer list of output column typmods */
+ List *colTypmods;
+ /* OID list of output column collation OIDs */
+ List *colCollations;
+ /* a list of SortGroupClause's */
+ List *groupClauses;
/* groupClauses is NIL if UNION ALL, but must be set otherwise */
} SetOperationStmt;
@@ -1849,7 +1935,8 @@ typedef struct PLAssignStmt
List *indirection; /* subscripts and field names, if any */
int nnames; /* number of names to use in ColumnRef */
SelectStmt *val; /* the PL/pgSQL expression to assign */
- int location; /* name's token location, or -1 if unknown */
+ /* name's token location, or -1 if unknown */
+ int location;
} PLAssignStmt;
@@ -2356,7 +2443,8 @@ typedef struct Constraint
char *conname; /* Constraint name, or NULL if unnamed */
bool deferrable; /* DEFERRABLE? */
bool initdeferred; /* INITIALLY DEFERRED? */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
/* Fields used for constraints with expressions (CHECK and DEFAULT): */
bool is_no_inherit; /* is constraint non-inheritable? */
@@ -3765,7 +3853,8 @@ typedef struct PublicationObjSpec
PublicationObjSpecType pubobjtype; /* type of this publication object */
char *name;
PublicationTable *pubtable;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} PublicationObjSpec;
typedef struct CreatePublicationStmt
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index c1234fcf36..f33d7fe167 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -99,7 +99,8 @@ typedef struct PlannedStmt
Node *utilityStmt; /* non-null if this is utility stmt */
/* statement location in source string (copied from Query) */
- int stmt_location; /* start location, or -1 if unknown */
+ /* start location, or -1 if unknown */
+ int stmt_location;
int stmt_len; /* length in bytes; 0 means "rest of string" */
} PlannedStmt;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 83e40e56d3..8d80c90ce7 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -98,19 +98,32 @@ typedef struct RangeVar
typedef struct TableFunc
{
NodeTag type;
- List *ns_uris; /* list of namespace URI expressions */
- List *ns_names; /* list of namespace names or NULL */
- Node *docexpr; /* input document expression */
- Node *rowexpr; /* row filter expression */
- List *colnames; /* column names (list of String) */
- List *coltypes; /* OID list of column type OIDs */
- List *coltypmods; /* integer list of column typmods */
- List *colcollations; /* OID list of column collation OIDs */
- List *colexprs; /* list of column filter expressions */
- List *coldefexprs; /* list of column default expressions */
- Bitmapset *notnulls; /* nullability flag for each output column */
- int ordinalitycol; /* counts from 0; -1 if none specified */
- int location; /* token location, or -1 if unknown */
+ /* list of namespace URI expressions */
+ List *ns_uris;
+ /* list of namespace names or NULL */
+ List *ns_names;
+ /* input document expression */
+ Node *docexpr;
+ /* row filter expression */
+ Node *rowexpr;
+ /* column names (list of String) */
+ List *colnames;
+ /* OID list of column type OIDs */
+ List *coltypes;
+ /* integer list of column typmods */
+ List *coltypmods;
+ /* OID list of column collation OIDs */
+ List *colcollations;
+ /* list of column filter expressions */
+ List *colexprs;
+ /* list of column default expressions */
+ List *coldefexprs;
+ /* nullability flag for each output column */
+ Bitmapset *notnulls;
+ /* counts from 0; -1 if none specified */
+ int ordinalitycol;
+ /* token location, or -1 if unknown */
+ int location;
} TableFunc;
/*
@@ -256,18 +269,27 @@ typedef struct Const
pg_node_attr(custom_copy_equal, custom_read_write)
Expr xpr;
- Oid consttype; /* pg_type OID of the constant's datatype */
- int32 consttypmod; /* typmod value, if any */
- Oid constcollid; /* OID of collation, or InvalidOid if none */
- int constlen; /* typlen of the constant's datatype */
- Datum constvalue; /* the constant's value */
- bool constisnull; /* whether the constant is null (if true,
- * constvalue is undefined) */
- bool constbyval; /* 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. */
- int location; /* token location, or -1 if unknown */
+ /* pg_type OID of the constant's datatype */
+ Oid consttype;
+ /* typmod value, if any */
+ int32 consttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid constcollid;
+ /* typlen of the constant's datatype */
+ int constlen;
+ /* the constant's value */
+ Datum constvalue;
+ /* whether the constant is null (if true, constvalue is undefined) */
+ bool constisnull;
+
+ /*
+ * 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;
} Const;
/*
@@ -311,9 +333,12 @@ typedef struct Param
ParamKind paramkind; /* kind of parameter. See above */
int paramid; /* numeric ID for parameter */
Oid paramtype; /* pg_type OID of parameter's datatype */
- int32 paramtypmod; /* typmod value, if known */
- Oid paramcollid; /* OID of collation, or InvalidOid if none */
- int location; /* token location, or -1 if unknown */
+ /* typmod value, if known */
+ int32 paramtypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid paramcollid;
+ /* token location, or -1 if unknown */
+ int location;
} Param;
/*
@@ -486,16 +511,26 @@ typedef struct GroupingFunc
typedef struct WindowFunc
{
Expr xpr;
- Oid winfnoid; /* pg_proc Oid of the function */
- Oid wintype; /* type Oid of result of the window function */
- Oid wincollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
- List *args; /* arguments to the window function */
- Expr *aggfilter; /* FILTER expression, if any */
- Index winref; /* index of associated WindowClause */
- bool winstar; /* true if argument list was really '*' */
- bool winagg; /* is function a simple aggregate? */
- int location; /* token location, or -1 if unknown */
+ /* pg_proc Oid of the function */
+ Oid winfnoid;
+ /* type Oid of result of the window function */
+ Oid wintype;
+ /* OID of collation of result */
+ Oid wincollid;
+ /* OID of collation that function should use */
+ Oid inputcollid;
+ /* arguments to the window function */
+ List *args;
+ /* FILTER expression, if any */
+ Expr *aggfilter;
+ /* index of associated WindowClause */
+ Index winref;
+ /* true if argument list was really '*' */
+ bool winstar;
+ /* is function a simple aggregate? */
+ bool winagg;
+ /* token location, or -1 if unknown */
+ int location;
} WindowFunc;
/*
@@ -539,20 +574,28 @@ typedef struct WindowFunc
typedef struct SubscriptingRef
{
Expr xpr;
- Oid refcontainertype; /* type of the container proper */
- Oid refelemtype; /* the container type's pg_type.typelem */
- Oid refrestype; /* type of the SubscriptingRef's result */
- int32 reftypmod; /* typmod of the result */
- Oid refcollid; /* collation of result, or InvalidOid if none */
- List *refupperindexpr; /* expressions that evaluate to upper
- * container indexes */
- List *reflowerindexpr; /* expressions that evaluate to lower
- * container indexes, or NIL for single
- * container element */
- Expr *refexpr; /* the expression that evaluates to a
- * container value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
+ /* type of the container proper */
+ Oid refcontainertype;
+ /* the container type's pg_type.typelem */
+ Oid refelemtype;
+ /* type of the SubscriptingRef's result */
+ Oid refrestype;
+ /* typmod of the result */
+ int32 reftypmod;
+ /* collation of result, or InvalidOid if none */
+ Oid refcollid;
+ /* expressions that evaluate to upper container indexes */
+ List *refupperindexpr;
+
+ /*
+ * expressions that evaluate to lower container indexes, or NIL for single
+ * container element.
+ */
+ List *reflowerindexpr;
+ /* the expression that evaluates to a container value */
+ Expr *refexpr;
+ /* expression for the source value, or NULL if fetch */
+ Expr *refassgnexpr;
} SubscriptingRef;
/*
@@ -595,16 +638,28 @@ typedef enum CoercionForm
typedef struct FuncExpr
{
Expr xpr;
- Oid funcid; /* PG_PROC OID of the function */
- Oid funcresulttype; /* PG_TYPE OID of result value */
- bool funcretset; /* true if function returns set */
- bool funcvariadic; /* true if variadic arguments have been
- * combined into an array last argument */
- CoercionForm funcformat; /* how to display this function call */
- Oid funccollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
- List *args; /* arguments to the function */
- int location; /* token location, or -1 if unknown */
+ /* PG_PROC OID of the function */
+ Oid funcid;
+ /* PG_TYPE OID of result value */
+ Oid funcresulttype;
+ /* true if function returns set */
+ bool funcretset;
+
+ /*
+ * true if variadic arguments have been combined into an array last
+ * argument
+ */
+ bool funcvariadic;
+ /* how to display this function call */
+ CoercionForm funcformat;
+ /* OID of collation of result */
+ Oid funccollid;
+ /* OID of collation that function should use */
+ Oid inputcollid;
+ /* arguments to the function */
+ List *args;
+ /* token location, or -1 if unknown */
+ int location;
} FuncExpr;
/*
@@ -624,10 +679,14 @@ typedef struct FuncExpr
typedef struct NamedArgExpr
{
Expr xpr;
- Expr *arg; /* the argument expression */
- char *name; /* the name */
- int argnumber; /* argument's number in positional notation */
- int location; /* argument name location, or -1 if unknown */
+ /* the argument expression */
+ Expr *arg;
+ /* the name */
+ char *name;
+ /* argument's number in positional notation */
+ int argnumber;
+ /* argument name location, or -1 if unknown */
+ int location;
} NamedArgExpr;
/*
@@ -765,7 +824,8 @@ typedef struct BoolExpr
Expr xpr;
BoolExprType boolop;
List *args; /* arguments to this expression */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} BoolExpr;
/*
@@ -838,9 +898,12 @@ typedef struct SubLink
SubLinkType subLinkType; /* see above */
int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */
Node *testexpr; /* outer-query test for ALL/ANY/ROWCOMPARE */
- List *operName; /* originally specified operator name */
- Node *subselect; /* subselect as Query* or raw parsetree */
- int location; /* token location, or -1 if unknown */
+ /* originally specified operator name */
+ List *operName;
+ /* subselect as Query* or raw parsetree */
+ Node *subselect;
+ /* token location, or -1 if unknown */
+ int location;
} SubLink;
/*
@@ -948,10 +1011,12 @@ typedef struct FieldSelect
Expr xpr;
Expr *arg; /* input expression */
AttrNumber fieldnum; /* attribute number of field to extract */
- Oid resulttype; /* type of the field (result type of this
- * node) */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation of the field */
+ /* type of the field (result type of this node) */
+ Oid resulttype;
+ /* output typmod (usually -1) */
+ int32 resulttypmod;
+ /* OID of collation of the field */
+ Oid resultcollid;
} FieldSelect;
/* ----------------
@@ -977,8 +1042,10 @@ typedef struct FieldStore
Expr xpr;
Expr *arg; /* input tuple value */
List *newvals; /* new value(s) for field(s) */
- List *fieldnums; /* integer list of field attnums */
- Oid resulttype; /* type of result (same as type of arg) */
+ /* integer list of field attnums */
+ List *fieldnums;
+ /* type of result (same as type of arg) */
+ Oid resulttype;
/* Like RowExpr, we deliberately omit a typmod and collation here */
} FieldStore;
@@ -1000,10 +1067,14 @@ typedef struct RelabelType
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion expression */
- int32 resulttypmod; /* output typmod (usually -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm relabelformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* output typmod (usually -1) */
+ int32 resulttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm relabelformat;
+ /* token location, or -1 if unknown */
+ int location;
} RelabelType;
/* ----------------
@@ -1021,9 +1092,12 @@ typedef struct CoerceViaIO
Expr *arg; /* input expression */
Oid resulttype; /* output type of coercion */
/* output typmod is not stored, but is presumed -1 */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm coerceformat;
+ /* token location, or -1 if unknown */
+ int location;
} CoerceViaIO;
/* ----------------
@@ -1045,10 +1119,14 @@ typedef struct ArrayCoerceExpr
Expr *arg; /* input expression (yields an array) */
Expr *elemexpr; /* expression representing per-element work */
Oid resulttype; /* output type of coercion (an array type) */
- int32 resulttypmod; /* output typmod (also element typmod) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coerceformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* output typmod (also element typmod) */
+ int32 resulttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm coerceformat;
+ /* token location, or -1 if unknown */
+ int location;
} ArrayCoerceExpr;
/* ----------------
@@ -1070,8 +1148,10 @@ typedef struct ConvertRowtypeExpr
Expr *arg; /* input expression */
Oid resulttype; /* output type (always a composite type) */
/* Like RowExpr, we deliberately omit a typmod and collation here */
- CoercionForm convertformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* how to display this node */
+ CoercionForm convertformat;
+ /* token location, or -1 if unknown */
+ int location;
} ConvertRowtypeExpr;
/*----------
@@ -1086,7 +1166,8 @@ typedef struct CollateExpr
Expr xpr;
Expr *arg; /* input expression */
Oid collOid; /* collation's OID */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CollateExpr;
/*----------
@@ -1114,12 +1195,15 @@ typedef struct CollateExpr
typedef struct CaseExpr
{
Expr xpr;
- Oid casetype; /* type of expression result */
- Oid casecollid; /* OID of collation, or InvalidOid if none */
+ /* type of expression result */
+ Oid casetype;
+ /* OID of collation, or InvalidOid if none */
+ Oid casecollid;
Expr *arg; /* implicit equality comparison argument */
List *args; /* the arguments (list of WHEN clauses) */
Expr *defresult; /* the default result (ELSE clause) */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CaseExpr;
/*
@@ -1130,7 +1214,8 @@ typedef struct CaseWhen
Expr xpr;
Expr *expr; /* condition expression */
Expr *result; /* substitution result */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} CaseWhen;
/*
@@ -1157,8 +1242,10 @@ typedef struct CaseTestExpr
{
Expr xpr;
Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
+ /* typemod for substituted value */
+ int32 typeMod;
+ /* collation for the substituted value */
+ Oid collation;
} CaseTestExpr;
/*
@@ -1172,12 +1259,18 @@ typedef struct CaseTestExpr
typedef struct ArrayExpr
{
Expr xpr;
- Oid array_typeid; /* type of expression result */
- Oid array_collid; /* OID of collation, or InvalidOid if none */
- Oid element_typeid; /* common type of array elements */
- List *elements; /* the array elements or sub-arrays */
- bool multidims; /* true if elements are sub-arrays */
- int location; /* token location, or -1 if unknown */
+ /* type of expression result */
+ Oid array_typeid;
+ /* OID of collation, or InvalidOid if none */
+ Oid array_collid;
+ /* common type of array elements */
+ Oid element_typeid;
+ /* the array elements or sub-arrays */
+ List *elements;
+ /* true if elements are sub-arrays */
+ bool multidims;
+ /* token location, or -1 if unknown */
+ int location;
} ArrayExpr;
/*
@@ -1205,7 +1298,9 @@ typedef struct RowExpr
{
Expr xpr;
List *args; /* the fields */
- Oid row_typeid; /* RECORDOID or a composite type's ID */
+
+ /* RECORDOID or a composite type's ID */
+ Oid row_typeid;
/*
* row_typeid cannot be a domain over composite, only plain composite. To
@@ -1219,9 +1314,15 @@ typedef struct RowExpr
* We don't need to store a collation either. The result type is
* necessarily composite, and composite types never have a collation.
*/
- CoercionForm row_format; /* how to display this node */
- List *colnames; /* list of String, or NIL */
- int location; /* token location, or -1 if unknown */
+
+ /* how to display this node */
+ CoercionForm row_format;
+
+ /* list of String, or NIL */
+ List *colnames;
+
+ /* token location, or -1 if unknown */
+ int location;
} RowExpr;
/*
@@ -1252,12 +1353,19 @@ typedef enum RowCompareType
typedef struct RowCompareExpr
{
Expr xpr;
- RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */
- List *opnos; /* OID list of pairwise comparison ops */
- List *opfamilies; /* OID list of containing operator families */
- List *inputcollids; /* OID list of collations for comparisons */
- List *largs; /* the left-hand input arguments */
- List *rargs; /* the right-hand input arguments */
+
+ /* LT LE GE or GT, never EQ or NE */
+ RowCompareType rctype;
+ /* OID list of pairwise comparison ops */
+ List *opnos;
+ /* OID list of containing operator families */
+ List *opfamilies;
+ /* OID list of collations for comparisons */
+ List *inputcollids;
+ /* the left-hand input arguments */
+ List *largs;
+ /* the right-hand input arguments */
+ List *rargs;
} RowCompareExpr;
/*
@@ -1266,10 +1374,14 @@ typedef struct RowCompareExpr
typedef struct CoalesceExpr
{
Expr xpr;
- Oid coalescetype; /* type of expression result */
- Oid coalescecollid; /* OID of collation, or InvalidOid if none */
- List *args; /* the arguments */
- int location; /* token location, or -1 if unknown */
+ /* type of expression result */
+ Oid coalescetype;
+ /* OID of collation, or InvalidOid if none */
+ Oid coalescecollid;
+ /* the arguments */
+ List *args;
+ /* token location, or -1 if unknown */
+ int location;
} CoalesceExpr;
/*
@@ -1284,12 +1396,18 @@ typedef enum MinMaxOp
typedef struct MinMaxExpr
{
Expr xpr;
- Oid minmaxtype; /* common type of arguments and result */
- Oid minmaxcollid; /* OID of collation of result */
- Oid inputcollid; /* OID of collation that function should use */
- MinMaxOp op; /* function to execute */
- List *args; /* the arguments */
- int location; /* token location, or -1 if unknown */
+ /* common type of arguments and result */
+ Oid minmaxtype;
+ /* OID of collation of result */
+ Oid minmaxcollid;
+ /* OID of collation that function should use */
+ Oid inputcollid;
+ /* function to execute */
+ MinMaxOp op;
+ /* the arguments */
+ List *args;
+ /* token location, or -1 if unknown */
+ int location;
} MinMaxExpr;
/*
@@ -1324,15 +1442,23 @@ typedef enum XmlOptionType
typedef struct XmlExpr
{
Expr xpr;
- XmlExprOp op; /* xml function ID */
- char *name; /* name in xml(NAME foo ...) syntaxes */
- List *named_args; /* non-XML expressions for xml_attributes */
- List *arg_names; /* parallel list of String values */
- List *args; /* list of expressions */
- XmlOptionType xmloption; /* DOCUMENT or CONTENT */
- Oid type; /* target type/typmod for XMLSERIALIZE */
+ /* xml function ID */
+ XmlExprOp op;
+ /* name in xml(NAME foo ...) syntaxes */
+ char *name;
+ /* non-XML expressions for xml_attributes */
+ List *named_args;
+ /* parallel list of String values */
+ List *arg_names;
+ /* list of expressions */
+ List *args;
+ /* DOCUMENT or CONTENT */
+ XmlOptionType xmloption;
+ /* target type/typmod for XMLSERIALIZE */
+ Oid type;
int32 typmod;
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} XmlExpr;
/* ----------------
@@ -1364,8 +1490,10 @@ typedef struct NullTest
Expr xpr;
Expr *arg; /* input expression */
NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
- bool argisrow; /* T to perform field-by-field null checks */
- int location; /* token location, or -1 if unknown */
+ /* T to perform field-by-field null checks */
+ bool argisrow;
+ /* token location, or -1 if unknown */
+ int location;
} NullTest;
/*
@@ -1387,7 +1515,8 @@ typedef struct BooleanTest
Expr xpr;
Expr *arg; /* input expression */
BoolTestType booltesttype; /* test type */
- int location; /* token location, or -1 if unknown */
+ /* token location, or -1 if unknown */
+ int location;
} BooleanTest;
/*
@@ -1404,10 +1533,14 @@ typedef struct CoerceToDomain
Expr xpr;
Expr *arg; /* input expression */
Oid resulttype; /* domain type ID (result type) */
- int32 resulttypmod; /* output typmod (currently always -1) */
- Oid resultcollid; /* OID of collation, or InvalidOid if none */
- CoercionForm coercionformat; /* how to display this node */
- int location; /* token location, or -1 if unknown */
+ /* output typmod (currently always -1) */
+ int32 resulttypmod;
+ /* OID of collation, or InvalidOid if none */
+ Oid resultcollid;
+ /* how to display this node */
+ CoercionForm coercionformat;
+ /* token location, or -1 if unknown */
+ int location;
} CoerceToDomain;
/*
@@ -1422,10 +1555,14 @@ typedef struct CoerceToDomain
typedef struct CoerceToDomainValue
{
Expr xpr;
- Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
- int location; /* token location, or -1 if unknown */
+ /* type for substituted value */
+ Oid typeId;
+ /* typemod for substituted value */
+ int32 typeMod;
+ /* collation for the substituted value */
+ Oid collation;
+ /* token location, or -1 if unknown */
+ int location;
} CoerceToDomainValue;
/*
@@ -1438,10 +1575,14 @@ typedef struct CoerceToDomainValue
typedef struct SetToDefault
{
Expr xpr;
- Oid typeId; /* type for substituted value */
- int32 typeMod; /* typemod for substituted value */
- Oid collation; /* collation for the substituted value */
- int location; /* token location, or -1 if unknown */
+ /* type for substituted value */
+ Oid typeId;
+ /* typemod for substituted value */
+ int32 typeMod;
+ /* collation for the substituted value */
+ Oid collation;
+ /* token location, or -1 if unknown */
+ int location;
} SetToDefault;
/*
@@ -1552,15 +1693,20 @@ typedef struct InferenceElem
typedef struct TargetEntry
{
Expr xpr;
- Expr *expr; /* expression to evaluate */
- AttrNumber resno; /* attribute number (see notes above) */
- char *resname; /* name of the column (could be NULL) */
- Index ressortgroupref; /* nonzero if referenced by a sort/group
- * clause */
- Oid resorigtbl; /* OID of column's source table */
- AttrNumber resorigcol; /* column's number in source table */
- bool resjunk; /* set to true to eliminate the attribute from
- * final target list */
+ /* expression to evaluate */
+ Expr *expr;
+ /* attribute number (see notes above) */
+ AttrNumber resno;
+ /* name of the column (could be NULL) */
+ char *resname;
+ /* nonzero if referenced by a sort/group clause */
+ Index ressortgroupref;
+ /* OID of column's source table */
+ Oid resorigtbl;
+ /* column's number in source table */
+ AttrNumber resorigcol;
+ /* set to true to eliminate the attribute from final target list */
+ bool resjunk;
} TargetEntry;
@@ -1642,11 +1788,16 @@ typedef struct JoinExpr
bool isNatural; /* Natural join? Will need to shape table */
Node *larg; /* left subtree */
Node *rarg; /* right subtree */
- List *usingClause; /* USING clause, if any (list of String) */
- Alias *join_using_alias; /* alias attached to USING clause, if any */
- Node *quals; /* qualifiers on join, if any */
- Alias *alias; /* user-written alias clause, if any */
- int rtindex; /* RT index assigned for join, or 0 */
+ /* USING clause, if any (list of String) */
+ List *usingClause;
+ /* alias attached to USING clause, if any */
+ Alias *join_using_alias;
+ /* qualifiers on join, if any */
+ Node *quals;
+ /* user-written alias clause, if any */
+ Alias *alias;
+ /* RT index assigned for join, or 0 */
+ int rtindex;
} JoinExpr;
/*----------
--
2.39.0
v4-0002-Move-query-jumble-code-to-src-backend-nodes.patchtext/x-diff; charset=us-asciiDownload
From cd672f8feb4c533c2ef1bbfb31088efc64ce7160 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 18 Jan 2023 14:37:07 +0900
Subject: [PATCH v4 2/4] Move query jumble code to src/backend/nodes/
This will ease a follow-up move that will generate automatically this
code:
- queryjumble.c -> queryjumblefuncs.c
- utils/queryjumble.h -> nodes/queryjumble.h
---
src/include/{utils => nodes}/queryjumble.h | 2 +-
src/include/parser/analyze.h | 2 +-
src/backend/nodes/Makefile | 1 +
src/backend/nodes/meson.build | 1 +
.../{utils/misc/queryjumble.c => nodes/queryjumblefuncs.c} | 6 +++---
src/backend/parser/analyze.c | 2 +-
src/backend/postmaster/postmaster.c | 2 +-
src/backend/utils/misc/Makefile | 1 -
src/backend/utils/misc/guc_tables.c | 2 +-
src/backend/utils/misc/meson.build | 1 -
contrib/pg_stat_statements/pg_stat_statements.c | 2 +-
11 files changed, 11 insertions(+), 11 deletions(-)
rename src/include/{utils => nodes}/queryjumble.h (98%)
rename src/backend/{utils/misc/queryjumble.c => nodes/queryjumblefuncs.c} (99%)
diff --git a/src/include/utils/queryjumble.h b/src/include/nodes/queryjumble.h
similarity index 98%
rename from src/include/utils/queryjumble.h
rename to src/include/nodes/queryjumble.h
index d372801410..204b8f74fd 100644
--- a/src/include/utils/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * src/include/utils/queryjumble.h
+ * src/include/nodes/queryjumble.h
*
*-------------------------------------------------------------------------
*/
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index c97be6efcf..1cef1833a6 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -15,8 +15,8 @@
#define ANALYZE_H
#include "nodes/params.h"
+#include "nodes/queryjumble.h"
#include "parser/parse_node.h"
-#include "utils/queryjumble.h"
/* Hook for plugins to get control at end of parse analysis */
typedef void (*post_parse_analyze_hook_type) (ParseState *pstate,
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 7c594be583..af12c64878 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -27,6 +27,7 @@ OBJS = \
outfuncs.o \
params.o \
print.o \
+ queryjumblefuncs.o \
read.o \
readfuncs.o \
tidbitmap.o \
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 2ff7dbac1d..9230515e7f 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -10,6 +10,7 @@ backend_sources += files(
'nodes.c',
'params.c',
'print.c',
+ 'queryjumblefuncs.c',
'read.c',
'tidbitmap.c',
'value.c',
diff --git a/src/backend/utils/misc/queryjumble.c b/src/backend/nodes/queryjumblefuncs.c
similarity index 99%
rename from src/backend/utils/misc/queryjumble.c
rename to src/backend/nodes/queryjumblefuncs.c
index 328995a7dc..16084842a3 100644
--- a/src/backend/utils/misc/queryjumble.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * queryjumble.c
+ * queryjumblefuncs.c
* Query normalization and fingerprinting.
*
* Normalization is a process whereby similar queries, typically differing only
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * src/backend/utils/misc/queryjumble.c
+ * src/backend/nodes/queryjumblefuncs.c
*
*-------------------------------------------------------------------------
*/
@@ -34,8 +34,8 @@
#include "common/hashfn.h"
#include "miscadmin.h"
+#include "nodes/queryjumble.h"
#include "parser/scansup.h"
-#include "utils/queryjumble.h"
#define JUMBLE_SIZE 1024 /* query serialization buffer size */
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5b90974e83..4a817b75ad 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -30,6 +30,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "nodes/queryjumble.h"
#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "parser/parse_agg.h"
@@ -50,7 +51,6 @@
#include "utils/backend_status.h"
#include "utils/builtins.h"
#include "utils/guc.h"
-#include "utils/queryjumble.h"
#include "utils/rel.h"
#include "utils/syscache.h"
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 9cedc1b9f0..f05a26d255 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -102,6 +102,7 @@
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
+#include "nodes/queryjumble.h"
#include "pg_getopt.h"
#include "pgstat.h"
#include "port/pg_bswap.h"
@@ -126,7 +127,6 @@
#include "utils/memutils.h"
#include "utils/pidfile.h"
#include "utils/ps_status.h"
-#include "utils/queryjumble.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index b9ee4eb48a..2910032930 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -26,7 +26,6 @@ OBJS = \
pg_rusage.o \
ps_status.o \
queryenvironment.o \
- queryjumble.o \
rls.o \
sampling.o \
superuser.o \
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 5025e80f89..f9bfbbbd95 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -43,6 +43,7 @@
#include "jit/jit.h"
#include "libpq/auth.h"
#include "libpq/libpq.h"
+#include "nodes/queryjumble.h"
#include "optimizer/cost.h"
#include "optimizer/geqo.h"
#include "optimizer/optimizer.h"
@@ -77,7 +78,6 @@
#include "utils/pg_locale.h"
#include "utils/portal.h"
#include "utils/ps_status.h"
-#include "utils/queryjumble.h"
#include "utils/inval.h"
#include "utils/xml.h"
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index e3e99ec5cb..f719c97c05 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -11,7 +11,6 @@ backend_sources += files(
'pg_rusage.c',
'ps_status.c',
'queryenvironment.c',
- 'queryjumble.c',
'rls.c',
'sampling.c',
'superuser.c',
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index a7a72783e5..ad1fe44496 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -55,6 +55,7 @@
#include "jit/jit.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/queryjumble.h"
#include "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
@@ -69,7 +70,6 @@
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
-#include "utils/queryjumble.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
--
2.39.0
v4-0003-Support-for-automated-query-jumble-with-all-Nodes.patchtext/x-diff; charset=us-asciiDownload
From a304b483d6b9101daf7494830af46d19da464161 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 20 Jan 2023 13:22:40 +0900
Subject: [PATCH v4 3/4] Support for automated query jumble with all Nodes
This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
src/include/nodes/bitmapset.h | 2 +-
src/include/nodes/nodes.h | 7 +
src/include/nodes/parsenodes.h | 126 ++--
src/include/nodes/primnodes.h | 269 ++++----
src/backend/nodes/README | 1 +
src/backend/nodes/gen_node_support.pl | 105 +++-
src/backend/nodes/meson.build | 2 +-
src/backend/nodes/queryjumblefuncs.c | 855 ++++++--------------------
8 files changed, 509 insertions(+), 858 deletions(-)
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 0dca6bc5fa..3d2225e1ae 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 10752e8011..6ecd944a90 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -59,6 +59,8 @@ typedef enum NodeTag
*
* - no_copy_equal: Shorthand for both no_copy and no_equal.
*
+ * - no_query_jumble: Does not support jumble() at all.
+ *
* - no_read: Does not support nodeRead() at all.
*
* - nodetag_only: Does not support copyObject(), equal(), outNode(),
@@ -97,6 +99,11 @@ 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.
+ *
+ * - 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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index eec51e3ee2..826ca0f84a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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;
@@ -1042,7 +1049,7 @@ typedef enum RTEKind
typedef struct RangeTblEntry
{
- pg_node_attr(custom_read_write)
+ pg_node_attr(custom_read_write, no_query_jumble)
NodeTag type;
@@ -1265,6 +1272,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 needs only to track the function expression.
*/
typedef struct RangeTblFunction
{
@@ -1272,20 +1281,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;
/*
@@ -1393,7 +1402,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;
/*
@@ -1458,7 +1467,7 @@ typedef enum GroupingSetKind
typedef struct GroupingSet
{
NodeTag type;
- GroupingSetKind kind;
+ GroupingSetKind kind pg_node_attr(query_jumble_ignore);
List *content;
int location;
} GroupingSet;
@@ -1479,35 +1488,38 @@ 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;
+ List *orderClause pg_node_attr(query_jumble_ignore);
int frameOptions; /* frame_clause options, see WindowDef */
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;
/*
@@ -1625,27 +1637,27 @@ typedef struct CommonTableExpr
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);
/* token location, or -1 if unknown */
int location;
/* 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 */
@@ -1683,11 +1695,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;
/*
@@ -1897,15 +1909,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;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 8d80c90ce7..8a4f64adc2 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -99,29 +99,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;
@@ -230,11 +230,11 @@ 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;
+ Oid varcollid pg_node_attr(query_jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
@@ -248,9 +248,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;
@@ -263,6 +263,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
{
@@ -272,24 +274,27 @@ 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;
/*
@@ -327,6 +332,7 @@ typedef enum ParamKind
PARAM_MULTIEXPR
} ParamKind;
+/* typmod and collation information are irrelevant for the query jumbling. */
typedef struct Param
{
Expr xpr;
@@ -334,9 +340,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;
@@ -389,6 +395,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
{
@@ -398,22 +407,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;
@@ -431,31 +440,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;
@@ -484,19 +493,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;
@@ -507,6 +519,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
{
@@ -514,11 +529,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 */
@@ -526,9 +541,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;
@@ -567,6 +582,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.
@@ -575,15 +592,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;
@@ -634,6 +651,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
{
@@ -641,21 +661,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 */
@@ -682,7 +702,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 */
@@ -698,6 +718,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
{
@@ -707,19 +730,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;
@@ -775,6 +798,10 @@ 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
{
@@ -784,19 +811,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;
@@ -899,7 +926,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;
/* token location, or -1 if unknown */
@@ -1012,11 +1039,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;
/* ----------------
@@ -1043,9 +1070,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;
@@ -1068,11 +1095,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);
/* token location, or -1 if unknown */
int location;
} RelabelType;
@@ -1093,9 +1120,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);
/* token location, or -1 if unknown */
int location;
} CoerceViaIO;
@@ -1120,11 +1147,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);
/* token location, or -1 if unknown */
int location;
} ArrayCoerceExpr;
@@ -1149,7 +1176,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);
/* token location, or -1 if unknown */
int location;
} ConvertRowtypeExpr;
@@ -1196,9 +1223,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) */
@@ -1243,9 +1270,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;
/*
@@ -1260,15 +1287,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;
@@ -1300,7 +1327,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
@@ -1316,10 +1343,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);
/* token location, or -1 if unknown */
int location;
@@ -1357,11 +1384,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 */
@@ -1375,9 +1402,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 */
@@ -1397,11 +1424,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 */
@@ -1445,18 +1472,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;
@@ -1491,7 +1518,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);
/* token location, or -1 if unknown */
int location;
} NullTest;
@@ -1527,6 +1554,8 @@ typedef struct BooleanTest
* checked will be determined. If the value passes, it is returned as the
* result; if not, an error is raised. Note that this is equivalent to
* RelabelType in the scenario where no constraints are applied.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct CoerceToDomain
{
@@ -1534,11 +1563,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);
/* token location, or -1 if unknown */
int location;
} CoerceToDomain;
@@ -1558,9 +1587,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;
@@ -1571,6 +1600,8 @@ typedef struct CoerceToDomainValue
* This is not an executable expression: it must be replaced by the actual
* column default expression during rewriting. But it is convenient to
* treat it as an expression node during parsing and rewriting.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct SetToDefault
{
@@ -1578,9 +1609,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;
@@ -1698,15 +1729,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;
@@ -1789,13 +1820,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;
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..7cf6e3b041 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -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.
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b3c1ead496..27b9e4e630 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -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
@@ -332,6 +335,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 +464,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 +1234,100 @@ 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;
+
+ 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)
{
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 9230515e7f..31467a12d3 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -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',
)
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16084842a3..278150fba0 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -21,7 +21,7 @@
* tree(s) generated from the query. The executor can then use this value
* to blame query costs on the proper queryId.
*
- * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -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,38 +102,29 @@ 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));
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
- }
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
return jstate;
}
@@ -154,34 +142,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.
@@ -219,621 +179,6 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
jstate->jumble_len = jumble_len;
}
-/*
- * 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
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- 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;
- }
- }
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
- ListCell *lc;
-
- foreach(lc, rowMarks)
- {
- RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
- if (!rowmark->pushedDown)
- {
- APP_JUMB(rowmark->rti);
- APP_JUMB(rowmark->strength);
- APP_JUMB(rowmark->waitPolicy);
- }
- }
-}
-
-/*
- * 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)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * 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);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- 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);
- }
- break;
- 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 */
- 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);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- 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);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- 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;
-
- APP_JUMB(mergeaction->matched);
- APP_JUMB(mergeaction->commandType);
- JumbleExpr(jstate, mergeaction->qual);
- JumbleExpr(jstate, (Node *) mergeaction->targetList);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- 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);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- 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);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* 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));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- 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);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- break;
- }
-}
-
/*
* Record location of constant within query string of query tree
* that is currently being walked.
@@ -859,3 +204,155 @@ RecordConstLocation(JumbleState *jstate, int location)
jstate->clocations_count++;
}
}
+
+#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)
+
+#include "queryjumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+ Node *expr = node;
+
+ if (expr == NULL)
+ return;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /*
+ * We always emit the node's NodeTag, then any additional fields that are
+ * considered significant, and then we recurse to any child nodes.
+ */
+ JUMBLE_FIELD(type);
+
+ switch (nodeTag(expr))
+ {
+#include "queryjumblefuncs.switch.c"
+
+ case T_List:
+ case T_IntList:
+ case T_OidList:
+ case T_XidList:
+ _jumbleList(jstate, expr);
+ break;
+
+ case T_RangeTblEntry:
+ _jumbleRangeTblEntry(jstate, expr);
+ break;
+
+ default:
+ /* Only a warning, since we can stumble along anyway */
+ elog(WARNING, "unrecognized node type: %d",
+ (int) nodeTag(expr));
+ break;
+ }
+
+ /* Special cases */
+ switch (nodeTag(expr))
+ {
+ case T_Param:
+ {
+ Param *p = (Param *) node;
+
+ /* Also, track the highest external Param id */
+ if (p->paramkind == PARAM_EXTERN &&
+ p->paramid > jstate->highest_extern_param_id)
+ jstate->highest_extern_param_id = p->paramid;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+ List *expr = (List *) node;
+ ListCell *l;
+
+ switch (expr->type)
+ {
+ case T_List:
+ foreach(l, expr)
+ _jumbleNode(jstate, lfirst(l));
+ break;
+ case T_IntList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_int(l));
+ break;
+ case T_OidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+ break;
+ case T_XidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+ break;
+ default:
+ elog(ERROR, "unrecognized list node type: %d",
+ (int) expr->type);
+ return;
+ }
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+ RangeTblEntry *expr = (RangeTblEntry *) node;
+
+ JUMBLE_FIELD(rtekind);
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(tablesample);
+ JUMBLE_FIELD(inh);
+ break;
+ case RTE_SUBQUERY:
+ JUMBLE_NODE(subquery);
+ break;
+ case RTE_JOIN:
+ JUMBLE_FIELD(jointype);
+ break;
+ case RTE_FUNCTION:
+ JUMBLE_NODE(functions);
+ break;
+ case RTE_TABLEFUNC:
+ JUMBLE_NODE(tablefunc);
+ break;
+ case RTE_VALUES:
+ JUMBLE_NODE(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.
+ */
+ JUMBLE_STRING(ctename);
+ JUMBLE_FIELD(ctelevelsup);
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ JUMBLE_STRING(enrname);
+ break;
+ case RTE_RESULT:
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+ break;
+ }
+}
--
2.39.0
v4-0004-Add-GUC-utility_query_id.patchtext/x-diff; charset=us-asciiDownload
From ffaa79144ff34eeaa89b55cbced2a182310ea522 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 20 Jan 2023 13:27:29 +0900
Subject: [PATCH v4 4/4] Add GUC utility_query_id
This GUC has two modes to control the computation method of query IDs
for utilities:
- 'string', the default, to hash the string query.
- 'jumble', to use the parsed tree.
---
src/include/nodes/queryjumble.h | 7 ++
src/backend/nodes/queryjumblefuncs.c | 81 ++++++++++++++-----
src/backend/utils/misc/guc_tables.c | 16 ++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
doc/src/sgml/config.sgml | 18 +++++
.../expected/pg_stat_statements.out | 31 +++++++
.../sql/pg_stat_statements.sql | 17 ++++
7 files changed, 151 insertions(+), 20 deletions(-)
diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h
index 204b8f74fd..261aea6bcf 100644
--- a/src/include/nodes/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -59,8 +59,15 @@ enum ComputeQueryIdType
COMPUTE_QUERY_ID_REGRESS
};
+enum UtilityQueryIdType
+{
+ UTILITY_QUERY_ID_STRING,
+ UTILITY_QUERY_ID_JUMBLE
+};
+
/* GUC parameters */
extern PGDLLIMPORT int compute_query_id;
+extern PGDLLIMPORT int utility_query_id;
extern const char *CleanQuerytext(const char *query, int *location, int *len);
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 278150fba0..dd9ab8f353 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -41,12 +41,15 @@
/* GUC parameters */
int compute_query_id = COMPUTE_QUERY_ID_AUTO;
+int utility_query_id = UTILITY_QUERY_ID_STRING;
/* True when compute_query_id is ON, or AUTO and a module requests them */
bool query_id_enabled = false;
static void AppendJumble(JumbleState *jstate,
const unsigned char *item, Size size);
+static uint64 compute_utility_query_id(const char *query_text,
+ int query_location, int query_len);
static void RecordConstLocation(JumbleState *jstate, int location);
static void _jumbleNode(JumbleState *jstate, Node *node);
static void _jumbleList(JumbleState *jstate, Node *node);
@@ -102,29 +105,39 @@ JumbleQuery(Query *query, const char *querytext)
Assert(IsQueryIdEnabled());
- jstate = (JumbleState *) palloc(sizeof(JumbleState));
+ if (query->utilityStmt &&
+ compute_query_id == UTILITY_QUERY_ID_STRING)
+ {
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
+ }
return jstate;
}
@@ -142,6 +155,34 @@ 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.
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index f9bfbbbd95..869e8a3a6f 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -294,6 +294,12 @@ static const struct config_enum_entry compute_query_id_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry utility_query_id_options[] = {
+ {"string", UTILITY_QUERY_ID_STRING, false},
+ {"jumble", UTILITY_QUERY_ID_JUMBLE, false},
+ {NULL, 0, false}
+};
+
/*
* Although only "on", "off", and "partition" are documented, we
* accept all the likely variants of "on" and "off".
@@ -4563,6 +4569,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"utility_query_id", PGC_SUSET, STATS_MONITORING,
+ gettext_noop("Controls method computing query ID for utilities."),
+ NULL
+ },
+ &utility_query_id,
+ UTILITY_QUERY_ID_STRING, utility_query_id_options,
+ NULL, NULL, NULL
+ },
+
{
{"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 4cceda4162..4d43c9d3c4 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -622,6 +622,7 @@
# - Monitoring -
#compute_query_id = auto
+#utility_query_id = string # string, jumble
#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 89d53f2a64..70c55f1a79 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8203,6 +8203,24 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-utility-query-id" xreflabel="utility_query_id">
+ <term><varname>utility_query_id</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>utility_query_id</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Controls the method used to compute the query identifier of a utility
+ query. Valid values are <literal>string</literal> to use a hash of the
+ query string and <literal>jumble</literal> to compute the query
+ identifier depending on the parsed tree of the utility query (less
+ performant, but allows for more parameterization of the queries
+ involved). The default is <literal>string</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-log-statement-stats">
<term><varname>log_statement_stats</varname> (<type>boolean</type>)
<indexterm>
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 9ac5c87c3a..8bdf8beec3 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -554,6 +554,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
pg_stat_statements_reset
--------------------------
@@ -592,6 +593,36 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+ query | calls | rows
+------------------------------------------------------------------------------+-------+------
+ Begin | 2 | 0
+ Commit | 2 | 0
+ Create Table test_utility_query (a int) | 2 | 0
+ Drop Table test_utility_query | 2 | 0
+ SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
+ SET utility_query_id = 'jumble' | 1 | 0
+(7 rows)
+
+RESET utility_query_id;
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 8f5c866225..81d663f81c 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -258,6 +258,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
SELECT 1;
@@ -272,6 +273,22 @@ DROP FUNCTION PLUS_TWO(INTEGER);
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+SELECT pg_stat_statements_reset();
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+RESET utility_query_id;
+
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
--
2.39.0
On 20.01.23 05:35, Michael Paquier wrote:
On Thu, Jan 19, 2023 at 09:42:03AM +0100, Peter Eisentraut wrote:
I see that in the 0003 patch, most location fields now have an explicit
markup with query_jumble_ignore. I thought we had previously resolved to
consider location fields to be automatically ignored unless explicitly
included (like for the Const node). This appears to invert that? Am I
missing something?As a result, I have rebased the patch set to use the two-attribute
approach: query_jumble_ignore and query_jumble_location.
Structurally, this looks okay to me now.
In your 0001 patch, most of the comment reformattings for location
fields are no longer needed, so you should undo those.
The 0002 patch looks good.
Those two could be committed with those adjustments, I think.
I'll read the 0003 again more carefully. I haven't studied the new 0004
yet.
On Fri, Jan 20, 2023 at 11:56:00AM +0100, Peter Eisentraut wrote:
In your 0001 patch, most of the comment reformattings for location fields
are no longer needed, so you should undo those.The 0002 patch looks good.
Okay, I have gone through these two again and applied what I had.
0001 has been cleaned up of the extra comment moves for the
locations. Now, I have kept a few changes for some of the nodes to
have some consistency with the other fields, in the case where most of
the fields at the end of the structures have to be marked with new
node attributes. This made the style of the header a bit more
elegant, IMV.
I'll read the 0003 again more carefully. I haven't studied the new 0004
yet.
Thanks, again. Rebased version attached.
--
Michael
Attachments:
v5-0003-Support-for-automated-query-jumble-with-all-Nodes.patchtext/x-diff; charset=us-asciiDownload
From 977214a0c7b175efe0deae1847d7aa33ad66c120 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Sat, 21 Jan 2023 12:29:26 +0900
Subject: [PATCH v5 3/4] Support for automated query jumble with all Nodes
This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
src/include/nodes/bitmapset.h | 2 +-
src/include/nodes/nodes.h | 7 +
src/include/nodes/parsenodes.h | 126 ++--
src/include/nodes/primnodes.h | 269 ++++----
src/backend/nodes/README | 1 +
src/backend/nodes/gen_node_support.pl | 105 +++-
src/backend/nodes/meson.build | 2 +-
src/backend/nodes/queryjumblefuncs.c | 855 ++++++--------------------
8 files changed, 509 insertions(+), 858 deletions(-)
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 0dca6bc5fa..3d2225e1ae 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 10752e8011..6ecd944a90 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -59,6 +59,8 @@ typedef enum NodeTag
*
* - no_copy_equal: Shorthand for both no_copy and no_equal.
*
+ * - no_query_jumble: Does not support jumble() at all.
+ *
* - no_read: Does not support nodeRead() at all.
*
* - nodetag_only: Does not support copyObject(), equal(), outNode(),
@@ -97,6 +99,11 @@ 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.
+ *
+ * - 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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 89335d95e7..12da5c120e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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, no_query_jumble)
NodeTag type;
@@ -1250,6 +1257,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 needs only to track the function expression.
*/
typedef struct RangeTblFunction
{
@@ -1257,20 +1266,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;
/*
@@ -1378,7 +1387,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;
/*
@@ -1443,7 +1452,7 @@ typedef enum GroupingSetKind
typedef struct GroupingSet
{
NodeTag type;
- GroupingSetKind kind;
+ GroupingSetKind kind pg_node_attr(query_jumble_ignore);
List *content;
int location;
} GroupingSet;
@@ -1464,35 +1473,38 @@ 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;
+ List *orderClause pg_node_attr(query_jumble_ignore);
int frameOptions; /* frame_clause options, see WindowDef */
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;
/*
@@ -1607,26 +1619,26 @@ typedef struct CommonTableExpr
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 */
@@ -1664,11 +1676,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;
/*
@@ -1877,15 +1889,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;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3bdde134f4..f81518c2fd 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -99,29 +99,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;
@@ -230,11 +230,11 @@ 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;
+ Oid varcollid pg_node_attr(query_jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
@@ -248,9 +248,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;
@@ -263,6 +263,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
{
@@ -272,24 +274,27 @@ 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;
/*
@@ -327,6 +332,7 @@ typedef enum ParamKind
PARAM_MULTIEXPR
} ParamKind;
+/* typmod and collation information are irrelevant for the query jumbling. */
typedef struct Param
{
Expr xpr;
@@ -334,9 +340,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;
@@ -389,6 +395,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
{
@@ -398,22 +407,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;
@@ -431,31 +440,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;
@@ -484,19 +493,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;
@@ -507,6 +519,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
{
@@ -514,11 +529,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 */
@@ -526,9 +541,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;
@@ -567,6 +582,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.
@@ -575,15 +592,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;
@@ -634,6 +651,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
{
@@ -641,21 +661,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 */
@@ -682,7 +702,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 */
@@ -698,6 +718,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
{
@@ -707,19 +730,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;
@@ -775,6 +798,10 @@ 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
{
@@ -784,19 +811,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;
@@ -898,7 +925,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 */
@@ -1010,11 +1037,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;
/* ----------------
@@ -1041,9 +1068,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;
@@ -1066,11 +1093,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;
@@ -1090,9 +1117,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;
@@ -1116,11 +1143,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;
@@ -1144,7 +1171,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;
@@ -1189,9 +1216,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) */
@@ -1234,9 +1261,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;
/*
@@ -1251,15 +1278,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;
@@ -1291,7 +1318,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
@@ -1307,10 +1334,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;
@@ -1347,11 +1374,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 */
@@ -1365,9 +1392,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 */
@@ -1387,11 +1414,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 */
@@ -1435,18 +1462,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;
@@ -1481,7 +1508,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;
@@ -1515,6 +1542,8 @@ typedef struct BooleanTest
* checked will be determined. If the value passes, it is returned as the
* result; if not, an error is raised. Note that this is equivalent to
* RelabelType in the scenario where no constraints are applied.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct CoerceToDomain
{
@@ -1522,11 +1551,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;
@@ -1545,9 +1574,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;
@@ -1558,6 +1587,8 @@ typedef struct CoerceToDomainValue
* This is not an executable expression: it must be replaced by the actual
* column default expression during rewriting. But it is convenient to
* treat it as an expression node during parsing and rewriting.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct SetToDefault
{
@@ -1565,9 +1596,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;
@@ -1685,15 +1716,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;
@@ -1776,13 +1807,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;
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..7cf6e3b041 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -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.
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b3c1ead496..27b9e4e630 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -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
@@ -332,6 +335,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 +464,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 +1234,100 @@ 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;
+
+ 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)
{
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 9230515e7f..31467a12d3 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -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',
)
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16084842a3..278150fba0 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -21,7 +21,7 @@
* tree(s) generated from the query. The executor can then use this value
* to blame query costs on the proper queryId.
*
- * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
@@ -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,38 +102,29 @@ 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));
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
- }
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
return jstate;
}
@@ -154,34 +142,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.
@@ -219,621 +179,6 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
jstate->jumble_len = jumble_len;
}
-/*
- * 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
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- 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;
- }
- }
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
- ListCell *lc;
-
- foreach(lc, rowMarks)
- {
- RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
- if (!rowmark->pushedDown)
- {
- APP_JUMB(rowmark->rti);
- APP_JUMB(rowmark->strength);
- APP_JUMB(rowmark->waitPolicy);
- }
- }
-}
-
-/*
- * 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)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * 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);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- 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);
- }
- break;
- 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 */
- 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);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- 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);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- 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;
-
- APP_JUMB(mergeaction->matched);
- APP_JUMB(mergeaction->commandType);
- JumbleExpr(jstate, mergeaction->qual);
- JumbleExpr(jstate, (Node *) mergeaction->targetList);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- 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);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- 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);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* 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));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- 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);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- break;
- }
-}
-
/*
* Record location of constant within query string of query tree
* that is currently being walked.
@@ -859,3 +204,155 @@ RecordConstLocation(JumbleState *jstate, int location)
jstate->clocations_count++;
}
}
+
+#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)
+
+#include "queryjumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+ Node *expr = node;
+
+ if (expr == NULL)
+ return;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /*
+ * We always emit the node's NodeTag, then any additional fields that are
+ * considered significant, and then we recurse to any child nodes.
+ */
+ JUMBLE_FIELD(type);
+
+ switch (nodeTag(expr))
+ {
+#include "queryjumblefuncs.switch.c"
+
+ case T_List:
+ case T_IntList:
+ case T_OidList:
+ case T_XidList:
+ _jumbleList(jstate, expr);
+ break;
+
+ case T_RangeTblEntry:
+ _jumbleRangeTblEntry(jstate, expr);
+ break;
+
+ default:
+ /* Only a warning, since we can stumble along anyway */
+ elog(WARNING, "unrecognized node type: %d",
+ (int) nodeTag(expr));
+ break;
+ }
+
+ /* Special cases */
+ switch (nodeTag(expr))
+ {
+ case T_Param:
+ {
+ Param *p = (Param *) node;
+
+ /* Also, track the highest external Param id */
+ if (p->paramkind == PARAM_EXTERN &&
+ p->paramid > jstate->highest_extern_param_id)
+ jstate->highest_extern_param_id = p->paramid;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+ List *expr = (List *) node;
+ ListCell *l;
+
+ switch (expr->type)
+ {
+ case T_List:
+ foreach(l, expr)
+ _jumbleNode(jstate, lfirst(l));
+ break;
+ case T_IntList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_int(l));
+ break;
+ case T_OidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+ break;
+ case T_XidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+ break;
+ default:
+ elog(ERROR, "unrecognized list node type: %d",
+ (int) expr->type);
+ return;
+ }
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+ RangeTblEntry *expr = (RangeTblEntry *) node;
+
+ JUMBLE_FIELD(rtekind);
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(tablesample);
+ JUMBLE_FIELD(inh);
+ break;
+ case RTE_SUBQUERY:
+ JUMBLE_NODE(subquery);
+ break;
+ case RTE_JOIN:
+ JUMBLE_FIELD(jointype);
+ break;
+ case RTE_FUNCTION:
+ JUMBLE_NODE(functions);
+ break;
+ case RTE_TABLEFUNC:
+ JUMBLE_NODE(tablefunc);
+ break;
+ case RTE_VALUES:
+ JUMBLE_NODE(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.
+ */
+ JUMBLE_STRING(ctename);
+ JUMBLE_FIELD(ctelevelsup);
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ JUMBLE_STRING(enrname);
+ break;
+ case RTE_RESULT:
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+ break;
+ }
+}
--
2.39.0
v5-0004-Add-GUC-utility_query_id.patchtext/x-diff; charset=us-asciiDownload
From 700f7895a189fca570d1b0088c844c776636c811 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 20 Jan 2023 13:27:29 +0900
Subject: [PATCH v5 4/4] Add GUC utility_query_id
This GUC has two modes to control the computation method of query IDs
for utilities:
- 'string', the default, to hash the string query.
- 'jumble', to use the parsed tree.
---
src/include/nodes/queryjumble.h | 7 ++
src/backend/nodes/queryjumblefuncs.c | 81 ++++++++++++++-----
src/backend/utils/misc/guc_tables.c | 16 ++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
doc/src/sgml/config.sgml | 18 +++++
.../expected/pg_stat_statements.out | 31 +++++++
.../sql/pg_stat_statements.sql | 17 ++++
7 files changed, 151 insertions(+), 20 deletions(-)
diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h
index 204b8f74fd..261aea6bcf 100644
--- a/src/include/nodes/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -59,8 +59,15 @@ enum ComputeQueryIdType
COMPUTE_QUERY_ID_REGRESS
};
+enum UtilityQueryIdType
+{
+ UTILITY_QUERY_ID_STRING,
+ UTILITY_QUERY_ID_JUMBLE
+};
+
/* GUC parameters */
extern PGDLLIMPORT int compute_query_id;
+extern PGDLLIMPORT int utility_query_id;
extern const char *CleanQuerytext(const char *query, int *location, int *len);
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 278150fba0..dd9ab8f353 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -41,12 +41,15 @@
/* GUC parameters */
int compute_query_id = COMPUTE_QUERY_ID_AUTO;
+int utility_query_id = UTILITY_QUERY_ID_STRING;
/* True when compute_query_id is ON, or AUTO and a module requests them */
bool query_id_enabled = false;
static void AppendJumble(JumbleState *jstate,
const unsigned char *item, Size size);
+static uint64 compute_utility_query_id(const char *query_text,
+ int query_location, int query_len);
static void RecordConstLocation(JumbleState *jstate, int location);
static void _jumbleNode(JumbleState *jstate, Node *node);
static void _jumbleList(JumbleState *jstate, Node *node);
@@ -102,29 +105,39 @@ JumbleQuery(Query *query, const char *querytext)
Assert(IsQueryIdEnabled());
- jstate = (JumbleState *) palloc(sizeof(JumbleState));
+ if (query->utilityStmt &&
+ compute_query_id == UTILITY_QUERY_ID_STRING)
+ {
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
+ }
return jstate;
}
@@ -142,6 +155,34 @@ 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.
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 4ac808ed22..97619c4e1d 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -294,6 +294,12 @@ static const struct config_enum_entry compute_query_id_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry utility_query_id_options[] = {
+ {"string", UTILITY_QUERY_ID_STRING, false},
+ {"jumble", UTILITY_QUERY_ID_JUMBLE, false},
+ {NULL, 0, false}
+};
+
/*
* Although only "on", "off", and "partition" are documented, we
* accept all the likely variants of "on" and "off".
@@ -4574,6 +4580,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"utility_query_id", PGC_SUSET, STATS_MONITORING,
+ gettext_noop("Controls method computing query ID for utilities."),
+ NULL
+ },
+ &utility_query_id,
+ UTILITY_QUERY_ID_STRING, utility_query_id_options,
+ NULL, NULL, NULL
+ },
+
{
{"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f..bbf95af59d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -623,6 +623,7 @@
# - Monitoring -
#compute_query_id = auto
+#utility_query_id = string # string, jumble
#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index dc9b78b0b7..e1e7a134cf 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8237,6 +8237,24 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-utility-query-id" xreflabel="utility_query_id">
+ <term><varname>utility_query_id</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>utility_query_id</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Controls the method used to compute the query identifier of a utility
+ query. Valid values are <literal>string</literal> to use a hash of the
+ query string and <literal>jumble</literal> to compute the query
+ identifier depending on the parsed tree of the utility query (less
+ performant, but allows for more parameterization of the queries
+ involved). The default is <literal>string</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-log-statement-stats">
<term><varname>log_statement_stats</varname> (<type>boolean</type>)
<indexterm>
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 9ac5c87c3a..8bdf8beec3 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -554,6 +554,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
pg_stat_statements_reset
--------------------------
@@ -592,6 +593,36 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+ query | calls | rows
+------------------------------------------------------------------------------+-------+------
+ Begin | 2 | 0
+ Commit | 2 | 0
+ Create Table test_utility_query (a int) | 2 | 0
+ Drop Table test_utility_query | 2 | 0
+ SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
+ SET utility_query_id = 'jumble' | 1 | 0
+(7 rows)
+
+RESET utility_query_id;
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 8f5c866225..81d663f81c 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -258,6 +258,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
SELECT 1;
@@ -272,6 +273,22 @@ DROP FUNCTION PLUS_TWO(INTEGER);
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+SELECT pg_stat_statements_reset();
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+RESET utility_query_id;
+
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
--
2.39.0
On 21.01.23 04:35, Michael Paquier wrote:
I'll read the 0003 again more carefully. I haven't studied the new 0004
yet.Thanks, again. Rebased version attached.
A couple of small fixes are attached.
There is something weird in _jumbleNode(). There are two switch
(nodeTag(expr)) statements. Maybe that's intentional, but then it
should be commented better, because now it looks more like an editing
mistake.
The handling of T_RangeTblEntry could be improved. In other contexts we
have for example a custom_copy attribute, which generates the switch
entry but not the function. Something like this could be useful here too.
Otherwise, this looks ok. I haven't checked whether it maintains the
exact behavior from before. What is the test coverage situation for this?
For the 0004 patch, it should be documented why one would want one
behavior or the other. That's totally unclear right now.
I think if we are going to accept 0004, then it might be better to
combine it with 0003. Otherwise, 0004 is just undoing a lot of the code
structure changes in JumbleQuery() that 0003 did.
Attachments:
0001-fixup-Support-for-automated-query-jumble-with-all-No.patchtext/plain; charset=UTF-8; name=0001-fixup-Support-for-automated-query-jumble-with-all-No.patchDownload
From 0597275ef439f070b1bd32ba25ca3581cbc2af30 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 23 Jan 2023 14:16:39 +0100
Subject: [PATCH] fixup! Support for automated query jumble with all Nodes
---
src/backend/nodes/queryjumblefuncs.c | 2 +-
src/include/nodes/nodes.h | 4 ++--
src/include/nodes/primnodes.h | 1 -
3 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 278150fba0..bdb5636b1b 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -21,7 +21,7 @@
* tree(s) generated from the query. The executor can then use this value
* to blame query costs on the proper queryId.
*
- * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6ecd944a90..e7f92df7cb 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -59,12 +59,12 @@ typedef enum NodeTag
*
* - no_copy_equal: Shorthand for both no_copy and no_equal.
*
- * - no_query_jumble: Does not support jumble() at all.
+ * - 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().
+ * or nodeRead(). XXX
*
* - special_read_write: Has special treatment in outNode() and nodeRead().
*
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index f81518c2fd..9ea819a6a7 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -799,7 +799,6 @@ typedef OpExpr NullIfExpr;
* 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.
*/
--
2.39.1
On Mon, Jan 23, 2023 at 02:27:13PM +0100, Peter Eisentraut wrote:
A couple of small fixes are attached.
Thanks.
There is something weird in _jumbleNode(). There are two switch
(nodeTag(expr)) statements. Maybe that's intentional, but then it should be
commented better, because now it looks more like an editing mistake.
This one is intentional, so as it is possible to track correctly the
highest param ID found while browsing the nodes. IMO it would be
confusing to add that into gen_node_support.pl. Another thing that
could be done is to switch Param to have a custom implementation, like
RangeTblEntry, though this removes the automation around the creation
of _jumbleParam(). I have clarified the comments around that.
The handling of T_RangeTblEntry could be improved. In other contexts we
have for example a custom_copy attribute, which generates the switch entry
but not the function. Something like this could be useful here too.
Hmm. Okay. Fine by me.
Otherwise, this looks ok. I haven't checked whether it maintains the exact
behavior from before. What is the test coverage situation for this?
0003 taken in isolation has some minimal coverage through
pg_stat_statements, though it turns around 15% with compute_query_id =
auto that would enforce the jumbling path only when pg_stat_statements
uses it. Still, my plan here is to enforce the loading of
pg_stat_statements with compute_query_id = regress and
utility_query_id = jumble (if needed) in a new buildfarm machine,
because that's the cheapest path. An extra possibility is to have
pg_regress kicked in a new TAP test with these settings, but that's
costly and we have already two of these :/ Another possibility is to
plug in that into 027_stream_regress or the pg_upgrade test suite with
new settings :/
Anyway, the regression tests of pg_stat_statements should be extended
a bit to cover more node types by default (Say COPY with DMLs for the
InsertStmt & co) to look at how these are showing up once normalized
using their parsed query, and we don't do much around that now.
Normalizing more DDLs should use this path, as well.
For the 0004 patch, it should be documented why one would want one behavior
or the other. That's totally unclear right now.
I am not 100% sure whether we should have this part at the end, but as
an exit path in case somebody complains about the extra load with the
automated jumbling compared to the hash of the query strings, that can
be used as a backup. Anyway, attached is what would be a
clarification.
I think if we are going to accept 0004, then it might be better to combine
it with 0003. Otherwise, 0004 is just undoing a lot of the code structure
changes in JumbleQuery() that 0003 did.
Makes sense. That would be my intention if 0004 is the most
acceptable and splitting things makes things a bit easier to review.
--
Michael
Attachments:
v6-0003-Support-for-automated-query-jumble-with-all-Nodes.patchtext/x-diff; charset=us-asciiDownload
From b6bb376b9181739e8325f48721ee8774f126be86 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 24 Jan 2023 15:29:06 +0900
Subject: [PATCH v6 3/4] Support for automated query jumble with all Nodes
This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
src/include/nodes/bitmapset.h | 2 +-
src/include/nodes/nodes.h | 13 +-
src/include/nodes/parsenodes.h | 126 ++--
src/include/nodes/primnodes.h | 268 ++++----
src/backend/nodes/README | 1 +
src/backend/nodes/gen_node_support.pl | 114 +++-
src/backend/nodes/meson.build | 2 +-
src/backend/nodes/queryjumblefuncs.c | 852 ++++++--------------------
8 files changed, 519 insertions(+), 859 deletions(-)
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 0dca6bc5fa..3d2225e1ae 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 10752e8011..d7a9e38436 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -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,11 @@ 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.
+ *
+ * - 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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 89335d95e7..f99fb5e909 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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;
@@ -1250,6 +1257,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 needs only to track the function expression.
*/
typedef struct RangeTblFunction
{
@@ -1257,20 +1266,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;
/*
@@ -1378,7 +1387,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;
/*
@@ -1443,7 +1452,7 @@ typedef enum GroupingSetKind
typedef struct GroupingSet
{
NodeTag type;
- GroupingSetKind kind;
+ GroupingSetKind kind pg_node_attr(query_jumble_ignore);
List *content;
int location;
} GroupingSet;
@@ -1464,35 +1473,38 @@ 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;
+ List *orderClause pg_node_attr(query_jumble_ignore);
int frameOptions; /* frame_clause options, see WindowDef */
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;
/*
@@ -1607,26 +1619,26 @@ typedef struct CommonTableExpr
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 */
@@ -1664,11 +1676,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;
/*
@@ -1877,15 +1889,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;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index dec7d5c775..8d5b68a0bc 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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;
@@ -227,11 +227,11 @@ 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;
+ Oid varcollid pg_node_attr(query_jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
@@ -245,9 +245,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;
@@ -260,6 +260,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
{
@@ -269,24 +271,27 @@ 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;
/*
@@ -324,6 +329,7 @@ typedef enum ParamKind
PARAM_MULTIEXPR
} ParamKind;
+/* typmod and collation information are irrelevant for the query jumbling. */
typedef struct Param
{
Expr xpr;
@@ -331,9 +337,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;
@@ -386,6 +392,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
{
@@ -395,22 +404,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;
@@ -428,31 +437,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;
@@ -481,19 +490,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;
@@ -504,6 +516,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
{
@@ -511,11 +526,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 */
@@ -523,9 +538,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;
@@ -564,6 +579,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.
@@ -572,15 +589,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;
@@ -631,6 +648,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
{
@@ -638,21 +658,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 */
@@ -679,7 +699,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 */
@@ -695,6 +715,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
{
@@ -704,19 +727,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;
@@ -772,6 +795,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
{
@@ -781,19 +807,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;
@@ -895,7 +921,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 */
@@ -1007,11 +1033,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;
/* ----------------
@@ -1038,9 +1064,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;
@@ -1063,11 +1089,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;
@@ -1087,9 +1113,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;
@@ -1113,11 +1139,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;
@@ -1141,7 +1167,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;
@@ -1186,9 +1212,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) */
@@ -1231,9 +1257,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;
/*
@@ -1248,15 +1274,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;
@@ -1288,7 +1314,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
@@ -1304,10 +1330,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;
@@ -1344,11 +1370,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 */
@@ -1362,9 +1388,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 */
@@ -1384,11 +1410,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 */
@@ -1432,18 +1458,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;
@@ -1478,7 +1504,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;
@@ -1512,6 +1538,8 @@ typedef struct BooleanTest
* checked will be determined. If the value passes, it is returned as the
* result; if not, an error is raised. Note that this is equivalent to
* RelabelType in the scenario where no constraints are applied.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct CoerceToDomain
{
@@ -1519,11 +1547,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;
@@ -1542,9 +1570,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;
@@ -1555,6 +1583,8 @@ typedef struct CoerceToDomainValue
* This is not an executable expression: it must be replaced by the actual
* column default expression during rewriting. But it is convenient to
* treat it as an expression node during parsing and rewriting.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct SetToDefault
{
@@ -1562,9 +1592,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;
@@ -1682,15 +1712,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;
@@ -1773,13 +1803,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;
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..7cf6e3b041 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -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.
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b3c1ead496..471333dc80 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -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)
{
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 9230515e7f..31467a12d3 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -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',
)
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16084842a3..16fdf7164a 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -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,38 +102,29 @@ 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));
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
- }
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
return jstate;
}
@@ -154,34 +142,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.
@@ -219,621 +179,6 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
jstate->jumble_len = jumble_len;
}
-/*
- * 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
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- 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;
- }
- }
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
- ListCell *lc;
-
- foreach(lc, rowMarks)
- {
- RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
- if (!rowmark->pushedDown)
- {
- APP_JUMB(rowmark->rti);
- APP_JUMB(rowmark->strength);
- APP_JUMB(rowmark->waitPolicy);
- }
- }
-}
-
-/*
- * 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)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * 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);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- 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);
- }
- break;
- 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 */
- 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);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- 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);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- 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;
-
- APP_JUMB(mergeaction->matched);
- APP_JUMB(mergeaction->commandType);
- JumbleExpr(jstate, mergeaction->qual);
- JumbleExpr(jstate, (Node *) mergeaction->targetList);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- 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);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- 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);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* 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));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- 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);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- break;
- }
-}
-
/*
* Record location of constant within query string of query tree
* that is currently being walked.
@@ -859,3 +204,154 @@ RecordConstLocation(JumbleState *jstate, int location)
jstate->clocations_count++;
}
}
+
+#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)
+
+#include "queryjumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+ Node *expr = node;
+
+ if (expr == NULL)
+ return;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /*
+ * We always emit the node's NodeTag, then any additional fields that are
+ * considered significant, and then we recurse to any child nodes.
+ */
+ JUMBLE_FIELD(type);
+
+ switch (nodeTag(expr))
+ {
+#include "queryjumblefuncs.switch.c"
+
+ case T_List:
+ case T_IntList:
+ case T_OidList:
+ case T_XidList:
+ _jumbleList(jstate, expr);
+ break;
+
+ 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;
+
+ /*
+ * 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;
+ default:
+ break;
+ }
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+ List *expr = (List *) node;
+ ListCell *l;
+
+ switch (expr->type)
+ {
+ case T_List:
+ foreach(l, expr)
+ _jumbleNode(jstate, lfirst(l));
+ break;
+ case T_IntList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_int(l));
+ break;
+ case T_OidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+ break;
+ case T_XidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+ break;
+ default:
+ elog(ERROR, "unrecognized list node type: %d",
+ (int) expr->type);
+ return;
+ }
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+ RangeTblEntry *expr = (RangeTblEntry *) node;
+
+ JUMBLE_FIELD(rtekind);
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(tablesample);
+ JUMBLE_FIELD(inh);
+ break;
+ case RTE_SUBQUERY:
+ JUMBLE_NODE(subquery);
+ break;
+ case RTE_JOIN:
+ JUMBLE_FIELD(jointype);
+ break;
+ case RTE_FUNCTION:
+ JUMBLE_NODE(functions);
+ break;
+ case RTE_TABLEFUNC:
+ JUMBLE_NODE(tablefunc);
+ break;
+ case RTE_VALUES:
+ JUMBLE_NODE(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.
+ */
+ JUMBLE_STRING(ctename);
+ JUMBLE_FIELD(ctelevelsup);
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ JUMBLE_STRING(enrname);
+ break;
+ case RTE_RESULT:
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+ break;
+ }
+}
--
2.39.0
v6-0004-Add-GUC-utility_query_id.patchtext/x-diff; charset=us-asciiDownload
From 6fe42d23f1ba3d648184852b32caf7db71020b71 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 24 Jan 2023 15:52:37 +0900
Subject: [PATCH v6 4/4] Add GUC utility_query_id
This GUC has two modes to control the computation method of query IDs
for utilities:
- 'string', the default, to hash the string query.
- 'jumble', to use the parsed tree.
---
src/include/nodes/queryjumble.h | 7 ++
src/backend/nodes/queryjumblefuncs.c | 81 ++++++++++++++-----
src/backend/utils/misc/guc_tables.c | 16 ++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
doc/src/sgml/config.sgml | 31 +++++++
.../expected/pg_stat_statements.out | 31 +++++++
.../sql/pg_stat_statements.sql | 17 ++++
7 files changed, 164 insertions(+), 20 deletions(-)
diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h
index 204b8f74fd..261aea6bcf 100644
--- a/src/include/nodes/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -59,8 +59,15 @@ enum ComputeQueryIdType
COMPUTE_QUERY_ID_REGRESS
};
+enum UtilityQueryIdType
+{
+ UTILITY_QUERY_ID_STRING,
+ UTILITY_QUERY_ID_JUMBLE
+};
+
/* GUC parameters */
extern PGDLLIMPORT int compute_query_id;
+extern PGDLLIMPORT int utility_query_id;
extern const char *CleanQuerytext(const char *query, int *location, int *len);
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16fdf7164a..b8738fba08 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -41,12 +41,15 @@
/* GUC parameters */
int compute_query_id = COMPUTE_QUERY_ID_AUTO;
+int utility_query_id = UTILITY_QUERY_ID_STRING;
/* True when compute_query_id is ON, or AUTO and a module requests them */
bool query_id_enabled = false;
static void AppendJumble(JumbleState *jstate,
const unsigned char *item, Size size);
+static uint64 compute_utility_query_id(const char *query_text,
+ int query_location, int query_len);
static void RecordConstLocation(JumbleState *jstate, int location);
static void _jumbleNode(JumbleState *jstate, Node *node);
static void _jumbleList(JumbleState *jstate, Node *node);
@@ -102,29 +105,39 @@ JumbleQuery(Query *query, const char *querytext)
Assert(IsQueryIdEnabled());
- jstate = (JumbleState *) palloc(sizeof(JumbleState));
+ if (query->utilityStmt &&
+ compute_query_id == UTILITY_QUERY_ID_STRING)
+ {
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
+ }
return jstate;
}
@@ -142,6 +155,34 @@ 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.
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 4ac808ed22..97619c4e1d 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -294,6 +294,12 @@ static const struct config_enum_entry compute_query_id_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry utility_query_id_options[] = {
+ {"string", UTILITY_QUERY_ID_STRING, false},
+ {"jumble", UTILITY_QUERY_ID_JUMBLE, false},
+ {NULL, 0, false}
+};
+
/*
* Although only "on", "off", and "partition" are documented, we
* accept all the likely variants of "on" and "off".
@@ -4574,6 +4580,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"utility_query_id", PGC_SUSET, STATS_MONITORING,
+ gettext_noop("Controls method computing query ID for utilities."),
+ NULL
+ },
+ &utility_query_id,
+ UTILITY_QUERY_ID_STRING, utility_query_id_options,
+ NULL, NULL, NULL
+ },
+
{
{"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f..bbf95af59d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -623,6 +623,7 @@
# - Monitoring -
#compute_query_id = auto
+#utility_query_id = string # string, jumble
#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f985afc009..4ccd148471 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8241,6 +8241,37 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-utility-query-id" xreflabel="utility_query_id">
+ <term><varname>utility_query_id</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>utility_query_id</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Controls the method used to compute the query identifier of a utility
+ query. Valid values are <literal>string</literal> to use a hash of the
+ query string and <literal>jumble</literal> to compute the query
+ identifier depending on the parsed tree of the utility query.
+ The default is <literal>string</literal>.
+ </para>
+ <para>
+ <literal>jumble</literal> is more costly than <literal>string</literal>
+ as the computation of the query identifier walks through the
+ post-parse-analysis representation of the queries for utility queries.
+ However, <literal>jumble</literal> is able to apply normalization
+ to the queries computed, meaning that queries written differently
+ but having the same query representation may be able to use the same
+ identifier.
+ For example, <literal>BEGIN;</literal> and <literal>begin;</literal>
+ will have the same query identifier under <literal>jumble</literal> as
+ both queries have the same query representation. The query identifier
+ would be different under <literal>string</literal>, because the query
+ strings are different.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-log-statement-stats">
<term><varname>log_statement_stats</varname> (<type>boolean</type>)
<indexterm>
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 9ac5c87c3a..8bdf8beec3 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -554,6 +554,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
pg_stat_statements_reset
--------------------------
@@ -592,6 +593,36 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+ query | calls | rows
+------------------------------------------------------------------------------+-------+------
+ Begin | 2 | 0
+ Commit | 2 | 0
+ Create Table test_utility_query (a int) | 2 | 0
+ Drop Table test_utility_query | 2 | 0
+ SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
+ SET utility_query_id = 'jumble' | 1 | 0
+(7 rows)
+
+RESET utility_query_id;
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 8f5c866225..81d663f81c 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -258,6 +258,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
SELECT 1;
@@ -272,6 +273,22 @@ DROP FUNCTION PLUS_TWO(INTEGER);
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+SELECT pg_stat_statements_reset();
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+RESET utility_query_id;
+
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
--
2.39.0
On Tue, Jan 24, 2023 at 03:57:56PM +0900, Michael Paquier wrote:
Makes sense. That would be my intention if 0004 is the most
acceptable and splitting things makes things a bit easier to review.
There was a silly mistake in 0004 where the jumbling code relied on
compute_query_id rather than utility_query_id, so fixed and rebased as
of v7 attached.
--
Michael
Attachments:
v7-0003-Support-for-automated-query-jumble-with-all-Nodes.patchtext/x-diff; charset=us-asciiDownload
From e1a35c02927ed38b04b1f628c460ef05f706621f Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Tue, 24 Jan 2023 15:29:06 +0900
Subject: [PATCH v7 3/4] Support for automated query jumble with all Nodes
This applies query jumbling in a consistent way to all the Nodes,
including DDLs & friends.
---
src/include/nodes/bitmapset.h | 2 +-
src/include/nodes/nodes.h | 13 +-
src/include/nodes/parsenodes.h | 126 ++--
src/include/nodes/primnodes.h | 268 ++++----
src/backend/nodes/README | 1 +
src/backend/nodes/gen_node_support.pl | 114 +++-
src/backend/nodes/meson.build | 2 +-
src/backend/nodes/queryjumblefuncs.c | 852 ++++++--------------------
8 files changed, 519 insertions(+), 859 deletions(-)
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 0dca6bc5fa..3d2225e1ae 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 10752e8011..d7a9e38436 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -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,11 @@ 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.
+ *
+ * - 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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 89335d95e7..f99fb5e909 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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;
@@ -1250,6 +1257,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 needs only to track the function expression.
*/
typedef struct RangeTblFunction
{
@@ -1257,20 +1266,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;
/*
@@ -1378,7 +1387,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;
/*
@@ -1443,7 +1452,7 @@ typedef enum GroupingSetKind
typedef struct GroupingSet
{
NodeTag type;
- GroupingSetKind kind;
+ GroupingSetKind kind pg_node_attr(query_jumble_ignore);
List *content;
int location;
} GroupingSet;
@@ -1464,35 +1473,38 @@ 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;
+ List *orderClause pg_node_attr(query_jumble_ignore);
int frameOptions; /* frame_clause options, see WindowDef */
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;
/*
@@ -1607,26 +1619,26 @@ typedef struct CommonTableExpr
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 */
@@ -1664,11 +1676,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;
/*
@@ -1877,15 +1889,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;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index dec7d5c775..8d5b68a0bc 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -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;
@@ -227,11 +227,11 @@ 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;
+ Oid varcollid pg_node_attr(query_jumble_ignore);
/*
* for subquery variables referencing outer relations; 0 in a normal var,
@@ -245,9 +245,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;
@@ -260,6 +260,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
{
@@ -269,24 +271,27 @@ 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;
/*
@@ -324,6 +329,7 @@ typedef enum ParamKind
PARAM_MULTIEXPR
} ParamKind;
+/* typmod and collation information are irrelevant for the query jumbling. */
typedef struct Param
{
Expr xpr;
@@ -331,9 +337,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;
@@ -386,6 +392,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
{
@@ -395,22 +404,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;
@@ -428,31 +437,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;
@@ -481,19 +490,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;
@@ -504,6 +516,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
{
@@ -511,11 +526,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 */
@@ -523,9 +538,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;
@@ -564,6 +579,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.
@@ -572,15 +589,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;
@@ -631,6 +648,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
{
@@ -638,21 +658,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 */
@@ -679,7 +699,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 */
@@ -695,6 +715,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
{
@@ -704,19 +727,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;
@@ -772,6 +795,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
{
@@ -781,19 +807,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;
@@ -895,7 +921,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 */
@@ -1007,11 +1033,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;
/* ----------------
@@ -1038,9 +1064,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;
@@ -1063,11 +1089,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;
@@ -1087,9 +1113,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;
@@ -1113,11 +1139,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;
@@ -1141,7 +1167,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;
@@ -1186,9 +1212,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) */
@@ -1231,9 +1257,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;
/*
@@ -1248,15 +1274,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;
@@ -1288,7 +1314,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
@@ -1304,10 +1330,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;
@@ -1344,11 +1370,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 */
@@ -1362,9 +1388,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 */
@@ -1384,11 +1410,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 */
@@ -1432,18 +1458,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;
@@ -1478,7 +1504,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;
@@ -1512,6 +1538,8 @@ typedef struct BooleanTest
* checked will be determined. If the value passes, it is returned as the
* result; if not, an error is raised. Note that this is equivalent to
* RelabelType in the scenario where no constraints are applied.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct CoerceToDomain
{
@@ -1519,11 +1547,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;
@@ -1542,9 +1570,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;
@@ -1555,6 +1583,8 @@ typedef struct CoerceToDomainValue
* This is not an executable expression: it must be replaced by the actual
* column default expression during rewriting. But it is convenient to
* treat it as an expression node during parsing and rewriting.
+ *
+ * typemod and collation are irrelevant for the query jumbling.
*/
typedef struct SetToDefault
{
@@ -1562,9 +1592,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;
@@ -1682,15 +1712,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;
@@ -1773,13 +1803,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;
diff --git a/src/backend/nodes/README b/src/backend/nodes/README
index 489a67eb89..7cf6e3b041 100644
--- a/src/backend/nodes/README
+++ b/src/backend/nodes/README
@@ -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.
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b3c1ead496..471333dc80 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -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)
{
diff --git a/src/backend/nodes/meson.build b/src/backend/nodes/meson.build
index 9230515e7f..31467a12d3 100644
--- a/src/backend/nodes/meson.build
+++ b/src/backend/nodes/meson.build
@@ -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',
)
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16084842a3..16fdf7164a 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -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,38 +102,29 @@ 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));
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
- }
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
return jstate;
}
@@ -154,34 +142,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.
@@ -219,621 +179,6 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
jstate->jumble_len = jumble_len;
}
-/*
- * 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
- */
-static void
-JumbleRangeTable(JumbleState *jstate, List *rtable)
-{
- ListCell *lc;
-
- foreach(lc, rtable)
- {
- RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc);
-
- APP_JUMB(rte->rtekind);
- switch (rte->rtekind)
- {
- 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;
- }
- }
-}
-
-/*
- * Jumble a rowMarks list
- */
-static void
-JumbleRowMarks(JumbleState *jstate, List *rowMarks)
-{
- ListCell *lc;
-
- foreach(lc, rowMarks)
- {
- RowMarkClause *rowmark = lfirst_node(RowMarkClause, lc);
-
- if (!rowmark->pushedDown)
- {
- APP_JUMB(rowmark->rti);
- APP_JUMB(rowmark->strength);
- APP_JUMB(rowmark->waitPolicy);
- }
- }
-}
-
-/*
- * 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)
-{
- ListCell *temp;
-
- if (node == NULL)
- return;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * 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);
-
- switch (nodeTag(node))
- {
- case T_Var:
- {
- Var *var = (Var *) node;
-
- APP_JUMB(var->varno);
- APP_JUMB(var->varattno);
- APP_JUMB(var->varlevelsup);
- }
- 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);
- }
- break;
- 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 */
- 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);
- }
- break;
- case T_NextValueExpr:
- {
- NextValueExpr *nve = (NextValueExpr *) node;
-
- APP_JUMB(nve->seqid);
- APP_JUMB(nve->typeId);
- }
- break;
- case T_InferenceElem:
- {
- InferenceElem *ie = (InferenceElem *) node;
-
- APP_JUMB(ie->infercollid);
- APP_JUMB(ie->inferopclass);
- JumbleExpr(jstate, ie->expr);
- }
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- APP_JUMB(tle->resno);
- APP_JUMB(tle->ressortgroupref);
- JumbleExpr(jstate, (Node *) tle->expr);
- }
- break;
- case T_RangeTblRef:
- {
- RangeTblRef *rtr = (RangeTblRef *) node;
-
- APP_JUMB(rtr->rtindex);
- }
- 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);
- }
- break;
- case T_FromExpr:
- {
- FromExpr *from = (FromExpr *) node;
-
- 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;
-
- APP_JUMB(mergeaction->matched);
- APP_JUMB(mergeaction->commandType);
- JumbleExpr(jstate, mergeaction->qual);
- JumbleExpr(jstate, (Node *) mergeaction->targetList);
- }
- break;
- case T_List:
- foreach(temp, (List *) node)
- {
- JumbleExpr(jstate, (Node *) lfirst(temp));
- }
- break;
- case T_IntList:
- foreach(temp, (List *) node)
- {
- APP_JUMB(lfirst_int(temp));
- }
- 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);
- }
- break;
- case T_GroupingSet:
- {
- GroupingSet *gsnode = (GroupingSet *) node;
-
- JumbleExpr(jstate, (Node *) gsnode->content);
- }
- 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);
- }
- break;
- case T_CommonTableExpr:
- {
- CommonTableExpr *cte = (CommonTableExpr *) node;
-
- /* 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));
- }
- break;
- case T_SetOperationStmt:
- {
- SetOperationStmt *setop = (SetOperationStmt *) node;
-
- APP_JUMB(setop->op);
- APP_JUMB(setop->all);
- JumbleExpr(jstate, setop->larg);
- JumbleExpr(jstate, setop->rarg);
- }
- 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);
- }
- break;
- default:
- /* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(node));
- break;
- }
-}
-
/*
* Record location of constant within query string of query tree
* that is currently being walked.
@@ -859,3 +204,154 @@ RecordConstLocation(JumbleState *jstate, int location)
jstate->clocations_count++;
}
}
+
+#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)
+
+#include "queryjumblefuncs.funcs.c"
+
+static void
+_jumbleNode(JumbleState *jstate, Node *node)
+{
+ Node *expr = node;
+
+ if (expr == NULL)
+ return;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /*
+ * We always emit the node's NodeTag, then any additional fields that are
+ * considered significant, and then we recurse to any child nodes.
+ */
+ JUMBLE_FIELD(type);
+
+ switch (nodeTag(expr))
+ {
+#include "queryjumblefuncs.switch.c"
+
+ case T_List:
+ case T_IntList:
+ case T_OidList:
+ case T_XidList:
+ _jumbleList(jstate, expr);
+ break;
+
+ 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;
+
+ /*
+ * 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;
+ default:
+ break;
+ }
+}
+
+static void
+_jumbleList(JumbleState *jstate, Node *node)
+{
+ List *expr = (List *) node;
+ ListCell *l;
+
+ switch (expr->type)
+ {
+ case T_List:
+ foreach(l, expr)
+ _jumbleNode(jstate, lfirst(l));
+ break;
+ case T_IntList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_int(l));
+ break;
+ case T_OidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_oid(l));
+ break;
+ case T_XidList:
+ foreach(l, expr)
+ JUMBLE_FIELD_SINGLE(lfirst_xid(l));
+ break;
+ default:
+ elog(ERROR, "unrecognized list node type: %d",
+ (int) expr->type);
+ return;
+ }
+}
+
+static void
+_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
+{
+ RangeTblEntry *expr = (RangeTblEntry *) node;
+
+ JUMBLE_FIELD(rtekind);
+ switch (expr->rtekind)
+ {
+ case RTE_RELATION:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(tablesample);
+ JUMBLE_FIELD(inh);
+ break;
+ case RTE_SUBQUERY:
+ JUMBLE_NODE(subquery);
+ break;
+ case RTE_JOIN:
+ JUMBLE_FIELD(jointype);
+ break;
+ case RTE_FUNCTION:
+ JUMBLE_NODE(functions);
+ break;
+ case RTE_TABLEFUNC:
+ JUMBLE_NODE(tablefunc);
+ break;
+ case RTE_VALUES:
+ JUMBLE_NODE(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.
+ */
+ JUMBLE_STRING(ctename);
+ JUMBLE_FIELD(ctelevelsup);
+ break;
+ case RTE_NAMEDTUPLESTORE:
+ JUMBLE_STRING(enrname);
+ break;
+ case RTE_RESULT:
+ break;
+ default:
+ elog(ERROR, "unrecognized RTE kind: %d", (int) expr->rtekind);
+ break;
+ }
+}
--
2.39.0
v7-0004-Add-GUC-utility_query_id.patchtext/x-diff; charset=us-asciiDownload
From a55d0adb5bbd6bd09e0b27d3b1a2b0ed956eb7b0 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 25 Jan 2023 08:47:40 +0900
Subject: [PATCH v7 4/4] Add GUC utility_query_id
This GUC has two modes to control the computation method of query IDs
for utilities:
- 'string', the default, to hash the string query.
- 'jumble', to use the parsed tree.
---
src/include/nodes/queryjumble.h | 7 ++
src/backend/nodes/queryjumblefuncs.c | 81 ++++++++++++++-----
src/backend/utils/misc/guc_tables.c | 16 ++++
src/backend/utils/misc/postgresql.conf.sample | 1 +
doc/src/sgml/config.sgml | 31 +++++++
.../expected/pg_stat_statements.out | 31 +++++++
.../sql/pg_stat_statements.sql | 17 ++++
7 files changed, 164 insertions(+), 20 deletions(-)
diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h
index 204b8f74fd..261aea6bcf 100644
--- a/src/include/nodes/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -59,8 +59,15 @@ enum ComputeQueryIdType
COMPUTE_QUERY_ID_REGRESS
};
+enum UtilityQueryIdType
+{
+ UTILITY_QUERY_ID_STRING,
+ UTILITY_QUERY_ID_JUMBLE
+};
+
/* GUC parameters */
extern PGDLLIMPORT int compute_query_id;
+extern PGDLLIMPORT int utility_query_id;
extern const char *CleanQuerytext(const char *query, int *location, int *len);
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16fdf7164a..d55adf5020 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -41,12 +41,15 @@
/* GUC parameters */
int compute_query_id = COMPUTE_QUERY_ID_AUTO;
+int utility_query_id = UTILITY_QUERY_ID_STRING;
/* True when compute_query_id is ON, or AUTO and a module requests them */
bool query_id_enabled = false;
static void AppendJumble(JumbleState *jstate,
const unsigned char *item, Size size);
+static uint64 compute_utility_query_id(const char *query_text,
+ int query_location, int query_len);
static void RecordConstLocation(JumbleState *jstate, int location);
static void _jumbleNode(JumbleState *jstate, Node *node);
static void _jumbleList(JumbleState *jstate, Node *node);
@@ -102,29 +105,39 @@ JumbleQuery(Query *query, const char *querytext)
Assert(IsQueryIdEnabled());
- jstate = (JumbleState *) palloc(sizeof(JumbleState));
+ if (query->utilityStmt &&
+ utility_query_id == UTILITY_QUERY_ID_STRING)
+ {
+ 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;
+ /* 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));
+ /* 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, to
- * prevent confusion with the utility-statement case.
- */
- if (query->queryId == UINT64CONST(0))
- query->queryId = UINT64CONST(1);
+ /*
+ * 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))
+ query->queryId = UINT64CONST(1);
+ }
return jstate;
}
@@ -142,6 +155,34 @@ 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.
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 4ac808ed22..97619c4e1d 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -294,6 +294,12 @@ static const struct config_enum_entry compute_query_id_options[] = {
{NULL, 0, false}
};
+static const struct config_enum_entry utility_query_id_options[] = {
+ {"string", UTILITY_QUERY_ID_STRING, false},
+ {"jumble", UTILITY_QUERY_ID_JUMBLE, false},
+ {NULL, 0, false}
+};
+
/*
* Although only "on", "off", and "partition" are documented, we
* accept all the likely variants of "on" and "off".
@@ -4574,6 +4580,16 @@ struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
+ {
+ {"utility_query_id", PGC_SUSET, STATS_MONITORING,
+ gettext_noop("Controls method computing query ID for utilities."),
+ NULL
+ },
+ &utility_query_id,
+ UTILITY_QUERY_ID_STRING, utility_query_id_options,
+ NULL, NULL, NULL
+ },
+
{
{"constraint_exclusion", PGC_USERSET, QUERY_TUNING_OTHER,
gettext_noop("Enables the planner to use constraints to optimize queries."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f..bbf95af59d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -623,6 +623,7 @@
# - Monitoring -
#compute_query_id = auto
+#utility_query_id = string # string, jumble
#log_statement_stats = off
#log_parser_stats = off
#log_planner_stats = off
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f985afc009..4ccd148471 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8241,6 +8241,37 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
</listitem>
</varlistentry>
+ <varlistentry id="guc-utility-query-id" xreflabel="utility_query_id">
+ <term><varname>utility_query_id</varname> (<type>enum</type>)
+ <indexterm>
+ <primary><varname>utility_query_id</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Controls the method used to compute the query identifier of a utility
+ query. Valid values are <literal>string</literal> to use a hash of the
+ query string and <literal>jumble</literal> to compute the query
+ identifier depending on the parsed tree of the utility query.
+ The default is <literal>string</literal>.
+ </para>
+ <para>
+ <literal>jumble</literal> is more costly than <literal>string</literal>
+ as the computation of the query identifier walks through the
+ post-parse-analysis representation of the queries for utility queries.
+ However, <literal>jumble</literal> is able to apply normalization
+ to the queries computed, meaning that queries written differently
+ but having the same query representation may be able to use the same
+ identifier.
+ For example, <literal>BEGIN;</literal> and <literal>begin;</literal>
+ will have the same query identifier under <literal>jumble</literal> as
+ both queries have the same query representation. The query identifier
+ would be different under <literal>string</literal>, because the query
+ strings are different.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-log-statement-stats">
<term><varname>log_statement_stats</varname> (<type>boolean</type>)
<indexterm>
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 9ac5c87c3a..8bdf8beec3 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -554,6 +554,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
pg_stat_statements_reset
--------------------------
@@ -592,6 +593,36 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+ query | calls | rows
+------------------------------------------------------------------------------+-------+------
+ Begin | 2 | 0
+ Commit | 2 | 0
+ Create Table test_utility_query (a int) | 2 | 0
+ Drop Table test_utility_query | 2 | 0
+ SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
+ SET utility_query_id = 'jumble' | 1 | 0
+(7 rows)
+
+RESET utility_query_id;
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 8f5c866225..81d663f81c 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -258,6 +258,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
+SET utility_query_id = 'string';
SELECT pg_stat_statements_reset();
SELECT 1;
@@ -272,6 +273,22 @@ DROP FUNCTION PLUS_TWO(INTEGER);
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+SELECT pg_stat_statements_reset();
+SET utility_query_id = 'jumble';
+-- These queries have a different string, but the same parsing
+-- representation.
+Begin;
+Create Table test_utility_query (a int);
+Drop Table test_utility_query;
+Commit;
+BEGIN;
+CREATE TABLE test_utility_query (a int);
+DROP TABLE test_utility_query;
+COMMIT;
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
+RESET utility_query_id;
+
--
-- Track the total number of rows retrieved or affected by the utility
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
--
2.39.0
On 24.01.23 07:57, Michael Paquier wrote:
For the 0004 patch, it should be documented why one would want one behavior
or the other. That's totally unclear right now.I am not 100% sure whether we should have this part at the end, but as
an exit path in case somebody complains about the extra load with the
automated jumbling compared to the hash of the query strings, that can
be used as a backup. Anyway, attached is what would be a
clarification.
Ok, the documentation make sense now. I wonder what the performance
impact is. Probably, nobody cares about microoptimizing CREATE TABLE
statements. But BEGIN/COMMIT could matter. However, whatever you do in
between the BEGIN and COMMIT will already be jumbled, so you're already
paying the overhead. Hopefully, jumbling such simple commands will have
no noticeable overhead.
In other words, we should test this and hopefully get rid of the
'string' method.
On 25.01.23 01:08, Michael Paquier wrote:
On Tue, Jan 24, 2023 at 03:57:56PM +0900, Michael Paquier wrote:
Makes sense. That would be my intention if 0004 is the most
acceptable and splitting things makes things a bit easier to review.There was a silly mistake in 0004 where the jumbling code relied on
compute_query_id rather than utility_query_id, so fixed and rebased as
of v7 attached.
Overall, this looks good to me.
There are a couple of repetitive comments, like "typmod and collation
information are irrelevant for the query jumbling". This applies to all
nodes, so we don't need to repeat it for a number of nodes (and then not
mention it for other nodes). Maybe there should be a central place
somewhere that describes "these kinds of fields should normally be ignored".
On Thu, Jan 26, 2023 at 09:37:13AM +0100, Peter Eisentraut wrote:
Ok, the documentation make sense now. I wonder what the performance impact
is. Probably, nobody cares about microoptimizing CREATE TABLE statements.
But BEGIN/COMMIT could matter. However, whatever you do in between the
BEGIN and COMMIT will already be jumbled, so you're already paying the
overhead. Hopefully, jumbling such simple commands will have no noticeable
overhead.In other words, we should test this and hopefully get rid of the 'string'
method.
Yep. I have mentioned a few numbers upthread, and this deserves
discussion.
FYI, I have done more micro-benchmarking to compare both methods for
utility queries by hijacking JumbleQuery() to run the computation in a
tight loop run N times (could not come up with a better idea to avoid
the repeated palloc/pfree overhead), as the path to stress is
_jumbleNode(). See the attached, that should be able to apply on top
of the latest patch set (named as .txt to not feed it to the CF bot,
and need to recompile to switch the iteration).
Using that, I can compile the following results for various cases (-O2
and compute_query_id=on):
query | mode | iterations | avg_runtime_ns | avg_jumble_ns
-------------------------+--------+------------+----------------+---------------
begin | string | 50000000 | 4.53116 | 4.54
begin | jumble | 50000000 | 30.94578 | 30.94
commit | string | 50000000 | 4.76004 | 4.74
commit | jumble | 50000000 | 31.4791 | 31.48
create table 1 column | string | 50000000 | 7.22836 | 7.08
create table 1 column | jumble | 50000000 | 152.10852 | 151.96
create table 5 columns | string | 50000000 | 12.43412 | 12.28
create table 5 columns | jumble | 50000000 | 352.88976 | 349.1
create table 20 columns | string | 5000000 | 49.591 | 48.2
create table 20 columns | jumble | 5000000 | 2272.4066 | 2271
drop table 1 column | string | 50000000 | 6.70538 | 6.56
drop table 1 column | jumble | 50000000 | 50.38 | 50.24
drop table 5 columns | string | 50000000 | 6.88256 | 6.74
drop table 5 columns | jumble | 50000000 | 50.02898 | 49.9
SET work_mem | string | 50000000 | 7.28752 | 7.28
SET work_mem | jumble | 50000000 | 91.66588 | 91.64
(16 rows)
avg_runtime_ns is (query runtime / iterations) and avg_jumble_ns is
the same with the difference between the start/end logs in the txt
patch attached. The overhead to run the query does not matter much if
you compare both. The time it takes to run a jumble is correlated to
the number of nodes to go through for each query, and there is a
larger gap for more nodes to go through. Well, a simple "begin" or
"commit" query has its computation time increase from 4ns to 30ns in
average which would be unnoticeable. The gap is larger for larger
nodes, like SET, still we jump from 7ns to 90ns in this case. DDLs
take the most hit with this method, where a 20-column CREATE TABLE
jumps from 50ns to 2us (note that the iteration is 10 times lower
here).
At the end, that would be unnoticeable for the average user, I guess,
but here are the numbers I get on my laptop :)
--
Michael
Attachments:
v8-Add-benchmark-tweaks-to-stress-jumbling-code.txttext/plain; charset=us-asciiDownload
From ee47c4e1137adc857184d76ab62232e9c3c95451 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 27 Jan 2023 10:58:34 +0900
Subject: [PATCH 1/1] Add benchmark tweaks to stress jumbling code
---
src/backend/nodes/queryjumblefuncs.c | 36 +++++++++++++++++++++-------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index d55adf5020..9c134cc5ae 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -101,32 +101,48 @@ CleanQuerytext(const char *query, int *location, int *len)
JumbleState *
JumbleQuery(Query *query, const char *querytext)
{
+#define MAX_QUERY_COMPUTE 5000000
+
JumbleState *jstate = NULL;
Assert(IsQueryIdEnabled());
+ elog(WARNING, "start JumbleQuery");
+
if (query->utilityStmt &&
utility_query_id == UTILITY_QUERY_ID_STRING)
{
- query->queryId = compute_utility_query_id(querytext,
- query->stmt_location,
- query->stmt_len);
+ for (int i = 0; i < MAX_QUERY_COMPUTE ; i++)
+ {
+ 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);
+ for (int i = 0; i < MAX_QUERY_COMPUTE ; i++)
+ {
+ memset(jstate->jumble, 0, sizeof(JUMBLE_SIZE));
+ /* Set up workspace for query jumbling */
+ jstate->jumble_len = 0;
+ jstate->clocations_buf_size = 32;
+ memset(jstate->clocations, 0,
+ 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));
@@ -139,6 +155,8 @@ JumbleQuery(Query *query, const char *querytext)
query->queryId = UINT64CONST(1);
}
+ elog(WARNING, "end JumbleQuery");
+
return jstate;
}
--
2.39.0
On Thu, Jan 26, 2023 at 09:39:05AM +0100, Peter Eisentraut wrote:
There are a couple of repetitive comments, like "typmod and collation
information are irrelevant for the query jumbling". This applies to all
nodes, so we don't need to repeat it for a number of nodes (and then not
mention it for other nodes). Maybe there should be a central place
somewhere that describes "these kinds of fields should normally be ignored".
The place that would make the most sense to me to centralize this
knowlegde is nodes.h itself, because that's where the node attributes
are defined?
--
Michael
On Tue, Jan 24, 2023 at 03:57:56PM +0900, Michael Paquier wrote:
Still, my plan here is to enforce the loading of
pg_stat_statements with compute_query_id = regress and
utility_query_id = jumble (if needed) in a new buildfarm machine,
Actually, about this specific point, I have been able to set up a
buildfarm machine that uses shared_preload_libraries =
pg_stat_statements and compute_query_id = regress in the base
configuration, which is uncovered yet. This works as long as one sets
up EXTRA_INSTALL => "contrib/pg_stat_statements" in build_env.
--
Michael
On Fri, Jan 27, 2023 at 11:59:47AM +0900, Michael Paquier wrote:
Using that, I can compile the following results for various cases (-O2
and compute_query_id=on):
query | mode | iterations | avg_runtime_ns | avg_jumble_ns
-------------------------+--------+------------+----------------+---------------
begin | string | 50000000 | 4.53116 | 4.54
begin | jumble | 50000000 | 30.94578 | 30.94
commit | string | 50000000 | 4.76004 | 4.74
commit | jumble | 50000000 | 31.4791 | 31.48
create table 1 column | string | 50000000 | 7.22836 | 7.08
create table 1 column | jumble | 50000000 | 152.10852 | 151.96
create table 5 columns | string | 50000000 | 12.43412 | 12.28
create table 5 columns | jumble | 50000000 | 352.88976 | 349.1
create table 20 columns | string | 5000000 | 49.591 | 48.2
create table 20 columns | jumble | 5000000 | 2272.4066 | 2271
drop table 1 column | string | 50000000 | 6.70538 | 6.56
drop table 1 column | jumble | 50000000 | 50.38 | 50.24
drop table 5 columns | string | 50000000 | 6.88256 | 6.74
drop table 5 columns | jumble | 50000000 | 50.02898 | 49.9
SET work_mem | string | 50000000 | 7.28752 | 7.28
SET work_mem | jumble | 50000000 | 91.66588 | 91.64
(16 rows)
Just to close the loop here, I have done more measurements to compare
the jumble done for some DMLs and some SELECTs between HEAD and the
patch (forgot to post some last Friday). Both methods show comparable
results:
query | mode | iterations | avg_runtime_ns | avg_jumble_ns
----------------------+--------+------------+----------------+---------------
insert table 10 cols | master | 50000000 | 377.17878 | 377.04
insert table 10 cols | jumble | 50000000 | 409.47924 | 409.34
insert table 20 cols | master | 50000000 | 692.94924 | 692.8
insert table 20 cols | jumble | 50000000 | 710.0901 | 709.96
insert table 5 cols | master | 50000000 | 232.44308 | 232.3
insert table 5 cols | jumble | 50000000 | 253.49854 | 253.36
select 10 cols | master | 50000000 | 449.13608 | 383.36
select 10 cols | jumble | 50000000 | 491.61912 | 323.86
select 5 cols | master | 50000000 | 277.477 | 277.46
select 5 cols | jumble | 50000000 | 323.88152 | 323.86
(10 rows)
The averages are in ns, so the difference does not bother me much.
There may be some noise mixed in that ;)
(Attached is the tweak I have applied on HEAD to get some numbers.)
--
Michael
Attachments:
jumble-benchmark.txttext/plain; charset=us-asciiDownload
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16084842a3..c66090bde3 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -101,10 +101,14 @@ CleanQuerytext(const char *query, int *location, int *len)
JumbleState *
JumbleQuery(Query *query, const char *querytext)
{
+#define MAX_QUERY_COMPUTE 50000000
+
JumbleState *jstate = NULL;
Assert(IsQueryIdEnabled());
+ elog(WARNING, "start JumbleQuery");
+
if (query->utilityStmt)
{
query->queryId = compute_utility_query_id(querytext,
@@ -114,18 +118,26 @@ JumbleQuery(Query *query, const char *querytext)
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 */
- JumbleQueryInternal(jstate, query);
+ for (int i = 0; i < MAX_QUERY_COMPUTE ; i++)
+ {
+ /* Set up workspace for query jumbling */
+ memset(jstate->jumble, 0, sizeof(JUMBLE_SIZE));
+ jstate->jumble_len = 0;
+ jstate->clocations_buf_size = 32;
+ memset(jstate->clocations, 0,
+ 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));
@@ -138,6 +150,8 @@ JumbleQuery(Query *query, const char *querytext)
query->queryId = UINT64CONST(1);
}
+ elog(WARNING, "end JumbleQuery");
+
return jstate;
}
On 27.01.23 04:07, Michael Paquier wrote:
On Thu, Jan 26, 2023 at 09:39:05AM +0100, Peter Eisentraut wrote:
There are a couple of repetitive comments, like "typmod and collation
information are irrelevant for the query jumbling". This applies to all
nodes, so we don't need to repeat it for a number of nodes (and then not
mention it for other nodes). Maybe there should be a central place
somewhere that describes "these kinds of fields should normally be ignored".The place that would make the most sense to me to centralize this
knowlegde is nodes.h itself, because that's where the node attributes
are defined?
Either that or src/backend/nodes/README.
On 27.01.23 03:59, Michael Paquier wrote:
At the end, that would be unnoticeable for the average user, I guess,
but here are the numbers I get on my laptop :)
Personally, I think we do not want the two jumble methods in parallel.
Maybe there are other opinions.
I'm going to set this thread as "Ready for Committer". Either wait a
bit for more feedback on this topic, or just go ahead with either
solution. We can leave it as a semi-open item for reconsideration later.
On Mon, Jan 30, 2023 at 11:46:06AM +0100, Peter Eisentraut wrote:
Either that or src/backend/nodes/README.
The README holds nothing about the node attributes currently, so
nodes.h feels most adapted here at the end..
Thanks,
--
Michael
On Mon, Jan 30, 2023 at 11:48:45AM +0100, Peter Eisentraut wrote:
I'm going to set this thread as "Ready for Committer". Either wait a bit
for more feedback on this topic, or just go ahead with either solution. We
can leave it as a semi-open item for reconsideration later.
All the measurements I have done for the last couple of days point me
in the direction that there is no need for an extra node based on the
averaged computation times (did a few more today with some long CREATE
FUNCTION, VIEW or event trigger queries, for example). Agreed to add
the extra option as something to consider at some point during beta,
as long as it is fresh. I am not convinced that it will be necessary
but let's see how it goes.
While reviewing all the nodes, I have noticed two mistakes for a few
things marked as query_jumble_ignore but they should not:
- orderClause in WindowClause
- aliascolnames in CommonTableExpr
The rest was fine.
varnullingrels has been added very recently, and there was in
queryjumblefuncs.c a comment explaining why it should be ignored.
This has been moved to nodes.h, like the others.
With all that in mind, I have spent my day polishing that and doing a
close lookup, and the patch has been applied. Thanks a lot!
--
Michael
On Tue, Jan 31, 2023 at 03:40:56PM +0900, Michael Paquier wrote:
With all that in mind, I have spent my day polishing that and doing a
close lookup, and the patch has been applied. Thanks a lot!
While working on a different patch, I have noticed a small issue in
the way the jumbling happens for A_Const, where ValUnion was not
getting jumbled correctly. This caused a few statements that rely on
this node to compile the same query IDs when using different values.
The full contents of pg_stat_statements for a regression database
point to:
- SET.
- COPY with queries.
- CREATE TABLE with partition bounds and default expressions.
This was causing some confusion in pg_stat_statements where some
utility queries would be incorrectly reported, and at this point the
intention is to keep this area compatible with the string-based method
when it comes to the values. Like read, write and copy nodes, we need
to compile the query ID based on the type of the value, which cannot
be automated. Attached is a patch to fix this issue with some
regression tests, that I'd like to get fixed before moving on with
more business in pg_stat_statements (aka properly show Const nodes for
utilities with normalized queries).
Comments or objections are welcome, of course.
(FWIW, I'd like to think that there is an argument to normalize the
A_Const nodes for a portion of the DDL queries, by ignoring their
values in the query jumbling and mark a location, which would be
really useful for some workloads, but that's a separate discussion I
am keeping for later.)
--
Michael
Attachments:
jumble-aconst.patchtext/x-diff; charset=us-asciiDownload
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 3d67787e7a..855da99ec0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -355,7 +355,7 @@ union ValUnion
typedef struct A_Const
{
- pg_node_attr(custom_copy_equal, custom_read_write)
+ pg_node_attr(custom_copy_equal, custom_read_write, custom_query_jumble)
NodeTag type;
union ValUnion val;
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 223d1bc826..72ce15e43d 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -49,6 +49,7 @@ static void AppendJumble(JumbleState *jstate,
const unsigned char *item, Size size);
static void RecordConstLocation(JumbleState *jstate, int location);
static void _jumbleNode(JumbleState *jstate, Node *node);
+static void _jumbleA_Const(JumbleState *jstate, Node *node);
static void _jumbleList(JumbleState *jstate, Node *node);
static void _jumbleRangeTblEntry(JumbleState *jstate, Node *node);
@@ -313,6 +314,40 @@ _jumbleList(JumbleState *jstate, Node *node)
}
}
+static void
+_jumbleA_Const(JumbleState *jstate, Node *node)
+{
+ A_Const *expr = (A_Const *) node;
+
+ JUMBLE_FIELD(isnull);
+ if (!expr->isnull)
+ {
+ JUMBLE_FIELD(val.node.type);
+ switch (nodeTag(&expr->val))
+ {
+ case T_Integer:
+ JUMBLE_FIELD(val.ival.ival);
+ break;
+ case T_Float:
+ JUMBLE_STRING(val.fval.fval);
+ break;
+ case T_Boolean:
+ JUMBLE_FIELD(val.boolval.boolval);
+ break;
+ case T_String:
+ JUMBLE_STRING(val.sval.sval);
+ break;
+ case T_BitString:
+ JUMBLE_STRING(val.bsval.bsval);
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(&expr->val));
+ break;
+ }
+ }
+}
+
static void
_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
{
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index fb9ccd920f..753062a9d7 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -579,6 +579,14 @@ NOTICE: table "test" does not exist, skipping
NOTICE: table "test" does not exist, skipping
NOTICE: function plus_one(pg_catalog.int4) does not exist, skipping
DROP FUNCTION PLUS_TWO(INTEGER);
+-- This SET query uses two different strings, still thet count as one entry.
+SET work_mem = '1MB';
+Set work_mem = '1MB';
+SET work_mem = '2MB';
+RESET work_mem;
+SET enable_seqscan = off;
+SET enable_seqscan = on;
+RESET enable_seqscan;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
@@ -588,10 +596,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP FUNCTION PLUS_TWO(INTEGER) | 1 | 0
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
+ RESET enable_seqscan | 1 | 0
+ RESET work_mem | 1 | 0
SELECT $1 | 1 | 1
SELECT pg_stat_statements_reset() | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
-(9 rows)
+ SET enable_seqscan = off | 1 | 0
+ SET enable_seqscan = on | 1 | 0
+ SET work_mem = '1MB' | 2 | 0
+ SET work_mem = '2MB' | 1 | 0
+(15 rows)
--
-- Track the total number of rows retrieved or affected by the utility
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index b82cddf16f..8ab8eb41ed 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -270,6 +270,14 @@ DROP TABLE IF EXISTS test \;
Drop Table If Exists test \;
DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER);
DROP FUNCTION PLUS_TWO(INTEGER);
+-- This SET query uses two different strings, still thet count as one entry.
+SET work_mem = '1MB';
+Set work_mem = '1MB';
+SET work_mem = '2MB';
+RESET work_mem;
+SET enable_seqscan = off;
+SET enable_seqscan = on;
+RESET enable_seqscan;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
On Sun, Feb 05, 2023 at 07:40:57PM +0900, Michael Paquier wrote:
Comments or objections are welcome, of course.
Done this part.
(FWIW, I'd like to think that there is an argument to normalize the
A_Const nodes for a portion of the DDL queries, by ignoring their
values in the query jumbling and mark a location, which would be
really useful for some workloads, but that's a separate discussion I
am keeping for later.)
And I have a patch pretty much OK for this part of the discussion.
Will post soon..
--
Michael
Michael Paquier <michael@paquier.xyz> writes:
With all that in mind, I have spent my day polishing that and doing a
close lookup, and the patch has been applied. Thanks a lot!
I have just noticed that this patch is generating useless jumbling
code for node types such as Path nodes and other planner infrastructure
nodes. That no doubt contributes to the miserable code coverage rating
for queryjumblefuncs.*.c, which have enough dead lines to drag down the
overall rating for all of backend/nodes/. Shouldn't a little more
attention have been paid to excluding entire node classes if they can
never appear in Query?
regards, tom lane
On Tue, Feb 07, 2023 at 05:32:07PM -0500, Tom Lane wrote:
I have just noticed that this patch is generating useless jumbling
code for node types such as Path nodes and other planner infrastructure
nodes. That no doubt contributes to the miserable code coverage rating
for queryjumblefuncs.*.c, which have enough dead lines to drag down the
overall rating for all of backend/nodes/. Shouldn't a little more
attention have been paid to excluding entire node classes if they can
never appear in Query?
This one was intentional to let extensions play with jumbling of such
nodes, but perhaps you are right that it makes little sense at this
stage. If there is an ask for it later, though.. Using
shared_preload_libraries = pg_stat_statements and compute_query_id =
regress shows that numbers go up to 60% for funcs.c and 30% for
switch.c. Removing nodes like as of the attached brings these numbers
respectively up to 94.5% and 93.5% for a check. With a check-world, I
measure respectively 96.7% and 96.1% because there is more coverage
for extensions, ALTER SYSTEM and database commands, roughly.
This could also be a file-level policy by enforcing no_query_jumble in
gen_node_support.pl by looking at the header name, still I favor
no_query_jumble to keep all the pg_node_attr() in a single area with
the headers. Note that the attached includes in 0002 the tweak to
enforce the computation with compute_query_id if you want to test it
yourself and check my numbers. This is useful IMO as we could detect
missing nodes for all queries (utilities or not), still doing this
change may deserve a separate discussion. Note that I am not seeing
any "unrecognized node type" in any of the logs for any queries.
As a side note, should we be more aggressive with the tests related to
the jumbling code since it is now in core? For example, XmlExpr or
MinMaxExpr, which are part of Query nodes that can be jumbled even in
older branches, have zero coverage by default as
coverage.postgresql.org reports, because everything goes through
pg_stat_statements and it includes no queries with such nodes. My
buildfarm member batta makes sure to stress that no nodes are missing
by overloading pg_stat_statements and compute_query_id = regress in
the configuration, so no nodes are missing from the computation, still
the coverage could be better across the board. Expanding the tests of
pg_stat_statements is needed in this area for some time, still could
there be a point in switching compute_query_id = regress so as it is a
synonym of "on" without the EXPLAIN output rather than "auto"? If the
setting is enforced by pg_regress, the coverage of queryjumble.c would
be so much better, at least..
--
Michael
Attachments:
0002-Switch-compute_query_id-regress-to-mean-on-and-force.patchtext/x-diff; charset=us-asciiDownload
From f7fa4a46c03187d01d1ac3d943aa132dad0a3a52 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 8 Feb 2023 15:42:50 +0900
Subject: [PATCH 2/2] Switch compute_query_id = regress to mean "on" and force
it in pg_regress
This is just a tweak to for tests with such code paths,
---
src/include/nodes/queryjumble.h | 2 ++
src/backend/commands/explain.c | 2 +-
src/test/regress/pg_regress.c | 3 ++-
doc/src/sgml/config.sgml | 2 +-
4 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h
index 204b8f74fd..3aa7d93255 100644
--- a/src/include/nodes/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -80,6 +80,8 @@ IsQueryIdEnabled(void)
return false;
if (compute_query_id == COMPUTE_QUERY_ID_ON)
return true;
+ if (compute_query_id == COMPUTE_QUERY_ID_REGRESS)
+ return true;
return query_id_enabled;
}
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index fbbf28cf06..5aba713348 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -777,7 +777,7 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
ExplainPrintSettings(es);
/*
- * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
+ * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_ON, but we don't show
* the queryid in any of the EXPLAIN plans to keep stable the results
* generated by regression test suites.
*/
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 6cd5998b9d..d3aafa156c 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -1876,8 +1876,9 @@ create_database(const char *dbname)
"ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
"ALTER DATABASE \"%s\" SET lc_time TO 'C';"
"ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
+ "ALTER DATABASE \"%s\" SET compute_query_id TO 'regress';"
"ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
- dbname, dbname, dbname, dbname, dbname, dbname);
+ dbname, dbname, dbname, dbname, dbname, dbname, dbname);
psql_end_command(buf, "postgres");
/*
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index d190be1925..b1bb3435a5 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8226,7 +8226,7 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
<literal>on</literal> (always enabled), <literal>auto</literal>,
which lets modules such as <xref linkend="pgstatstatements"/>
automatically enable it, and <literal>regress</literal> which
- has the same effect as <literal>auto</literal>, except that the
+ has the same effect as <literal>on</literal>, except that the
query identifier is not shown in the <literal>EXPLAIN</literal> output
in order to facilitate automated regression testing.
The default is <literal>auto</literal>.
--
2.39.1
0001-Mark-more-nodes-with-node-attribute-no_query_jumble.patchtext/x-diff; charset=us-asciiDownload
From 5d8d730e5fc28acdf75e33b00c56c172b400ffe3 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 8 Feb 2023 15:22:28 +0900
Subject: [PATCH 1/2] Mark more nodes with node attribute no_query_jumble
This mostly covers plan and path nodes, which should never be included
in the jumbling because we ignore these in Query nodes.
---
src/include/nodes/parsenodes.h | 8 +++
src/include/nodes/pathnodes.h | 126 ++++++++++++++++++++++++++-------
src/include/nodes/plannodes.h | 108 +++++++++++++++++++++++++---
src/include/nodes/primnodes.h | 4 ++
4 files changed, 210 insertions(+), 36 deletions(-)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 855da99ec0..29e2940b7d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1239,6 +1239,8 @@ typedef struct RangeTblEntry
*/
typedef struct RTEPermissionInfo
{
+ pg_node_attr(no_query_jumble)
+
NodeTag type;
Oid relid; /* relation OID */
@@ -1596,6 +1598,8 @@ typedef enum CTEMaterialize
typedef struct CTESearchClause
{
+ pg_node_attr(no_query_jumble)
+
NodeTag type;
List *search_col_list;
bool search_breadth_first;
@@ -1605,6 +1609,8 @@ typedef struct CTESearchClause
typedef struct CTECycleClause
{
+ pg_node_attr(no_query_jumble)
+
NodeTag type;
List *cycle_col_list;
char *cycle_mark_column;
@@ -1731,6 +1737,8 @@ typedef struct TriggerTransition
*/
typedef struct RawStmt
{
+ pg_node_attr(no_query_jumble)
+
NodeTag type;
Node *stmt; /* raw parse tree */
int stmt_location; /* start location, or -1 if unknown */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 0d4b1ec4e4..453dedaec8 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -94,7 +94,7 @@ typedef enum UpperRelationKind
*/
typedef struct PlannerGlobal
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -194,7 +194,7 @@ typedef struct PlannerInfo PlannerInfo;
struct PlannerInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -853,7 +853,7 @@ typedef enum RelOptKind
typedef struct RelOptInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1098,7 +1098,7 @@ typedef struct IndexOptInfo IndexOptInfo;
struct IndexOptInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1208,7 +1208,7 @@ struct IndexOptInfo
*/
typedef struct ForeignKeyOptInfo
{
- pg_node_attr(custom_read_write, no_copy_equal, no_read)
+ pg_node_attr(custom_read_write, no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1258,7 +1258,7 @@ typedef struct ForeignKeyOptInfo
*/
typedef struct StatisticExtInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1309,7 +1309,7 @@ typedef struct StatisticExtInfo
*/
typedef struct JoinDomain
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1371,7 +1371,7 @@ typedef struct JoinDomain
*/
typedef struct EquivalenceClass
{
- pg_node_attr(custom_read_write, no_copy_equal, no_read)
+ pg_node_attr(custom_read_write, no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1422,7 +1422,7 @@ typedef struct EquivalenceClass
*/
typedef struct EquivalenceMember
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1455,7 +1455,7 @@ typedef struct EquivalenceMember
*/
typedef struct PathKey
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
@@ -1503,7 +1503,7 @@ typedef enum VolatileFunctionStatus
*/
typedef struct PathTarget
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1550,7 +1550,7 @@ typedef struct PathTarget
*/
typedef struct ParamPathInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1596,7 +1596,7 @@ typedef struct ParamPathInfo
*/
typedef struct Path
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1684,6 +1684,8 @@ typedef struct Path
*/
typedef struct IndexPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
IndexOptInfo *indexinfo;
List *indexclauses;
@@ -1730,7 +1732,7 @@ typedef struct IndexPath
*/
typedef struct IndexClause
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
struct RestrictInfo *rinfo; /* original restriction or join clause */
@@ -1759,6 +1761,8 @@ typedef struct IndexClause
*/
typedef struct BitmapHeapPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *bitmapqual; /* IndexPath, BitmapAndPath, BitmapOrPath */
} BitmapHeapPath;
@@ -1771,6 +1775,8 @@ typedef struct BitmapHeapPath
*/
typedef struct BitmapAndPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *bitmapquals; /* IndexPaths and BitmapOrPaths */
Selectivity bitmapselectivity;
@@ -1784,6 +1790,8 @@ typedef struct BitmapAndPath
*/
typedef struct BitmapOrPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *bitmapquals; /* IndexPaths and BitmapAndPaths */
Selectivity bitmapselectivity;
@@ -1798,6 +1806,8 @@ typedef struct BitmapOrPath
*/
typedef struct TidPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *tidquals; /* qual(s) involving CTID = something */
} TidPath;
@@ -1810,6 +1820,8 @@ typedef struct TidPath
*/
typedef struct TidRangePath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *tidrangequals;
} TidRangePath;
@@ -1824,6 +1836,8 @@ typedef struct TidRangePath
*/
typedef struct SubqueryScanPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing subquery execution */
} SubqueryScanPath;
@@ -1840,6 +1854,8 @@ typedef struct SubqueryScanPath
*/
typedef struct ForeignPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *fdw_outerpath;
List *fdw_private;
@@ -1868,6 +1884,8 @@ struct CustomPathMethods;
typedef struct CustomPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
uint32 flags; /* mask of CUSTOMPATH_* flags, see
* nodes/extensible.h */
@@ -1893,6 +1911,8 @@ typedef struct CustomPath
*/
typedef struct AppendPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *subpaths; /* list of component Paths */
/* Index of first partial path in subpaths; list_length(subpaths) if none */
@@ -1917,6 +1937,8 @@ extern bool is_dummy_rel(RelOptInfo *rel);
*/
typedef struct MergeAppendPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *subpaths; /* list of component Paths */
Cardinality limit_tuples; /* hard limit on output tuples, or -1 */
@@ -1931,6 +1953,8 @@ typedef struct MergeAppendPath
*/
typedef struct GroupResultPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *quals;
} GroupResultPath;
@@ -1943,6 +1967,8 @@ typedef struct GroupResultPath
*/
typedef struct MaterialPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath;
} MaterialPath;
@@ -1954,6 +1980,8 @@ typedef struct MaterialPath
*/
typedef struct MemoizePath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* outerpath to cache tuples from */
List *hash_operators; /* OIDs of hash equality ops for cache keys */
@@ -1989,6 +2017,8 @@ typedef enum UniquePathMethod
typedef struct UniquePath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath;
UniquePathMethod umethod;
@@ -2003,6 +2033,8 @@ typedef struct UniquePath
*/
typedef struct GatherPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path for each worker */
bool single_copy; /* don't execute path more than once */
@@ -2015,6 +2047,8 @@ typedef struct GatherPath
*/
typedef struct GatherMergePath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path for each worker */
int num_workers; /* number of workers sought to help */
@@ -2027,7 +2061,7 @@ typedef struct GatherMergePath
typedef struct JoinPath
{
- pg_node_attr(abstract)
+ pg_node_attr(abstract, no_query_jumble)
Path path;
@@ -2054,6 +2088,8 @@ typedef struct JoinPath
typedef struct NestPath
{
+ pg_node_attr(no_query_jumble)
+
JoinPath jpath;
} NestPath;
@@ -2094,6 +2130,8 @@ typedef struct NestPath
typedef struct MergePath
{
+ pg_node_attr(no_query_jumble)
+
JoinPath jpath;
List *path_mergeclauses; /* join clauses to be used for merge */
List *outersortkeys; /* keys for explicit sort, if any */
@@ -2113,6 +2151,8 @@ typedef struct MergePath
typedef struct HashPath
{
+ pg_node_attr(no_query_jumble)
+
JoinPath jpath;
List *path_hashclauses; /* join clauses used for hashing */
int num_batches; /* number of batches expected */
@@ -2135,6 +2175,8 @@ typedef struct HashPath
*/
typedef struct ProjectionPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
bool dummypp; /* true if no separate Result is needed */
@@ -2147,6 +2189,8 @@ typedef struct ProjectionPath
*/
typedef struct ProjectSetPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
} ProjectSetPath;
@@ -2161,6 +2205,8 @@ typedef struct ProjectSetPath
*/
typedef struct SortPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
} SortPath;
@@ -2173,6 +2219,8 @@ typedef struct SortPath
*/
typedef struct IncrementalSortPath
{
+ pg_node_attr(no_query_jumble)
+
SortPath spath;
int nPresortedCols; /* number of presorted columns */
} IncrementalSortPath;
@@ -2187,6 +2235,8 @@ typedef struct IncrementalSortPath
*/
typedef struct GroupPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
List *groupClause; /* a list of SortGroupClause's */
@@ -2201,6 +2251,8 @@ typedef struct GroupPath
*/
typedef struct UpperUniquePath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
int numkeys; /* number of pathkey columns to compare */
@@ -2215,6 +2267,8 @@ typedef struct UpperUniquePath
*/
typedef struct AggPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
AggStrategy aggstrategy; /* basic strategy, see nodes.h */
@@ -2231,7 +2285,7 @@ typedef struct AggPath
typedef struct GroupingSetData
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
List *set; /* grouping set as list of sortgrouprefs */
@@ -2240,7 +2294,7 @@ typedef struct GroupingSetData
typedef struct RollupData
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
List *groupClause; /* applicable subset of parse->groupClause */
@@ -2257,6 +2311,8 @@ typedef struct RollupData
typedef struct GroupingSetsPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
AggStrategy aggstrategy; /* basic strategy */
@@ -2270,6 +2326,8 @@ typedef struct GroupingSetsPath
*/
typedef struct MinMaxAggPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
List *mmaggregates; /* list of MinMaxAggInfo */
List *quals; /* HAVING quals, if any */
@@ -2280,6 +2338,8 @@ typedef struct MinMaxAggPath
*/
typedef struct WindowAggPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
WindowClause *winclause; /* WindowClause we'll be using */
@@ -2293,6 +2353,8 @@ typedef struct WindowAggPath
*/
typedef struct SetOpPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
SetOpCmd cmd; /* what to do, see nodes.h */
@@ -2308,6 +2370,8 @@ typedef struct SetOpPath
*/
typedef struct RecursiveUnionPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *leftpath; /* paths representing input sources */
Path *rightpath;
@@ -2321,6 +2385,8 @@ typedef struct RecursiveUnionPath
*/
typedef struct LockRowsPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
List *rowMarks; /* a list of PlanRowMark's */
@@ -2336,6 +2402,8 @@ typedef struct LockRowsPath
*/
typedef struct ModifyTablePath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* Path producing source data */
CmdType operation; /* INSERT, UPDATE, DELETE, or MERGE */
@@ -2359,6 +2427,8 @@ typedef struct ModifyTablePath
*/
typedef struct LimitPath
{
+ pg_node_attr(no_query_jumble)
+
Path path;
Path *subpath; /* path representing input source */
Node *limitOffset; /* OFFSET parameter, or NULL if none */
@@ -2509,7 +2579,7 @@ typedef struct LimitPath
typedef struct RestrictInfo
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
@@ -2724,6 +2794,8 @@ typedef struct MergeScanSelCache
typedef struct PlaceHolderVar
{
+ pg_node_attr(no_query_jumble)
+
Expr xpr;
/* the represented expression */
@@ -2825,7 +2897,7 @@ typedef struct SpecialJoinInfo SpecialJoinInfo;
struct SpecialJoinInfo
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
Relids min_lefthand; /* base+OJ relids in minimum LHS for join */
@@ -2853,7 +2925,7 @@ struct SpecialJoinInfo
*/
typedef struct OuterJoinClauseInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
RestrictInfo *rinfo; /* a mergejoinable outer-join clause */
@@ -2892,6 +2964,8 @@ typedef struct OuterJoinClauseInfo
typedef struct AppendRelInfo
{
+ pg_node_attr(no_query_jumble)
+
NodeTag type;
/*
@@ -2967,7 +3041,7 @@ typedef struct AppendRelInfo
*/
typedef struct RowIdentityVarInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3005,7 +3079,7 @@ typedef struct RowIdentityVarInfo
typedef struct PlaceHolderInfo
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
@@ -3038,7 +3112,7 @@ typedef struct PlaceHolderInfo
*/
typedef struct MinMaxAggInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3116,7 +3190,7 @@ typedef struct MinMaxAggInfo
*/
typedef struct PlannerParamItem
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3296,7 +3370,7 @@ typedef struct JoinCostWorkspace
*/
typedef struct AggInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3330,7 +3404,7 @@ typedef struct AggInfo
*/
typedef struct AggTransInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 4781a9c632..9199c16f6f 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -46,7 +46,7 @@
*/
typedef struct PlannedStmt
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
@@ -122,7 +122,7 @@ typedef struct PlannedStmt
*/
typedef struct Plan
{
- pg_node_attr(abstract, no_equal)
+ pg_node_attr(abstract, no_equal, no_query_jumble)
NodeTag type;
@@ -199,6 +199,8 @@ typedef struct Plan
*/
typedef struct Result
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
Node *resconstantqual;
} Result;
@@ -211,6 +213,8 @@ typedef struct Result
*/
typedef struct ProjectSet
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
} ProjectSet;
@@ -231,6 +235,8 @@ typedef struct ProjectSet
*/
typedef struct ModifyTable
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
CmdType operation; /* INSERT, UPDATE, DELETE, or MERGE */
bool canSetTag; /* do we set the command tag/es_processed? */
@@ -265,6 +271,8 @@ struct PartitionPruneInfo; /* forward reference to struct below */
*/
typedef struct Append
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
Bitmapset *apprelids; /* RTIs of appendrel(s) formed by this node */
List *appendplans;
@@ -287,6 +295,8 @@ typedef struct Append
*/
typedef struct MergeAppend
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* RTIs of appendrel(s) formed by this node */
@@ -325,6 +335,8 @@ typedef struct MergeAppend
*/
typedef struct RecursiveUnion
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* ID of Param representing work table */
@@ -356,6 +368,8 @@ typedef struct RecursiveUnion
*/
typedef struct BitmapAnd
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
List *bitmapplans;
} BitmapAnd;
@@ -370,6 +384,8 @@ typedef struct BitmapAnd
*/
typedef struct BitmapOr
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
bool isshared;
List *bitmapplans;
@@ -384,7 +400,7 @@ typedef struct BitmapOr
*/
typedef struct Scan
{
- pg_node_attr(abstract)
+ pg_node_attr(abstract, no_query_jumble)
Plan plan;
Index scanrelid; /* relid is index into the range table */
@@ -396,6 +412,8 @@ typedef struct Scan
*/
typedef struct SeqScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
} SeqScan;
@@ -405,6 +423,8 @@ typedef struct SeqScan
*/
typedef struct SampleScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
/* use struct pointer to avoid including parsenodes.h here */
struct TableSampleClause *tablesample;
@@ -449,6 +469,8 @@ typedef struct SampleScan
*/
typedef struct IndexScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
Oid indexid; /* OID of index to scan */
List *indexqual; /* list of index quals (usually OpExprs) */
@@ -492,6 +514,8 @@ typedef struct IndexScan
*/
typedef struct IndexOnlyScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
Oid indexid; /* OID of index to scan */
List *indexqual; /* list of index quals (usually OpExprs) */
@@ -520,6 +544,8 @@ typedef struct IndexOnlyScan
*/
typedef struct BitmapIndexScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
Oid indexid; /* OID of index to scan */
bool isshared; /* Create shared bitmap if set */
@@ -538,6 +564,8 @@ typedef struct BitmapIndexScan
*/
typedef struct BitmapHeapScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
List *bitmapqualorig; /* index quals, in standard expr form */
} BitmapHeapScan;
@@ -552,6 +580,8 @@ typedef struct BitmapHeapScan
*/
typedef struct TidScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
List *tidquals; /* qual(s) involving CTID = something */
} TidScan;
@@ -565,6 +595,8 @@ typedef struct TidScan
*/
typedef struct TidRangeScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
List *tidrangequals; /* qual(s) involving CTID op something */
} TidRangeScan;
@@ -598,6 +630,8 @@ typedef enum SubqueryScanStatus
typedef struct SubqueryScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
Plan *subplan;
SubqueryScanStatus scanstatus;
@@ -609,6 +643,8 @@ typedef struct SubqueryScan
*/
typedef struct FunctionScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
List *functions; /* list of RangeTblFunction nodes */
bool funcordinality; /* WITH ORDINALITY */
@@ -620,6 +656,8 @@ typedef struct FunctionScan
*/
typedef struct ValuesScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
List *values_lists; /* list of expression lists */
} ValuesScan;
@@ -630,6 +668,8 @@ typedef struct ValuesScan
*/
typedef struct TableFuncScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
TableFunc *tablefunc; /* table function node */
} TableFuncScan;
@@ -640,6 +680,8 @@ typedef struct TableFuncScan
*/
typedef struct CteScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
int ctePlanId; /* ID of init SubPlan for CTE */
int cteParam; /* ID of Param representing CTE output */
@@ -651,6 +693,8 @@ typedef struct CteScan
*/
typedef struct NamedTuplestoreScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
char *enrname; /* Name given to Ephemeral Named Relation */
} NamedTuplestoreScan;
@@ -661,6 +705,8 @@ typedef struct NamedTuplestoreScan
*/
typedef struct WorkTableScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
int wtParam; /* ID of Param representing work table */
} WorkTableScan;
@@ -707,6 +753,8 @@ typedef struct WorkTableScan
*/
typedef struct ForeignScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */
Index resultRelation; /* direct modification target's RT index */
@@ -739,6 +787,8 @@ struct CustomScanMethods;
typedef struct CustomScan
{
+ pg_node_attr(no_query_jumble)
+
Scan scan;
uint32 flags; /* mask of CUSTOMPATH_* flags, see
* nodes/extensible.h */
@@ -786,7 +836,7 @@ typedef struct CustomScan
*/
typedef struct Join
{
- pg_node_attr(abstract)
+ pg_node_attr(abstract, no_query_jumble)
Plan plan;
JoinType jointype;
@@ -807,13 +857,15 @@ typedef struct Join
*/
typedef struct NestLoop
{
+ pg_node_attr(no_query_jumble)
+
Join join;
List *nestParams; /* list of NestLoopParam nodes */
} NestLoop;
typedef struct NestLoopParam
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
int paramno; /* number of the PARAM_EXEC Param to set */
@@ -833,6 +885,8 @@ typedef struct NestLoopParam
*/
typedef struct MergeJoin
{
+ pg_node_attr(no_query_jumble)
+
Join join;
/* Can we skip mark/restore calls? */
@@ -862,6 +916,8 @@ typedef struct MergeJoin
*/
typedef struct HashJoin
{
+ pg_node_attr(no_query_jumble)
+
Join join;
List *hashclauses;
List *hashoperators;
@@ -880,6 +936,8 @@ typedef struct HashJoin
*/
typedef struct Material
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
} Material;
@@ -889,6 +947,8 @@ typedef struct Material
*/
typedef struct Memoize
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* size of the two arrays below */
@@ -931,6 +991,8 @@ typedef struct Memoize
*/
typedef struct Sort
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* number of sort-key columns */
@@ -955,6 +1017,8 @@ typedef struct Sort
*/
typedef struct IncrementalSort
{
+ pg_node_attr(no_query_jumble)
+
Sort sort;
int nPresortedCols; /* number of presorted columns */
} IncrementalSort;
@@ -967,6 +1031,8 @@ typedef struct IncrementalSort
*/
typedef struct Group
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* number of grouping columns */
@@ -996,6 +1062,8 @@ typedef struct Group
*/
typedef struct Agg
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* basic strategy, see nodes.h */
@@ -1038,6 +1106,8 @@ typedef struct Agg
*/
typedef struct WindowAgg
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* ID referenced by window functions */
@@ -1112,6 +1182,8 @@ typedef struct WindowAgg
*/
typedef struct Unique
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* number of columns to check for uniqueness */
@@ -1140,6 +1212,8 @@ typedef struct Unique
*/
typedef struct Gather
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
int num_workers; /* planned number of worker processes */
int rescan_param; /* ID of Param that signals a rescan, or -1 */
@@ -1155,6 +1229,8 @@ typedef struct Gather
*/
typedef struct GatherMerge
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* planned number of worker processes */
@@ -1197,6 +1273,8 @@ typedef struct GatherMerge
*/
typedef struct Hash
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/*
@@ -1217,6 +1295,8 @@ typedef struct Hash
*/
typedef struct SetOp
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* what to do, see nodes.h */
@@ -1256,6 +1336,8 @@ typedef struct SetOp
*/
typedef struct LockRows
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
List *rowMarks; /* a list of PlanRowMark's */
int epqParam; /* ID of Param for EvalPlanQual re-eval */
@@ -1270,6 +1352,8 @@ typedef struct LockRows
*/
typedef struct Limit
{
+ pg_node_attr(no_query_jumble)
+
Plan plan;
/* OFFSET parameter, or NULL if none */
@@ -1377,7 +1461,7 @@ typedef enum RowMarkType
*/
typedef struct PlanRowMark
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
Index rti; /* range table index of markable relation */
@@ -1425,7 +1509,7 @@ typedef struct PlanRowMark
*/
typedef struct PartitionPruneInfo
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
Bitmapset *root_parent_relids;
@@ -1452,7 +1536,7 @@ typedef struct PartitionPruneInfo
*/
typedef struct PartitionedRelPruneInfo
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
@@ -1495,7 +1579,7 @@ typedef struct PartitionedRelPruneInfo
*/
typedef struct PartitionPruneStep
{
- pg_node_attr(abstract, no_equal)
+ pg_node_attr(abstract, no_equal, no_query_jumble)
NodeTag type;
int step_id;
@@ -1530,6 +1614,8 @@ typedef struct PartitionPruneStep
*/
typedef struct PartitionPruneStepOp
{
+ pg_node_attr(no_query_jumble)
+
PartitionPruneStep step;
StrategyNumber opstrategy;
@@ -1552,6 +1638,8 @@ typedef enum PartitionPruneCombineOp
typedef struct PartitionPruneStepCombine
{
+ pg_node_attr(no_query_jumble)
+
PartitionPruneStep step;
PartitionPruneCombineOp combineOp;
@@ -1570,7 +1658,7 @@ typedef struct PartitionPruneStepCombine
*/
typedef struct PlanInvalItem
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
int cacheId; /* a syscache ID, see utils/syscache.h */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 6d740be5c0..1be1642d92 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -982,6 +982,8 @@ typedef struct SubLink
*/
typedef struct SubPlan
{
+ pg_node_attr(no_query_jumble)
+
Expr xpr;
/* Fields copied from original SubLink: */
SubLinkType subLinkType; /* see above */
@@ -1029,6 +1031,8 @@ typedef struct SubPlan
*/
typedef struct AlternativeSubPlan
{
+ pg_node_attr(no_query_jumble)
+
Expr xpr;
List *subplans; /* SubPlan(s) with equivalent results */
} AlternativeSubPlan;
--
2.39.1
Hi,
On 2023-02-08 15:47:51 +0900, Michael Paquier wrote:
This one was intentional to let extensions play with jumbling of such
nodes, but perhaps you are right that it makes little sense at this
stage. If there is an ask for it later, though.. Using
shared_preload_libraries = pg_stat_statements and compute_query_id =
regress shows that numbers go up to 60% for funcs.c and 30% for
switch.c. Removing nodes like as of the attached brings these numbers
respectively up to 94.5% and 93.5% for a check. With a check-world, I
measure respectively 96.7% and 96.1% because there is more coverage
for extensions, ALTER SYSTEM and database commands, roughly.
Given that we already pay the price of multiple regress runs, and that
jumbling is now really a core feature, perhaps we should enable
pg_stat_statements in pg_upgrade or 027_stream_regress.pl? I'd hope it
wouldn't add a meaningful amount of time? A tiny bit of verification at the
end should also be ok.
Both pg_upgrade and 027_stream_regress.pl have some advantages. The former
would test pg_upgrade interactions with shared_preload_libraries, the latter
could do some basic checks of pg_stat_statements on a standby.
Greetings,
Andres Freund
On Tue, Feb 07, 2023 at 11:01:03PM -0800, Andres Freund wrote:
Given that we already pay the price of multiple regress runs, and that
jumbling is now really a core feature, perhaps we should enable
pg_stat_statements in pg_upgrade or 027_stream_regress.pl? I'd hope it
wouldn't add a meaningful amount of time? A tiny bit of verification at the
end should also be ok.
Yeah, I have briefly mentioned this part upthread:
/messages/by-id/Y8+BdCOjxykre5es@paquier.xyz
It would not, I guess, as long as pg_stat_statements.max is set large
enough in the TAP test. There are currently 21k~22k entries in the
regression database, much larger than the default of 5000 so this may
become an issue on small-ish machines if left untouched even in a TAP
test.
Both pg_upgrade and 027_stream_regress.pl have some advantages. The former
would test pg_upgrade interactions with shared_preload_libraries, the latter
could do some basic checks of pg_stat_statements on a standby.
Yes, there could be more checks, potentially useful for both cases, so
I may choose both at the end of the day. Checking the consistency of
the contents of pg_stat_statements across a pg_upgrade run for the
same version may be one thing? I am not sure if it is that
interesting, TBH, still that's one idea :)
--
Michael
On Wed, Feb 08, 2023 at 03:47:51PM +0900, Michael Paquier wrote:
This one was intentional to let extensions play with jumbling of such
nodes, but perhaps you are right that it makes little sense at this
stage. If there is an ask for it later, though.. Using
shared_preload_libraries = pg_stat_statements and compute_query_id =
regress shows that numbers go up to 60% for funcs.c and 30% for
switch.c. Removing nodes like as of the attached brings these numbers
respectively up to 94.5% and 93.5% for a check. With a check-world, I
measure respectively 96.7% and 96.1% because there is more coverage
for extensions, ALTER SYSTEM and database commands, roughly.
Tom, did you get a chance to look at what is proposed here and expand
the use of query_jumble_ignore in the definitions of the nodes rather
than have an enforced per-file policy in gen_node_support.pl? The
attached improves the situation by as much as we can, still the
numbers reported by coverage.postgresql.org won't go that up until we
enforce query jumbling testing on the regression database (something
we should have done since this code moved to core, I guess).
--
Michael
Michael Paquier <michael@paquier.xyz> writes:
Tom, did you get a chance to look at what is proposed here and expand
the use of query_jumble_ignore in the definitions of the nodes rather
than have an enforced per-file policy in gen_node_support.pl?
Sorry, didn't look at it before.
I'm okay with the pathnodes.h changes --- although surely you don't need
changes like this:
- pg_node_attr(abstract)
+ pg_node_attr(abstract, no_query_jumble)
"abstract" should already imply "no_query_jumble".
I wonder too if you could shorten the changes by making no_query_jumble
an inheritable attribute, and then just applying it to Path and Plan.
The changes in parsenodes.h seem wrong, except for RawStmt. Those node
types are used in parsed queries, aren't they?
regards, tom lane
On Thu, Feb 09, 2023 at 06:12:50PM -0500, Tom Lane wrote:
I'm okay with the pathnodes.h changes --- although surely you don't need
changes like this:- pg_node_attr(abstract) + pg_node_attr(abstract, no_query_jumble)"abstract" should already imply "no_query_jumble".
Okay, understood. Following this string of thoughts, I am a bit
surprised for two cases, though:
- PartitionPruneStep.
- Plan.
Both are abstract and both are marked with no_equal. I guess that
applying no_query_jumble to both of them is fine, and that's what you
mean?
I wonder too if you could shorten the changes by making no_query_jumble
an inheritable attribute, and then just applying it to Path and Plan.
Ah. I did not catch what you meant here at first, but I think that I
do now. Are you referring to the part of gen_node_support.pl where we
propagate properties when a node is a supertype? This part would be
taken into account when a node is parsed but we find that its first
member is already tracked as a node:
# Propagate some node attributes from supertypes
if ($supertype)
{
push @no_copy, $in_struct
if elem $supertype, @no_copy;
push @no_equal, $in_struct
if elem $supertype, @no_equal;
push @no_read, $in_struct
if elem $supertype, @no_read;
+ push @no_query_jumble, $in_struct
+ if elem $supertype, @no_query_jumble;
}
A benefit of doing that would also discard all the Scan and Sort
nodes. So like the other no_* attributes, it makes sense to force the
inheritance here.
The changes in parsenodes.h seem wrong, except for RawStmt. Those node
types are used in parsed queries, aren't they?
RTEPermissionInfo is a recent addition, as of a61b1f7. This commit
documents it as a plan node, still it is part of a Query while being
ignored in the query jumbling since its introduction, so I am a bit
confused by this one.
Anyway, none of these need to be included in the query jumbling
currently because they are ignored, but I'd be fine to generate their
code by default as they could become relevant if other nodes begin to
rely on them more heavily, as being part of queries. Peter E. has
mentioned upthread that a few nodes should include more jumbling while
some other parts should be ignored. This should be analyzed
separately because ~15 does not seem to be strictly right, either.
Attached is a patch refreshed with all that. Feel free to ignore 0002
as that's just useful to enforce the tests to go through the jumbling
code. The attached reaches 95.0% of line coverage after a check-world
in funcs.c.
Thoughts?
--
Michael
Attachments:
v2-0001-Mark-more-nodes-with-node-attribute-no_query_jumb.patchtext/x-diff; charset=us-asciiDownload
From 316df43f7574f54ff93606549167102e319f5473 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 10 Feb 2023 11:15:24 +0900
Subject: [PATCH v2 1/2] Mark more nodes with node attribute no_query_jumble
This mostly covers Plan and Path nodes, which should never be included
in the jumbling because we ignore these in Query nodes. no_query_jumble
is now an inherited attribute to be able to achieve that, like its read
and write counterparts.
---
src/include/nodes/nodes.h | 8 ++--
src/include/nodes/parsenodes.h | 5 +++
src/include/nodes/pathnodes.h | 54 ++++++++++++++-------------
src/include/nodes/plannodes.h | 16 ++++----
src/include/nodes/primnodes.h | 4 ++
src/backend/nodes/gen_node_support.pl | 2 +
6 files changed, 52 insertions(+), 37 deletions(-)
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 75dfe1919d..e6cf520547 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -77,10 +77,10 @@ typedef enum NodeTag
*
* Node types can be supertypes of other types whether or not they are marked
* abstract: if a node struct appears as the first field of another struct
- * type, then it is the supertype of that type. The no_copy, no_equal, and
- * no_read node attributes are automatically inherited from the supertype.
- * (Notice that nodetag_only does not inherit, so it's not quite equivalent
- * to a combination of other attributes.)
+ * type, then it is the supertype of that type. The no_copy, no_equal,
+ * no_query_jumble and no_read node attributes are automatically inherited
+ * from the supertype. (Notice that nodetag_only does not inherit, so it's
+ * not quite equivalent to a combination of other attributes.)
*
* Valid node field attributes:
*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 855da99ec0..9c97148b69 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1728,9 +1728,14 @@ typedef struct TriggerTransition
*
* stmt_location/stmt_len identify the portion of the source text string
* containing this raw statement (useful for multi-statement strings).
+ *
+ * This is irrelevant for query jumbling, as this is not used in parsed
+ * queries.
*/
typedef struct RawStmt
{
+ pg_node_attr(no_query_jumble)
+
NodeTag type;
Node *stmt; /* raw parse tree */
int stmt_location; /* start location, or -1 if unknown */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 0d4b1ec4e4..be4d791212 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -94,7 +94,7 @@ typedef enum UpperRelationKind
*/
typedef struct PlannerGlobal
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -194,7 +194,7 @@ typedef struct PlannerInfo PlannerInfo;
struct PlannerInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -853,7 +853,7 @@ typedef enum RelOptKind
typedef struct RelOptInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1098,7 +1098,7 @@ typedef struct IndexOptInfo IndexOptInfo;
struct IndexOptInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1208,7 +1208,7 @@ struct IndexOptInfo
*/
typedef struct ForeignKeyOptInfo
{
- pg_node_attr(custom_read_write, no_copy_equal, no_read)
+ pg_node_attr(custom_read_write, no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1258,7 +1258,7 @@ typedef struct ForeignKeyOptInfo
*/
typedef struct StatisticExtInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1309,7 +1309,7 @@ typedef struct StatisticExtInfo
*/
typedef struct JoinDomain
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1371,7 +1371,7 @@ typedef struct JoinDomain
*/
typedef struct EquivalenceClass
{
- pg_node_attr(custom_read_write, no_copy_equal, no_read)
+ pg_node_attr(custom_read_write, no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1422,7 +1422,7 @@ typedef struct EquivalenceClass
*/
typedef struct EquivalenceMember
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1455,7 +1455,7 @@ typedef struct EquivalenceMember
*/
typedef struct PathKey
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
@@ -1503,7 +1503,7 @@ typedef enum VolatileFunctionStatus
*/
typedef struct PathTarget
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1550,7 +1550,7 @@ typedef struct PathTarget
*/
typedef struct ParamPathInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1596,7 +1596,7 @@ typedef struct ParamPathInfo
*/
typedef struct Path
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -1730,7 +1730,7 @@ typedef struct IndexPath
*/
typedef struct IndexClause
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
struct RestrictInfo *rinfo; /* original restriction or join clause */
@@ -2231,7 +2231,7 @@ typedef struct AggPath
typedef struct GroupingSetData
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
List *set; /* grouping set as list of sortgrouprefs */
@@ -2240,7 +2240,7 @@ typedef struct GroupingSetData
typedef struct RollupData
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
List *groupClause; /* applicable subset of parse->groupClause */
@@ -2509,7 +2509,7 @@ typedef struct LimitPath
typedef struct RestrictInfo
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
@@ -2724,6 +2724,8 @@ typedef struct MergeScanSelCache
typedef struct PlaceHolderVar
{
+ pg_node_attr(no_query_jumble)
+
Expr xpr;
/* the represented expression */
@@ -2825,7 +2827,7 @@ typedef struct SpecialJoinInfo SpecialJoinInfo;
struct SpecialJoinInfo
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
Relids min_lefthand; /* base+OJ relids in minimum LHS for join */
@@ -2853,7 +2855,7 @@ struct SpecialJoinInfo
*/
typedef struct OuterJoinClauseInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
RestrictInfo *rinfo; /* a mergejoinable outer-join clause */
@@ -2892,6 +2894,8 @@ typedef struct OuterJoinClauseInfo
typedef struct AppendRelInfo
{
+ pg_node_attr(no_query_jumble)
+
NodeTag type;
/*
@@ -2967,7 +2971,7 @@ typedef struct AppendRelInfo
*/
typedef struct RowIdentityVarInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3005,7 +3009,7 @@ typedef struct RowIdentityVarInfo
typedef struct PlaceHolderInfo
{
- pg_node_attr(no_read)
+ pg_node_attr(no_read, no_query_jumble)
NodeTag type;
@@ -3038,7 +3042,7 @@ typedef struct PlaceHolderInfo
*/
typedef struct MinMaxAggInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3116,7 +3120,7 @@ typedef struct MinMaxAggInfo
*/
typedef struct PlannerParamItem
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3296,7 +3300,7 @@ typedef struct JoinCostWorkspace
*/
typedef struct AggInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
@@ -3330,7 +3334,7 @@ typedef struct AggInfo
*/
typedef struct AggTransInfo
{
- pg_node_attr(no_copy_equal, no_read)
+ pg_node_attr(no_copy_equal, no_read, no_query_jumble)
NodeTag type;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 4781a9c632..659bd05c0c 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -46,7 +46,7 @@
*/
typedef struct PlannedStmt
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
@@ -122,7 +122,7 @@ typedef struct PlannedStmt
*/
typedef struct Plan
{
- pg_node_attr(abstract, no_equal)
+ pg_node_attr(abstract, no_equal, no_query_jumble)
NodeTag type;
@@ -813,7 +813,7 @@ typedef struct NestLoop
typedef struct NestLoopParam
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
int paramno; /* number of the PARAM_EXEC Param to set */
@@ -1377,7 +1377,7 @@ typedef enum RowMarkType
*/
typedef struct PlanRowMark
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
Index rti; /* range table index of markable relation */
@@ -1425,7 +1425,7 @@ typedef struct PlanRowMark
*/
typedef struct PartitionPruneInfo
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
Bitmapset *root_parent_relids;
@@ -1452,7 +1452,7 @@ typedef struct PartitionPruneInfo
*/
typedef struct PartitionedRelPruneInfo
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
@@ -1495,7 +1495,7 @@ typedef struct PartitionedRelPruneInfo
*/
typedef struct PartitionPruneStep
{
- pg_node_attr(abstract, no_equal)
+ pg_node_attr(abstract, no_equal, no_query_jumble)
NodeTag type;
int step_id;
@@ -1570,7 +1570,7 @@ typedef struct PartitionPruneStepCombine
*/
typedef struct PlanInvalItem
{
- pg_node_attr(no_equal)
+ pg_node_attr(no_equal, no_query_jumble)
NodeTag type;
int cacheId; /* a syscache ID, see utils/syscache.h */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 6d740be5c0..1be1642d92 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -982,6 +982,8 @@ typedef struct SubLink
*/
typedef struct SubPlan
{
+ pg_node_attr(no_query_jumble)
+
Expr xpr;
/* Fields copied from original SubLink: */
SubLinkType subLinkType; /* see above */
@@ -1029,6 +1031,8 @@ typedef struct SubPlan
*/
typedef struct AlternativeSubPlan
{
+ pg_node_attr(no_query_jumble)
+
Expr xpr;
List *subplans; /* SubPlan(s) with equivalent results */
} AlternativeSubPlan;
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 19ed29657c..2e57db915b 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -422,6 +422,8 @@ foreach my $infile (@ARGV)
if elem $supertype, @no_equal;
push @no_read, $in_struct
if elem $supertype, @no_read;
+ push @no_query_jumble, $in_struct
+ if elem $supertype, @no_query_jumble;
}
}
--
2.39.1
v2-0002-Switch-compute_query_id-regress-to-mean-on-and-fo.patchtext/x-diff; charset=us-asciiDownload
From 51bc070c098721fd91ae729408365a222c3d1959 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Fri, 10 Feb 2023 11:21:57 +0900
Subject: [PATCH v2 2/2] Switch compute_query_id = regress to mean "on" and
force it in pg_regress
This is just a tweak to force the tests to go through the query
jumbling.
---
src/include/nodes/queryjumble.h | 2 ++
src/backend/commands/explain.c | 2 +-
src/backend/nodes/queryjumblefuncs.c | 2 +-
src/test/regress/pg_regress.c | 3 ++-
doc/src/sgml/config.sgml | 2 +-
5 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/include/nodes/queryjumble.h b/src/include/nodes/queryjumble.h
index 204b8f74fd..3aa7d93255 100644
--- a/src/include/nodes/queryjumble.h
+++ b/src/include/nodes/queryjumble.h
@@ -80,6 +80,8 @@ IsQueryIdEnabled(void)
return false;
if (compute_query_id == COMPUTE_QUERY_ID_ON)
return true;
+ if (compute_query_id == COMPUTE_QUERY_ID_REGRESS)
+ return true;
return query_id_enabled;
}
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index fbbf28cf06..5aba713348 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -777,7 +777,7 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
ExplainPrintSettings(es);
/*
- * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
+ * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_ON, but we don't show
* the queryid in any of the EXPLAIN plans to keep stable the results
* generated by regression test suites.
*/
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index d7fd72d70f..4d594106ec 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -257,7 +257,7 @@ _jumbleNode(JumbleState *jstate, Node *node)
default:
/* Only a warning, since we can stumble along anyway */
- elog(WARNING, "unrecognized node type: %d",
+ elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(expr));
break;
}
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 6cd5998b9d..d3aafa156c 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -1876,8 +1876,9 @@ create_database(const char *dbname)
"ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
"ALTER DATABASE \"%s\" SET lc_time TO 'C';"
"ALTER DATABASE \"%s\" SET bytea_output TO 'hex';"
+ "ALTER DATABASE \"%s\" SET compute_query_id TO 'regress';"
"ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
- dbname, dbname, dbname, dbname, dbname, dbname);
+ dbname, dbname, dbname, dbname, dbname, dbname, dbname);
psql_end_command(buf, "postgres");
/*
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 8c56b134a8..56f8dac286 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -8226,7 +8226,7 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
<literal>on</literal> (always enabled), <literal>auto</literal>,
which lets modules such as <xref linkend="pgstatstatements"/>
automatically enable it, and <literal>regress</literal> which
- has the same effect as <literal>auto</literal>, except that the
+ has the same effect as <literal>on</literal>, except that the
query identifier is not shown in the <literal>EXPLAIN</literal> output
in order to facilitate automated regression testing.
The default is <literal>auto</literal>.
--
2.39.1
Michael Paquier <michael@paquier.xyz> writes:
Okay, understood. Following this string of thoughts, I am a bit
surprised for two cases, though:
- PartitionPruneStep.
- Plan.
Both are abstract and both are marked with no_equal. I guess that
applying no_query_jumble to both of them is fine, and that's what you
mean?
On second thought, the point of that is to allow the no_equal property
to automatically inherit to child node types, so doing likewise
for no_query_jumble is sensible.
The changes in parsenodes.h seem wrong, except for RawStmt. Those node
types are used in parsed queries, aren't they?
RTEPermissionInfo is a recent addition, as of a61b1f7. This commit
documents it as a plan node, still it is part of a Query while being
ignored in the query jumbling since its introduction, so I am a bit
confused by this one.
Hmm ... it is part of Query, so that documentation is wrong, and the
fact that it's not reached by query jumbling kind of seems like a bug.
However, it might be that everything in it is derived from something
else that *is* covered by jumbling, in which case that's okay, if
underdocumented.
... Peter E. has
mentioned upthread that a few nodes should include more jumbling while
some other parts should be ignored. This should be analyzed
separately because ~15 does not seem to be strictly right, either.
Yeah. It'd surprise me not at all if people have overlooked that.
v2 looks good to me as far as it goes. I agree these other questions
deserve a separate look.
regards, tom lane
On Fri, Feb 10, 2023 at 04:40:08PM -0500, Tom Lane wrote:
v2 looks good to me as far as it goes.
Thanks. I have applied that after an extra lookup.
I agree these other questions deserve a separate look.
Okay, I may be able to come back to that. Another point is that we
need to do a better job in forcing the execution of the query jumbling
in one of the TAP tests running pg_regress, outside
pg_stat_statements, to maximize coverage. Will see to that on a
separate thread.
--
Michael
On Mon, Jan 30, 2023 at 11:48:45AM +0100, Peter Eisentraut wrote:
On 27.01.23 03:59, Michael Paquier wrote:
At the end, that would be unnoticeable for the average user, I guess,
but here are the numbers I get on my laptop :)Personally, I think we do not want the two jumble methods in parallel.
Maybe there are other opinions.
(Thanks Jonathan for the poke.)
Now that we are in mid-beta for 16, it would be a good time to
conclude on this open item:
"Reconsider a utility_query_id GUC to control if query jumbling of
utilities can go through the past string-only mode and the new mode?"
In Postgres ~15, utility commands used a hash of the query string to
compute their query ID. The current query jumbling code uses a Query
instead, like any other queries. I have registered this open item as
a self-reminder, mostly in case there would be an argument to have a
GUC where users could switch from one mode to another. See here as
well for some computation times for each method (table is in ns, wiht
millions of iterations):
/messages/by-id/Y9eeYinDb1AcpWrG@paquier.xyz
I still don't think that we need both methods based on these numbers,
but there may be more opinions about that? Are people OK if this open
item is discarded?
--
Michael
On 11/7/2023 05:35, Michael Paquier wrote:
On Mon, Jan 30, 2023 at 11:48:45AM +0100, Peter Eisentraut wrote:
On 27.01.23 03:59, Michael Paquier wrote:
At the end, that would be unnoticeable for the average user, I guess,
but here are the numbers I get on my laptop :)Personally, I think we do not want the two jumble methods in parallel.
Maybe there are other opinions.
(Thanks Jonathan for the poke.)
Now that we are in mid-beta for 16, it would be a good time to
conclude on this open item:
"Reconsider a utility_query_id GUC to control if query jumbling of
utilities can go through the past string-only mode and the new mode?"In Postgres ~15, utility commands used a hash of the query string to
compute their query ID. The current query jumbling code uses a Query
instead, like any other queries. I have registered this open item as
a self-reminder, mostly in case there would be an argument to have a
GUC where users could switch from one mode to another. See here as
well for some computation times for each method (table is in ns, wiht
millions of iterations):
/messages/by-id/Y9eeYinDb1AcpWrG@paquier.xyzI still don't think that we need both methods based on these numbers,
but there may be more opinions about that? Are people OK if this open
item is discarded?
I vote for only one method based on a query tree structure.
BTW, did you think about different algorithms of queryId generation?
Auto-generated queryId code can open a way for extensions to have
easy-supporting custom queryIds.
--
regards,
Andrey Lepikhov
Postgres Professional
On Tue, Jul 11, 2023 at 12:29:29PM +0700, Andrey Lepikhov wrote:
I vote for only one method based on a query tree structure.
Noted
BTW, did you think about different algorithms of queryId generation?
Not really, except if you are referring to the possibility of being
able to handle differently different portions of the nodes depending
on a context given by the callers willing to do a query jumbling
computation. (For example, choose to *not* silence the Const nodes,
etc.)
Auto-generated queryId code can open a way for extensions to have
easy-supporting custom queryIds.
Extensions can control that at some extent, already.
--
Michael
On 11/7/2023 12:35, Michael Paquier wrote:
On Tue, Jul 11, 2023 at 12:29:29PM +0700, Andrey Lepikhov wrote:
I vote for only one method based on a query tree structure.
Noted
BTW, did you think about different algorithms of queryId generation?
Not really, except if you are referring to the possibility of being
able to handle differently different portions of the nodes depending
on a context given by the callers willing to do a query jumbling
computation. (For example, choose to *not* silence the Const nodes,
etc.)
Yes, I have two requests on different queryId algorithms:
1. With suppressed Const nodes.
2. With replacement of Oids with full names - to give a chance to see
the same queryId at different instances for the same query.
It is quite trivial to implement, but not easy in support.
Auto-generated queryId code can open a way for extensions to have
easy-supporting custom queryIds.Extensions can control that at some extent, already.
--
Michael
--
regards,
Andrey Lepikhov
Postgres Professional
On Tue, Jul 11, 2023 at 07:35:43AM +0900, Michael Paquier wrote:
I still don't think that we need both methods based on these numbers,
but there may be more opinions about that? Are people OK if this open
item is discarded?
Hearing nothing about this point, removed from the open item list,
then.
--
Michael