Branch data 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-2026, 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 "common/int.h"
44 : : #include "miscadmin.h"
45 : : #include "nodes/nodeFuncs.h"
46 : : #include "nodes/queryjumble.h"
47 : : #include "utils/lsyscache.h"
48 : : #include "parser/scanner.h"
49 : : #include "parser/scansup.h"
50 : :
51 : : #define JUMBLE_SIZE 1024 /* query serialization buffer size */
52 : :
53 : : /* GUC parameters */
54 : : int compute_query_id = COMPUTE_QUERY_ID_AUTO;
55 : :
56 : : /*
57 : : * True when compute_query_id is ON or AUTO, and a module requests them.
58 : : *
59 : : * Note that IsQueryIdEnabled() should be used instead of checking
60 : : * query_id_enabled or compute_query_id directly when we want to know
61 : : * whether query identifiers are computed in the core or not.
62 : : */
63 : : bool query_id_enabled = false;
64 : :
65 : : static JumbleState *InitJumble(void);
66 : : static int64 DoJumble(JumbleState *jstate, Node *node);
67 : : static void AppendJumble(JumbleState *jstate,
68 : : const unsigned char *value, Size size);
69 : : static void FlushPendingNulls(JumbleState *jstate);
70 : : static void RecordConstLocation(JumbleState *jstate,
71 : : bool extern_param,
72 : : int location, int len);
73 : : static void _jumbleNode(JumbleState *jstate, Node *node);
74 : : static void _jumbleList(JumbleState *jstate, Node *node);
75 : : static void _jumbleElements(JumbleState *jstate, List *elements, Node *node);
76 : : static void _jumbleParam(JumbleState *jstate, Node *node);
77 : : static void _jumbleA_Const(JumbleState *jstate, Node *node);
78 : : static void _jumbleVariableSetStmt(JumbleState *jstate, Node *node);
79 : : static void _jumbleRangeTblEntry_eref(JumbleState *jstate,
80 : : RangeTblEntry *rte,
81 : : Alias *expr);
82 : :
83 : : /*
84 : : * Given a possibly multi-statement source string, confine our attention to the
85 : : * relevant part of the string.
86 : : */
87 : : const char *
88 : 106040 : CleanQuerytext(const char *query, int *location, int *len)
89 : : {
90 : 106040 : int query_location = *location;
91 : 106040 : int query_len = *len;
92 : :
93 : : /* First apply starting offset, unless it's -1 (unknown). */
94 [ + + ]: 106040 : if (query_location >= 0)
95 : : {
96 : : Assert(query_location <= strlen(query));
97 : 105837 : query += query_location;
98 : : /* Length of 0 (or -1) means "rest of string" */
99 [ + + ]: 105837 : if (query_len <= 0)
100 : 16763 : query_len = strlen(query);
101 : : else
102 : : Assert(query_len <= strlen(query));
103 : : }
104 : : else
105 : : {
106 : : /* If query location is unknown, distrust query_len as well */
107 : 203 : query_location = 0;
108 : 203 : query_len = strlen(query);
109 : : }
110 : :
111 : : /*
112 : : * Discard leading and trailing whitespace, too. Use scanner_isspace()
113 : : * not libc's isspace(), because we want to match the lexer's behavior.
114 : : *
115 : : * Note: the parser now strips leading comments and whitespace from the
116 : : * reported stmt_location, so this first loop will only iterate in the
117 : : * unusual case that the location didn't propagate to here. But the
118 : : * statement length will extend to the end-of-string or terminating
119 : : * semicolon, so the second loop often does something useful.
120 : : */
121 [ + - + + ]: 106041 : while (query_len > 0 && scanner_isspace(query[0]))
122 : 1 : query++, query_location++, query_len--;
123 [ + - + + ]: 106866 : while (query_len > 0 && scanner_isspace(query[query_len - 1]))
124 : 826 : query_len--;
125 : :
126 : 106040 : *location = query_location;
127 : 106040 : *len = query_len;
128 : :
129 : 106040 : return query;
130 : : }
131 : :
132 : : /*
133 : : * JumbleQuery
134 : : * Recursively process the given Query producing a 64-bit hash value by
135 : : * hashing the relevant fields and record that value in the Query's queryId
136 : : * field. Return the JumbleState object used for jumbling the query.
137 : : */
138 : : JumbleState *
139 : 85696 : JumbleQuery(Query *query)
140 : : {
141 : : JumbleState *jstate;
142 : :
143 : : Assert(IsQueryIdEnabled());
144 : :
145 : 85696 : jstate = InitJumble();
146 : :
147 : 85696 : query->queryId = DoJumble(jstate, (Node *) query);
148 : :
149 : : /*
150 : : * If we are unlucky enough to get a hash of zero, use 1 instead for
151 : : * normal statements and 2 for utility queries.
152 : : */
153 [ - + ]: 85696 : if (query->queryId == INT64CONST(0))
154 : : {
155 [ # # ]: 0 : if (query->utilityStmt)
156 : 0 : query->queryId = INT64CONST(2);
157 : : else
158 : 0 : query->queryId = INT64CONST(1);
159 : : }
160 : :
161 : 85696 : return jstate;
162 : : }
163 : :
164 : : /*
165 : : * Enables query identifier computation.
166 : : *
167 : : * Third-party plugins can use this function to inform core that they require
168 : : * a query identifier to be computed.
169 : : */
170 : : void
171 : 15 : EnableQueryId(void)
172 : : {
173 [ + - ]: 15 : if (compute_query_id != COMPUTE_QUERY_ID_OFF)
174 : 15 : query_id_enabled = true;
175 : 15 : }
176 : :
177 : : /*
178 : : * InitJumble
179 : : * Allocate a JumbleState object and make it ready to jumble.
180 : : */
181 : : static JumbleState *
182 : 85696 : InitJumble(void)
183 : : {
184 : : JumbleState *jstate;
185 : :
186 : 85696 : jstate = palloc_object(JumbleState);
187 : :
188 : : /* Set up workspace for query jumbling */
189 : 85696 : jstate->jumble = (unsigned char *) palloc(JUMBLE_SIZE);
190 : 85696 : jstate->jumble_len = 0;
191 : 85696 : jstate->clocations_buf_size = 32;
192 : 85696 : jstate->clocations = palloc_array(LocationLen, jstate->clocations_buf_size);
193 : 85696 : jstate->clocations_count = 0;
194 : 85696 : jstate->highest_extern_param_id = 0;
195 : 85696 : jstate->pending_nulls = 0;
196 : 85696 : jstate->has_squashed_lists = false;
197 : : #ifdef USE_ASSERT_CHECKING
198 : : jstate->total_jumble_len = 0;
199 : : #endif
200 : :
201 : 85696 : return jstate;
202 : : }
203 : :
204 : : /*
205 : : * DoJumble
206 : : * Jumble the given Node using the given JumbleState and return the resulting
207 : : * jumble hash.
208 : : */
209 : : static int64
210 : 85696 : DoJumble(JumbleState *jstate, Node *node)
211 : : {
212 : : /* Jumble the given node */
213 : 85696 : _jumbleNode(jstate, node);
214 : :
215 : : /* Flush any pending NULLs before doing the final hash */
216 [ + + ]: 85696 : if (jstate->pending_nulls > 0)
217 : 84929 : FlushPendingNulls(jstate);
218 : :
219 : : /* Squashed list found, reset highest_extern_param_id */
220 [ + + ]: 85696 : if (jstate->has_squashed_lists)
221 : 1521 : jstate->highest_extern_param_id = 0;
222 : :
223 : : /* Process the jumble buffer and produce the hash value */
224 : 85696 : return DatumGetInt64(hash_any_extended(jstate->jumble,
225 : 85696 : jstate->jumble_len,
226 : : 0));
227 : : }
228 : :
229 : : /*
230 : : * AppendJumbleInternal: Internal function for appending to the jumble buffer
231 : : *
232 : : * Note: Callers must ensure that size > 0.
233 : : */
234 : : static pg_always_inline void
235 : 6176746 : AppendJumbleInternal(JumbleState *jstate, const unsigned char *item,
236 : : Size size)
237 : : {
238 : 6176746 : unsigned char *jumble = jstate->jumble;
239 : 6176746 : Size jumble_len = jstate->jumble_len;
240 : :
241 : : /* Ensure the caller didn't mess up */
242 : : Assert(size > 0);
243 : :
244 : : /*
245 : : * Fast path for when there's enough space left in the buffer. This is
246 : : * worthwhile as means the memcpy can be inlined into very efficient code
247 : : * when 'size' is a compile-time constant.
248 : : */
249 [ + + ]: 6176746 : if (likely(size <= JUMBLE_SIZE - jumble_len))
250 : : {
251 : 6173386 : memcpy(jumble + jumble_len, item, size);
252 : 6173386 : jstate->jumble_len += size;
253 : :
254 : : #ifdef USE_ASSERT_CHECKING
255 : : jstate->total_jumble_len += size;
256 : : #endif
257 : :
258 : 6173386 : return;
259 : : }
260 : :
261 : : /*
262 : : * Whenever the jumble buffer is full, we hash the current contents and
263 : : * reset the buffer to contain just that hash value, thus relying on the
264 : : * hash to summarize everything so far.
265 : : */
266 : : do
267 : : {
268 : : Size part_size;
269 : :
270 [ + + ]: 5986 : if (unlikely(jumble_len >= JUMBLE_SIZE))
271 : : {
272 : : int64 start_hash;
273 : :
274 : 3459 : start_hash = DatumGetInt64(hash_any_extended(jumble,
275 : : JUMBLE_SIZE, 0));
276 : 3459 : memcpy(jumble, &start_hash, sizeof(start_hash));
277 : 3459 : jumble_len = sizeof(start_hash);
278 : : }
279 : 5986 : part_size = Min(size, JUMBLE_SIZE - jumble_len);
280 : 5986 : memcpy(jumble + jumble_len, item, part_size);
281 : 5986 : jumble_len += part_size;
282 : 5986 : item += part_size;
283 : 5986 : size -= part_size;
284 : :
285 : : #ifdef USE_ASSERT_CHECKING
286 : : jstate->total_jumble_len += part_size;
287 : : #endif
288 [ + + ]: 5986 : } while (size > 0);
289 : :
290 : 3360 : jstate->jumble_len = jumble_len;
291 : : }
292 : :
293 : : /*
294 : : * AppendJumble
295 : : * Add 'size' bytes of the given jumble 'value' to the jumble state
296 : : */
297 : : static pg_noinline void
298 : 213562 : AppendJumble(JumbleState *jstate, const unsigned char *value, Size size)
299 : : {
300 [ + + ]: 213562 : if (jstate->pending_nulls > 0)
301 : 32538 : FlushPendingNulls(jstate);
302 : :
303 : 213562 : AppendJumbleInternal(jstate, value, size);
304 : 213562 : }
305 : :
306 : : /*
307 : : * AppendJumbleNull
308 : : * For jumbling NULL pointers
309 : : */
310 : : static pg_always_inline void
311 : 3152276 : AppendJumbleNull(JumbleState *jstate)
312 : : {
313 : 3152276 : jstate->pending_nulls++;
314 : 3152276 : }
315 : :
316 : : /*
317 : : * AppendJumble8
318 : : * Add the first byte from the given 'value' pointer to the jumble state
319 : : */
320 : : static pg_noinline void
321 : 583012 : AppendJumble8(JumbleState *jstate, const unsigned char *value)
322 : : {
323 [ + + ]: 583012 : if (jstate->pending_nulls > 0)
324 : 230645 : FlushPendingNulls(jstate);
325 : :
326 : 583012 : AppendJumbleInternal(jstate, value, 1);
327 : 583012 : }
328 : :
329 : : /*
330 : : * AppendJumble16
331 : : * Add the first 2 bytes from the given 'value' pointer to the jumble
332 : : * state.
333 : : */
334 : : static pg_noinline void
335 : 449717 : AppendJumble16(JumbleState *jstate, const unsigned char *value)
336 : : {
337 [ + + ]: 449717 : if (jstate->pending_nulls > 0)
338 : 20505 : FlushPendingNulls(jstate);
339 : :
340 : 449717 : AppendJumbleInternal(jstate, value, 2);
341 : 449717 : }
342 : :
343 : : /*
344 : : * AppendJumble32
345 : : * Add the first 4 bytes from the given 'value' pointer to the jumble
346 : : * state.
347 : : */
348 : : static pg_noinline void
349 : 3913896 : AppendJumble32(JumbleState *jstate, const unsigned char *value)
350 : : {
351 [ + + ]: 3913896 : if (jstate->pending_nulls > 0)
352 : 647942 : FlushPendingNulls(jstate);
353 : :
354 : 3913896 : AppendJumbleInternal(jstate, value, 4);
355 : 3913896 : }
356 : :
357 : : /*
358 : : * AppendJumble64
359 : : * Add the first 8 bytes from the given 'value' pointer to the jumble
360 : : * state.
361 : : */
362 : : static pg_noinline void
363 : 0 : AppendJumble64(JumbleState *jstate, const unsigned char *value)
364 : : {
365 [ # # ]: 0 : if (jstate->pending_nulls > 0)
366 : 0 : FlushPendingNulls(jstate);
367 : :
368 : 0 : AppendJumbleInternal(jstate, value, 8);
369 : 0 : }
370 : :
371 : : /*
372 : : * FlushPendingNulls
373 : : * Incorporate the pending_nulls value into the jumble buffer.
374 : : *
375 : : * Note: Callers must ensure that there's at least 1 pending NULL.
376 : : */
377 : : static pg_always_inline void
378 : 1016559 : FlushPendingNulls(JumbleState *jstate)
379 : : {
380 : : Assert(jstate->pending_nulls > 0);
381 : :
382 : 1016559 : AppendJumbleInternal(jstate,
383 : 1016559 : (const unsigned char *) &jstate->pending_nulls, 4);
384 : 1016559 : jstate->pending_nulls = 0;
385 : 1016559 : }
386 : :
387 : :
388 : : /*
389 : : * Record the location of some kind of constant within a query string.
390 : : * These are not only bare constants but also expressions that ultimately
391 : : * constitute a constant, such as those inside casts and simple function
392 : : * calls; if extern_param, then it corresponds to a PARAM_EXTERN Param.
393 : : *
394 : : * If length is -1, it indicates a single such constant element. If
395 : : * it's a positive integer, it indicates the length of a squashable
396 : : * list of them.
397 : : */
398 : : static void
399 : 140773 : RecordConstLocation(JumbleState *jstate, bool extern_param, int location, int len)
400 : : {
401 : : /* -1 indicates unknown or undefined location */
402 [ + + ]: 140773 : if (location >= 0)
403 : : {
404 : : /* enlarge array if needed */
405 [ + + ]: 132717 : if (jstate->clocations_count >= jstate->clocations_buf_size)
406 : : {
407 : 79 : jstate->clocations_buf_size *= 2;
408 : 79 : jstate->clocations = repalloc_array(jstate->clocations,
409 : : LocationLen,
410 : : jstate->clocations_buf_size);
411 : : }
412 : 132717 : jstate->clocations[jstate->clocations_count].location = location;
413 : :
414 : : /*
415 : : * Lengths are either positive integers (indicating a squashable
416 : : * list), or -1.
417 : : */
418 : : Assert(len > -1 || len == -1);
419 : 132717 : jstate->clocations[jstate->clocations_count].length = len;
420 : 132717 : jstate->clocations[jstate->clocations_count].squashed = (len > -1);
421 : 132717 : jstate->clocations[jstate->clocations_count].extern_param = extern_param;
422 : 132717 : jstate->clocations_count++;
423 : : }
424 : 140773 : }
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
437 : 6955 : IsSquashableConstant(Node *element)
438 : : {
439 : 486 : restart:
440 [ + + + + : 7441 : switch (nodeTag(element))
+ + ]
441 : : {
442 : 415 : case T_RelabelType:
443 : : /* Unwrap RelabelType */
444 : 415 : element = (Node *) ((RelabelType *) element)->arg;
445 : 415 : goto restart;
446 : :
447 : 71 : case T_CoerceViaIO:
448 : : /* Unwrap CoerceViaIO */
449 : 71 : element = (Node *) ((CoerceViaIO *) element)->arg;
450 : 71 : goto restart;
451 : :
452 : 6441 : case T_Const:
453 : 6441 : return true;
454 : :
455 : 80 : case T_Param:
456 : 80 : return castNode(Param, element)->paramkind == PARAM_EXTERN;
457 : :
458 : 326 : case T_FuncExpr:
459 : : {
460 : 326 : FuncExpr *func = (FuncExpr *) element;
461 : : ListCell *temp;
462 : :
463 [ + + ]: 326 : if (func->funcformat != COERCE_IMPLICIT_CAST &&
464 [ + + ]: 210 : func->funcformat != COERCE_EXPLICIT_CAST)
465 : 145 : return false;
466 : :
467 [ - + ]: 181 : if (func->funcid > FirstGenbkiObjectId)
468 : 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 : : */
478 [ + - + + : 359 : foreach(temp, func->args)
+ + ]
479 : : {
480 : 181 : Node *arg = lfirst(temp);
481 : :
482 [ + + ]: 181 : 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 : 178 : return true;
493 : : }
494 : :
495 : 108 : default:
496 : 108 : 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
511 : 2607 : IsSquashableConstantList(List *elements)
512 : : {
513 : : ListCell *temp;
514 : :
515 : : /* If the list is too short, we don't try to squash it. */
516 [ + + ]: 2607 : if (list_length(elements) < 2)
517 : 257 : return false;
518 : :
519 [ + - + + : 9045 : foreach(temp, elements)
+ + ]
520 : : {
521 [ + + ]: 6948 : if (!IsSquashableConstant(lfirst(temp)))
522 : 253 : return false;
523 : : }
524 : :
525 : 2097 : 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
561 : 4651598 : _jumbleNode(JumbleState *jstate, Node *node)
562 : : {
563 : 4651598 : Node *expr = node;
564 : : #ifdef USE_ASSERT_CHECKING
565 : : Size prev_jumble_len = jstate->total_jumble_len;
566 : : #endif
567 : :
568 [ + + ]: 4651598 : if (expr == NULL)
569 : : {
570 : 2877681 : AppendJumbleNull(jstate);
571 : 2877681 : return;
572 : : }
573 : :
574 : : /* Guard against stack overflow due to overly complex expressions */
575 : 1773917 : 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 : : */
581 : 1773917 : JUMBLE_FIELD(type);
582 : :
583 [ + + + + : 1773917 : switch (nodeTag(expr))
+ + + + +
+ - + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ - - - +
+ + + - +
+ - + - +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
- + + + +
+ - + + -
+ + + + +
+ + + - -
+ + + + +
+ + + + +
+ + + - -
- + + + +
+ + + + +
+ + + + -
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + -
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
- + + + +
+ + - - -
- + + + +
- + - ]
584 : : {
585 : : #include "queryjumblefuncs.switch.c"
586 : :
587 : 429515 : case T_List:
588 : : case T_IntList:
589 : : case T_OidList:
590 : : case T_XidList:
591 : 429515 : _jumbleList(jstate, expr);
592 : 429515 : break;
593 : :
594 : 0 : default:
595 : : /* Only a warning, since we can stumble along anyway */
596 [ # # ]: 0 : elog(WARNING, "unrecognized node type: %d",
597 : : (int) nodeTag(expr));
598 : 0 : break;
599 : : }
600 : :
601 : : /* Ensure we added something to the jumble buffer */
602 : : Assert(jstate->total_jumble_len > prev_jumble_len);
603 : : }
604 : :
605 : : static void
606 : 429515 : _jumbleList(JumbleState *jstate, Node *node)
607 : : {
608 : 429515 : List *expr = (List *) node;
609 : : ListCell *l;
610 : :
611 [ + + - - : 429515 : switch (expr->type)
- ]
612 : : {
613 : 428950 : case T_List:
614 [ + - + + : 1199499 : foreach(l, expr)
+ + ]
615 : 770549 : _jumbleNode(jstate, lfirst(l));
616 : 428950 : break;
617 : 565 : case T_IntList:
618 [ + - + + : 1242 : foreach(l, expr)
+ + ]
619 : 677 : AppendJumble32(jstate, (const unsigned char *) &lfirst_int(l));
620 : 565 : break;
621 : 0 : case T_OidList:
622 [ # # # # : 0 : foreach(l, expr)
# # ]
623 : 0 : AppendJumble32(jstate, (const unsigned char *) &lfirst_oid(l));
624 : 0 : break;
625 : 0 : case T_XidList:
626 [ # # # # : 0 : foreach(l, expr)
# # ]
627 : 0 : AppendJumble32(jstate, (const unsigned char *) &lfirst_xid(l));
628 : 0 : break;
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
647 : 2607 : _jumbleElements(JumbleState *jstate, List *elements, Node *node)
648 : : {
649 : 2607 : bool normalize_list = false;
650 : :
651 [ + + ]: 2607 : if (IsSquashableConstantList(elements))
652 : : {
653 [ + - ]: 2097 : if (IsA(node, ArrayExpr))
654 : : {
655 : 2097 : ArrayExpr *aexpr = (ArrayExpr *) node;
656 : :
657 [ + + + - ]: 2097 : if (aexpr->list_start > 0 && aexpr->list_end > 0)
658 : : {
659 : 2054 : RecordConstLocation(jstate,
660 : : false,
661 : 2054 : aexpr->list_start + 1,
662 : 2054 : (aexpr->list_end - aexpr->list_start) - 1);
663 : 2054 : normalize_list = true;
664 : 2054 : jstate->has_squashed_lists = true;
665 : : }
666 : : }
667 : : }
668 : :
669 [ + + ]: 2607 : if (!normalize_list)
670 : : {
671 : 553 : _jumbleNode(jstate, (Node *) elements);
672 : : }
673 : 2607 : }
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 : 6158 : _jumbleParam(JumbleState *jstate, Node *node)
681 : : {
682 : 6158 : Param *expr = (Param *) node;
683 : :
684 : 6158 : JUMBLE_FIELD(paramkind);
685 : 6158 : JUMBLE_FIELD(paramid);
686 : 6158 : JUMBLE_FIELD(paramtype);
687 : : /* paramtypmod and paramcollid are ignored */
688 : :
689 [ + + ]: 6158 : 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 : 5188 : 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 [ + + ]: 5188 : if (expr->paramid > jstate->highest_extern_param_id)
705 : 4270 : jstate->highest_extern_param_id = expr->paramid;
706 : : }
707 : 6158 : }
708 : :
709 : : static void
710 : 10234 : _jumbleA_Const(JumbleState *jstate, Node *node)
711 : : {
712 : 10234 : A_Const *expr = (A_Const *) node;
713 : :
714 : 10234 : JUMBLE_FIELD(isnull);
715 [ + + ]: 10234 : if (!expr->isnull)
716 : : {
717 : 10134 : JUMBLE_FIELD(val.node.type);
718 [ + + + + : 10134 : switch (nodeTag(&expr->val))
+ - ]
719 : : {
720 : 4770 : case T_Integer:
721 : 4770 : JUMBLE_FIELD(val.ival.ival);
722 : 4770 : break;
723 : 35 : case T_Float:
724 [ + - ]: 35 : JUMBLE_STRING(val.fval.fval);
725 : 35 : break;
726 : 141 : case T_Boolean:
727 : 141 : JUMBLE_FIELD(val.boolval.boolval);
728 : 141 : break;
729 : 5186 : case T_String:
730 [ + - ]: 5186 : JUMBLE_STRING(val.sval.sval);
731 : 5186 : break;
732 : 2 : case T_BitString:
733 [ + - ]: 2 : JUMBLE_STRING(val.bsval.bsval);
734 : 2 : break;
735 : 0 : default:
736 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d",
737 : : (int) nodeTag(&expr->val));
738 : : break;
739 : : }
740 : : }
741 : 10234 : }
742 : :
743 : : static void
744 : 2960 : _jumbleVariableSetStmt(JumbleState *jstate, Node *node)
745 : : {
746 : 2960 : VariableSetStmt *expr = (VariableSetStmt *) node;
747 : :
748 : 2960 : JUMBLE_FIELD(kind);
749 [ + + ]: 2960 : JUMBLE_STRING(name);
750 : :
751 : : /*
752 : : * Account for the list of arguments in query jumbling only if told by the
753 : : * parser.
754 : : */
755 [ + + ]: 2960 : if (expr->jumble_args)
756 : 60 : JUMBLE_NODE(args);
757 : 2960 : JUMBLE_FIELD(is_local);
758 : 2960 : JUMBLE_LOCATION(location);
759 : 2960 : }
760 : :
761 : : /*
762 : : * Custom query jumble function for RangeTblEntry.eref.
763 : : */
764 : : static void
765 : 87552 : _jumbleRangeTblEntry_eref(JumbleState *jstate,
766 : : RangeTblEntry *rte,
767 : : Alias *expr)
768 : : {
769 : 87552 : JUMBLE_FIELD(type);
770 : :
771 : : /*
772 : : * This includes only the table name, the list of column names is ignored.
773 : : */
774 [ + - ]: 87552 : JUMBLE_STRING(aliasname);
775 : 87552 : }
776 : :
777 : : /*
778 : : * CompLocation: comparator for qsorting LocationLen structs by location
779 : : */
780 : : static int
781 : 40130 : CompLocation(const void *a, const void *b)
782 : : {
783 : 40130 : int l = ((const LocationLen *) a)->location;
784 : 40130 : int r = ((const LocationLen *) b)->location;
785 : :
786 : 40130 : return pg_cmp_s32(l, r);
787 : : }
788 : :
789 : : /*
790 : : * Given a valid SQL string and an array of constant-location records, return
791 : : * the textual lengths of those constants in a newly allocated LocationLen
792 : : * array, or NULL if there are no constants.
793 : : *
794 : : * The constants may use any allowed constant syntax, such as float literals,
795 : : * bit-strings, single-quoted strings and dollar-quoted strings. This is
796 : : * accomplished by using the public API for the core scanner.
797 : : *
798 : : * It is the caller's job to ensure that the string is a valid SQL statement
799 : : * with constants at the indicated locations. Since in practice the string
800 : : * has already been parsed, and the locations that the caller provides will
801 : : * have originated from within the authoritative parser, this should not be
802 : : * a problem.
803 : : *
804 : : * Multiple constants can have the same location. We reset lengths of those
805 : : * past the first to -1 so that they can later be ignored.
806 : : *
807 : : * If query_loc > 0, then "query" has been advanced by that much compared to
808 : : * the original string start, as is the case with multi-statement strings, so
809 : : * we need to translate the provided locations to compensate. (This lets us
810 : : * avoid re-scanning statements before the one of interest, so it's worth
811 : : * doing.)
812 : : *
813 : : * N.B. There is an assumption that a '-' character at a Const location begins
814 : : * a negative numeric constant. This precludes there ever being another
815 : : * reason for a constant to start with a '-'.
816 : : *
817 : : * It is the caller's responsibility to free the result, if necessary.
818 : : */
819 : : LocationLen *
820 : 11711 : ComputeConstantLengths(const JumbleState *jstate, const char *query,
821 : : int query_loc)
822 : : {
823 : : LocationLen *locs;
824 : : core_yyscan_t yyscanner;
825 : : core_yy_extra_type yyextra;
826 : : core_YYSTYPE yylval;
827 : : YYLTYPE yylloc;
828 : :
829 [ - + ]: 11711 : if (jstate->clocations_count == 0)
830 : 0 : return NULL;
831 : :
832 : : /* Copy constant locations to avoid modifying jstate */
833 : 11711 : locs = palloc_array(LocationLen, jstate->clocations_count);
834 : 11711 : memcpy(locs, jstate->clocations, jstate->clocations_count * sizeof(LocationLen));
835 : :
836 : : /*
837 : : * Sort the records by location so that we can process them in order while
838 : : * scanning the query text.
839 : : */
840 [ + + ]: 11711 : if (jstate->clocations_count > 1)
841 : 7421 : qsort(locs, jstate->clocations_count,
842 : : sizeof(LocationLen), CompLocation);
843 : :
844 : : /* initialize the flex scanner --- should match raw_parser() */
845 : 11711 : yyscanner = scanner_init(query,
846 : : &yyextra,
847 : : &ScanKeywords,
848 : : ScanKeywordTokens);
849 : :
850 : : /* Search for each constant, in sequence */
851 [ + + ]: 47080 : for (int i = 0; i < jstate->clocations_count; i++)
852 : : {
853 : : int loc;
854 : : int tok;
855 : :
856 : : /* Ignore constants after the first one in the same location */
857 [ + + + + ]: 35369 : if (i > 0 && locs[i].location == locs[i - 1].location)
858 : : {
859 : 719 : locs[i].length = -1;
860 : 719 : continue;
861 : : }
862 : :
863 [ + + ]: 34650 : if (locs[i].squashed)
864 : 690 : continue; /* squashable list, ignore */
865 : :
866 : : /*
867 : : * Adjust the constant's location using the provided starting location
868 : : * of the current statement. This allows us to avoid scanning a
869 : : * multi-statement string from the beginning.
870 : : */
871 : 33960 : loc = locs[i].location - query_loc;
872 : : Assert(loc >= 0);
873 : :
874 : : /*
875 : : * We have a valid location for a constant that's not a dupe. Lex
876 : : * tokens until we find the desired constant.
877 : : */
878 : : for (;;)
879 : : {
880 : 262086 : tok = core_yylex(&yylval, &yylloc, yyscanner);
881 : :
882 : : /* We should not hit end-of-string, but if we do, behave sanely */
883 [ - + ]: 262086 : if (tok == 0)
884 : 0 : break; /* out of inner for-loop */
885 : :
886 : : /*
887 : : * We should find the token position exactly, but if we somehow
888 : : * run past it, work with that.
889 : : */
890 [ + + ]: 262086 : if (yylloc >= loc)
891 : : {
892 [ + + ]: 33960 : if (query[loc] == '-')
893 : : {
894 : : /*
895 : : * It's a negative value - this is the one and only case
896 : : * where we replace more than a single token.
897 : : *
898 : : * Do not compensate for the special-case adjustment of
899 : : * location to that of the leading '-' operator in the
900 : : * event of a negative constant (see doNegate() in
901 : : * gram.y). It is also useful for our purposes to start
902 : : * from the minus symbol. In this way, queries like
903 : : * "select * from foo where bar = 1" and "select * from
904 : : * foo where bar = -2" can be treated similarly.
905 : : */
906 : 382 : tok = core_yylex(&yylval, &yylloc, yyscanner);
907 [ - + ]: 382 : if (tok == 0)
908 : 0 : break; /* out of inner for-loop */
909 : : }
910 : :
911 : : /*
912 : : * We now rely on the assumption that flex has placed a zero
913 : : * byte after the text of the current token in scanbuf.
914 : : */
915 : 33960 : locs[i].length = strlen(yyextra.scanbuf + loc);
916 : 33960 : break; /* out of inner for-loop */
917 : : }
918 : : }
919 : :
920 : : /* If we hit end-of-string, give up, leaving remaining lengths -1 */
921 [ - + ]: 33960 : if (tok == 0)
922 : 0 : break;
923 : : }
924 : :
925 : 11711 : scanner_finish(yyscanner);
926 : :
927 : 11711 : return locs;
928 : : }
|