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