Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * queryjumblefuncs.c
4 : : * Query normalization and fingerprinting.
5 : : *
6 : : * Normalization is a process whereby similar queries, typically differing only
7 : : * in their constants (though the exact rules are somewhat more subtle than
8 : : * that) are recognized as equivalent, and are tracked as a single entry. This
9 : : * is particularly useful for non-prepared queries.
10 : : *
11 : : * Normalization is implemented by fingerprinting queries, selectively
12 : : * serializing those fields of each query tree's nodes that are judged to be
13 : : * essential to the query. This is referred to as a query jumble. This is
14 : : * distinct from a regular serialization in that various extraneous
15 : : * information is ignored as irrelevant or not essential to the query, such
16 : : * as the collations of Vars and, most notably, the values of constants.
17 : : *
18 : : * This jumble is acquired at the end of parse analysis of each query, and
19 : : * a 64-bit hash of it is stored into the query's Query.queryId field.
20 : : * The server then copies this value around, making it available in plan
21 : : * tree(s) generated from the query. The executor can then use this value
22 : : * to blame query costs on the proper queryId.
23 : : *
24 : : * Arrays of two or more constants and PARAM_EXTERN parameters are "squashed"
25 : : * and contribute only once to the jumble. This has the effect that queries
26 : : * that differ only on the length of such lists have the same queryId.
27 : : *
28 : : *
29 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
30 : : * Portions Copyright (c) 1994, Regents of the University of California
31 : : *
32 : : *
33 : : * IDENTIFICATION
34 : : * src/backend/nodes/queryjumblefuncs.c
35 : : *
36 : : *-------------------------------------------------------------------------
37 : : */
38 : : #include "postgres.h"
39 : :
40 : : #include "access/transam.h"
41 : : #include "catalog/pg_proc.h"
42 : : #include "common/hashfn.h"
43 : : #include "miscadmin.h"
44 : : #include "nodes/nodeFuncs.h"
45 : : #include "nodes/queryjumble.h"
46 : : #include "utils/lsyscache.h"
47 : : #include "parser/scansup.h"
48 : :
49 : : #define JUMBLE_SIZE 1024 /* query serialization buffer size */
50 : :
51 : : /* GUC parameters */
52 : : int compute_query_id = COMPUTE_QUERY_ID_AUTO;
53 : :
54 : : /*
55 : : * True when compute_query_id is ON or AUTO, and a module requests them.
56 : : *
57 : : * Note that IsQueryIdEnabled() should be used instead of checking
58 : : * query_id_enabled or compute_query_id directly when we want to know
59 : : * whether query identifiers are computed in the core or not.
60 : : */
61 : : bool query_id_enabled = false;
62 : :
63 : : static JumbleState *InitJumble(void);
64 : : static int64 DoJumble(JumbleState *jstate, Node *node);
65 : : static void AppendJumble(JumbleState *jstate,
66 : : const unsigned char *value, Size size);
67 : : static void FlushPendingNulls(JumbleState *jstate);
68 : : static void RecordConstLocation(JumbleState *jstate,
69 : : bool extern_param,
70 : : int location, int len);
71 : : static void _jumbleNode(JumbleState *jstate, Node *node);
72 : : static void _jumbleList(JumbleState *jstate, Node *node);
73 : : static void _jumbleElements(JumbleState *jstate, List *elements, Node *node);
74 : : static void _jumbleParam(JumbleState *jstate, Node *node);
75 : : static void _jumbleA_Const(JumbleState *jstate, Node *node);
76 : : static void _jumbleVariableSetStmt(JumbleState *jstate, Node *node);
77 : : static void _jumbleRangeTblEntry_eref(JumbleState *jstate,
78 : : RangeTblEntry *rte,
79 : : Alias *expr);
80 : :
81 : : /*
82 : : * Given a possibly multi-statement source string, confine our attention to the
83 : : * relevant part of the string.
84 : : */
85 : : const char *
1613 bruce@momjian.us 86 :CBC 99089 : CleanQuerytext(const char *query, int *location, int *len)
87 : : {
1578 tgl@sss.pgh.pa.us 88 : 99089 : int query_location = *location;
89 : 99089 : int query_len = *len;
90 : :
91 : : /* First apply starting offset, unless it's -1 (unknown). */
1613 bruce@momjian.us 92 [ + + ]: 99089 : if (query_location >= 0)
93 : : {
94 [ - + ]: 98902 : Assert(query_location <= strlen(query));
95 : 98902 : query += query_location;
96 : : /* Length of 0 (or -1) means "rest of string" */
97 [ + + ]: 98902 : if (query_len <= 0)
98 : 18739 : query_len = strlen(query);
99 : : else
100 [ - + ]: 80163 : Assert(query_len <= strlen(query));
101 : : }
102 : : else
103 : : {
104 : : /* If query location is unknown, distrust query_len as well */
105 : 187 : query_location = 0;
106 : 187 : query_len = strlen(query);
107 : : }
108 : :
109 : : /*
110 : : * Discard leading and trailing whitespace, too. Use scanner_isspace()
111 : : * not libc's isspace(), because we want to match the lexer's behavior.
112 : : *
113 : : * Note: the parser now strips leading comments and whitespace from the
114 : : * reported stmt_location, so this first loop will only iterate in the
115 : : * unusual case that the location didn't propagate to here. But the
116 : : * statement length will extend to the end-of-string or terminating
117 : : * semicolon, so the second loop often does something useful.
118 : : */
119 [ + - + + ]: 99090 : while (query_len > 0 && scanner_isspace(query[0]))
120 : 1 : query++, query_location++, query_len--;
121 [ + - + + ]: 99774 : while (query_len > 0 && scanner_isspace(query[query_len - 1]))
122 : 685 : query_len--;
123 : :
124 : 99089 : *location = query_location;
125 : 99089 : *len = query_len;
126 : :
127 : 99089 : return query;
128 : : }
129 : :
130 : : /*
131 : : * JumbleQuery
132 : : * Recursively process the given Query producing a 64-bit hash value by
133 : : * hashing the relevant fields and record that value in the Query's queryId
134 : : * field. Return the JumbleState object used for jumbling the query.
135 : : */
136 : : JumbleState *
801 michael@paquier.xyz 137 : 79190 : JumbleQuery(Query *query)
138 : : {
139 : : JumbleState *jstate;
140 : :
1575 alvherre@alvh.no-ip. 141 [ - + ]: 79190 : Assert(IsQueryIdEnabled());
142 : :
163 drowley@postgresql.o 143 : 79190 : jstate = InitJumble();
144 : :
145 : 79190 : query->queryId = DoJumble(jstate, (Node *) query);
146 : :
147 : : /*
148 : : * If we are unlucky enough to get a hash of zero, use 1 instead for
149 : : * normal statements and 2 for utility queries.
150 : : */
99 151 [ - + ]: 79190 : if (query->queryId == INT64CONST(0))
152 : : {
949 michael@paquier.xyz 153 [ # # ]:UBC 0 : if (query->utilityStmt)
99 drowley@postgresql.o 154 : 0 : query->queryId = INT64CONST(2);
155 : : else
156 : 0 : query->queryId = INT64CONST(1);
157 : : }
158 : :
1613 bruce@momjian.us 159 :CBC 79190 : return jstate;
160 : : }
161 : :
162 : : /*
163 : : * Enables query identifier computation.
164 : : *
165 : : * Third-party plugins can use this function to inform core that they require
166 : : * a query identifier to be computed.
167 : : */
168 : : void
1575 alvherre@alvh.no-ip. 169 : 7 : EnableQueryId(void)
170 : : {
171 [ + - ]: 7 : if (compute_query_id != COMPUTE_QUERY_ID_OFF)
172 : 7 : query_id_enabled = true;
173 : 7 : }
174 : :
175 : : /*
176 : : * InitJumble
177 : : * Allocate a JumbleState object and make it ready to jumble.
178 : : */
179 : : static JumbleState *
163 drowley@postgresql.o 180 : 79190 : InitJumble(void)
181 : : {
182 : : JumbleState *jstate;
183 : :
184 : 79190 : jstate = (JumbleState *) palloc(sizeof(JumbleState));
185 : :
186 : : /* Set up workspace for query jumbling */
187 : 79190 : jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
188 : 79190 : jstate->jumble_len = 0;
189 : 79190 : jstate->clocations_buf_size = 32;
190 : 79190 : jstate->clocations = (LocationLen *) palloc(jstate->clocations_buf_size *
191 : : sizeof(LocationLen));
192 : 79190 : jstate->clocations_count = 0;
193 : 79190 : jstate->highest_extern_param_id = 0;
194 : 79190 : jstate->pending_nulls = 0;
74 alvherre@kurilemu.de 195 : 79190 : jstate->has_squashed_lists = false;
196 : : #ifdef USE_ASSERT_CHECKING
163 drowley@postgresql.o 197 : 79190 : jstate->total_jumble_len = 0;
198 : : #endif
199 : :
200 : 79190 : return jstate;
201 : : }
202 : :
203 : : /*
204 : : * DoJumble
205 : : * Jumble the given Node using the given JumbleState and return the resulting
206 : : * jumble hash.
207 : : */
208 : : static int64
209 : 79190 : DoJumble(JumbleState *jstate, Node *node)
210 : : {
211 : : /* Jumble the given node */
212 : 79190 : _jumbleNode(jstate, node);
213 : :
214 : : /* Flush any pending NULLs before doing the final hash */
215 [ + + ]: 79190 : if (jstate->pending_nulls > 0)
216 : 78461 : FlushPendingNulls(jstate);
217 : :
218 : : /* Squashed list found, reset highest_extern_param_id */
74 alvherre@kurilemu.de 219 [ + + ]: 79190 : if (jstate->has_squashed_lists)
220 : 1435 : jstate->highest_extern_param_id = 0;
221 : :
222 : : /* Process the jumble buffer and produce the hash value */
99 drowley@postgresql.o 223 : 79190 : return DatumGetInt64(hash_any_extended(jstate->jumble,
224 : 79190 : jstate->jumble_len,
225 : : 0));
226 : : }
227 : :
228 : : /*
229 : : * AppendJumbleInternal: Internal function for appending to the jumble buffer
230 : : *
231 : : * Note: Callers must ensure that size > 0.
232 : : */
233 : : static pg_attribute_always_inline void
163 234 : 5643103 : AppendJumbleInternal(JumbleState *jstate, const unsigned char *item,
235 : : Size size)
236 : : {
1613 bruce@momjian.us 237 : 5643103 : unsigned char *jumble = jstate->jumble;
238 : 5643103 : Size jumble_len = jstate->jumble_len;
239 : :
240 : : /* Ensure the caller didn't mess up */
163 drowley@postgresql.o 241 [ - + ]: 5643103 : Assert(size > 0);
242 : :
243 : : /*
244 : : * Fast path for when there's enough space left in the buffer. This is
245 : : * worthwhile as means the memcpy can be inlined into very efficient code
246 : : * when 'size' is a compile-time constant.
247 : : */
248 [ + + ]: 5643103 : if (likely(size <= JUMBLE_SIZE - jumble_len))
249 : : {
250 : 5640020 : memcpy(jumble + jumble_len, item, size);
251 : 5640020 : jstate->jumble_len += size;
252 : :
253 : : #ifdef USE_ASSERT_CHECKING
254 : 5640020 : jstate->total_jumble_len += size;
255 : : #endif
256 : :
257 : 5640020 : return;
258 : : }
259 : :
260 : : /*
261 : : * Whenever the jumble buffer is full, we hash the current contents and
262 : : * reset the buffer to contain just that hash value, thus relying on the
263 : : * hash to summarize everything so far.
264 : : */
265 : : do
266 : : {
267 : : Size part_size;
268 : :
269 [ + + ]: 5443 : if (unlikely(jumble_len >= JUMBLE_SIZE))
270 : : {
271 : : int64 start_hash;
272 : :
99 273 : 3182 : start_hash = DatumGetInt64(hash_any_extended(jumble,
274 : : JUMBLE_SIZE, 0));
1613 bruce@momjian.us 275 : 3182 : memcpy(jumble, &start_hash, sizeof(start_hash));
276 : 3182 : jumble_len = sizeof(start_hash);
277 : : }
278 : 5443 : part_size = Min(size, JUMBLE_SIZE - jumble_len);
279 : 5443 : memcpy(jumble + jumble_len, item, part_size);
280 : 5443 : jumble_len += part_size;
281 : 5443 : item += part_size;
282 : 5443 : size -= part_size;
283 : :
284 : : #ifdef USE_ASSERT_CHECKING
163 drowley@postgresql.o 285 : 5443 : jstate->total_jumble_len += part_size;
286 : : #endif
287 [ + + ]: 5443 : } while (size > 0);
288 : :
1613 bruce@momjian.us 289 : 3083 : jstate->jumble_len = jumble_len;
290 : : }
291 : :
292 : : /*
293 : : * AppendJumble
294 : : * Add 'size' bytes of the given jumble 'value' to the jumble state
295 : : */
296 : : static pg_noinline void
163 drowley@postgresql.o 297 : 191093 : AppendJumble(JumbleState *jstate, const unsigned char *value, Size size)
298 : : {
299 [ + + ]: 191093 : if (jstate->pending_nulls > 0)
300 : 28261 : FlushPendingNulls(jstate);
301 : :
302 : 191093 : AppendJumbleInternal(jstate, value, size);
303 : 191093 : }
304 : :
305 : : /*
306 : : * AppendJumbleNull
307 : : * For jumbling NULL pointers
308 : : */
309 : : static pg_attribute_always_inline void
310 : 2532080 : AppendJumbleNull(JumbleState *jstate)
311 : : {
312 : 2532080 : jstate->pending_nulls++;
313 : 2532080 : }
314 : :
315 : : /*
316 : : * AppendJumble8
317 : : * Add the first byte from the given 'value' pointer to the jumble state
318 : : */
319 : : static pg_noinline void
320 : 520651 : AppendJumble8(JumbleState *jstate, const unsigned char *value)
321 : : {
322 [ + + ]: 520651 : if (jstate->pending_nulls > 0)
323 : 209607 : FlushPendingNulls(jstate);
324 : :
325 : 520651 : AppendJumbleInternal(jstate, value, 1);
326 : 520651 : }
327 : :
328 : : /*
329 : : * AppendJumble16
330 : : * Add the first 2 bytes from the given 'value' pointer to the jumble
331 : : * state.
332 : : */
333 : : static pg_noinline void
334 : 429012 : AppendJumble16(JumbleState *jstate, const unsigned char *value)
335 : : {
336 [ + + ]: 429012 : if (jstate->pending_nulls > 0)
337 : 18632 : FlushPendingNulls(jstate);
338 : :
339 : 429012 : AppendJumbleInternal(jstate, value, 2);
340 : 429012 : }
341 : :
342 : : /*
343 : : * AppendJumble32
344 : : * Add the first 4 bytes from the given 'value' pointer to the jumble
345 : : * state.
346 : : */
347 : : static pg_noinline void
348 : 3579641 : AppendJumble32(JumbleState *jstate, const unsigned char *value)
349 : : {
350 [ + + ]: 3579641 : if (jstate->pending_nulls > 0)
351 : 587745 : FlushPendingNulls(jstate);
352 : :
353 : 3579641 : AppendJumbleInternal(jstate, value, 4);
354 : 3579641 : }
355 : :
356 : : /*
357 : : * AppendJumble64
358 : : * Add the first 8 bytes from the given 'value' pointer to the jumble
359 : : * state.
360 : : */
361 : : static pg_noinline void
163 drowley@postgresql.o 362 :LBC (545) : AppendJumble64(JumbleState *jstate, const unsigned char *value)
363 : : {
364 [ # # ]: (545) : if (jstate->pending_nulls > 0)
163 drowley@postgresql.o 365 :UBC 0 : FlushPendingNulls(jstate);
366 : :
163 drowley@postgresql.o 367 :LBC (545) : AppendJumbleInternal(jstate, value, 8);
368 : (545) : }
369 : :
370 : : /*
371 : : * FlushPendingNulls
372 : : * Incorporate the pending_nulls value into the jumble buffer.
373 : : *
374 : : * Note: Callers must ensure that there's at least 1 pending NULL.
375 : : */
376 : : static pg_attribute_always_inline void
163 drowley@postgresql.o 377 :CBC 922706 : FlushPendingNulls(JumbleState *jstate)
378 : : {
379 [ - + ]: 922706 : Assert(jstate->pending_nulls > 0);
380 : :
381 : 922706 : AppendJumbleInternal(jstate,
382 : 922706 : (const unsigned char *) &jstate->pending_nulls, 4);
383 : 922706 : jstate->pending_nulls = 0;
384 : 922706 : }
385 : :
386 : :
387 : : /*
388 : : * Record the location of some kind of constant within a query string.
389 : : * These are not only bare constants but also expressions that ultimately
390 : : * constitute a constant, such as those inside casts and simple function
391 : : * calls; if extern_param, then it corresponds to a PARAM_EXTERN Param.
392 : : *
393 : : * If length is -1, it indicates a single such constant element. If
394 : : * it's a positive integer, it indicates the length of a squashable
395 : : * list of them.
396 : : */
397 : : static void
74 alvherre@kurilemu.de 398 : 125097 : RecordConstLocation(JumbleState *jstate, bool extern_param, int location, int len)
399 : : {
400 : : /* -1 indicates unknown or undefined location */
949 michael@paquier.xyz 401 [ + + ]: 125097 : if (location >= 0)
402 : : {
403 : : /* enlarge array if needed */
404 [ + + ]: 118054 : if (jstate->clocations_count >= jstate->clocations_buf_size)
405 : : {
406 : 59 : jstate->clocations_buf_size *= 2;
407 : 59 : jstate->clocations = (LocationLen *)
408 : 59 : repalloc(jstate->clocations,
409 : 59 : jstate->clocations_buf_size *
410 : : sizeof(LocationLen));
411 : : }
412 : 118054 : jstate->clocations[jstate->clocations_count].location = location;
413 : :
414 : : /*
415 : : * Lengths are either positive integers (indicating a squashable
416 : : * list), or -1.
417 : : */
86 alvherre@kurilemu.de 418 [ + + - + ]: 118054 : Assert(len > -1 || len == -1);
419 : 118054 : jstate->clocations[jstate->clocations_count].length = len;
420 : 118054 : jstate->clocations[jstate->clocations_count].squashed = (len > -1);
74 421 : 118054 : jstate->clocations[jstate->clocations_count].extern_param = extern_param;
949 michael@paquier.xyz 422 : 118054 : jstate->clocations_count++;
423 : : }
1613 bruce@momjian.us 424 : 125097 : }
425 : :
426 : : /*
427 : : * Subroutine for _jumbleElements: Verify a few simple cases where we can
428 : : * deduce that the expression is a constant:
429 : : *
430 : : * - See through any wrapping RelabelType and CoerceViaIO layers.
431 : : * - If it's a FuncExpr, check that the function is a builtin
432 : : * cast and its arguments are Const.
433 : : * - Otherwise test if the expression is a simple Const or a
434 : : * PARAM_EXTERN param.
435 : : */
436 : : static bool
86 alvherre@kurilemu.de 437 : 6404 : IsSquashableConstant(Node *element)
438 : : {
74 439 : 300 : restart:
86 440 [ + + + + : 6704 : switch (nodeTag(element))
+ + ]
441 : : {
74 442 : 230 : case T_RelabelType:
443 : : /* Unwrap RelabelType */
444 : 230 : element = (Node *) ((RelabelType *) element)->arg;
445 : 230 : goto restart;
446 : :
447 : 70 : case T_CoerceViaIO:
448 : : /* Unwrap CoerceViaIO */
449 : 70 : element = (Node *) ((CoerceViaIO *) element)->arg;
450 : 70 : goto restart;
451 : :
452 : 5921 : case T_Const:
453 : 5921 : return true;
454 : :
455 : 73 : case T_Param:
456 : 73 : return castNode(Param, element)->paramkind == PARAM_EXTERN;
457 : :
86 458 : 306 : case T_FuncExpr:
459 : : {
460 : 306 : FuncExpr *func = (FuncExpr *) element;
461 : : ListCell *temp;
462 : :
463 [ + + ]: 306 : if (func->funcformat != COERCE_IMPLICIT_CAST &&
464 [ + + ]: 190 : func->funcformat != COERCE_EXPLICIT_CAST)
465 : 127 : return false;
466 : :
467 [ - + ]: 179 : if (func->funcid > FirstGenbkiObjectId)
86 alvherre@kurilemu.de 468 :UBC 0 : return false;
469 : :
470 : : /*
471 : : * We can check function arguments recursively, being careful
472 : : * about recursing too deep. At each recursion level it's
473 : : * enough to test the stack on the first element. (Note that
474 : : * I wasn't able to hit this without bloating the stack
475 : : * artificially in this function: the parser errors out before
476 : : * stack size becomes a problem here.)
477 : : */
86 alvherre@kurilemu.de 478 [ + - + + :CBC 355 : foreach(temp, func->args)
+ + ]
479 : : {
480 : 179 : Node *arg = lfirst(temp);
481 : :
482 [ + + ]: 179 : if (!IsA(arg, Const))
483 : : {
484 [ + - - + ]: 14 : if (foreach_current_index(temp) == 0 &&
485 : 7 : stack_is_too_deep())
486 : 3 : return false;
487 [ + + ]: 7 : else if (!IsSquashableConstant(arg))
488 : 3 : return false;
489 : : }
490 : : }
491 : :
492 : 176 : return true;
493 : : }
494 : :
495 : 104 : default:
74 496 : 104 : return false;
497 : : }
498 : : }
499 : :
500 : : /*
501 : : * Subroutine for _jumbleElements: Verify whether the provided list
502 : : * can be squashed, meaning it contains only constant expressions.
503 : : *
504 : : * Return value indicates if squashing is possible.
505 : : *
506 : : * Note that this function searches only for explicit Const nodes with
507 : : * possibly very simple decorations on top and PARAM_EXTERN parameters,
508 : : * and does not try to simplify expressions.
509 : : */
510 : : static bool
86 511 : 2413 : IsSquashableConstantList(List *elements)
512 : : {
513 : : ListCell *temp;
514 : :
515 : : /* If the list is too short, we don't try to squash it. */
163 alvherre@alvh.no-ip. 516 [ + + ]: 2413 : if (list_length(elements) < 2)
172 517 : 220 : return false;
518 : :
519 [ + - + + : 8359 : foreach(temp, elements)
+ + ]
520 : : {
86 alvherre@kurilemu.de 521 [ + + ]: 6397 : if (!IsSquashableConstant(lfirst(temp)))
172 alvherre@alvh.no-ip. 522 : 231 : return false;
523 : : }
524 : :
525 : 1962 : return true;
526 : : }
527 : :
528 : : #define JUMBLE_NODE(item) \
529 : : _jumbleNode(jstate, (Node *) expr->item)
530 : : #define JUMBLE_ELEMENTS(list, node) \
531 : : _jumbleElements(jstate, (List *) expr->list, node)
532 : : #define JUMBLE_LOCATION(location) \
533 : : RecordConstLocation(jstate, false, expr->location, -1)
534 : : #define JUMBLE_FIELD(item) \
535 : : do { \
536 : : if (sizeof(expr->item) == 8) \
537 : : AppendJumble64(jstate, (const unsigned char *) &(expr->item)); \
538 : : else if (sizeof(expr->item) == 4) \
539 : : AppendJumble32(jstate, (const unsigned char *) &(expr->item)); \
540 : : else if (sizeof(expr->item) == 2) \
541 : : AppendJumble16(jstate, (const unsigned char *) &(expr->item)); \
542 : : else if (sizeof(expr->item) == 1) \
543 : : AppendJumble8(jstate, (const unsigned char *) &(expr->item)); \
544 : : else \
545 : : AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item)); \
546 : : } while (0)
547 : : #define JUMBLE_STRING(str) \
548 : : do { \
549 : : if (expr->str) \
550 : : AppendJumble(jstate, (const unsigned char *) (expr->str), strlen(expr->str) + 1); \
551 : : else \
552 : : AppendJumbleNull(jstate); \
553 : : } while(0)
554 : : /* Function name used for the node field attribute custom_query_jumble. */
555 : : #define JUMBLE_CUSTOM(nodetype, item) \
556 : : _jumble##nodetype##_##item(jstate, expr, expr->item)
557 : :
558 : : #include "queryjumblefuncs.funcs.c"
559 : :
560 : : static void
949 michael@paquier.xyz 561 : 3898017 : _jumbleNode(JumbleState *jstate, Node *node)
562 : : {
563 : 3898017 : Node *expr = node;
564 : : #ifdef USE_ASSERT_CHECKING
163 drowley@postgresql.o 565 : 3898017 : Size prev_jumble_len = jstate->total_jumble_len;
566 : : #endif
567 : :
949 michael@paquier.xyz 568 [ + + ]: 3898017 : if (expr == NULL)
569 : : {
163 drowley@postgresql.o 570 : 2286099 : AppendJumbleNull(jstate);
1613 bruce@momjian.us 571 : 2286099 : return;
572 : : }
573 : :
574 : : /* Guard against stack overflow due to overly complex expressions */
575 : 1611918 : check_stack_depth();
576 : :
577 : : /*
578 : : * We always emit the node's NodeTag, then any additional fields that are
579 : : * considered significant, and then we recurse to any child nodes.
580 : : */
949 michael@paquier.xyz 581 : 1611918 : JUMBLE_FIELD(type);
582 : :
583 [ + + + + : 1611918 : switch (nodeTag(expr))
+ + + + +
+ - + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ - - - +
+ + + - +
+ - + - +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ - + + -
+ + - + +
+ + + + +
- - + + +
+ + + + +
+ + + + -
- - + + +
+ + + + +
+ + + + +
- + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
- + + + +
+ + + + +
+ + + + +
+ + + + +
+ - + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
- - + + +
+ - + - ]
584 : : {
585 : : #include "queryjumblefuncs.switch.c"
586 : :
587 : 383360 : case T_List:
588 : : case T_IntList:
589 : : case T_OidList:
590 : : case T_XidList:
591 : 383360 : _jumbleList(jstate, expr);
1613 bruce@momjian.us 592 : 383360 : break;
593 : :
949 michael@paquier.xyz 594 :UBC 0 : default:
595 : : /* Only a warning, since we can stumble along anyway */
596 [ # # ]: 0 : elog(WARNING, "unrecognized node type: %d",
597 : : (int) nodeTag(expr));
1613 bruce@momjian.us 598 : 0 : break;
599 : : }
600 : :
601 : : /* Ensure we added something to the jumble buffer */
163 drowley@postgresql.o 602 [ - + ]:CBC 1611918 : Assert(jstate->total_jumble_len > prev_jumble_len);
603 : : }
604 : :
605 : : static void
949 michael@paquier.xyz 606 : 383360 : _jumbleList(JumbleState *jstate, Node *node)
607 : : {
608 : 383360 : List *expr = (List *) node;
609 : : ListCell *l;
610 : :
611 [ + + - - : 383360 : switch (expr->type)
- ]
612 : : {
613 : 382886 : case T_List:
614 [ + - + + : 1080139 : foreach(l, expr)
+ + ]
615 : 697253 : _jumbleNode(jstate, lfirst(l));
1613 bruce@momjian.us 616 : 382886 : break;
949 michael@paquier.xyz 617 : 474 : case T_IntList:
618 [ + - + + : 1056 : foreach(l, expr)
+ + ]
163 drowley@postgresql.o 619 : 582 : AppendJumble32(jstate, (const unsigned char *) &lfirst_int(l));
1613 bruce@momjian.us 620 : 474 : break;
949 michael@paquier.xyz 621 :UBC 0 : case T_OidList:
622 [ # # # # : 0 : foreach(l, expr)
# # ]
163 drowley@postgresql.o 623 : 0 : AppendJumble32(jstate, (const unsigned char *) &lfirst_oid(l));
1613 bruce@momjian.us 624 : 0 : break;
949 michael@paquier.xyz 625 : 0 : case T_XidList:
626 [ # # # # : 0 : foreach(l, expr)
# # ]
163 drowley@postgresql.o 627 : 0 : AppendJumble32(jstate, (const unsigned char *) &lfirst_xid(l));
1613 bruce@momjian.us 628 : 0 : break;
949 michael@paquier.xyz 629 : 0 : default:
630 [ # # ]: 0 : elog(ERROR, "unrecognized list node type: %d",
631 : : (int) expr->type);
632 : : return;
633 : : }
634 : : }
635 : :
636 : : /*
637 : : * We try to jumble lists of expressions as one individual item regardless
638 : : * of how many elements are in the list. This is know as squashing, which
639 : : * results in different queries jumbling to the same query_id, if the only
640 : : * difference is the number of elements in the list.
641 : : *
642 : : * We allow constants and PARAM_EXTERN parameters to be squashed. To normalize
643 : : * such queries, we use the start and end locations of the list of elements in
644 : : * a list.
645 : : */
646 : : static void
74 alvherre@kurilemu.de 647 :CBC 2413 : _jumbleElements(JumbleState *jstate, List *elements, Node *node)
648 : : {
649 : 2413 : bool normalize_list = false;
650 : :
651 [ + + ]: 2413 : if (IsSquashableConstantList(elements))
652 : : {
653 [ + - ]: 1962 : if (IsA(node, ArrayExpr))
654 : : {
655 : 1962 : ArrayExpr *aexpr = (ArrayExpr *) node;
656 : :
657 [ + + + - ]: 1962 : if (aexpr->list_start > 0 && aexpr->list_end > 0)
658 : : {
659 : 1923 : RecordConstLocation(jstate,
660 : : false,
661 : 1923 : aexpr->list_start + 1,
662 : 1923 : (aexpr->list_end - aexpr->list_start) - 1);
663 : 1923 : normalize_list = true;
664 : 1923 : jstate->has_squashed_lists = true;
665 : : }
666 : : }
667 : : }
668 : :
669 [ + + ]: 2413 : if (!normalize_list)
670 : : {
671 : 490 : _jumbleNode(jstate, (Node *) elements);
672 : : }
673 : 2413 : }
674 : :
675 : : /*
676 : : * We store the highest param ID of extern params. This can later be used
677 : : * to start the numbering of the placeholder for squashed lists.
678 : : */
679 : : static void
680 : 5244 : _jumbleParam(JumbleState *jstate, Node *node)
681 : : {
682 : 5244 : Param *expr = (Param *) node;
683 : :
684 : 5244 : JUMBLE_FIELD(paramkind);
685 : 5244 : JUMBLE_FIELD(paramid);
686 : 5244 : JUMBLE_FIELD(paramtype);
687 : : /* paramtypmode and paramcollid are ignored */
688 : :
689 [ + + ]: 5244 : if (expr->paramkind == PARAM_EXTERN)
690 : : {
691 : : /*
692 : : * At this point, only external parameter locations outside of
693 : : * squashable lists will be recorded.
694 : : */
695 : 4367 : RecordConstLocation(jstate, true, expr->location, -1);
696 : :
697 : : /*
698 : : * Update the highest Param id seen, in order to start normalization
699 : : * correctly.
700 : : *
701 : : * Note: This value is reset at the end of jumbling if there exists a
702 : : * squashable list. See the comment in the definition of JumbleState.
703 : : */
704 [ + + ]: 4367 : if (expr->paramid > jstate->highest_extern_param_id)
705 : 3767 : jstate->highest_extern_param_id = expr->paramid;
706 : : }
707 : 5244 : }
708 : :
709 : : static void
942 michael@paquier.xyz 710 : 8524 : _jumbleA_Const(JumbleState *jstate, Node *node)
711 : : {
712 : 8524 : A_Const *expr = (A_Const *) node;
713 : :
714 : 8524 : JUMBLE_FIELD(isnull);
715 [ + + ]: 8524 : if (!expr->isnull)
716 : : {
717 : 8438 : JUMBLE_FIELD(val.node.type);
718 [ + + + + : 8438 : switch (nodeTag(&expr->val))
+ - ]
719 : : {
720 : 3850 : case T_Integer:
721 : 3850 : JUMBLE_FIELD(val.ival.ival);
722 : 3850 : break;
723 : 29 : case T_Float:
724 [ + - ]: 29 : JUMBLE_STRING(val.fval.fval);
725 : 29 : break;
726 : 130 : case T_Boolean:
727 : 130 : JUMBLE_FIELD(val.boolval.boolval);
728 : 130 : break;
729 : 4427 : case T_String:
730 [ + - ]: 4427 : JUMBLE_STRING(val.sval.sval);
731 : 4427 : break;
732 : 2 : case T_BitString:
733 [ + - ]: 2 : JUMBLE_STRING(val.bsval.bsval);
734 : 2 : break;
942 michael@paquier.xyz 735 :UBC 0 : default:
736 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
737 : : (int) nodeTag(&expr->val));
738 : : break;
739 : : }
740 : : }
942 michael@paquier.xyz 741 :CBC 8524 : }
742 : :
743 : : static void
341 744 : 2614 : _jumbleVariableSetStmt(JumbleState *jstate, Node *node)
745 : : {
746 : 2614 : VariableSetStmt *expr = (VariableSetStmt *) node;
747 : :
748 : 2614 : JUMBLE_FIELD(kind);
749 [ + + ]: 2614 : JUMBLE_STRING(name);
750 : :
751 : : /*
752 : : * Account for the list of arguments in query jumbling only if told by the
753 : : * parser.
754 : : */
755 [ + + ]: 2614 : if (expr->jumble_args)
756 : 60 : JUMBLE_NODE(args);
757 : 2614 : JUMBLE_FIELD(is_local);
758 : 2614 : JUMBLE_LOCATION(location);
759 : 2614 : }
760 : :
761 : : /*
762 : : * Custom query jumble function for RangeTblEntry.eref.
763 : : */
764 : : static void
164 765 : 80361 : _jumbleRangeTblEntry_eref(JumbleState *jstate,
766 : : RangeTblEntry *rte,
767 : : Alias *expr)
768 : : {
769 : 80361 : JUMBLE_FIELD(type);
770 : :
771 : : /*
772 : : * This includes only the table name, the list of column names is ignored.
773 : : */
774 [ + - ]: 80361 : JUMBLE_STRING(aliasname);
775 : 80361 : }
|