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