v2-0001-Support-for-automated-query-jumble-with-all-Nodes.patch
text/x-diff
Filename: v2-0001-Support-for-automated-query-jumble-with-all-Nodes.patch
Type: text/x-diff
Part: 0
Patch
Format: format-patch
Series: patch v2-0001
Subject: Support for automated query jumble with all Nodes
| File | + | − |
|---|---|---|
| src/backend/nodes/gen_node_support.pl | 104 | 1 |
| src/backend/nodes/jumblefuncs.c | 358 | 0 |
| src/backend/nodes/Makefile | 1 | 0 |
| src/backend/nodes/meson.build | 1 | 0 |
| src/backend/nodes/README | 1 | 0 |
| src/backend/utils/misc/Makefile | 0 | 1 |
| src/backend/utils/misc/meson.build | 0 | 1 |
| src/backend/utils/misc/queryjumble.c | 0 | 861 |
| src/include/nodes/bitmapset.h | 1 | 1 |
| src/include/nodes/nodes.h | 7 | 0 |
| src/include/nodes/parsenodes.h | 124 | 60 |
| src/include/nodes/primnodes.h | 246 | 125 |
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