Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeIndexscan.c
4 : : * Routines to support indexed scans of relations
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/nodeIndexscan.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : /*
16 : : * INTERFACE ROUTINES
17 : : * ExecIndexScan scans a relation using an index
18 : : * IndexNext retrieve next tuple using index
19 : : * IndexNextWithReorder same, but recheck ORDER BY expressions
20 : : * ExecInitIndexScan creates and initializes state info.
21 : : * ExecReScanIndexScan rescans the indexed relation.
22 : : * ExecEndIndexScan releases all storage.
23 : : * ExecIndexMarkPos marks scan position.
24 : : * ExecIndexRestrPos restores scan position.
25 : : * ExecIndexScanEstimate estimates DSM space needed for parallel index scan
26 : : * ExecIndexScanInitializeDSM initialize DSM for parallel indexscan
27 : : * ExecIndexScanReInitializeDSM reinitialize DSM for fresh scan
28 : : * ExecIndexScanInitializeWorker attach to DSM info in parallel worker
29 : : */
30 : : #include "postgres.h"
31 : :
32 : : #include "access/nbtree.h"
33 : : #include "access/relscan.h"
34 : : #include "access/tableam.h"
35 : : #include "catalog/pg_am.h"
36 : : #include "executor/executor.h"
37 : : #include "executor/instrument.h"
38 : : #include "executor/nodeIndexscan.h"
39 : : #include "lib/pairingheap.h"
40 : : #include "miscadmin.h"
41 : : #include "nodes/nodeFuncs.h"
42 : : #include "utils/array.h"
43 : : #include "utils/datum.h"
44 : : #include "utils/lsyscache.h"
45 : : #include "utils/rel.h"
46 : : #include "utils/sortsupport.h"
47 : :
48 : : /*
49 : : * When an ordering operator is used, tuples fetched from the index that
50 : : * need to be reordered are queued in a pairing heap, as ReorderTuples.
51 : : */
52 : : typedef struct
53 : : {
54 : : pairingheap_node ph_node;
55 : : HeapTuple htup;
56 : : Datum *orderbyvals;
57 : : bool *orderbynulls;
58 : : } ReorderTuple;
59 : :
60 : : static TupleTableSlot *IndexNext(IndexScanState *node);
61 : : static TupleTableSlot *IndexNextWithReorder(IndexScanState *node);
62 : : static void EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext);
63 : : static bool IndexRecheck(IndexScanState *node, TupleTableSlot *slot);
64 : : static int cmp_orderbyvals(const Datum *adist, const bool *anulls,
65 : : const Datum *bdist, const bool *bnulls,
66 : : IndexScanState *node);
67 : : static int reorderqueue_cmp(const pairingheap_node *a,
68 : : const pairingheap_node *b, void *arg);
69 : : static void reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
70 : : const Datum *orderbyvals, const bool *orderbynulls);
71 : : static HeapTuple reorderqueue_pop(IndexScanState *node);
72 : :
73 : :
74 : : /* ----------------------------------------------------------------
75 : : * IndexNext
76 : : *
77 : : * Retrieve a tuple from the IndexScan node's currentRelation
78 : : * using the index specified in the IndexScanState information.
79 : : * ----------------------------------------------------------------
80 : : */
81 : : static TupleTableSlot *
82 : 1380052 : IndexNext(IndexScanState *node)
83 : : {
84 : : EState *estate;
85 : : ExprContext *econtext;
86 : : ScanDirection direction;
87 : : IndexScanDesc scandesc;
88 : : TupleTableSlot *slot;
89 : :
90 : : /*
91 : : * extract necessary information from index scan node
92 : : */
93 : 1380052 : estate = node->ss.ps.state;
94 : :
95 : : /*
96 : : * Determine which direction to scan the index in based on the plan's scan
97 : : * direction and the current direction of execution.
98 : : */
99 : 1380052 : direction = ScanDirectionCombine(estate->es_direction,
100 : : ((IndexScan *) node->ss.ps.plan)->indexorderdir);
101 : 1380052 : scandesc = node->iss_ScanDesc;
102 : 1380052 : econtext = node->ss.ps.ps_ExprContext;
103 : 1380052 : slot = node->ss.ss_ScanTupleSlot;
104 : :
105 [ + + ]: 1380052 : if (scandesc == NULL)
106 : : {
107 : : /*
108 : : * We reach here if the index scan is not parallel, or if we're
109 : : * serially executing an index scan that was planned to be parallel.
110 : : */
111 [ + + ]: 83540 : scandesc = index_beginscan(node->ss.ss_currentRelation,
112 : : node->iss_RelationDesc,
113 : : estate->es_snapshot,
114 : : node->iss_Instrument,
115 : : node->iss_NumScanKeys,
116 : : node->iss_NumOrderByKeys,
117 : 83540 : ScanRelIsReadOnly(&node->ss) ?
118 : : SO_HINT_REL_READ_ONLY : SO_NONE);
119 : :
120 : 83540 : node->iss_ScanDesc = scandesc;
121 : :
122 : : /*
123 : : * If no run-time keys to calculate or they are ready, go ahead and
124 : : * pass the scankeys to the index AM.
125 : : */
126 [ + + + - ]: 83540 : if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
127 : 83540 : index_rescan(scandesc,
128 : : node->iss_ScanKeys, node->iss_NumScanKeys,
129 : : node->iss_OrderByKeys, node->iss_NumOrderByKeys);
130 : : }
131 : :
132 : : /*
133 : : * ok, now that we have what we need, fetch the next tuple.
134 : : */
135 [ + + ]: 1381904 : while (index_getnext_slot(scandesc, direction, slot))
136 : : {
137 [ + + ]: 1100782 : CHECK_FOR_INTERRUPTS();
138 : :
139 : : /*
140 : : * If the index was lossy, we have to recheck the index quals using
141 : : * the fetched tuple.
142 : : */
143 [ + + ]: 1100782 : if (scandesc->xs_recheck)
144 : : {
145 : 221896 : econtext->ecxt_scantuple = slot;
146 [ + + ]: 221896 : if (!ExecQualAndReset(node->indexqualorig, econtext))
147 : : {
148 : : /* Fails recheck, so drop it and loop back for another */
149 [ - + ]: 1852 : InstrCountFiltered2(node, 1);
150 : 1852 : continue;
151 : : }
152 : : }
153 : :
154 : 1098930 : return slot;
155 : : }
156 : :
157 : : /*
158 : : * if we get here it means the index scan failed so we are at the end of
159 : : * the scan..
160 : : */
161 : 281120 : node->iss_ReachedEnd = true;
162 : 281120 : return ExecClearTuple(slot);
163 : : }
164 : :
165 : : /* ----------------------------------------------------------------
166 : : * IndexNextWithReorder
167 : : *
168 : : * Like IndexNext, but this version can also re-check ORDER BY
169 : : * expressions, and reorder the tuples as necessary.
170 : : * ----------------------------------------------------------------
171 : : */
172 : : static TupleTableSlot *
173 : 55101 : IndexNextWithReorder(IndexScanState *node)
174 : : {
175 : : EState *estate;
176 : : ExprContext *econtext;
177 : : IndexScanDesc scandesc;
178 : : TupleTableSlot *slot;
179 : 55101 : ReorderTuple *topmost = NULL;
180 : : bool was_exact;
181 : : Datum *lastfetched_vals;
182 : : bool *lastfetched_nulls;
183 : : int cmp;
184 : :
185 : 55101 : estate = node->ss.ps.state;
186 : :
187 : : /*
188 : : * Only forward scan is supported with reordering. Note: we can get away
189 : : * with just Asserting here because the system will not try to run the
190 : : * plan backwards if ExecSupportsBackwardScan() says it won't work.
191 : : * Currently, that is guaranteed because no index AMs support both
192 : : * amcanorderbyop and amcanbackward; if any ever do,
193 : : * ExecSupportsBackwardScan() will need to consider indexorderbys
194 : : * explicitly.
195 : : */
196 : : Assert(!ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir));
197 : : Assert(ScanDirectionIsForward(estate->es_direction));
198 : :
199 : 55101 : scandesc = node->iss_ScanDesc;
200 : 55101 : econtext = node->ss.ps.ps_ExprContext;
201 : 55101 : slot = node->ss.ss_ScanTupleSlot;
202 : :
203 [ + + ]: 55101 : if (scandesc == NULL)
204 : : {
205 : : /*
206 : : * We reach here if the index scan is not parallel, or if we're
207 : : * serially executing an index scan that was planned to be parallel.
208 : : */
209 [ + - ]: 29 : scandesc = index_beginscan(node->ss.ss_currentRelation,
210 : : node->iss_RelationDesc,
211 : : estate->es_snapshot,
212 : : node->iss_Instrument,
213 : : node->iss_NumScanKeys,
214 : : node->iss_NumOrderByKeys,
215 : 29 : ScanRelIsReadOnly(&node->ss) ?
216 : : SO_HINT_REL_READ_ONLY : SO_NONE);
217 : :
218 : 29 : node->iss_ScanDesc = scandesc;
219 : :
220 : : /*
221 : : * If no run-time keys to calculate or they are ready, go ahead and
222 : : * pass the scankeys to the index AM.
223 : : */
224 [ + + + - ]: 29 : if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
225 : 29 : index_rescan(scandesc,
226 : : node->iss_ScanKeys, node->iss_NumScanKeys,
227 : : node->iss_OrderByKeys, node->iss_NumOrderByKeys);
228 : : }
229 : :
230 : : for (;;)
231 : : {
232 [ - + ]: 58556 : CHECK_FOR_INTERRUPTS();
233 : :
234 : : /*
235 : : * Check the reorder queue first. If the topmost tuple in the queue
236 : : * has an ORDER BY value smaller than (or equal to) the value last
237 : : * returned by the index, we can return it now.
238 : : */
239 [ + + ]: 58556 : if (!pairingheap_is_empty(node->iss_ReorderQueue))
240 : : {
241 : 6823 : topmost = (ReorderTuple *) pairingheap_first(node->iss_ReorderQueue);
242 : :
243 [ + + + + ]: 13642 : if (node->iss_ReachedEnd ||
244 : 6819 : cmp_orderbyvals(topmost->orderbyvals,
245 : 6819 : topmost->orderbynulls,
246 : 6819 : scandesc->xs_orderbyvals,
247 : 6819 : scandesc->xs_orderbynulls,
248 : : node) <= 0)
249 : : {
250 : : HeapTuple tuple;
251 : :
252 : 3381 : tuple = reorderqueue_pop(node);
253 : :
254 : : /* Pass 'true', as the tuple in the queue is a palloc'd copy */
255 : 3381 : ExecForceStoreHeapTuple(tuple, slot, true);
256 : 3381 : return slot;
257 : : }
258 : : }
259 [ + + ]: 51733 : else if (node->iss_ReachedEnd)
260 : : {
261 : : /* Queue is empty, and no more tuples from index. We're done. */
262 : 12 : return ExecClearTuple(slot);
263 : : }
264 : :
265 : : /*
266 : : * Fetch next tuple from the index.
267 : : */
268 : 55163 : next_indextuple:
269 [ + + ]: 57923 : if (!index_getnext_slot(scandesc, ForwardScanDirection, slot))
270 : : {
271 : : /*
272 : : * No more tuples from the index. But we still need to drain any
273 : : * remaining tuples from the queue before we're done.
274 : : */
275 : 12 : node->iss_ReachedEnd = true;
276 : 12 : continue;
277 : : }
278 : :
279 : : /*
280 : : * If the index was lossy, we have to recheck the index quals and
281 : : * ORDER BY expressions using the fetched tuple.
282 : : */
283 [ + + ]: 57911 : if (scandesc->xs_recheck)
284 : : {
285 : 6084 : econtext->ecxt_scantuple = slot;
286 [ + + ]: 6084 : if (!ExecQualAndReset(node->indexqualorig, econtext))
287 : : {
288 : : /* Fails recheck, so drop it and loop back for another */
289 [ - + ]: 2760 : InstrCountFiltered2(node, 1);
290 : : /* allow this loop to be cancellable */
291 [ - + ]: 2760 : CHECK_FOR_INTERRUPTS();
292 : 2760 : goto next_indextuple;
293 : : }
294 : : }
295 : :
296 [ + + ]: 55151 : if (scandesc->xs_recheckorderby)
297 : : {
298 : 3524 : econtext->ecxt_scantuple = slot;
299 : 3524 : ResetExprContext(econtext);
300 : 3524 : EvalOrderByExpressions(node, econtext);
301 : :
302 : : /*
303 : : * Was the ORDER BY value returned by the index accurate? The
304 : : * recheck flag means that the index can return inaccurate values,
305 : : * but then again, the value returned for any particular tuple
306 : : * could also be exactly correct. Compare the value returned by
307 : : * the index with the recalculated value. (If the value returned
308 : : * by the index happened to be exact right, we can often avoid
309 : : * pushing the tuple to the queue, just to pop it back out again.)
310 : : */
311 : 3524 : cmp = cmp_orderbyvals(node->iss_OrderByValues,
312 : 3524 : node->iss_OrderByNulls,
313 : 3524 : scandesc->xs_orderbyvals,
314 : 3524 : scandesc->xs_orderbynulls,
315 : : node);
316 [ - + ]: 3524 : if (cmp < 0)
317 [ # # ]: 0 : elog(ERROR, "index returned tuples in wrong order");
318 [ + + ]: 3524 : else if (cmp == 0)
319 : 85 : was_exact = true;
320 : : else
321 : 3439 : was_exact = false;
322 : 3524 : lastfetched_vals = node->iss_OrderByValues;
323 : 3524 : lastfetched_nulls = node->iss_OrderByNulls;
324 : : }
325 : : else
326 : : {
327 : 51627 : was_exact = true;
328 : 51627 : lastfetched_vals = scandesc->xs_orderbyvals;
329 : 51627 : lastfetched_nulls = scandesc->xs_orderbynulls;
330 : : }
331 : :
332 : : /*
333 : : * Can we return this tuple immediately, or does it need to be pushed
334 : : * to the reorder queue? If the ORDER BY expression values returned
335 : : * by the index were inaccurate, we can't return it yet, because the
336 : : * next tuple from the index might need to come before this one. Also,
337 : : * we can't return it yet if there are any smaller tuples in the queue
338 : : * already.
339 : : */
340 [ + + + + : 55208 : if (!was_exact || (topmost && cmp_orderbyvals(lastfetched_vals,
+ + ]
341 : : lastfetched_nulls,
342 : 57 : topmost->orderbyvals,
343 : 57 : topmost->orderbynulls,
344 : : node) > 0))
345 : : {
346 : : /* Put this tuple to the queue */
347 : 3443 : reorderqueue_push(node, slot, lastfetched_vals, lastfetched_nulls);
348 : 3443 : continue;
349 : : }
350 : : else
351 : : {
352 : : /* Can return this tuple immediately. */
353 : 51708 : return slot;
354 : : }
355 : : }
356 : :
357 : : /*
358 : : * if we get here it means the index scan failed so we are at the end of
359 : : * the scan..
360 : : */
361 : : return ExecClearTuple(slot);
362 : : }
363 : :
364 : : /*
365 : : * Calculate the expressions in the ORDER BY clause, based on the heap tuple.
366 : : */
367 : : static void
368 : 3524 : EvalOrderByExpressions(IndexScanState *node, ExprContext *econtext)
369 : : {
370 : : int i;
371 : : ListCell *l;
372 : : MemoryContext oldContext;
373 : :
374 : 3524 : oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
375 : :
376 : 3524 : i = 0;
377 [ + - + + : 7048 : foreach(l, node->indexorderbyorig)
+ + ]
378 : : {
379 : 3524 : ExprState *orderby = (ExprState *) lfirst(l);
380 : :
381 : 7048 : node->iss_OrderByValues[i] = ExecEvalExpr(orderby,
382 : : econtext,
383 : 3524 : &node->iss_OrderByNulls[i]);
384 : 3524 : i++;
385 : : }
386 : :
387 : 3524 : MemoryContextSwitchTo(oldContext);
388 : 3524 : }
389 : :
390 : : /*
391 : : * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual
392 : : */
393 : : static bool
394 : 74 : IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
395 : : {
396 : : ExprContext *econtext;
397 : :
398 : : /*
399 : : * extract necessary information from index scan node
400 : : */
401 : 74 : econtext = node->ss.ps.ps_ExprContext;
402 : :
403 : : /* Does the tuple meet the indexqual condition? */
404 : 74 : econtext->ecxt_scantuple = slot;
405 : 74 : return ExecQualAndReset(node->indexqualorig, econtext);
406 : : }
407 : :
408 : :
409 : : /*
410 : : * Compare ORDER BY expression values.
411 : : */
412 : : static int
413 : 19443 : cmp_orderbyvals(const Datum *adist, const bool *anulls,
414 : : const Datum *bdist, const bool *bnulls,
415 : : IndexScanState *node)
416 : : {
417 : : int i;
418 : : int result;
419 : :
420 [ + + ]: 19579 : for (i = 0; i < node->iss_NumOrderByKeys; i++)
421 : : {
422 : 19443 : SortSupport ssup = &node->iss_SortSupport[i];
423 : :
424 : : /*
425 : : * Handle nulls. We only need to support NULLS LAST ordering, because
426 : : * match_pathkeys_to_index() doesn't consider indexorderby
427 : : * implementation otherwise.
428 : : */
429 [ - + - - ]: 19443 : if (anulls[i] && !bnulls[i])
430 : 0 : return 1;
431 [ + - - + ]: 19443 : else if (!anulls[i] && bnulls[i])
432 : 0 : return -1;
433 [ - + - - ]: 19443 : else if (anulls[i] && bnulls[i])
434 : 0 : return 0;
435 : :
436 : 19443 : result = ssup->comparator(adist[i], bdist[i], ssup);
437 [ + + ]: 19443 : if (result != 0)
438 : 19307 : return result;
439 : : }
440 : :
441 : 136 : return 0;
442 : : }
443 : :
444 : : /*
445 : : * Pairing heap provides getting topmost (greatest) element while KNN provides
446 : : * ascending sort. That's why we invert the sort order.
447 : : */
448 : : static int
449 : 9043 : reorderqueue_cmp(const pairingheap_node *a, const pairingheap_node *b,
450 : : void *arg)
451 : : {
452 : 9043 : const ReorderTuple *rta = (const ReorderTuple *) a;
453 : 9043 : const ReorderTuple *rtb = (const ReorderTuple *) b;
454 : 9043 : IndexScanState *node = (IndexScanState *) arg;
455 : :
456 : : /* exchange argument order to invert the sort order */
457 : 18086 : return cmp_orderbyvals(rtb->orderbyvals, rtb->orderbynulls,
458 : 9043 : rta->orderbyvals, rta->orderbynulls,
459 : : node);
460 : : }
461 : :
462 : : /*
463 : : * Helper function to push a tuple to the reorder queue.
464 : : */
465 : : static void
466 : 3443 : reorderqueue_push(IndexScanState *node, TupleTableSlot *slot,
467 : : const Datum *orderbyvals, const bool *orderbynulls)
468 : : {
469 : 3443 : IndexScanDesc scandesc = node->iss_ScanDesc;
470 : 3443 : EState *estate = node->ss.ps.state;
471 : 3443 : MemoryContext oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
472 : : ReorderTuple *rt;
473 : : int i;
474 : :
475 : 3443 : rt = palloc_object(ReorderTuple);
476 : 3443 : rt->htup = ExecCopySlotHeapTuple(slot);
477 : 3443 : rt->orderbyvals = palloc_array(Datum, scandesc->numberOfOrderBys);
478 : 3443 : rt->orderbynulls = palloc_array(bool, scandesc->numberOfOrderBys);
479 [ + + ]: 6886 : for (i = 0; i < node->iss_NumOrderByKeys; i++)
480 : : {
481 [ + - ]: 3443 : if (!orderbynulls[i])
482 : 3443 : rt->orderbyvals[i] = datumCopy(orderbyvals[i],
483 : 3443 : node->iss_OrderByTypByVals[i],
484 : 3443 : node->iss_OrderByTypLens[i]);
485 : : else
486 : 0 : rt->orderbyvals[i] = (Datum) 0;
487 : 3443 : rt->orderbynulls[i] = orderbynulls[i];
488 : : }
489 : 3443 : pairingheap_add(node->iss_ReorderQueue, &rt->ph_node);
490 : :
491 : 3443 : MemoryContextSwitchTo(oldContext);
492 : 3443 : }
493 : :
494 : : /*
495 : : * Helper function to pop the next tuple from the reorder queue.
496 : : */
497 : : static HeapTuple
498 : 3421 : reorderqueue_pop(IndexScanState *node)
499 : : {
500 : : HeapTuple result;
501 : : ReorderTuple *topmost;
502 : : int i;
503 : :
504 : 3421 : topmost = (ReorderTuple *) pairingheap_remove_first(node->iss_ReorderQueue);
505 : :
506 : 3421 : result = topmost->htup;
507 [ + + ]: 6842 : for (i = 0; i < node->iss_NumOrderByKeys; i++)
508 : : {
509 [ - + - - ]: 3421 : if (!node->iss_OrderByTypByVals[i] && !topmost->orderbynulls[i])
510 : 0 : pfree(DatumGetPointer(topmost->orderbyvals[i]));
511 : : }
512 : 3421 : pfree(topmost->orderbyvals);
513 : 3421 : pfree(topmost->orderbynulls);
514 : 3421 : pfree(topmost);
515 : :
516 : 3421 : return result;
517 : : }
518 : :
519 : :
520 : : /* ----------------------------------------------------------------
521 : : * ExecIndexScan(node)
522 : : * ----------------------------------------------------------------
523 : : */
524 : : static TupleTableSlot *
525 : 1268651 : ExecIndexScan(PlanState *pstate)
526 : : {
527 : 1268651 : IndexScanState *node = castNode(IndexScanState, pstate);
528 : :
529 : : /*
530 : : * If we have runtime keys and they've not already been set up, do it now.
531 : : */
532 [ + + + + ]: 1268651 : if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)
533 : 15484 : ExecReScan((PlanState *) node);
534 : :
535 [ + + ]: 1268651 : if (node->iss_NumOrderByKeys > 0)
536 : 55101 : return ExecScan(&node->ss,
537 : : (ExecScanAccessMtd) IndexNextWithReorder,
538 : : (ExecScanRecheckMtd) IndexRecheck);
539 : : else
540 : 1213550 : return ExecScan(&node->ss,
541 : : (ExecScanAccessMtd) IndexNext,
542 : : (ExecScanRecheckMtd) IndexRecheck);
543 : : }
544 : :
545 : : /* ----------------------------------------------------------------
546 : : * ExecReScanIndexScan(node)
547 : : *
548 : : * Recalculates the values of any scan keys whose value depends on
549 : : * information known at runtime, then rescans the indexed relation.
550 : : *
551 : : * Updating the scan key was formerly done separately in
552 : : * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
553 : : * rescans of indices and relations/general streams more uniform.
554 : : * ----------------------------------------------------------------
555 : : */
556 : : void
557 : 325281 : ExecReScanIndexScan(IndexScanState *node)
558 : : {
559 : : /*
560 : : * If we are doing runtime key calculations (ie, any of the index key
561 : : * values weren't simple Consts), compute the new key values. But first,
562 : : * reset the context so we don't leak memory as each outer tuple is
563 : : * scanned. Note this assumes that we will recalculate *all* runtime keys
564 : : * on each call.
565 : : */
566 [ + + ]: 325281 : if (node->iss_NumRuntimeKeys != 0)
567 : : {
568 : 318289 : ExprContext *econtext = node->iss_RuntimeContext;
569 : :
570 : 318289 : ResetExprContext(econtext);
571 : 318289 : ExecIndexEvalRuntimeKeys(econtext,
572 : : node->iss_RuntimeKeys,
573 : : node->iss_NumRuntimeKeys);
574 : : }
575 : 325281 : node->iss_RuntimeKeysReady = true;
576 : :
577 : : /* flush the reorder queue */
578 [ + + ]: 325281 : if (node->iss_ReorderQueue)
579 : : {
580 : : HeapTuple tuple;
581 : :
582 [ + + ]: 84 : while (!pairingheap_is_empty(node->iss_ReorderQueue))
583 : : {
584 : 40 : tuple = reorderqueue_pop(node);
585 : 40 : heap_freetuple(tuple);
586 : : }
587 : : }
588 : :
589 : : /* reset index scan */
590 [ + + ]: 325281 : if (node->iss_ScanDesc)
591 : 288550 : index_rescan(node->iss_ScanDesc,
592 : : node->iss_ScanKeys, node->iss_NumScanKeys,
593 : : node->iss_OrderByKeys, node->iss_NumOrderByKeys);
594 : 325281 : node->iss_ReachedEnd = false;
595 : :
596 : 325281 : ExecScanReScan(&node->ss);
597 : 325281 : }
598 : :
599 : :
600 : : /*
601 : : * ExecIndexEvalRuntimeKeys
602 : : * Evaluate any runtime key values, and update the scankeys.
603 : : */
604 : : void
605 : 468231 : ExecIndexEvalRuntimeKeys(ExprContext *econtext,
606 : : IndexRuntimeKeyInfo *runtimeKeys, int numRuntimeKeys)
607 : : {
608 : : int j;
609 : : MemoryContext oldContext;
610 : :
611 : : /* We want to keep the key values in per-tuple memory */
612 : 468231 : oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
613 : :
614 [ + + ]: 956480 : for (j = 0; j < numRuntimeKeys; j++)
615 : : {
616 : 488249 : ScanKey scan_key = runtimeKeys[j].scan_key;
617 : 488249 : ExprState *key_expr = runtimeKeys[j].key_expr;
618 : : Datum scanvalue;
619 : : bool isNull;
620 : :
621 : : /*
622 : : * For each run-time key, extract the run-time expression and evaluate
623 : : * it with respect to the current context. We then stick the result
624 : : * into the proper scan key.
625 : : *
626 : : * Note: the result of the eval could be a pass-by-ref value that's
627 : : * stored in some outer scan's tuple, not in
628 : : * econtext->ecxt_per_tuple_memory. We assume that the outer tuple
629 : : * will stay put throughout our scan. If this is wrong, we could copy
630 : : * the result into our context explicitly, but I think that's not
631 : : * necessary.
632 : : *
633 : : * It's also entirely possible that the result of the eval is a
634 : : * toasted value. In this case we should forcibly detoast it, to
635 : : * avoid repeat detoastings each time the value is examined by an
636 : : * index support function.
637 : : */
638 : 488249 : scanvalue = ExecEvalExpr(key_expr,
639 : : econtext,
640 : : &isNull);
641 [ + + ]: 488249 : if (isNull)
642 : : {
643 : 1643 : scan_key->sk_argument = scanvalue;
644 : 1643 : scan_key->sk_flags |= SK_ISNULL;
645 : : }
646 : : else
647 : : {
648 [ + + ]: 486606 : if (runtimeKeys[j].key_toastable)
649 : 46596 : scanvalue = PointerGetDatum(PG_DETOAST_DATUM(scanvalue));
650 : 486606 : scan_key->sk_argument = scanvalue;
651 : 486606 : scan_key->sk_flags &= ~SK_ISNULL;
652 : : }
653 : : }
654 : :
655 : 468231 : MemoryContextSwitchTo(oldContext);
656 : 468231 : }
657 : :
658 : : /*
659 : : * ExecIndexEvalArrayKeys
660 : : * Evaluate any array key values, and set up to iterate through arrays.
661 : : *
662 : : * Returns true if there are array elements to consider; false means there
663 : : * is at least one null or empty array, so no match is possible. On true
664 : : * result, the scankeys are initialized with the first elements of the arrays.
665 : : */
666 : : bool
667 : 37 : ExecIndexEvalArrayKeys(ExprContext *econtext,
668 : : IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
669 : : {
670 : 37 : bool result = true;
671 : : int j;
672 : : MemoryContext oldContext;
673 : :
674 : : /* We want to keep the arrays in per-tuple memory */
675 : 37 : oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
676 : :
677 [ + + ]: 74 : for (j = 0; j < numArrayKeys; j++)
678 : : {
679 : 37 : ScanKey scan_key = arrayKeys[j].scan_key;
680 : 37 : ExprState *array_expr = arrayKeys[j].array_expr;
681 : : Datum arraydatum;
682 : : bool isNull;
683 : : ArrayType *arrayval;
684 : : int16 elmlen;
685 : : bool elmbyval;
686 : : char elmalign;
687 : : int num_elems;
688 : : Datum *elem_values;
689 : : bool *elem_nulls;
690 : :
691 : : /*
692 : : * Compute and deconstruct the array expression. (Notes in
693 : : * ExecIndexEvalRuntimeKeys() apply here too.)
694 : : */
695 : 37 : arraydatum = ExecEvalExpr(array_expr,
696 : : econtext,
697 : : &isNull);
698 [ - + ]: 37 : if (isNull)
699 : : {
700 : 0 : result = false;
701 : 0 : break; /* no point in evaluating more */
702 : : }
703 : 37 : arrayval = DatumGetArrayTypeP(arraydatum);
704 : : /* We could cache this data, but not clear it's worth it */
705 : 37 : get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
706 : : &elmlen, &elmbyval, &elmalign);
707 : 37 : deconstruct_array(arrayval,
708 : : ARR_ELEMTYPE(arrayval),
709 : : elmlen, elmbyval, elmalign,
710 : : &elem_values, &elem_nulls, &num_elems);
711 [ - + ]: 37 : if (num_elems <= 0)
712 : : {
713 : 0 : result = false;
714 : 0 : break; /* no point in evaluating more */
715 : : }
716 : :
717 : : /*
718 : : * Note: we expect the previous array data, if any, to be
719 : : * automatically freed by resetting the per-tuple context; hence no
720 : : * pfree's here.
721 : : */
722 : 37 : arrayKeys[j].elem_values = elem_values;
723 : 37 : arrayKeys[j].elem_nulls = elem_nulls;
724 : 37 : arrayKeys[j].num_elems = num_elems;
725 : 37 : scan_key->sk_argument = elem_values[0];
726 [ - + ]: 37 : if (elem_nulls[0])
727 : 0 : scan_key->sk_flags |= SK_ISNULL;
728 : : else
729 : 37 : scan_key->sk_flags &= ~SK_ISNULL;
730 : 37 : arrayKeys[j].next_elem = 1;
731 : : }
732 : :
733 : 37 : MemoryContextSwitchTo(oldContext);
734 : :
735 : 37 : return result;
736 : : }
737 : :
738 : : /*
739 : : * ExecIndexAdvanceArrayKeys
740 : : * Advance to the next set of array key values, if any.
741 : : *
742 : : * Returns true if there is another set of values to consider, false if not.
743 : : * On true result, the scankeys are initialized with the next set of values.
744 : : */
745 : : bool
746 : 15771 : ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
747 : : {
748 : 15771 : bool found = false;
749 : : int j;
750 : :
751 : : /*
752 : : * Note we advance the rightmost array key most quickly, since it will
753 : : * correspond to the lowest-order index column among the available
754 : : * qualifications. This is hypothesized to result in better locality of
755 : : * access in the index.
756 : : */
757 [ + + ]: 15808 : for (j = numArrayKeys - 1; j >= 0; j--)
758 : : {
759 : 74 : ScanKey scan_key = arrayKeys[j].scan_key;
760 : 74 : int next_elem = arrayKeys[j].next_elem;
761 : 74 : int num_elems = arrayKeys[j].num_elems;
762 : 74 : Datum *elem_values = arrayKeys[j].elem_values;
763 : 74 : bool *elem_nulls = arrayKeys[j].elem_nulls;
764 : :
765 [ + + ]: 74 : if (next_elem >= num_elems)
766 : : {
767 : 37 : next_elem = 0;
768 : 37 : found = false; /* need to advance next array key */
769 : : }
770 : : else
771 : 37 : found = true;
772 : 74 : scan_key->sk_argument = elem_values[next_elem];
773 [ - + ]: 74 : if (elem_nulls[next_elem])
774 : 0 : scan_key->sk_flags |= SK_ISNULL;
775 : : else
776 : 74 : scan_key->sk_flags &= ~SK_ISNULL;
777 : 74 : arrayKeys[j].next_elem = next_elem + 1;
778 [ + + ]: 74 : if (found)
779 : 37 : break;
780 : : }
781 : :
782 : 15771 : return found;
783 : : }
784 : :
785 : :
786 : : /* ----------------------------------------------------------------
787 : : * ExecEndIndexScan
788 : : * ----------------------------------------------------------------
789 : : */
790 : : void
791 : 107255 : ExecEndIndexScan(IndexScanState *node)
792 : : {
793 : : Relation indexRelationDesc;
794 : : IndexScanDesc indexScanDesc;
795 : :
796 : : /*
797 : : * extract information from the node
798 : : */
799 : 107255 : indexRelationDesc = node->iss_RelationDesc;
800 : 107255 : indexScanDesc = node->iss_ScanDesc;
801 : :
802 : : /*
803 : : * When ending a parallel worker, copy the statistics gathered by the
804 : : * worker back into shared memory so that it can be picked up by the main
805 : : * process to report in EXPLAIN ANALYZE
806 : : */
807 [ + + + + ]: 107255 : if (node->iss_SharedInfo != NULL && IsParallelWorker())
808 : : {
809 : : IndexScanInstrumentation *winstrument;
810 : :
811 : : Assert(ParallelWorkerNumber < node->iss_SharedInfo->num_workers);
812 : 180 : winstrument = &node->iss_SharedInfo->winstrument[ParallelWorkerNumber];
813 : :
814 : : /*
815 : : * We have to accumulate the stats rather than performing a memcpy.
816 : : * When a Gather/GatherMerge node finishes it will perform planner
817 : : * shutdown on the workers. On rescan it will spin up new workers
818 : : * which will have a new IndexOnlyScanState and zeroed stats.
819 : : */
820 : 180 : winstrument->nsearches += node->iss_Instrument->nsearches;
821 : : }
822 : :
823 : : /*
824 : : * close the index relation (no-op if we didn't open it)
825 : : */
826 [ + + ]: 107255 : if (indexScanDesc)
827 : 83127 : index_endscan(indexScanDesc);
828 [ + + ]: 107255 : if (indexRelationDesc)
829 : 105013 : index_close(indexRelationDesc, NoLock);
830 : 107255 : }
831 : :
832 : : /* ----------------------------------------------------------------
833 : : * ExecIndexMarkPos
834 : : *
835 : : * Note: we assume that no caller attempts to set a mark before having read
836 : : * at least one tuple. Otherwise, iss_ScanDesc might still be NULL.
837 : : * ----------------------------------------------------------------
838 : : */
839 : : void
840 : 4048 : ExecIndexMarkPos(IndexScanState *node)
841 : : {
842 : 4048 : EState *estate = node->ss.ps.state;
843 : 4048 : EPQState *epqstate = estate->es_epq_active;
844 : :
845 [ + + ]: 4048 : if (epqstate != NULL)
846 : : {
847 : : /*
848 : : * We are inside an EvalPlanQual recheck. If a test tuple exists for
849 : : * this relation, then we shouldn't access the index at all. We would
850 : : * instead need to save, and later restore, the state of the
851 : : * relsubs_done flag, so that re-fetching the test tuple is possible.
852 : : * However, given the assumption that no caller sets a mark at the
853 : : * start of the scan, we can only get here with relsubs_done[i]
854 : : * already set, and so no state need be saved.
855 : : */
856 : 1 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
857 : :
858 : : Assert(scanrelid > 0);
859 [ - + ]: 1 : if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
860 [ # # ]: 0 : epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
861 : : {
862 : : /* Verify the claim above */
863 [ - + ]: 1 : if (!epqstate->relsubs_done[scanrelid - 1])
864 [ # # ]: 0 : elog(ERROR, "unexpected ExecIndexMarkPos call in EPQ recheck");
865 : 1 : return;
866 : : }
867 : : }
868 : :
869 : 4047 : index_markpos(node->iss_ScanDesc);
870 : : }
871 : :
872 : : /* ----------------------------------------------------------------
873 : : * ExecIndexRestrPos
874 : : * ----------------------------------------------------------------
875 : : */
876 : : void
877 : 36018 : ExecIndexRestrPos(IndexScanState *node)
878 : : {
879 : 36018 : EState *estate = node->ss.ps.state;
880 : 36018 : EPQState *epqstate = estate->es_epq_active;
881 : :
882 [ - + ]: 36018 : if (estate->es_epq_active != NULL)
883 : : {
884 : : /* See comments in ExecIndexMarkPos */
885 : 0 : Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
886 : :
887 : : Assert(scanrelid > 0);
888 [ # # ]: 0 : if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
889 [ # # ]: 0 : epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
890 : : {
891 : : /* Verify the claim above */
892 [ # # ]: 0 : if (!epqstate->relsubs_done[scanrelid - 1])
893 [ # # ]: 0 : elog(ERROR, "unexpected ExecIndexRestrPos call in EPQ recheck");
894 : 0 : return;
895 : : }
896 : : }
897 : :
898 : 36018 : index_restrpos(node->iss_ScanDesc);
899 : : }
900 : :
901 : : /* ----------------------------------------------------------------
902 : : * ExecInitIndexScan
903 : : *
904 : : * Initializes the index scan's state information, creates
905 : : * scan keys, and opens the base and index relations.
906 : : *
907 : : * Note: index scans have 2 sets of state information because
908 : : * we have to keep track of the base relation and the
909 : : * index relation.
910 : : * ----------------------------------------------------------------
911 : : */
912 : : IndexScanState *
913 : 107810 : ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
914 : : {
915 : : IndexScanState *indexstate;
916 : : Relation currentRelation;
917 : : LOCKMODE lockmode;
918 : :
919 : : /*
920 : : * create state structure
921 : : */
922 : 107810 : indexstate = makeNode(IndexScanState);
923 : 107810 : indexstate->ss.ps.plan = (Plan *) node;
924 : 107810 : indexstate->ss.ps.state = estate;
925 : 107810 : indexstate->ss.ps.ExecProcNode = ExecIndexScan;
926 : :
927 : : /*
928 : : * Miscellaneous initialization
929 : : *
930 : : * create expression context for node
931 : : */
932 : 107810 : ExecAssignExprContext(estate, &indexstate->ss.ps);
933 : :
934 : : /*
935 : : * open the scan relation
936 : : */
937 : 107810 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
938 : :
939 : 107810 : indexstate->ss.ss_currentRelation = currentRelation;
940 : 107810 : indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
941 : :
942 : : /*
943 : : * get the scan type from the relation descriptor.
944 : : */
945 : 107810 : ExecInitScanTupleSlot(estate, &indexstate->ss,
946 : : RelationGetDescr(currentRelation),
947 : : table_slot_callbacks(currentRelation),
948 : : TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS);
949 : :
950 : : /*
951 : : * Initialize result type and projection.
952 : : */
953 : 107810 : ExecInitResultTypeTL(&indexstate->ss.ps);
954 : 107810 : ExecAssignScanProjectionInfo(&indexstate->ss);
955 : :
956 : : /*
957 : : * initialize child expressions
958 : : *
959 : : * Note: we don't initialize all of the indexqual expression, only the
960 : : * sub-parts corresponding to runtime keys (see below). Likewise for
961 : : * indexorderby, if any. But the indexqualorig expression is always
962 : : * initialized even though it will only be used in some uncommon cases ---
963 : : * would be nice to improve that. (Problem is that any SubPlans present
964 : : * in the expression must be found now...)
965 : : */
966 : 107810 : indexstate->ss.ps.qual =
967 : 107810 : ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
968 : 107810 : indexstate->indexqualorig =
969 : 107810 : ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
970 : 107810 : indexstate->indexorderbyorig =
971 : 107810 : ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
972 : :
973 : : /*
974 : : * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
975 : : * here. This allows an index-advisor plugin to EXPLAIN a plan containing
976 : : * references to nonexistent indexes.
977 : : */
978 [ + + ]: 107810 : if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
979 : 2242 : return indexstate;
980 : :
981 : : /* Set up instrumentation of index scans if requested */
982 [ + + ]: 105568 : if (estate->es_instrument)
983 : 628 : indexstate->iss_Instrument = palloc0_object(IndexScanInstrumentation);
984 : :
985 : : /* Open the index relation. */
986 : 105568 : lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
987 : 105568 : indexstate->iss_RelationDesc = index_open(node->indexid, lockmode);
988 : :
989 : : /*
990 : : * Initialize index-specific scan state
991 : : */
992 : 105568 : indexstate->iss_RuntimeKeysReady = false;
993 : 105568 : indexstate->iss_RuntimeKeys = NULL;
994 : 105568 : indexstate->iss_NumRuntimeKeys = 0;
995 : :
996 : : /*
997 : : * build the index scan keys from the index qualification
998 : : */
999 : 105568 : ExecIndexBuildScanKeys((PlanState *) indexstate,
1000 : : indexstate->iss_RelationDesc,
1001 : : node->indexqual,
1002 : : false,
1003 : 105568 : &indexstate->iss_ScanKeys,
1004 : : &indexstate->iss_NumScanKeys,
1005 : : &indexstate->iss_RuntimeKeys,
1006 : : &indexstate->iss_NumRuntimeKeys,
1007 : : NULL, /* no ArrayKeys */
1008 : : NULL);
1009 : :
1010 : : /*
1011 : : * any ORDER BY exprs have to be turned into scankeys in the same way
1012 : : */
1013 : 105568 : ExecIndexBuildScanKeys((PlanState *) indexstate,
1014 : : indexstate->iss_RelationDesc,
1015 : : node->indexorderby,
1016 : : true,
1017 : 105568 : &indexstate->iss_OrderByKeys,
1018 : : &indexstate->iss_NumOrderByKeys,
1019 : : &indexstate->iss_RuntimeKeys,
1020 : : &indexstate->iss_NumRuntimeKeys,
1021 : : NULL, /* no ArrayKeys */
1022 : : NULL);
1023 : :
1024 : : /* Initialize sort support, if we need to re-check ORDER BY exprs */
1025 [ + + ]: 105568 : if (indexstate->iss_NumOrderByKeys > 0)
1026 : : {
1027 : 29 : int numOrderByKeys = indexstate->iss_NumOrderByKeys;
1028 : : int i;
1029 : : ListCell *lco;
1030 : : ListCell *lcx;
1031 : :
1032 : : /*
1033 : : * Prepare sort support, and look up the data type for each ORDER BY
1034 : : * expression.
1035 : : */
1036 : : Assert(numOrderByKeys == list_length(node->indexorderbyops));
1037 : : Assert(numOrderByKeys == list_length(node->indexorderbyorig));
1038 : 29 : indexstate->iss_SortSupport = palloc0_array(SortSupportData, numOrderByKeys);
1039 : 29 : indexstate->iss_OrderByTypByVals = palloc_array(bool, numOrderByKeys);
1040 : 29 : indexstate->iss_OrderByTypLens = palloc_array(int16, numOrderByKeys);
1041 : 29 : i = 0;
1042 [ + - + + : 58 : forboth(lco, node->indexorderbyops, lcx, node->indexorderbyorig)
+ - + + +
+ + - +
+ ]
1043 : : {
1044 : 29 : Oid orderbyop = lfirst_oid(lco);
1045 : 29 : Node *orderbyexpr = (Node *) lfirst(lcx);
1046 : 29 : Oid orderbyType = exprType(orderbyexpr);
1047 : 29 : Oid orderbyColl = exprCollation(orderbyexpr);
1048 : 29 : SortSupport orderbysort = &indexstate->iss_SortSupport[i];
1049 : :
1050 : : /* Initialize sort support */
1051 : 29 : orderbysort->ssup_cxt = CurrentMemoryContext;
1052 : 29 : orderbysort->ssup_collation = orderbyColl;
1053 : : /* See cmp_orderbyvals() comments on NULLS LAST */
1054 : 29 : orderbysort->ssup_nulls_first = false;
1055 : : /* ssup_attno is unused here and elsewhere */
1056 : 29 : orderbysort->ssup_attno = 0;
1057 : : /* No abbreviation */
1058 : 29 : orderbysort->abbreviate = false;
1059 : 29 : PrepareSortSupportFromOrderingOp(orderbyop, orderbysort);
1060 : :
1061 : 29 : get_typlenbyval(orderbyType,
1062 : 29 : &indexstate->iss_OrderByTypLens[i],
1063 : 29 : &indexstate->iss_OrderByTypByVals[i]);
1064 : 29 : i++;
1065 : : }
1066 : :
1067 : : /* allocate arrays to hold the re-calculated distances */
1068 : 29 : indexstate->iss_OrderByValues = palloc_array(Datum, numOrderByKeys);
1069 : 29 : indexstate->iss_OrderByNulls = palloc_array(bool, numOrderByKeys);
1070 : :
1071 : : /* and initialize the reorder queue */
1072 : 29 : indexstate->iss_ReorderQueue = pairingheap_allocate(reorderqueue_cmp,
1073 : : indexstate);
1074 : : }
1075 : :
1076 : : /*
1077 : : * If we have runtime keys, we need an ExprContext to evaluate them. The
1078 : : * node's standard context won't do because we want to reset that context
1079 : : * for every tuple. So, build another context just like the other one...
1080 : : * -tgl 7/11/00
1081 : : */
1082 [ + + ]: 105568 : if (indexstate->iss_NumRuntimeKeys != 0)
1083 : : {
1084 : 49256 : ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
1085 : :
1086 : 49256 : ExecAssignExprContext(estate, &indexstate->ss.ps);
1087 : 49256 : indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
1088 : 49256 : indexstate->ss.ps.ps_ExprContext = stdecontext;
1089 : : }
1090 : : else
1091 : : {
1092 : 56312 : indexstate->iss_RuntimeContext = NULL;
1093 : : }
1094 : :
1095 : : /*
1096 : : * all done.
1097 : : */
1098 : 105568 : return indexstate;
1099 : : }
1100 : :
1101 : :
1102 : : /*
1103 : : * ExecIndexBuildScanKeys
1104 : : * Build the index scan keys from the index qualification expressions
1105 : : *
1106 : : * The index quals are passed to the index AM in the form of a ScanKey array.
1107 : : * This routine sets up the ScanKeys, fills in all constant fields of the
1108 : : * ScanKeys, and prepares information about the keys that have non-constant
1109 : : * comparison values. We divide index qual expressions into five types:
1110 : : *
1111 : : * 1. Simple operator with constant comparison value ("indexkey op constant").
1112 : : * For these, we just fill in a ScanKey containing the constant value.
1113 : : *
1114 : : * 2. Simple operator with non-constant value ("indexkey op expression").
1115 : : * For these, we create a ScanKey with everything filled in except the
1116 : : * expression value, and set up an IndexRuntimeKeyInfo struct to drive
1117 : : * evaluation of the expression at the right times.
1118 : : *
1119 : : * 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)").
1120 : : * For these, we create a header ScanKey plus a subsidiary ScanKey array,
1121 : : * as specified in access/skey.h. The elements of the row comparison
1122 : : * can have either constant or non-constant comparison values.
1123 : : *
1124 : : * 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). If the index
1125 : : * supports amsearcharray, we handle these the same as simple operators,
1126 : : * setting the SK_SEARCHARRAY flag to tell the AM to handle them. Otherwise,
1127 : : * we create a ScanKey with everything filled in except the comparison value,
1128 : : * and set up an IndexArrayKeyInfo struct to drive processing of the qual.
1129 : : * (Note that if we use an IndexArrayKeyInfo struct, the array expression is
1130 : : * always treated as requiring runtime evaluation, even if it's a constant.)
1131 : : *
1132 : : * 5. NullTest ("indexkey IS NULL/IS NOT NULL"). We just fill in the
1133 : : * ScanKey properly.
1134 : : *
1135 : : * This code is also used to prepare ORDER BY expressions for amcanorderbyop
1136 : : * indexes. The behavior is exactly the same, except that we have to look up
1137 : : * the operator differently. Note that only cases 1 and 2 are currently
1138 : : * possible for ORDER BY.
1139 : : *
1140 : : * Input params are:
1141 : : *
1142 : : * planstate: executor state node we are working for
1143 : : * index: the index we are building scan keys for
1144 : : * quals: indexquals (or indexorderbys) expressions
1145 : : * isorderby: true if processing ORDER BY exprs, false if processing quals
1146 : : * *runtimeKeys: ptr to pre-existing IndexRuntimeKeyInfos, or NULL if none
1147 : : * *numRuntimeKeys: number of pre-existing runtime keys
1148 : : *
1149 : : * Output params are:
1150 : : *
1151 : : * *scanKeys: receives ptr to array of ScanKeys
1152 : : * *numScanKeys: receives number of scankeys
1153 : : * *runtimeKeys: receives ptr to array of IndexRuntimeKeyInfos, or NULL if none
1154 : : * *numRuntimeKeys: receives number of runtime keys
1155 : : * *arrayKeys: receives ptr to array of IndexArrayKeyInfos, or NULL if none
1156 : : * *numArrayKeys: receives number of array keys
1157 : : *
1158 : : * Caller may pass NULL for arrayKeys and numArrayKeys to indicate that
1159 : : * IndexArrayKeyInfos are not supported.
1160 : : */
1161 : : void
1162 : 244941 : ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
1163 : : List *quals, bool isorderby,
1164 : : ScanKey *scanKeys, int *numScanKeys,
1165 : : IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
1166 : : IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
1167 : : {
1168 : : ListCell *qual_cell;
1169 : : ScanKey scan_keys;
1170 : : IndexRuntimeKeyInfo *runtime_keys;
1171 : : IndexArrayKeyInfo *array_keys;
1172 : : int n_scan_keys;
1173 : : int n_runtime_keys;
1174 : : int max_runtime_keys;
1175 : : int n_array_keys;
1176 : : int j;
1177 : :
1178 : : /* Allocate array for ScanKey structs: one per qual */
1179 : 244941 : n_scan_keys = list_length(quals);
1180 : 244941 : scan_keys = palloc_array(ScanKeyData, n_scan_keys);
1181 : :
1182 : : /*
1183 : : * runtime_keys array is dynamically resized as needed. We handle it this
1184 : : * way so that the same runtime keys array can be shared between
1185 : : * indexquals and indexorderbys, which will be processed in separate calls
1186 : : * of this function. Caller must be sure to pass in NULL/0 for first
1187 : : * call.
1188 : : */
1189 : 244941 : runtime_keys = *runtimeKeys;
1190 : 244941 : n_runtime_keys = max_runtime_keys = *numRuntimeKeys;
1191 : :
1192 : : /* Allocate array_keys as large as it could possibly need to be */
1193 : 244941 : array_keys = palloc0_array(IndexArrayKeyInfo, n_scan_keys);
1194 : 244941 : n_array_keys = 0;
1195 : :
1196 : : /*
1197 : : * for each opclause in the given qual, convert the opclause into a single
1198 : : * scan key
1199 : : */
1200 : 244941 : j = 0;
1201 [ + + + + : 395450 : foreach(qual_cell, quals)
+ + ]
1202 : : {
1203 : 150509 : Expr *clause = (Expr *) lfirst(qual_cell);
1204 : 150509 : ScanKey this_scan_key = &scan_keys[j++];
1205 : : Oid opno; /* operator's OID */
1206 : : RegProcedure opfuncid; /* operator proc id used in scan */
1207 : : Oid opfamily; /* opfamily of index column */
1208 : : int op_strategy; /* operator's strategy number */
1209 : : Oid op_lefttype; /* operator's declared input types */
1210 : : Oid op_righttype;
1211 : : Expr *leftop; /* expr on lhs of operator */
1212 : : Expr *rightop; /* expr on rhs ... */
1213 : : AttrNumber varattno; /* att number used in scan */
1214 : : int indnkeyatts;
1215 : :
1216 : 150509 : indnkeyatts = IndexRelationGetNumberOfKeyAttributes(index);
1217 [ + + ]: 150509 : if (IsA(clause, OpExpr))
1218 : : {
1219 : : /* indexkey op const or indexkey op expression */
1220 : 148847 : int flags = 0;
1221 : : Datum scanvalue;
1222 : :
1223 : 148847 : opno = ((OpExpr *) clause)->opno;
1224 : 148847 : opfuncid = ((OpExpr *) clause)->opfuncid;
1225 : :
1226 : : /*
1227 : : * leftop should be the index key Var, possibly relabeled
1228 : : */
1229 : 148847 : leftop = (Expr *) get_leftop(clause);
1230 : :
1231 [ + - - + ]: 148847 : if (leftop && IsA(leftop, RelabelType))
1232 : 0 : leftop = ((RelabelType *) leftop)->arg;
1233 : :
1234 : : Assert(leftop != NULL);
1235 : :
1236 [ + - ]: 148847 : if (!(IsA(leftop, Var) &&
1237 [ - + ]: 148847 : ((Var *) leftop)->varno == INDEX_VAR))
1238 [ # # ]: 0 : elog(ERROR, "indexqual doesn't have key on left side");
1239 : :
1240 : 148847 : varattno = ((Var *) leftop)->varattno;
1241 [ + - - + ]: 148847 : if (varattno < 1 || varattno > indnkeyatts)
1242 [ # # ]: 0 : elog(ERROR, "bogus index qualification");
1243 : :
1244 : : /*
1245 : : * We have to look up the operator's strategy number. This
1246 : : * provides a cross-check that the operator does match the index.
1247 : : */
1248 : 148847 : opfamily = index->rd_opfamily[varattno - 1];
1249 : :
1250 : 148847 : get_op_opfamily_properties(opno, opfamily, isorderby,
1251 : : &op_strategy,
1252 : : &op_lefttype,
1253 : : &op_righttype);
1254 : :
1255 [ + + ]: 148847 : if (isorderby)
1256 : 123 : flags |= SK_ORDER_BY;
1257 : :
1258 : : /*
1259 : : * rightop is the constant or variable comparison value
1260 : : */
1261 : 148847 : rightop = (Expr *) get_rightop(clause);
1262 : :
1263 [ + - + + ]: 148847 : if (rightop && IsA(rightop, RelabelType))
1264 : 2490 : rightop = ((RelabelType *) rightop)->arg;
1265 : :
1266 : : Assert(rightop != NULL);
1267 : :
1268 [ + + ]: 148847 : if (IsA(rightop, Const))
1269 : : {
1270 : : /* OK, simple constant comparison value */
1271 : 87038 : scanvalue = ((Const *) rightop)->constvalue;
1272 [ - + ]: 87038 : if (((Const *) rightop)->constisnull)
1273 : 0 : flags |= SK_ISNULL;
1274 : : }
1275 : : else
1276 : : {
1277 : : /* Need to treat this one as a runtime key */
1278 [ + + ]: 61809 : if (n_runtime_keys >= max_runtime_keys)
1279 : : {
1280 [ + + ]: 54563 : if (max_runtime_keys == 0)
1281 : : {
1282 : 54559 : max_runtime_keys = 8;
1283 : 54559 : runtime_keys = palloc_array(IndexRuntimeKeyInfo, max_runtime_keys);
1284 : : }
1285 : : else
1286 : : {
1287 : 4 : max_runtime_keys *= 2;
1288 : 4 : runtime_keys = repalloc_array(runtime_keys,
1289 : : IndexRuntimeKeyInfo, max_runtime_keys);
1290 : : }
1291 : : }
1292 : 61809 : runtime_keys[n_runtime_keys].scan_key = this_scan_key;
1293 : 123618 : runtime_keys[n_runtime_keys].key_expr =
1294 : 61809 : ExecInitExpr(rightop, planstate);
1295 : 61809 : runtime_keys[n_runtime_keys].key_toastable =
1296 : 61809 : TypeIsToastable(op_righttype);
1297 : 61809 : n_runtime_keys++;
1298 : 61809 : scanvalue = (Datum) 0;
1299 : : }
1300 : :
1301 : : /*
1302 : : * initialize the scan key's fields appropriately
1303 : : */
1304 : 148847 : ScanKeyEntryInitialize(this_scan_key,
1305 : : flags,
1306 : : varattno, /* attribute number to scan */
1307 : : op_strategy, /* op's strategy */
1308 : : op_righttype, /* strategy subtype */
1309 : : ((OpExpr *) clause)->inputcollid, /* collation */
1310 : : opfuncid, /* reg proc to use */
1311 : : scanvalue); /* constant */
1312 : : }
1313 [ + + ]: 1662 : else if (IsA(clause, RowCompareExpr))
1314 : : {
1315 : : /* (indexkey, indexkey, ...) op (expression, expression, ...) */
1316 : 72 : RowCompareExpr *rc = (RowCompareExpr *) clause;
1317 : : ScanKey first_sub_key;
1318 : : int n_sub_key;
1319 : : ListCell *largs_cell;
1320 : : ListCell *rargs_cell;
1321 : : ListCell *opnos_cell;
1322 : : ListCell *collids_cell;
1323 : :
1324 : : Assert(!isorderby);
1325 : :
1326 : 72 : first_sub_key = palloc_array(ScanKeyData, list_length(rc->opnos));
1327 : 72 : n_sub_key = 0;
1328 : :
1329 : : /* Scan RowCompare columns and generate subsidiary ScanKey items */
1330 [ + - + + : 216 : forfour(largs_cell, rc->largs, rargs_cell, rc->rargs,
+ - + + +
- + + + -
+ + + + +
- + - + -
+ + ]
1331 : : opnos_cell, rc->opnos, collids_cell, rc->inputcollids)
1332 : : {
1333 : 144 : ScanKey this_sub_key = &first_sub_key[n_sub_key];
1334 : 144 : int flags = SK_ROW_MEMBER;
1335 : : Datum scanvalue;
1336 : : Oid inputcollation;
1337 : :
1338 : 144 : leftop = (Expr *) lfirst(largs_cell);
1339 : 144 : rightop = (Expr *) lfirst(rargs_cell);
1340 : 144 : opno = lfirst_oid(opnos_cell);
1341 : 144 : inputcollation = lfirst_oid(collids_cell);
1342 : :
1343 : : /*
1344 : : * leftop should be the index key Var, possibly relabeled
1345 : : */
1346 [ + - - + ]: 144 : if (leftop && IsA(leftop, RelabelType))
1347 : 0 : leftop = ((RelabelType *) leftop)->arg;
1348 : :
1349 : : Assert(leftop != NULL);
1350 : :
1351 [ + - ]: 144 : if (!(IsA(leftop, Var) &&
1352 [ - + ]: 144 : ((Var *) leftop)->varno == INDEX_VAR))
1353 [ # # ]: 0 : elog(ERROR, "indexqual doesn't have key on left side");
1354 : :
1355 : 144 : varattno = ((Var *) leftop)->varattno;
1356 : :
1357 : : /*
1358 : : * We have to look up the operator's associated support
1359 : : * function
1360 : : */
1361 [ + - + - ]: 144 : if (!index->rd_indam->amcanorder ||
1362 [ - + ]: 144 : varattno < 1 || varattno > indnkeyatts)
1363 [ # # ]: 0 : elog(ERROR, "bogus RowCompare index qualification");
1364 : 144 : opfamily = index->rd_opfamily[varattno - 1];
1365 : :
1366 : 144 : get_op_opfamily_properties(opno, opfamily, isorderby,
1367 : : &op_strategy,
1368 : : &op_lefttype,
1369 : : &op_righttype);
1370 : :
1371 [ - + ]: 144 : if (op_strategy != rc->cmptype)
1372 [ # # ]: 0 : elog(ERROR, "RowCompare index qualification contains wrong operator");
1373 : :
1374 : 144 : opfuncid = get_opfamily_proc(opfamily,
1375 : : op_lefttype,
1376 : : op_righttype,
1377 : : BTORDER_PROC);
1378 [ - + ]: 144 : if (!RegProcedureIsValid(opfuncid))
1379 [ # # ]: 0 : elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
1380 : : BTORDER_PROC, op_lefttype, op_righttype, opfamily);
1381 : :
1382 : : /*
1383 : : * rightop is the constant or variable comparison value
1384 : : */
1385 [ + - - + ]: 144 : if (rightop && IsA(rightop, RelabelType))
1386 : 0 : rightop = ((RelabelType *) rightop)->arg;
1387 : :
1388 : : Assert(rightop != NULL);
1389 : :
1390 [ + - ]: 144 : if (IsA(rightop, Const))
1391 : : {
1392 : : /* OK, simple constant comparison value */
1393 : 144 : scanvalue = ((Const *) rightop)->constvalue;
1394 [ + + ]: 144 : if (((Const *) rightop)->constisnull)
1395 : 28 : flags |= SK_ISNULL;
1396 : : }
1397 : : else
1398 : : {
1399 : : /* Need to treat this one as a runtime key */
1400 [ # # ]: 0 : if (n_runtime_keys >= max_runtime_keys)
1401 : : {
1402 [ # # ]: 0 : if (max_runtime_keys == 0)
1403 : : {
1404 : 0 : max_runtime_keys = 8;
1405 : 0 : runtime_keys = palloc_array(IndexRuntimeKeyInfo, max_runtime_keys);
1406 : : }
1407 : : else
1408 : : {
1409 : 0 : max_runtime_keys *= 2;
1410 : 0 : runtime_keys = repalloc_array(runtime_keys,
1411 : : IndexRuntimeKeyInfo, max_runtime_keys);
1412 : : }
1413 : : }
1414 : 0 : runtime_keys[n_runtime_keys].scan_key = this_sub_key;
1415 : 0 : runtime_keys[n_runtime_keys].key_expr =
1416 : 0 : ExecInitExpr(rightop, planstate);
1417 : 0 : runtime_keys[n_runtime_keys].key_toastable =
1418 : 0 : TypeIsToastable(op_righttype);
1419 : 0 : n_runtime_keys++;
1420 : 0 : scanvalue = (Datum) 0;
1421 : : }
1422 : :
1423 : : /*
1424 : : * initialize the subsidiary scan key's fields appropriately
1425 : : */
1426 : 144 : ScanKeyEntryInitialize(this_sub_key,
1427 : : flags,
1428 : : varattno, /* attribute number */
1429 : : op_strategy, /* op's strategy */
1430 : : op_righttype, /* strategy subtype */
1431 : : inputcollation, /* collation */
1432 : : opfuncid, /* reg proc to use */
1433 : : scanvalue); /* constant */
1434 : 144 : n_sub_key++;
1435 : : }
1436 : :
1437 : : /* Mark the last subsidiary scankey correctly */
1438 : 72 : first_sub_key[n_sub_key - 1].sk_flags |= SK_ROW_END;
1439 : :
1440 : : /*
1441 : : * We don't use ScanKeyEntryInitialize for the header because it
1442 : : * isn't going to contain a valid sk_func pointer.
1443 : : */
1444 [ + - + - : 720 : MemSet(this_scan_key, 0, sizeof(ScanKeyData));
+ - + - +
+ ]
1445 : 72 : this_scan_key->sk_flags = SK_ROW_HEADER;
1446 : 72 : this_scan_key->sk_attno = first_sub_key->sk_attno;
1447 : 72 : this_scan_key->sk_strategy = rc->cmptype;
1448 : : /* sk_subtype, sk_collation, sk_func not used in a header */
1449 : 72 : this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
1450 : : }
1451 [ + + ]: 1590 : else if (IsA(clause, ScalarArrayOpExpr))
1452 : : {
1453 : : /* indexkey op ANY (array-expression) */
1454 : 1225 : ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
1455 : 1225 : int flags = 0;
1456 : : Datum scanvalue;
1457 : :
1458 : : Assert(!isorderby);
1459 : :
1460 : : Assert(saop->useOr);
1461 : 1225 : opno = saop->opno;
1462 : 1225 : opfuncid = saop->opfuncid;
1463 : :
1464 : : /*
1465 : : * leftop should be the index key Var, possibly relabeled
1466 : : */
1467 : 1225 : leftop = (Expr *) linitial(saop->args);
1468 : :
1469 [ + - - + ]: 1225 : if (leftop && IsA(leftop, RelabelType))
1470 : 0 : leftop = ((RelabelType *) leftop)->arg;
1471 : :
1472 : : Assert(leftop != NULL);
1473 : :
1474 [ + - ]: 1225 : if (!(IsA(leftop, Var) &&
1475 [ - + ]: 1225 : ((Var *) leftop)->varno == INDEX_VAR))
1476 [ # # ]: 0 : elog(ERROR, "indexqual doesn't have key on left side");
1477 : :
1478 : 1225 : varattno = ((Var *) leftop)->varattno;
1479 [ + - - + ]: 1225 : if (varattno < 1 || varattno > indnkeyatts)
1480 [ # # ]: 0 : elog(ERROR, "bogus index qualification");
1481 : :
1482 : : /*
1483 : : * We have to look up the operator's strategy number. This
1484 : : * provides a cross-check that the operator does match the index.
1485 : : */
1486 : 1225 : opfamily = index->rd_opfamily[varattno - 1];
1487 : :
1488 : 1225 : get_op_opfamily_properties(opno, opfamily, isorderby,
1489 : : &op_strategy,
1490 : : &op_lefttype,
1491 : : &op_righttype);
1492 : :
1493 : : /*
1494 : : * rightop is the constant or variable array value
1495 : : */
1496 : 1225 : rightop = (Expr *) lsecond(saop->args);
1497 : :
1498 [ + - - + ]: 1225 : if (rightop && IsA(rightop, RelabelType))
1499 : 0 : rightop = ((RelabelType *) rightop)->arg;
1500 : :
1501 : : Assert(rightop != NULL);
1502 : :
1503 [ + + ]: 1225 : if (index->rd_indam->amsearcharray)
1504 : : {
1505 : : /* Index AM will handle this like a simple operator */
1506 : 1188 : flags |= SK_SEARCHARRAY;
1507 [ + + ]: 1188 : if (IsA(rightop, Const))
1508 : : {
1509 : : /* OK, simple constant comparison value */
1510 : 1134 : scanvalue = ((Const *) rightop)->constvalue;
1511 [ + + ]: 1134 : if (((Const *) rightop)->constisnull)
1512 : 4 : flags |= SK_ISNULL;
1513 : : }
1514 : : else
1515 : : {
1516 : : /* Need to treat this one as a runtime key */
1517 [ + - ]: 54 : if (n_runtime_keys >= max_runtime_keys)
1518 : : {
1519 [ + - ]: 54 : if (max_runtime_keys == 0)
1520 : : {
1521 : 54 : max_runtime_keys = 8;
1522 : 54 : runtime_keys = palloc_array(IndexRuntimeKeyInfo, max_runtime_keys);
1523 : : }
1524 : : else
1525 : : {
1526 : 0 : max_runtime_keys *= 2;
1527 : 0 : runtime_keys = repalloc_array(runtime_keys,
1528 : : IndexRuntimeKeyInfo, max_runtime_keys);
1529 : : }
1530 : : }
1531 : 54 : runtime_keys[n_runtime_keys].scan_key = this_scan_key;
1532 : 108 : runtime_keys[n_runtime_keys].key_expr =
1533 : 54 : ExecInitExpr(rightop, planstate);
1534 : :
1535 : : /*
1536 : : * Careful here: the runtime expression is not of
1537 : : * op_righttype, but rather is an array of same; so
1538 : : * TypeIsToastable() isn't helpful. However, we can
1539 : : * assume that all array types are toastable.
1540 : : */
1541 : 54 : runtime_keys[n_runtime_keys].key_toastable = true;
1542 : 54 : n_runtime_keys++;
1543 : 54 : scanvalue = (Datum) 0;
1544 : : }
1545 : : }
1546 : : else
1547 : : {
1548 : : /* Executor has to expand the array value */
1549 : 37 : array_keys[n_array_keys].scan_key = this_scan_key;
1550 : 74 : array_keys[n_array_keys].array_expr =
1551 : 37 : ExecInitExpr(rightop, planstate);
1552 : : /* the remaining fields were zeroed by palloc0 */
1553 : 37 : n_array_keys++;
1554 : 37 : scanvalue = (Datum) 0;
1555 : : }
1556 : :
1557 : : /*
1558 : : * initialize the scan key's fields appropriately
1559 : : */
1560 : 1225 : ScanKeyEntryInitialize(this_scan_key,
1561 : : flags,
1562 : : varattno, /* attribute number to scan */
1563 : : op_strategy, /* op's strategy */
1564 : : op_righttype, /* strategy subtype */
1565 : : saop->inputcollid, /* collation */
1566 : : opfuncid, /* reg proc to use */
1567 : : scanvalue); /* constant */
1568 : : }
1569 [ + - ]: 365 : else if (IsA(clause, NullTest))
1570 : : {
1571 : : /* indexkey IS NULL or indexkey IS NOT NULL */
1572 : 365 : NullTest *ntest = (NullTest *) clause;
1573 : : int flags;
1574 : :
1575 : : Assert(!isorderby);
1576 : :
1577 : : /*
1578 : : * argument should be the index key Var, possibly relabeled
1579 : : */
1580 : 365 : leftop = ntest->arg;
1581 : :
1582 [ + - - + ]: 365 : if (leftop && IsA(leftop, RelabelType))
1583 : 0 : leftop = ((RelabelType *) leftop)->arg;
1584 : :
1585 : : Assert(leftop != NULL);
1586 : :
1587 [ + - ]: 365 : if (!(IsA(leftop, Var) &&
1588 [ - + ]: 365 : ((Var *) leftop)->varno == INDEX_VAR))
1589 [ # # ]: 0 : elog(ERROR, "NullTest indexqual has wrong key");
1590 : :
1591 : 365 : varattno = ((Var *) leftop)->varattno;
1592 : :
1593 : : /*
1594 : : * initialize the scan key's fields appropriately
1595 : : */
1596 [ + + - ]: 365 : switch (ntest->nulltesttype)
1597 : : {
1598 : 131 : case IS_NULL:
1599 : 131 : flags = SK_ISNULL | SK_SEARCHNULL;
1600 : 131 : break;
1601 : 234 : case IS_NOT_NULL:
1602 : 234 : flags = SK_ISNULL | SK_SEARCHNOTNULL;
1603 : 234 : break;
1604 : 0 : default:
1605 [ # # ]: 0 : elog(ERROR, "unrecognized nulltesttype: %d",
1606 : : (int) ntest->nulltesttype);
1607 : : flags = 0; /* keep compiler quiet */
1608 : : break;
1609 : : }
1610 : :
1611 : 365 : ScanKeyEntryInitialize(this_scan_key,
1612 : : flags,
1613 : : varattno, /* attribute number to scan */
1614 : : InvalidStrategy, /* no strategy */
1615 : : InvalidOid, /* no strategy subtype */
1616 : : InvalidOid, /* no collation */
1617 : : InvalidOid, /* no reg proc for this */
1618 : : (Datum) 0); /* constant */
1619 : : }
1620 : : else
1621 [ # # ]: 0 : elog(ERROR, "unsupported indexqual type: %d",
1622 : : (int) nodeTag(clause));
1623 : : }
1624 : :
1625 : : Assert(n_runtime_keys <= max_runtime_keys);
1626 : :
1627 : : /* Get rid of any unused arrays */
1628 [ + + ]: 244941 : if (n_array_keys == 0)
1629 : : {
1630 : 244904 : pfree(array_keys);
1631 : 244904 : array_keys = NULL;
1632 : : }
1633 : :
1634 : : /*
1635 : : * Return info to our caller.
1636 : : */
1637 : 244941 : *scanKeys = scan_keys;
1638 : 244941 : *numScanKeys = n_scan_keys;
1639 : 244941 : *runtimeKeys = runtime_keys;
1640 : 244941 : *numRuntimeKeys = n_runtime_keys;
1641 [ + + ]: 244941 : if (arrayKeys)
1642 : : {
1643 : 14257 : *arrayKeys = array_keys;
1644 : 14257 : *numArrayKeys = n_array_keys;
1645 : : }
1646 [ - + ]: 230684 : else if (n_array_keys != 0)
1647 [ # # ]: 0 : elog(ERROR, "ScalarArrayOpExpr index qual found where not allowed");
1648 : 244941 : }
1649 : :
1650 : : /* ----------------------------------------------------------------
1651 : : * Parallel Scan Support
1652 : : * ----------------------------------------------------------------
1653 : : */
1654 : :
1655 : : /* ----------------------------------------------------------------
1656 : : * ExecIndexScanEstimate
1657 : : *
1658 : : * Compute the amount of space we'll need in the parallel
1659 : : * query DSM, and inform pcxt->estimator about our needs.
1660 : : * ----------------------------------------------------------------
1661 : : */
1662 : : void
1663 : 12 : ExecIndexScanEstimate(IndexScanState *node,
1664 : : ParallelContext *pcxt)
1665 : : {
1666 : 12 : EState *estate = node->ss.ps.state;
1667 : :
1668 : 12 : node->iss_PscanLen = index_parallelscan_estimate(node->iss_RelationDesc,
1669 : : node->iss_NumScanKeys,
1670 : : node->iss_NumOrderByKeys,
1671 : : estate->es_snapshot);
1672 : 12 : shm_toc_estimate_chunk(&pcxt->estimator, node->iss_PscanLen);
1673 : 12 : shm_toc_estimate_keys(&pcxt->estimator, 1);
1674 : 12 : }
1675 : :
1676 : : /* ----------------------------------------------------------------
1677 : : * ExecIndexScanInitializeDSM
1678 : : *
1679 : : * Set up a parallel index scan descriptor.
1680 : : * ----------------------------------------------------------------
1681 : : */
1682 : : void
1683 : 12 : ExecIndexScanInitializeDSM(IndexScanState *node,
1684 : : ParallelContext *pcxt)
1685 : : {
1686 : 12 : EState *estate = node->ss.ps.state;
1687 : : ParallelIndexScanDesc piscan;
1688 : :
1689 : 12 : piscan = shm_toc_allocate(pcxt->toc, node->iss_PscanLen);
1690 : 12 : index_parallelscan_initialize(node->ss.ss_currentRelation,
1691 : : node->iss_RelationDesc,
1692 : : estate->es_snapshot,
1693 : : piscan);
1694 : 12 : shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
1695 : :
1696 : 12 : node->iss_ScanDesc =
1697 [ + - ]: 12 : index_beginscan_parallel(node->ss.ss_currentRelation,
1698 : : node->iss_RelationDesc,
1699 : : node->iss_Instrument,
1700 : : node->iss_NumScanKeys,
1701 : : node->iss_NumOrderByKeys,
1702 : : piscan,
1703 : 12 : ScanRelIsReadOnly(&node->ss) ?
1704 : : SO_HINT_REL_READ_ONLY : SO_NONE);
1705 : :
1706 : : /*
1707 : : * If no run-time keys to calculate or they are ready, go ahead and pass
1708 : : * the scankeys to the index AM.
1709 : : */
1710 [ + + - + ]: 12 : if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1711 : 8 : index_rescan(node->iss_ScanDesc,
1712 : : node->iss_ScanKeys, node->iss_NumScanKeys,
1713 : : node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1714 : 12 : }
1715 : :
1716 : : /* ----------------------------------------------------------------
1717 : : * ExecIndexScanReInitializeDSM
1718 : : *
1719 : : * Reset shared state before beginning a fresh scan.
1720 : : * ----------------------------------------------------------------
1721 : : */
1722 : : void
1723 : 8 : ExecIndexScanReInitializeDSM(IndexScanState *node,
1724 : : ParallelContext *pcxt)
1725 : : {
1726 : : Assert(node->ss.ps.plan->parallel_aware);
1727 : 8 : index_parallelrescan(node->iss_ScanDesc);
1728 : 8 : }
1729 : :
1730 : : /* ----------------------------------------------------------------
1731 : : * ExecIndexScanInitializeWorker
1732 : : *
1733 : : * Copy relevant information from TOC into planstate.
1734 : : * ----------------------------------------------------------------
1735 : : */
1736 : : void
1737 : 80 : ExecIndexScanInitializeWorker(IndexScanState *node,
1738 : : ParallelWorkerContext *pwcxt)
1739 : : {
1740 : : ParallelIndexScanDesc piscan;
1741 : :
1742 : 80 : piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
1743 : :
1744 : 80 : node->iss_ScanDesc =
1745 [ + - ]: 80 : index_beginscan_parallel(node->ss.ss_currentRelation,
1746 : : node->iss_RelationDesc,
1747 : : node->iss_Instrument,
1748 : : node->iss_NumScanKeys,
1749 : : node->iss_NumOrderByKeys,
1750 : : piscan,
1751 : 80 : ScanRelIsReadOnly(&node->ss) ?
1752 : : SO_HINT_REL_READ_ONLY : SO_NONE);
1753 : :
1754 : : /*
1755 : : * If no run-time keys to calculate or they are ready, go ahead and pass
1756 : : * the scankeys to the index AM.
1757 : : */
1758 [ + + - + ]: 80 : if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)
1759 : 64 : index_rescan(node->iss_ScanDesc,
1760 : : node->iss_ScanKeys, node->iss_NumScanKeys,
1761 : : node->iss_OrderByKeys, node->iss_NumOrderByKeys);
1762 : 80 : }
1763 : :
1764 : : /*
1765 : : * Compute the amount of space we'll need for the shared instrumentation and
1766 : : * inform pcxt->estimator.
1767 : : */
1768 : : void
1769 : 276 : ExecIndexScanInstrumentEstimate(IndexScanState *node,
1770 : : ParallelContext *pcxt)
1771 : : {
1772 : : Size size;
1773 : :
1774 [ + + - + ]: 276 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1775 : 96 : return;
1776 : :
1777 : : /*
1778 : : * This size calculation is trivial enough that we don't bother saving it
1779 : : * in the IndexScanState. We'll recalculate the needed size in
1780 : : * ExecIndexScanInstrumentInitDSM().
1781 : : */
1782 : 180 : size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
1783 : 180 : mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
1784 : 180 : shm_toc_estimate_chunk(&pcxt->estimator, size);
1785 : 180 : shm_toc_estimate_keys(&pcxt->estimator, 1);
1786 : : }
1787 : :
1788 : : /*
1789 : : * Set up parallel index scan instrumentation.
1790 : : */
1791 : : void
1792 : 276 : ExecIndexScanInstrumentInitDSM(IndexScanState *node,
1793 : : ParallelContext *pcxt)
1794 : : {
1795 : : Size size;
1796 : :
1797 [ + + - + ]: 276 : if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1798 : 96 : return;
1799 : :
1800 : 180 : size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
1801 : 180 : mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
1802 : 180 : node->iss_SharedInfo =
1803 : 180 : (SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc, size);
1804 : :
1805 : : /* Each per-worker area must start out as zeroes */
1806 : 180 : memset(node->iss_SharedInfo, 0, size);
1807 : 180 : node->iss_SharedInfo->num_workers = pcxt->nworkers;
1808 : 180 : shm_toc_insert(pcxt->toc,
1809 : 180 : node->ss.ps.plan->plan_node_id +
1810 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
1811 : 180 : node->iss_SharedInfo);
1812 : : }
1813 : :
1814 : : /*
1815 : : * Look up and save the location of the shared instrumentation.
1816 : : */
1817 : : void
1818 : 424 : ExecIndexScanInstrumentInitWorker(IndexScanState *node,
1819 : : ParallelWorkerContext *pwcxt)
1820 : : {
1821 [ + + ]: 424 : if (!node->ss.ps.instrument)
1822 : 244 : return;
1823 : :
1824 : 180 : node->iss_SharedInfo = (SharedIndexScanInstrumentation *)
1825 : 180 : shm_toc_lookup(pwcxt->toc,
1826 : 180 : node->ss.ps.plan->plan_node_id +
1827 : : PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
1828 : : false);
1829 : : }
1830 : :
1831 : : /* ----------------------------------------------------------------
1832 : : * ExecIndexScanRetrieveInstrumentation
1833 : : *
1834 : : * Transfer index scan statistics from DSM to private memory.
1835 : : * ----------------------------------------------------------------
1836 : : */
1837 : : void
1838 : 180 : ExecIndexScanRetrieveInstrumentation(IndexScanState *node)
1839 : : {
1840 : 180 : SharedIndexScanInstrumentation *SharedInfo = node->iss_SharedInfo;
1841 : : size_t size;
1842 : :
1843 [ - + ]: 180 : if (SharedInfo == NULL)
1844 : 0 : return;
1845 : :
1846 : : /* Create a copy of SharedInfo in backend-local memory */
1847 : 180 : size = offsetof(SharedIndexScanInstrumentation, winstrument) +
1848 : 180 : SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
1849 : 180 : node->iss_SharedInfo = palloc(size);
1850 : 180 : memcpy(node->iss_SharedInfo, SharedInfo, size);
1851 : : }
|