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