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