Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * execGrouping.c
4 : : * executor utility routines for grouping, hashing, and aggregation
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/executor/execGrouping.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include <math.h>
18 : :
19 : : #include "access/htup_details.h"
20 : : #include "access/parallel.h"
21 : : #include "common/hashfn.h"
22 : : #include "executor/executor.h"
23 : : #include "miscadmin.h"
24 : : #include "utils/lsyscache.h"
25 : :
26 : : static int TupleHashTableMatch(struct tuplehash_hash *tb, MinimalTuple tuple1, MinimalTuple tuple2);
27 : : static inline uint32 TupleHashTableHash_internal(struct tuplehash_hash *tb,
28 : : MinimalTuple tuple);
29 : : static inline TupleHashEntry LookupTupleHashEntry_internal(TupleHashTable hashtable,
30 : : TupleTableSlot *slot,
31 : : bool *isnew, uint32 hash);
32 : :
33 : : /*
34 : : * Define parameters for tuple hash table code generation. The interface is
35 : : * *also* declared in execnodes.h (to generate the types, which are externally
36 : : * visible).
37 : : */
38 : : #define SH_PREFIX tuplehash
39 : : #define SH_ELEMENT_TYPE TupleHashEntryData
40 : : #define SH_KEY_TYPE MinimalTuple
41 : : #define SH_KEY firstTuple
42 : : #define SH_HASH_KEY(tb, key) TupleHashTableHash_internal(tb, key)
43 : : #define SH_EQUAL(tb, a, b) TupleHashTableMatch(tb, a, b) == 0
44 : : #define SH_SCOPE extern
45 : : #define SH_STORE_HASH
46 : : #define SH_GET_HASH(tb, a) a->hash
47 : : #define SH_DEFINE
48 : : #include "lib/simplehash.h"
49 : :
50 : :
51 : : /*****************************************************************************
52 : : * Utility routines for grouping tuples together
53 : : *****************************************************************************/
54 : :
55 : : /*
56 : : * execTuplesMatchPrepare
57 : : * Build expression that can be evaluated using ExecQual(), returning
58 : : * whether an ExprContext's inner/outer tuples are NOT DISTINCT
59 : : */
60 : : ExprState *
61 : 7897 : execTuplesMatchPrepare(TupleDesc desc,
62 : : int numCols,
63 : : const AttrNumber *keyColIdx,
64 : : const Oid *eqOperators,
65 : : const Oid *collations,
66 : : PlanState *parent)
67 : : {
68 : : Oid *eqFunctions;
69 : : int i;
70 : : ExprState *expr;
71 : :
72 [ + + ]: 7897 : if (numCols == 0)
73 : 35 : return NULL;
74 : :
75 : 7862 : eqFunctions = palloc_array(Oid, numCols);
76 : :
77 : : /* lookup equality functions */
78 [ + + ]: 21461 : for (i = 0; i < numCols; i++)
79 : 13599 : eqFunctions[i] = get_opcode(eqOperators[i]);
80 : :
81 : : /* build actual expression */
82 : 7862 : expr = ExecBuildGroupingEqual(desc, desc, NULL, NULL,
83 : : numCols, keyColIdx, eqFunctions, collations,
84 : : parent);
85 : :
86 : 7862 : return expr;
87 : : }
88 : :
89 : : /*
90 : : * execTuplesHashPrepare
91 : : * Look up the equality and hashing functions needed for a TupleHashTable.
92 : : *
93 : : * This is similar to execTuplesMatchPrepare, but we also need to find the
94 : : * hash functions associated with the equality operators. *eqFunctions and
95 : : * *hashFunctions receive the palloc'd result arrays.
96 : : *
97 : : * Note: we expect that the given operators are not cross-type comparisons.
98 : : */
99 : : void
100 : 6054 : execTuplesHashPrepare(int numCols,
101 : : const Oid *eqOperators,
102 : : Oid **eqFuncOids,
103 : : FmgrInfo **hashFunctions)
104 : : {
105 : : int i;
106 : :
107 : 6054 : *eqFuncOids = palloc_array(Oid, numCols);
108 : 6054 : *hashFunctions = palloc_array(FmgrInfo, numCols);
109 : :
110 [ + + ]: 16126 : for (i = 0; i < numCols; i++)
111 : : {
112 : 10072 : Oid eq_opr = eqOperators[i];
113 : : Oid eq_function;
114 : : Oid left_hash_function;
115 : : Oid right_hash_function;
116 : :
117 : 10072 : eq_function = get_opcode(eq_opr);
118 [ - + ]: 10072 : if (!get_op_hash_functions(eq_opr,
119 : : &left_hash_function, &right_hash_function))
120 [ # # ]: 0 : elog(ERROR, "could not find hash function for hash operator %u",
121 : : eq_opr);
122 : : /* We're not supporting cross-type cases here */
123 : : Assert(left_hash_function == right_hash_function);
124 : 10072 : (*eqFuncOids)[i] = eq_function;
125 : 10072 : fmgr_info(right_hash_function, &(*hashFunctions)[i]);
126 : : }
127 : 6054 : }
128 : :
129 : :
130 : : /*****************************************************************************
131 : : * Utility routines for all-in-memory hash tables
132 : : *
133 : : * These routines build hash tables for grouping tuples together (eg, for
134 : : * hash aggregation). There is one entry for each not-distinct set of tuples
135 : : * presented.
136 : : *****************************************************************************/
137 : :
138 : : /*
139 : : * Construct an empty TupleHashTable
140 : : *
141 : : * parent: PlanState node that will own this hash table
142 : : * inputDesc: tuple descriptor for input tuples
143 : : * inputOps: slot ops for input tuples, or NULL if unknown or not fixed
144 : : * numCols: number of columns to be compared (length of next 4 arrays)
145 : : * keyColIdx: indexes of tuple columns to compare
146 : : * eqfuncoids: OIDs of equality comparison functions to use
147 : : * hashfunctions: FmgrInfos of datatype-specific hashing functions to use
148 : : * collations: collations to use in comparisons
149 : : * nelements: initial estimate of hashtable size
150 : : * additionalsize: size of data that may be stored along with the hash entry
151 : : * metacxt: memory context for long-lived data and the simplehash table
152 : : * tuplescxt: memory context in which to store the hashed tuples themselves
153 : : * tempcxt: short-lived context for evaluation hash and comparison functions
154 : : * use_variable_hash_iv: if true, adjust hash IV per-parallel-worker
155 : : *
156 : : * The hashfunctions array may be made with execTuplesHashPrepare(). Note they
157 : : * are not cross-type functions, but expect to see the table datatype(s)
158 : : * on both sides.
159 : : *
160 : : * Note that the keyColIdx, hashfunctions, and collations arrays must be
161 : : * allocated in storage that will live as long as the hashtable does.
162 : : *
163 : : * The metacxt and tuplescxt are separate because it's usually desirable for
164 : : * tuplescxt to be a BumpContext to avoid memory wastage, while metacxt must
165 : : * support pfree in case the simplehash table needs to be enlarged. (We could
166 : : * simplify the API of TupleHashTables by managing the tuplescxt internally.
167 : : * But that would be disadvantageous to nodeAgg.c and nodeSubplan.c, which use
168 : : * a single tuplescxt for multiple TupleHashTables that are reset together.)
169 : : *
170 : : * LookupTupleHashEntry, FindTupleHashEntry, and related functions may leak
171 : : * memory in the tempcxt. It is caller's responsibility to reset that context
172 : : * reasonably often, typically once per tuple. (We do it that way, rather
173 : : * than managing an extra context within the hashtable, because in many cases
174 : : * the caller can specify a tempcxt that it needs to reset per-tuple anyway.)
175 : : *
176 : : * We don't currently provide DestroyTupleHashTable functionality; the hash
177 : : * table will be cleaned up at destruction of the metacxt. (Some callers
178 : : * bother to delete the tuplescxt explicitly, though it'd be sufficient to
179 : : * ensure it's a child of the metacxt.) There's not much point in working
180 : : * harder than this so long as the expression-evaluation infrastructure
181 : : * behaves similarly.
182 : : */
183 : : TupleHashTable
184 : 5153 : BuildTupleHashTable(PlanState *parent,
185 : : TupleDesc inputDesc,
186 : : const TupleTableSlotOps *inputOps,
187 : : int numCols,
188 : : AttrNumber *keyColIdx,
189 : : const Oid *eqfuncoids,
190 : : FmgrInfo *hashfunctions,
191 : : Oid *collations,
192 : : double nelements,
193 : : Size additionalsize,
194 : : MemoryContext metacxt,
195 : : MemoryContext tuplescxt,
196 : : MemoryContext tempcxt,
197 : : bool use_variable_hash_iv)
198 : : {
199 : : TupleHashTable hashtable;
200 : : uint32 nbuckets;
201 : : MemoryContext oldcontext;
202 : 5153 : uint32 hash_iv = 0;
203 : :
204 : : /*
205 : : * tuplehash_create requires a uint32 element count, so we had better
206 : : * clamp the given nelements to fit in that. As long as we have to do
207 : : * that, we might as well protect against completely insane input like
208 : : * zero or NaN. But it is not our job here to enforce issues like staying
209 : : * within hash_mem: the caller should have done that, and we don't have
210 : : * enough info to second-guess.
211 : : */
212 [ + - - + ]: 5153 : if (isnan(nelements) || nelements <= 0)
213 : 0 : nbuckets = 1;
214 [ - + ]: 5153 : else if (nelements >= PG_UINT32_MAX)
215 : 0 : nbuckets = PG_UINT32_MAX;
216 : : else
217 : 5153 : nbuckets = (uint32) nelements;
218 : :
219 : : /* tuplescxt must be separate, else ResetTupleHashTable breaks things */
220 : : Assert(metacxt != tuplescxt);
221 : :
222 : : /* ensure additionalsize is maxalign'ed */
223 : 5153 : additionalsize = MAXALIGN(additionalsize);
224 : :
225 : 5153 : oldcontext = MemoryContextSwitchTo(metacxt);
226 : :
227 : 5153 : hashtable = palloc_object(TupleHashTableData);
228 : :
229 : 5153 : hashtable->numCols = numCols;
230 : 5153 : hashtable->keyColIdx = keyColIdx;
231 : 5153 : hashtable->tab_collations = collations;
232 : 5153 : hashtable->tuplescxt = tuplescxt;
233 : 5153 : hashtable->tempcxt = tempcxt;
234 : 5153 : hashtable->additionalsize = additionalsize;
235 : 5153 : hashtable->inputslot = NULL;
236 : 5153 : hashtable->in_hash_expr = NULL;
237 : 5153 : hashtable->cur_eq_func = NULL;
238 : :
239 : : /*
240 : : * If parallelism is in use, even if the leader backend is performing the
241 : : * scan itself, we don't want to create the hashtable exactly the same way
242 : : * in all workers. As hashtables are iterated over in keyspace-order,
243 : : * doing so in all processes in the same way is likely to lead to
244 : : * "unbalanced" hashtables when the table size initially is
245 : : * underestimated.
246 : : */
247 [ + + ]: 5153 : if (use_variable_hash_iv)
248 : 673 : hash_iv = murmurhash32(ParallelWorkerNumber);
249 : :
250 : 5153 : hashtable->hashtab = tuplehash_create(metacxt, nbuckets, hashtable);
251 : :
252 : : /*
253 : : * We copy the input tuple descriptor just for safety --- we assume all
254 : : * input tuples will have equivalent descriptors.
255 : : */
256 : 5153 : hashtable->tableslot = MakeSingleTupleTableSlot(CreateTupleDescCopy(inputDesc),
257 : : &TTSOpsMinimalTuple);
258 : :
259 : : /* build hash ExprState for all columns */
260 : 5153 : hashtable->tab_hash_expr = ExecBuildHash32FromAttrs(inputDesc,
261 : : inputOps,
262 : : hashfunctions,
263 : : collations,
264 : : numCols,
265 : : keyColIdx,
266 : : parent,
267 : : hash_iv);
268 : :
269 : : /* build comparator for all columns */
270 : 5153 : hashtable->tab_eq_func = ExecBuildGroupingEqual(inputDesc, inputDesc,
271 : : inputOps,
272 : : &TTSOpsMinimalTuple,
273 : : numCols,
274 : : keyColIdx, eqfuncoids, collations,
275 : : parent);
276 : :
277 : : /*
278 : : * While not pretty, it's ok to not shut down this context, but instead
279 : : * rely on the containing memory context being reset, as
280 : : * ExecBuildGroupingEqual() only builds a very simple expression calling
281 : : * functions (i.e. nothing that'd employ RegisterExprContextCallback()).
282 : : */
283 : 5153 : hashtable->exprcontext = CreateStandaloneExprContext();
284 : :
285 : 5153 : MemoryContextSwitchTo(oldcontext);
286 : :
287 : 5153 : return hashtable;
288 : : }
289 : :
290 : : /*
291 : : * Reset contents of the hashtable to be empty, preserving all the non-content
292 : : * state.
293 : : *
294 : : * Note: in usages where several TupleHashTables share a tuplescxt, all must
295 : : * be reset together, as the first one's reset call will destroy all their
296 : : * data. The additional reset calls for the rest will redundantly reset the
297 : : * tuplescxt. But because of mcxt.c's isReset flag, that's cheap enough that
298 : : * we need not avoid it.
299 : : */
300 : : void
301 : 128942 : ResetTupleHashTable(TupleHashTable hashtable)
302 : : {
303 : 128942 : tuplehash_reset(hashtable->hashtab);
304 : 128942 : MemoryContextReset(hashtable->tuplescxt);
305 : 128942 : }
306 : :
307 : : /*
308 : : * Estimate the amount of space needed for a TupleHashTable with nentries
309 : : * entries, if the tuples have average data width tupleWidth and the caller
310 : : * requires additionalsize extra space per entry.
311 : : *
312 : : * Return SIZE_MAX if it'd overflow size_t.
313 : : *
314 : : * nentries is "double" because this is meant for use by the planner,
315 : : * which typically works with double rowcount estimates. So we'd need to
316 : : * clamp to integer somewhere and that might as well be here. We do expect
317 : : * the value not to be NaN or negative, else the result will be garbage.
318 : : */
319 : : Size
320 : 3892 : EstimateTupleHashTableSpace(double nentries,
321 : : Size tupleWidth,
322 : : Size additionalsize)
323 : : {
324 : : Size sh_space;
325 : : double tuples_space;
326 : :
327 : : /* First estimate the space needed for the simplehash table */
328 : 3892 : sh_space = tuplehash_estimate_space(nentries);
329 : :
330 : : /* Give up if that's already too big */
331 [ + + ]: 3892 : if (sh_space >= SIZE_MAX)
332 : 5 : return sh_space;
333 : :
334 : : /*
335 : : * Compute space needed for hashed tuples with additional data. nentries
336 : : * must be somewhat sane, so it should be safe to compute this product.
337 : : *
338 : : * We assume that the hashed tuples will be kept in a BumpContext so that
339 : : * there is not additional per-tuple overhead.
340 : : *
341 : : * (Note that this is only accurate if MEMORY_CONTEXT_CHECKING is off,
342 : : * else bump.c will add a MemoryChunk header to each tuple. However, it
343 : : * seems undesirable for debug builds to make different planning choices
344 : : * than production builds, so we assume the production behavior always.)
345 : : */
346 : 3887 : tuples_space = nentries * (MAXALIGN(SizeofMinimalTupleHeader) +
347 : 3887 : MAXALIGN(tupleWidth) +
348 : 3887 : MAXALIGN(additionalsize));
349 : :
350 : : /*
351 : : * Check for size_t overflow. This coding is trickier than it may appear,
352 : : * because on 64-bit machines SIZE_MAX cannot be represented exactly as a
353 : : * double. We must cast it explicitly to suppress compiler warnings about
354 : : * an inexact conversion, and we must trust that any double value that
355 : : * compares strictly less than "(double) SIZE_MAX" will cast to a
356 : : * representable size_t value.
357 : : */
358 [ - + ]: 3887 : if (sh_space + tuples_space >= (double) SIZE_MAX)
359 : 0 : return SIZE_MAX;
360 : :
361 : : /* We don't bother estimating size of the miscellaneous overhead data */
362 : 3887 : return (Size) (sh_space + tuples_space);
363 : : }
364 : :
365 : : /*
366 : : * Find or create a hashtable entry for the tuple group containing the
367 : : * given tuple. The tuple must be the same type as the hashtable entries.
368 : : *
369 : : * If isnew is NULL, we do not create new entries; we return NULL if no
370 : : * match is found.
371 : : *
372 : : * If hash is not NULL, we set it to the calculated hash value. This allows
373 : : * callers access to the hash value even if no entry is returned.
374 : : *
375 : : * If isnew isn't NULL, then a new entry is created if no existing entry
376 : : * matches. On return, *isnew is true if the entry is newly created,
377 : : * false if it existed already. The additional data in the new entry has
378 : : * been zeroed.
379 : : */
380 : : TupleHashEntry
381 : 5541984 : LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
382 : : bool *isnew, uint32 *hash)
383 : : {
384 : : TupleHashEntry entry;
385 : : MemoryContext oldContext;
386 : : uint32 local_hash;
387 : :
388 : : /* Need to run the hash functions in short-lived context */
389 : 5541984 : oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
390 : :
391 : : /* set up data needed by hash and match functions */
392 : 5541984 : hashtable->inputslot = slot;
393 : 5541984 : hashtable->in_hash_expr = hashtable->tab_hash_expr;
394 : 5541984 : hashtable->cur_eq_func = hashtable->tab_eq_func;
395 : :
396 : 5541984 : local_hash = TupleHashTableHash_internal(hashtable->hashtab, NULL);
397 : 5541980 : entry = LookupTupleHashEntry_internal(hashtable, slot, isnew, local_hash);
398 : :
399 [ + + ]: 5541980 : if (hash != NULL)
400 : 4838029 : *hash = local_hash;
401 : :
402 : : Assert(entry == NULL || entry->hash == local_hash);
403 : :
404 : 5541980 : MemoryContextSwitchTo(oldContext);
405 : :
406 : 5541980 : return entry;
407 : : }
408 : :
409 : : /*
410 : : * Compute the hash value for a tuple
411 : : */
412 : : uint32
413 : 0 : TupleHashTableHash(TupleHashTable hashtable, TupleTableSlot *slot)
414 : : {
415 : : MemoryContext oldContext;
416 : : uint32 hash;
417 : :
418 : 0 : hashtable->inputslot = slot;
419 : 0 : hashtable->in_hash_expr = hashtable->tab_hash_expr;
420 : :
421 : : /* Need to run the hash functions in short-lived context */
422 : 0 : oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
423 : :
424 : 0 : hash = TupleHashTableHash_internal(hashtable->hashtab, NULL);
425 : :
426 : 0 : MemoryContextSwitchTo(oldContext);
427 : :
428 : 0 : return hash;
429 : : }
430 : :
431 : : /*
432 : : * A variant of LookupTupleHashEntry for callers that have already computed
433 : : * the hash value.
434 : : */
435 : : TupleHashEntry
436 : 777914 : LookupTupleHashEntryHash(TupleHashTable hashtable, TupleTableSlot *slot,
437 : : bool *isnew, uint32 hash)
438 : : {
439 : : TupleHashEntry entry;
440 : : MemoryContext oldContext;
441 : :
442 : : /* Need to run the hash functions in short-lived context */
443 : 777914 : oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
444 : :
445 : : /* set up data needed by hash and match functions */
446 : 777914 : hashtable->inputslot = slot;
447 : 777914 : hashtable->in_hash_expr = hashtable->tab_hash_expr;
448 : 777914 : hashtable->cur_eq_func = hashtable->tab_eq_func;
449 : :
450 : 777914 : entry = LookupTupleHashEntry_internal(hashtable, slot, isnew, hash);
451 : : Assert(entry == NULL || entry->hash == hash);
452 : :
453 : 777914 : MemoryContextSwitchTo(oldContext);
454 : :
455 : 777914 : return entry;
456 : : }
457 : :
458 : : /*
459 : : * Search for a hashtable entry matching the given tuple. No entry is
460 : : * created if there's not a match. This is similar to the non-creating
461 : : * case of LookupTupleHashEntry, except that it supports cross-type
462 : : * comparisons, in which the given tuple is not of the same type as the
463 : : * table entries. The caller must provide the hash ExprState to use for
464 : : * the input tuple, as well as the equality ExprState, since these may be
465 : : * different from the table's internal functions.
466 : : */
467 : : TupleHashEntry
468 : 606554 : FindTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
469 : : ExprState *eqcomp,
470 : : ExprState *hashexpr)
471 : : {
472 : : TupleHashEntry entry;
473 : : MemoryContext oldContext;
474 : : MinimalTuple key;
475 : :
476 : : /* Need to run the hash functions in short-lived context */
477 : 606554 : oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
478 : :
479 : : /* Set up data needed by hash and match functions */
480 : 606554 : hashtable->inputslot = slot;
481 : 606554 : hashtable->in_hash_expr = hashexpr;
482 : 606554 : hashtable->cur_eq_func = eqcomp;
483 : :
484 : : /* Search the hash table */
485 : 606554 : key = NULL; /* flag to reference inputslot */
486 : 606554 : entry = tuplehash_lookup(hashtable->hashtab, key);
487 : 606554 : MemoryContextSwitchTo(oldContext);
488 : :
489 : 606554 : return entry;
490 : : }
491 : :
492 : : /*
493 : : * If tuple is NULL, use the input slot instead. This convention avoids the
494 : : * need to materialize virtual input tuples unless they actually need to get
495 : : * copied into the table.
496 : : *
497 : : * Also, the caller must select an appropriate memory context for running
498 : : * the hash functions.
499 : : */
500 : : static uint32
501 : 6148538 : TupleHashTableHash_internal(struct tuplehash_hash *tb,
502 : : MinimalTuple tuple)
503 : : {
504 : 6148538 : TupleHashTable hashtable = (TupleHashTable) tb->private_data;
505 : : uint32 hashkey;
506 : : TupleTableSlot *slot;
507 : : bool isnull;
508 : :
509 [ + - ]: 6148538 : if (tuple == NULL)
510 : : {
511 : : /* Process the current input tuple for the table */
512 : 6148538 : hashtable->exprcontext->ecxt_innertuple = hashtable->inputslot;
513 : 6148538 : hashkey = DatumGetUInt32(ExecEvalExpr(hashtable->in_hash_expr,
514 : : hashtable->exprcontext,
515 : : &isnull));
516 : : }
517 : : else
518 : : {
519 : : /*
520 : : * Process a tuple already stored in the table.
521 : : *
522 : : * (this case never actually occurs due to the way simplehash.h is
523 : : * used, as the hash-value is stored in the entries)
524 : : */
525 : 0 : slot = hashtable->exprcontext->ecxt_innertuple = hashtable->tableslot;
526 : 0 : ExecStoreMinimalTuple(tuple, slot, false);
527 : 0 : hashkey = DatumGetUInt32(ExecEvalExpr(hashtable->tab_hash_expr,
528 : : hashtable->exprcontext,
529 : : &isnull));
530 : : }
531 : :
532 : : /*
533 : : * The hashing done above, even with an initial value, doesn't tend to
534 : : * result in good hash perturbation. Running the value produced above
535 : : * through murmurhash32 leads to near perfect hash perturbation.
536 : : */
537 : 6148534 : return murmurhash32(hashkey);
538 : : }
539 : :
540 : : /*
541 : : * Does the work of LookupTupleHashEntry and LookupTupleHashEntryHash. Useful
542 : : * so that we can avoid switching the memory context multiple times for
543 : : * LookupTupleHashEntry.
544 : : *
545 : : * NB: This function may or may not change the memory context. Caller is
546 : : * expected to change it back.
547 : : */
548 : : static inline TupleHashEntry
549 : 6319894 : LookupTupleHashEntry_internal(TupleHashTable hashtable, TupleTableSlot *slot,
550 : : bool *isnew, uint32 hash)
551 : : {
552 : : TupleHashEntryData *entry;
553 : : bool found;
554 : : MinimalTuple key;
555 : :
556 : 6319894 : key = NULL; /* flag to reference inputslot */
557 : :
558 [ + + ]: 6319894 : if (isnew)
559 : : {
560 : 5206331 : entry = tuplehash_insert_hash(hashtable->hashtab, key, hash, &found);
561 : :
562 [ + + ]: 5206331 : if (found)
563 : : {
564 : : /* found pre-existing entry */
565 : 4488691 : *isnew = false;
566 : : }
567 : : else
568 : : {
569 : : /* created new entry */
570 : 717640 : *isnew = true;
571 : :
572 : 717640 : MemoryContextSwitchTo(hashtable->tuplescxt);
573 : :
574 : : /*
575 : : * Copy the first tuple into the tuples context, and request
576 : : * additionalsize extra bytes before the allocation.
577 : : *
578 : : * The caller can get a pointer to the additional data with
579 : : * TupleHashEntryGetAdditional(), and store arbitrary data there.
580 : : * Placing both the tuple and additional data in the same
581 : : * allocation avoids the need to store an extra pointer in
582 : : * TupleHashEntryData or allocate an additional chunk.
583 : : */
584 : 717640 : entry->firstTuple = ExecCopySlotMinimalTupleExtra(slot,
585 : : hashtable->additionalsize);
586 : : }
587 : : }
588 : : else
589 : : {
590 : 1113563 : entry = tuplehash_lookup_hash(hashtable->hashtab, key, hash);
591 : : }
592 : :
593 : 6319894 : return entry;
594 : : }
595 : :
596 : : /*
597 : : * See whether two tuples (presumably of the same hash value) match
598 : : */
599 : : static int
600 : 4854020 : TupleHashTableMatch(struct tuplehash_hash *tb, MinimalTuple tuple1, MinimalTuple tuple2)
601 : : {
602 : : TupleTableSlot *slot1;
603 : : TupleTableSlot *slot2;
604 : 4854020 : TupleHashTable hashtable = (TupleHashTable) tb->private_data;
605 : 4854020 : ExprContext *econtext = hashtable->exprcontext;
606 : :
607 : : /*
608 : : * We assume that simplehash.h will only ever call us with the first
609 : : * argument being an actual table entry, and the second argument being
610 : : * LookupTupleHashEntry's dummy TupleHashEntryData. The other direction
611 : : * could be supported too, but is not currently required.
612 : : */
613 : : Assert(tuple1 != NULL);
614 : 4854020 : slot1 = hashtable->tableslot;
615 : 4854020 : ExecStoreMinimalTuple(tuple1, slot1, false);
616 : : Assert(tuple2 == NULL);
617 : 4854020 : slot2 = hashtable->inputslot;
618 : :
619 : : /* For crosstype comparisons, the inputslot must be first */
620 : 4854020 : econtext->ecxt_innertuple = slot2;
621 : 4854020 : econtext->ecxt_outertuple = slot1;
622 : 4854020 : return !ExecQualAndReset(hashtable->cur_eq_func, econtext);
623 : : }
|