On Sat, Jan 07, 2023 at 07:37:49AM +0100, Peter Eisentraut wrote: > On 07.12.22 08:56, Michael Paquier wrote: >> The location of the Nodes is quite invasive because we only care about >> that for T_Const now in the query jumbling, and this could be >> compensated with a third pg_node_attr() that tracks for the "int >> location" of a Node whether it should participate in the jumbling or >> not. > > The generation script already has a way to identify location fields, by ($t > eq 'int' && $f =~ 'location$'), so you could use that as well.
I did not recall exactly everything here, but there are two parts to the logic: - gen_node_support.pl uses exactly this condition when scanning the nodes to put the correct macro to mark a location to track, calling down RecordConstLocation(). - Marking a bunch of nodes as jumble_ignore is actually necessary, or we may finish by silencing parts of queries that should be semantically unrelevant to the queries jumbled (ColumnRef is one). Using a "jumble_ignore" flag is equally invasive to using an "jumble_include" flag for each field, I am afraid, as the number of fields in the nodes included in jumbles is pretty equivalent to the number of fields ignored. I tend to prefer the approach of ignoring things though, which is more consistent with the practive for node read, write and copy. Anyway, when it comes to the location, another thing that can be considered here would be to require a node-level flag for the nodes on which we want to track the location. This overlaps a bit with the fields satisfying "($t eq 'int' && $f =~ 'location$')", but it removes most of the code changes like this one as at the end we only care about the location for Const nodes: - int location; /* token location, or -1 if unknown */ + int location pg_node_attr(jumble_ignore); /* token location, or -1 + * if unknown */ I have taken this approach in v2 of the patch, shaving ~100 lines of more code as there is no need to mark all these location fields with "jumble_ignore" anymore, except for Const, of course. >> There is also an argument where we would want to not include by >> default new fields added to a Node, but that would require added more >> pg_node_attr() than what's done here. > > I'm concerned about the large number of additional field annotations this > adds. We have been careful so far to document the use of each attribute, > e.g., *why* does a field not need to be copied etc. This patch adds dozens > and dozens of annotations without any explanation at all. Now, the code > this replaces also has no documentation, but maybe this is the time to add > some. That's fair, though it is not doing to buy us much to update all the nodes with similar small comments, as well. As far as I know, there are basiscally three things here: typmods, collation information, and internal data of the nodes stored during parse-analyze. I have added more documentation to track what looks like the most relevant areas. I have begun running some performance tests with this stuff and HEAD to see if this leads to any difference in the query ID compilation (compute_query_id = on, on scissors roughly) with a simple set of short commands (like BEGIN/COMMIT) or longer ones, and I am seeing a speedup trend actually (?). I still need to think more about a set of tests here, but I think that micro-benchmarking of JumbleQuery() is the most adapted approach to minimize the noise, with a few nodes of various sizes (Const, Query, ColumnRef, anything..). Thoughts? -- Michael
From 28d8b2ede0c7cfe25a4efa2d49f05106d61b55d1 Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@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
signature.asc
Description: PGP signature