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