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