From 28d8b2ede0c7cfe25a4efa2d49f05106d61b55d1 Mon Sep 17 00:00:00 2001 From: Michael Paquier 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