Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitf31aad9

Browse files
committed
Fix query jumbling to account for NULL nodes
Previously NULL nodes were ignored. This could cause issues where thecomputed query ID could match for queries where fields that are next toeach other in their Node struct where one field was NULL and the othernon-NULL. For example, the Query struct had distinctClause and sortClausenext to each other. If someone wrote;SELECT DISTINCT c1 FROM t;and then;SELECT c1 FROM t ORDER BY c1;these would produce the same query ID since, in the first query, weignored the NULL sortClause and appended the jumble bytes for thedistictClause. In the latter query, since we did nothing for the NULLdistinctClause then jumble the non-NULL sortClause, and since the noderepresentation stored is the same in both cases, the query IDs wereidentical.Here we fix this by always accounting for NULL nodes by recording thatwe saw a NULL in the jumble buffer. This fixes the issue as the order thatthe NULL is recorded isn't the same in the above two queries.Author: Bykov Ivan <i.bykov@modernsys.ru>Author: Michael Paquier <michael@paquier.xyz>Author: David Rowley <dgrowleyml@gmail.com>Discussion:https://postgr.es/m/aafce7966e234372b2ba876c0193f1e9%40localhost.localdomain
1 parent44fe6ce commitf31aad9

File tree

4 files changed

+238
-21
lines changed

4 files changed

+238
-21
lines changed

‎contrib/pg_stat_statements/expected/select.out

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,86 @@ SELECT 1 AS "int";
1919
1
2020
(1 row)
2121

22+
-- LIMIT and OFFSET patterns
23+
-- Try some query permutations which once produced identical query IDs
24+
SELECT 1 AS "int" LIMIT 1;
25+
int
26+
-----
27+
1
28+
(1 row)
29+
30+
SELECT 1 AS "int" LIMIT 2;
31+
int
32+
-----
33+
1
34+
(1 row)
35+
36+
SELECT 1 AS "int" OFFSET 1;
37+
int
38+
-----
39+
(0 rows)
40+
41+
SELECT 1 AS "int" OFFSET 2;
42+
int
43+
-----
44+
(0 rows)
45+
46+
SELECT 1 AS "int" OFFSET 1 LIMIT 1;
47+
int
48+
-----
49+
(0 rows)
50+
51+
SELECT 1 AS "int" OFFSET 2 LIMIT 2;
52+
int
53+
-----
54+
(0 rows)
55+
56+
SELECT 1 AS "int" LIMIT 1 OFFSET 1;
57+
int
58+
-----
59+
(0 rows)
60+
61+
SELECT 1 AS "int" LIMIT 3 OFFSET 3;
62+
int
63+
-----
64+
(0 rows)
65+
66+
SELECT 1 AS "int" OFFSET 1 FETCH FIRST 2 ROW ONLY;
67+
int
68+
-----
69+
(0 rows)
70+
71+
SELECT 1 AS "int" OFFSET 2 FETCH FIRST 3 ROW ONLY;
72+
int
73+
-----
74+
(0 rows)
75+
76+
-- DISTINCT and ORDER BY patterns
77+
-- Try some query permutations which once produced identical query IDs
78+
SELECT DISTINCT 1 AS "int";
79+
int
80+
-----
81+
1
82+
(1 row)
83+
84+
SELECT DISTINCT 2 AS "int";
85+
int
86+
-----
87+
2
88+
(1 row)
89+
90+
SELECT 1 AS "int" ORDER BY 1;
91+
int
92+
-----
93+
1
94+
(1 row)
95+
96+
SELECT 2 AS "int" ORDER BY 1;
97+
int
98+
-----
99+
2
100+
(1 row)
101+
22102
/* this comment should not appear in the output */
23103
SELECT 'hello'
24104
-- but this one will appear
@@ -135,17 +215,22 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
135215
3 | 3 | SELECT $1 + $2 + $3 AS "add"
136216
1 | 1 | SELECT $1 AS "float"
137217
2 | 2 | SELECT $1 AS "int"
218+
2 | 2 | SELECT $1 AS "int" LIMIT $2
219+
2 | 0 | SELECT $1 AS "int" OFFSET $2
220+
6 | 0 | SELECT $1 AS "int" OFFSET $2 LIMIT $3
221+
2 | 2 | SELECT $1 AS "int" ORDER BY 1
138222
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
139223
1 | 1 | SELECT $1 || $2
140224
1 | 1 | SELECT $1, $2 LIMIT $3
225+
2 | 2 | SELECT DISTINCT $1 AS "int"
141226
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
142227
1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
143228
1 | 2 | WITH t(f) AS ( +
144229
| | VALUES ($1), ($2) +
145230
| | ) +
146231
| | SELECT f FROM t ORDER BY f
147232
1 | 1 | select $1::jsonb ? $2
148-
(12 rows)
233+
(17 rows)
149234

150235
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
151236
t

‎contrib/pg_stat_statements/sql/select.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
1212
--
1313
SELECT1AS"int";
1414

15+
-- LIMIT and OFFSET patterns
16+
-- Try some query permutations which once produced identical query IDs
17+
SELECT1AS"int"LIMIT1;
18+
SELECT1AS"int"LIMIT2;
19+
SELECT1AS"int" OFFSET1;
20+
SELECT1AS"int" OFFSET2;
21+
SELECT1AS"int" OFFSET1LIMIT1;
22+
SELECT1AS"int" OFFSET2LIMIT2;
23+
SELECT1AS"int"LIMIT1 OFFSET1;
24+
SELECT1AS"int"LIMIT3 OFFSET3;
25+
SELECT1AS"int" OFFSET1 FETCH FIRST2 ROW ONLY;
26+
SELECT1AS"int" OFFSET2 FETCH FIRST3 ROW ONLY;
27+
28+
-- DISTINCT and ORDER BY patterns
29+
-- Try some query permutations which once produced identical query IDs
30+
SELECT DISTINCT1AS"int";
31+
SELECT DISTINCT2AS"int";
32+
SELECT1AS"int"ORDER BY1;
33+
SELECT2AS"int"ORDER BY1;
34+
1535
/* this comment should not appear in the output*/
1636
SELECT'hello'
1737
-- but this one will appear

‎src/backend/nodes/queryjumblefuncs.c

Lines changed: 120 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,11 @@ boolquery_id_squash_values = false;
5858
*/
5959
boolquery_id_enabled= false;
6060

61+
staticJumbleState*InitJumble(void);
62+
staticuint64DoJumble(JumbleState*jstate,Node*node);
6163
staticvoidAppendJumble(JumbleState*jstate,
6264
constunsignedchar*item,Sizesize);
65+
staticvoidFlushPendingNulls(JumbleState*jstate);
6366
staticvoidRecordConstLocation(JumbleState*jstate,
6467
intlocation,boolmerged);
6568
staticvoid_jumbleNode(JumbleState*jstate,Node*node);
@@ -120,29 +123,22 @@ CleanQuerytext(const char *query, int *location, int *len)
120123
returnquery;
121124
}
122125

126+
/*
127+
* JumbleQuery
128+
*Recursively process the given Query producing a 64-bit hash value by
129+
*hashing the relevant fields and record that value in the Query's queryId
130+
*field. Return the JumbleState object used for jumbling the query.
131+
*/
123132
JumbleState*
124133
JumbleQuery(Query*query)
125134
{
126-
JumbleState*jstate=NULL;
135+
JumbleState*jstate;
127136

128137
Assert(IsQueryIdEnabled());
129138

130-
jstate=(JumbleState*)palloc(sizeof(JumbleState));
139+
jstate=InitJumble();
131140

132-
/* Set up workspace for query jumbling */
133-
jstate->jumble= (unsignedchar*)palloc(JUMBLE_SIZE);
134-
jstate->jumble_len=0;
135-
jstate->clocations_buf_size=32;
136-
jstate->clocations= (LocationLen*)
137-
palloc(jstate->clocations_buf_size*sizeof(LocationLen));
138-
jstate->clocations_count=0;
139-
jstate->highest_extern_param_id=0;
140-
141-
/* Compute query ID and mark the Query node with it */
142-
_jumbleNode(jstate, (Node*)query);
143-
query->queryId=DatumGetUInt64(hash_any_extended(jstate->jumble,
144-
jstate->jumble_len,
145-
0));
141+
query->queryId=DoJumble(jstate, (Node*)query);
146142

147143
/*
148144
* If we are unlucky enough to get a hash of zero, use 1 instead for
@@ -173,11 +169,59 @@ EnableQueryId(void)
173169
}
174170

175171
/*
176-
*AppendJumble: Append a value that is substantive in a given query to
177-
* the current jumble.
172+
*InitJumble
173+
*Allocate a JumbleState object and make it ready to jumble.
178174
*/
179-
staticvoid
180-
AppendJumble(JumbleState*jstate,constunsignedchar*item,Sizesize)
175+
staticJumbleState*
176+
InitJumble(void)
177+
{
178+
JumbleState*jstate;
179+
180+
jstate= (JumbleState*)palloc(sizeof(JumbleState));
181+
182+
/* Set up workspace for query jumbling */
183+
jstate->jumble= (unsignedchar*)palloc(JUMBLE_SIZE);
184+
jstate->jumble_len=0;
185+
jstate->clocations_buf_size=32;
186+
jstate->clocations= (LocationLen*)palloc(jstate->clocations_buf_size*
187+
sizeof(LocationLen));
188+
jstate->clocations_count=0;
189+
jstate->highest_extern_param_id=0;
190+
jstate->pending_nulls=0;
191+
#ifdefUSE_ASSERT_CHECKING
192+
jstate->total_jumble_len=0;
193+
#endif
194+
195+
returnjstate;
196+
}
197+
198+
/*
199+
* DoJumble
200+
*Jumble the given Node using the given JumbleState and return the resulting
201+
*jumble hash.
202+
*/
203+
staticuint64
204+
DoJumble(JumbleState*jstate,Node*node)
205+
{
206+
/* Jumble the given node */
207+
_jumbleNode(jstate,node);
208+
209+
/* Flush any pending NULLs before doing the final hash */
210+
if (jstate->pending_nulls>0)
211+
FlushPendingNulls(jstate);
212+
213+
/* Process the jumble buffer and produce the hash value */
214+
returnDatumGetUInt64(hash_any_extended(jstate->jumble,
215+
jstate->jumble_len,
216+
0));
217+
}
218+
219+
/*
220+
* AppendJumbleInternal: Internal function for appending to the jumble buffer
221+
*/
222+
staticpg_attribute_always_inlinevoid
223+
AppendJumbleInternal(JumbleState*jstate,constunsignedchar*item,
224+
Sizesize)
181225
{
182226
unsignedchar*jumble=jstate->jumble;
183227
Sizejumble_len=jstate->jumble_len;
@@ -205,10 +249,55 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size)
205249
jumble_len+=part_size;
206250
item+=part_size;
207251
size-=part_size;
252+
253+
#ifdefUSE_ASSERT_CHECKING
254+
jstate->total_jumble_len+=part_size;
255+
#endif
208256
}
257+
209258
jstate->jumble_len=jumble_len;
210259
}
211260

261+
/*
262+
* AppendJumble
263+
*Add 'size' bytes of the given jumble 'value' to the jumble state
264+
*/
265+
staticpg_noinlinevoid
266+
AppendJumble(JumbleState*jstate,constunsignedchar*value,Sizesize)
267+
{
268+
if (jstate->pending_nulls>0)
269+
FlushPendingNulls(jstate);
270+
271+
AppendJumbleInternal(jstate,value,size);
272+
}
273+
274+
/*
275+
* AppendJumbleNull
276+
*For jumbling NULL pointers
277+
*/
278+
staticpg_attribute_always_inlinevoid
279+
AppendJumbleNull(JumbleState*jstate)
280+
{
281+
jstate->pending_nulls++;
282+
}
283+
284+
/*
285+
* FlushPendingNulls
286+
*Incorporate the pending_null value into the jumble buffer.
287+
*
288+
* Note: Callers must ensure that there's at least 1 pending NULL.
289+
*/
290+
staticpg_attribute_always_inlinevoid
291+
FlushPendingNulls(JumbleState*jstate)
292+
{
293+
Assert(jstate->pending_nulls>0);
294+
295+
AppendJumbleInternal(jstate,
296+
(constunsignedchar*)&jstate->pending_nulls,4);
297+
jstate->pending_nulls=0;
298+
}
299+
300+
212301
/*
213302
* Record location of constant within query string of query tree that is
214303
* currently being walked.
@@ -335,6 +424,8 @@ IsSquashableConstList(List *elements, Node **firstExpr, Node **lastExpr)
335424
do { \
336425
if (expr->str) \
337426
AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \
427+
else \
428+
AppendJumbleNull(jstate); \
338429
} while(0)
339430
/* Function name used for the node field attribute custom_query_jumble. */
340431
#defineJUMBLE_CUSTOM(nodetype,item) \
@@ -385,9 +476,15 @@ static void
385476
_jumbleNode(JumbleState*jstate,Node*node)
386477
{
387478
Node*expr=node;
479+
#ifdefUSE_ASSERT_CHECKING
480+
Sizeprev_jumble_len=jstate->total_jumble_len;
481+
#endif
388482

389483
if (expr==NULL)
484+
{
485+
AppendJumbleNull(jstate);
390486
return;
487+
}
391488

392489
/* Guard against stack overflow due to overly complex expressions */
393490
check_stack_depth();
@@ -435,6 +532,9 @@ _jumbleNode(JumbleState *jstate, Node *node)
435532
default:
436533
break;
437534
}
535+
536+
/* Ensure we added something to the jumble buffer */
537+
Assert(jstate->total_jumble_len>prev_jumble_len);
438538
}
439539

440540
staticvoid

‎src/include/nodes/queryjumble.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ typedef struct JumbleState
5454

5555
/* highest Param id we've seen, in order to start normalization correctly */
5656
inthighest_extern_param_id;
57+
58+
/*
59+
* Count of the number of NULL nodes seen since last appending a value.
60+
* These are flushed out to the jumble buffer before subsequent appends
61+
* and before performing the final jumble hash.
62+
*/
63+
unsignedintpending_nulls;
64+
65+
#ifdefUSE_ASSERT_CHECKING
66+
/* The total number of bytes added to the jumble buffer */
67+
Sizetotal_jumble_len;
68+
#endif
5769
}JumbleState;
5870

5971
/* Values for the compute_query_id GUC */

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp