Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * indexam.c
4 : : * general index access method routines
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/access/index/indexam.c
12 : : *
13 : : * INTERFACE ROUTINES
14 : : * index_open - open an index relation by relation OID
15 : : * index_close - close an index relation
16 : : * index_beginscan - start a scan of an index with amgettuple
17 : : * index_beginscan_bitmap - start a scan of an index with amgetbitmap
18 : : * index_rescan - restart a scan of an index
19 : : * index_endscan - end a scan
20 : : * index_insert - insert an index tuple into a relation
21 : : * index_markpos - mark a scan position
22 : : * index_restrpos - restore a scan position
23 : : * index_parallelscan_estimate - estimate shared memory for parallel scan
24 : : * index_parallelscan_initialize - initialize parallel scan
25 : : * index_parallelrescan - (re)start a parallel scan of an index
26 : : * index_beginscan_parallel - join parallel index scan
27 : : * index_getbitmap - get all tuples from a scan
28 : : * index_bulk_delete - bulk deletion of index tuples
29 : : * index_vacuum_cleanup - post-deletion cleanup of an index
30 : : * index_can_return - does index support index-only scans?
31 : : * index_getprocid - get a support procedure OID
32 : : * index_getprocinfo - get a support procedure's lookup info
33 : : *
34 : : * NOTES
35 : : * This file contains the index_ routines which used
36 : : * to be a scattered collection of stuff in access/genam.
37 : : *
38 : : *-------------------------------------------------------------------------
39 : : */
40 : :
41 : : #include "postgres.h"
42 : :
43 : : #include "access/amapi.h"
44 : : #include "access/relation.h"
45 : : #include "access/reloptions.h"
46 : : #include "access/relscan.h"
47 : : #include "access/tableam.h"
48 : : #include "catalog/index.h"
49 : : #include "catalog/pg_type.h"
50 : : #include "nodes/execnodes.h"
51 : : #include "pgstat.h"
52 : : #include "storage/lmgr.h"
53 : : #include "storage/lock.h"
54 : : #include "storage/predicate.h"
55 : : #include "utils/ruleutils.h"
56 : : #include "utils/snapmgr.h"
57 : : #include "utils/syscache.h"
58 : :
59 : :
60 : : /* ----------------------------------------------------------------
61 : : * macros used in index_ routines
62 : : *
63 : : * Note: the ReindexIsProcessingIndex() check in RELATION_CHECKS is there
64 : : * to check that we don't try to scan or do retail insertions into an index
65 : : * that is currently being rebuilt or pending rebuild. This helps to catch
66 : : * things that don't work when reindexing system catalogs, as well as prevent
67 : : * user errors like index expressions that access their own tables. The check
68 : : * doesn't prevent the actual rebuild because we don't use RELATION_CHECKS
69 : : * when calling the index AM's ambuild routine, and there is no reason for
70 : : * ambuild to call its subsidiary routines through this file.
71 : : * ----------------------------------------------------------------
72 : : */
73 : : #define RELATION_CHECKS \
74 : : do { \
75 : : Assert(RelationIsValid(indexRelation)); \
76 : : Assert(indexRelation->rd_indam); \
77 : : if (unlikely(ReindexIsProcessingIndex(RelationGetRelid(indexRelation)))) \
78 : : ereport(ERROR, \
79 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
80 : : errmsg("cannot access index \"%s\" while it is being reindexed", \
81 : : RelationGetRelationName(indexRelation)))); \
82 : : } while(0)
83 : :
84 : : #define SCAN_CHECKS \
85 : : ( \
86 : : AssertMacro(scan), \
87 : : AssertMacro(RelationIsValid(scan->indexRelation)), \
88 : : AssertMacro(scan->indexRelation->rd_indam) \
89 : : )
90 : :
91 : : #define CHECK_REL_PROCEDURE(pname) \
92 : : do { \
93 : : if (indexRelation->rd_indam->pname == NULL) \
94 : : elog(ERROR, "function \"%s\" is not defined for index \"%s\"", \
95 : : CppAsString(pname), RelationGetRelationName(indexRelation)); \
96 : : } while(0)
97 : :
98 : : #define CHECK_SCAN_PROCEDURE(pname) \
99 : : do { \
100 : : if (scan->indexRelation->rd_indam->pname == NULL) \
101 : : elog(ERROR, "function \"%s\" is not defined for index \"%s\"", \
102 : : CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \
103 : : } while(0)
104 : :
105 : : static inline void validate_relation_as_index(Relation r);
106 : : static pg_always_inline IndexScanDesc index_beginscan_internal(Relation indexRelation,
107 : : Relation heapRelation,
108 : : int nkeys,
109 : : int norderbys,
110 : : Snapshot snapshot,
111 : : ParallelIndexScanDesc pscan,
112 : : IndexScanInstrumentation *instrument,
113 : : bool index_only_scan,
114 : : bool temp_snap,
115 : : uint32 flags);
116 : :
117 : :
118 : : /* ----------------------------------------------------------------
119 : : * index_ interface functions
120 : : * ----------------------------------------------------------------
121 : : */
122 : :
123 : : /* ----------------
124 : : * index_open - open an index relation by relation OID
125 : : *
126 : : * If lockmode is not "NoLock", the specified kind of lock is
127 : : * obtained on the index. (Generally, NoLock should only be
128 : : * used if the caller knows it has some appropriate lock on the
129 : : * index already.)
130 : : *
131 : : * An error is raised if the index does not exist.
132 : : *
133 : : * This is a convenience routine adapted for indexscan use.
134 : : * Some callers may prefer to use relation_open directly.
135 : : * ----------------
136 : : */
137 : : Relation
7356 tgl@sss.pgh.pa.us 138 :CBC 11889833 : index_open(Oid relationId, LOCKMODE lockmode)
139 : : {
140 : : Relation r;
141 : :
142 : 11889833 : r = relation_open(relationId, lockmode);
143 : :
209 peter@eisentraut.org 144 : 11889828 : validate_relation_as_index(r);
145 : :
976 michael@paquier.xyz 146 : 11889814 : return r;
147 : : }
148 : :
149 : : /* ----------------
150 : : * try_index_open - open an index relation by relation OID
151 : : *
152 : : * Same as index_open, except return NULL instead of failing
153 : : * if the relation does not exist.
154 : : * ----------------
155 : : */
156 : : Relation
157 : 1087 : try_index_open(Oid relationId, LOCKMODE lockmode)
158 : : {
159 : : Relation r;
160 : :
161 : 1087 : r = try_relation_open(relationId, lockmode);
162 : :
163 : : /* leave if index does not exist */
164 [ - + ]: 1087 : if (!r)
976 michael@paquier.xyz 165 :UBC 0 : return NULL;
166 : :
209 peter@eisentraut.org 167 :CBC 1087 : validate_relation_as_index(r);
168 : :
9864 tgl@sss.pgh.pa.us 169 : 1087 : return r;
170 : : }
171 : :
172 : : /* ----------------
173 : : * index_close - close an index relation
174 : : *
175 : : * If lockmode is not "NoLock", we then release the specified lock.
176 : : *
177 : : * Note that it is often sensible to hold a lock beyond index_close;
178 : : * in that case, the lock is released automatically at xact end.
179 : : * ----------------
180 : : */
181 : : void
7356 182 : 11917230 : index_close(Relation relation, LOCKMODE lockmode)
183 : : {
184 : 11917230 : LockRelId relid = relation->rd_lockInfo.lockRelId;
185 : :
186 [ + - - + ]: 11917230 : Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
187 : :
188 : : /* The relcache does the real work... */
10605 bruce@momjian.us 189 : 11917230 : RelationClose(relation);
190 : :
7356 tgl@sss.pgh.pa.us 191 [ + + ]: 11917230 : if (lockmode != NoLock)
192 : 10885356 : UnlockRelationId(&relid, lockmode);
11030 scrappy@hub.org 193 : 11917230 : }
194 : :
195 : : /* ----------------
196 : : * validate_relation_as_index
197 : : *
198 : : * Make sure relkind is an index or a partitioned index.
199 : : * ----------------
200 : : */
201 : : static inline void
209 peter@eisentraut.org 202 : 11890915 : validate_relation_as_index(Relation r)
203 : : {
976 michael@paquier.xyz 204 [ + + ]: 11890915 : if (r->rd_rel->relkind != RELKIND_INDEX &&
205 [ + + ]: 7861 : r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
206 [ + - ]: 14 : ereport(ERROR,
207 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
208 : : errmsg("\"%s\" is not an index",
209 : : RelationGetRelationName(r))));
210 : 11890901 : }
211 : :
212 : :
213 : : /* ----------------
214 : : * index_insert - insert an index tuple into a relation
215 : : * ----------------
216 : : */
217 : : bool
8889 tgl@sss.pgh.pa.us 218 : 6038512 : index_insert(Relation indexRelation,
219 : : Datum *values,
220 : : bool *isnull,
221 : : ItemPointer heap_t_ctid,
222 : : Relation heapRelation,
223 : : IndexUniqueCheck checkUnique,
224 : : bool indexUnchanged,
225 : : IndexInfo *indexInfo)
226 : : {
10605 bruce@momjian.us 227 [ - + - + : 6038512 : RELATION_CHECKS;
- + - - ]
3899 tgl@sss.pgh.pa.us 228 [ - + - - ]: 6038512 : CHECK_REL_PROCEDURE(aminsert);
229 : :
2799 andres@anarazel.de 230 [ + + ]: 6038512 : if (!(indexRelation->rd_indam->ampredlocks))
5704 heikki.linnakangas@i 231 : 333455 : CheckForSerializableConflictIn(indexRelation,
232 : : (ItemPointer) NULL,
233 : : InvalidBlockNumber);
234 : :
2799 andres@anarazel.de 235 : 6038512 : return indexRelation->rd_indam->aminsert(indexRelation, values, isnull,
236 : : heap_t_ctid, heapRelation,
237 : : checkUnique, indexUnchanged,
238 : : indexInfo);
239 : : }
240 : :
241 : : /* -------------------------
242 : : * index_insert_cleanup - clean up after all index inserts are done
243 : : * -------------------------
244 : : */
245 : : void
1030 tomas.vondra@postgre 246 : 2196007 : index_insert_cleanup(Relation indexRelation,
247 : : IndexInfo *indexInfo)
248 : : {
249 [ - + - + : 2196007 : RELATION_CHECKS;
- + - - ]
250 : :
884 251 [ + + ]: 2196007 : if (indexRelation->rd_indam->aminsertcleanup)
252 : 740 : indexRelation->rd_indam->aminsertcleanup(indexRelation, indexInfo);
1030 253 : 2196007 : }
254 : :
255 : : /*
256 : : * index_beginscan - start a scan of an index with amgettuple
257 : : *
258 : : * Caller must be holding suitable locks on the heap and the index.
259 : : */
260 : : IndexScanDesc
8889 tgl@sss.pgh.pa.us 261 : 8664486 : index_beginscan(Relation heapRelation,
262 : : Relation indexRelation,
263 : : bool index_only_scan,
264 : : Snapshot snapshot,
265 : : IndexScanInstrumentation *instrument,
266 : : int nkeys, int norderbys,
267 : : uint32 flags)
268 : : {
1384 akorotkov@postgresql 269 [ - + ]: 8664486 : Assert(snapshot != InvalidSnapshot);
5 pg@bowt.ie 270 [ - + ]:GNC 8664486 : pg_assume(heapRelation != NULL);
271 : :
272 : : /* Check that a historic snapshot is not used for non-catalog tables */
394 heikki.linnakangas@i 273 [ + + ]:CBC 8664486 : if (IsHistoricMVCCSnapshot(snapshot) &&
274 [ + + + - : 17247 : !RelationIsAccessibleInLogicalDecoding(heapRelation))
+ - - + -
- - - - +
- - - - -
- - - -
- ]
275 : : {
394 heikki.linnakangas@i 276 [ # # ]:UBC 0 : ereport(ERROR,
277 : : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
278 : : errmsg("cannot query non-catalog table \"%s\" during logical decoding",
279 : : RelationGetRelationName(heapRelation))));
280 : : }
281 : :
5 pg@bowt.ie 282 :GNC 8664486 : return index_beginscan_internal(indexRelation, heapRelation,
283 : : nkeys, norderbys,
284 : : snapshot, NULL, instrument,
285 : : index_only_scan, false, flags);
286 : : }
287 : :
288 : : /*
289 : : * index_beginscan_bitmap - start a scan of an index with amgetbitmap
290 : : *
291 : : * As above, caller had better be holding some lock on the parent heap
292 : : * relation, even though it's not explicitly mentioned here.
293 : : */
294 : : IndexScanDesc
6737 tgl@sss.pgh.pa.us 295 :CBC 13233 : index_beginscan_bitmap(Relation indexRelation,
296 : : Snapshot snapshot,
297 : : IndexScanInstrumentation *instrument,
298 : : int nkeys)
299 : : {
1384 akorotkov@postgresql 300 [ - + ]: 13233 : Assert(snapshot != InvalidSnapshot);
5 pg@bowt.ie 301 [ - + - - ]:GNC 13233 : Assert(IsMVCCLikeSnapshot(snapshot));
302 : :
303 : 13233 : return index_beginscan_internal(indexRelation, NULL, nkeys, 0, snapshot,
304 : : NULL, instrument, false, false, SO_NONE);
305 : : }
306 : :
307 : : /*
308 : : * index_beginscan_internal --- common code for index_beginscan variants
309 : : *
310 : : * When heapRelation is not NULL, also initializes table AM index scan state.
311 : : */
312 : : static pg_always_inline IndexScanDesc
313 : 8677977 : index_beginscan_internal(Relation indexRelation, Relation heapRelation,
314 : : int nkeys, int norderbys, Snapshot snapshot,
315 : : ParallelIndexScanDesc pscan,
316 : : IndexScanInstrumentation *instrument,
317 : : bool index_only_scan, bool temp_snap, uint32 flags)
318 : : {
319 : : IndexScanDesc scan;
320 : :
10605 bruce@momjian.us 321 [ - + - + :CBC 8677977 : RELATION_CHECKS;
- + - - ]
3899 tgl@sss.pgh.pa.us 322 [ - + - - ]: 8677977 : CHECK_REL_PROCEDURE(ambeginscan);
323 : :
2799 andres@anarazel.de 324 [ + + ]: 8677977 : if (!(indexRelation->rd_indam->ampredlocks))
5576 heikki.linnakangas@i 325 : 2953 : PredicateLockRelation(indexRelation, snapshot);
326 : :
327 : : /*
328 : : * We hold a reference count to the relcache entry throughout the scan.
329 : : */
7356 tgl@sss.pgh.pa.us 330 : 8677977 : RelationIncrementReferenceCount(indexRelation);
331 : :
332 : : /*
333 : : * Tell the AM to open a scan.
334 : : */
2799 andres@anarazel.de 335 : 8677977 : scan = indexRelation->rd_indam->ambeginscan(indexRelation, nkeys,
336 : : norderbys);
337 : : /* Initialize information for parallel scan. */
3526 rhaas@postgresql.org 338 : 8677977 : scan->parallel_scan = pscan;
339 : 8677977 : scan->xs_temp_snap = temp_snap;
340 : :
5 pg@bowt.ie 341 :GNC 8677977 : scan->xs_snapshot = snapshot;
342 : 8677977 : scan->instrument = instrument;
343 : :
344 : : /*
345 : : * Initialize heap-side scan state when a heap relation is provided.
346 : : * Bitmap index scans don't provide one.
347 : : */
348 [ + + ]: 8677977 : if (heapRelation != NULL)
349 : : {
350 : 8664744 : scan->heapRelation = heapRelation;
351 : 8664744 : scan->xs_want_itup = index_only_scan;
352 : 8664744 : scan->xs_heap_continue = false;
353 : :
354 : : /*
355 : : * The "name" type's btree opclass stores index keys as cstrings
356 : : * rather than names to save space, so keys returned by an index-only
357 : : * scan must be re-padded to NAMEDATALEN allocations. Set up the
358 : : * state tableam_index_fill_ios_slot uses to do that. We detect such
359 : : * columns generically (stored type CSTRINGOID, opclass input type
360 : : * NAMEOID) in case other opclasses adopt the same optimization.
361 : : */
362 [ + + ]: 8664744 : if (index_only_scan)
363 : : {
364 : 90753 : int indnkeyatts = indexRelation->rd_index->indnkeyatts;
365 : 90753 : int namecount = 0;
366 : :
367 [ + + ]: 195617 : for (int attnum = 0; attnum < indnkeyatts; attnum++)
368 : : {
369 [ + + ]: 104864 : if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
370 [ + - ]: 5086 : indexRelation->rd_opcintype[attnum] == NAMEOID)
371 : 5086 : namecount++;
372 : : }
373 : :
374 [ + + ]: 90753 : if (unlikely(namecount > 0))
375 : : {
376 : 5086 : int idx = 0;
377 : :
378 : 5086 : scan->xs_name_cstring_attnums = palloc_array(AttrNumber, namecount);
379 [ + + ]: 13012 : for (int attnum = 0; attnum < indnkeyatts; attnum++)
380 : : {
381 [ + + ]: 7926 : if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
382 [ + - ]: 5086 : indexRelation->rd_opcintype[attnum] == NAMEOID)
383 : 5086 : scan->xs_name_cstring_attnums[idx++] = (AttrNumber) attnum;
384 : : }
385 : :
386 : 5086 : scan->xs_name_cstring_buf = palloc(namecount * NAMEDATALEN);
387 : 5086 : scan->xs_name_cstring_count = namecount;
388 : : }
389 : : }
390 : :
391 : : /* set up table AM state for the index scan (sets xs_table_opaque) */
392 : 8664744 : table_index_scan_begin(scan, flags);
393 : :
394 : : /* table AM must set these for us */
395 [ + - - + ]: 8664744 : Assert(scan->xs_getnext_slot != NULL && scan->xs_table_opaque != NULL);
396 : : }
397 : :
3526 rhaas@postgresql.org 398 :CBC 8677977 : return scan;
399 : : }
400 : :
401 : : /* ----------------
402 : : * index_rescan - (re)start a scan of an index
403 : : *
404 : : * During a restart, the caller may specify a new set of scankeys and/or
405 : : * orderbykeys; but the number of keys cannot differ from what index_beginscan
406 : : * was told. (Later we might relax that to "must not exceed", but currently
407 : : * the index AMs tend to assume that scan->numberOfKeys is what to believe.)
408 : : * To restart the scan without changing keys, pass NULL for the key arrays.
409 : : * (Of course, keys *must* be passed on the first call, unless
410 : : * scan->numberOfKeys is zero.)
411 : : * ----------------
412 : : */
413 : : void
5771 tgl@sss.pgh.pa.us 414 : 9116175 : index_rescan(IndexScanDesc scan,
415 : : ScanKey keys, int nkeys,
416 : : ScanKey orderbys, int norderbys)
417 : : {
10605 bruce@momjian.us 418 [ - + - + : 9116175 : SCAN_CHECKS;
- + ]
3899 tgl@sss.pgh.pa.us 419 [ - + - - ]: 9116175 : CHECK_SCAN_PROCEDURE(amrescan);
420 : :
5771 421 [ - + ]: 9116175 : Assert(nkeys == scan->numberOfKeys);
422 [ - + ]: 9116175 : Assert(norderbys == scan->numberOfOrderBys);
423 : :
424 : : /* reset table AM state for rescan */
5 pg@bowt.ie 425 [ + + ]:GNC 9116175 : if (scan->xs_table_opaque)
426 : 9100770 : table_index_scan_reset(scan);
427 : :
3378 tgl@sss.pgh.pa.us 428 :CBC 9116175 : scan->kill_prior_tuple = false; /* for safety */
2750 andres@anarazel.de 429 : 9116175 : scan->xs_heap_continue = false;
430 : :
2799 431 : 9116175 : scan->indexRelation->rd_indam->amrescan(scan, keys, nkeys,
432 : : orderbys, norderbys);
11030 scrappy@hub.org 433 : 9116175 : }
434 : :
435 : : /* ----------------
436 : : * index_endscan - end a scan
437 : : * ----------------
438 : : */
439 : : void
440 : 8676690 : index_endscan(IndexScanDesc scan)
441 : : {
10605 bruce@momjian.us 442 [ - + - + : 8676690 : SCAN_CHECKS;
- + ]
3899 tgl@sss.pgh.pa.us 443 [ - + - - ]: 8676690 : CHECK_SCAN_PROCEDURE(amendscan);
444 : :
445 : : /* Release resources (like buffer pins) from table accesses */
5 pg@bowt.ie 446 [ + + ]:GNC 8676690 : if (scan->xs_table_opaque)
447 : 8663538 : table_index_scan_end(scan);
448 : :
449 : : /* End the AM's scan */
2799 andres@anarazel.de 450 :CBC 8676690 : scan->indexRelation->rd_indam->amendscan(scan);
451 : :
452 : : /* Release index refcount acquired by index_beginscan */
8889 tgl@sss.pgh.pa.us 453 : 8676690 : RelationDecrementReferenceCount(scan->indexRelation);
454 : :
3526 rhaas@postgresql.org 455 [ + + ]: 8676690 : if (scan->xs_temp_snap)
456 : 258 : UnregisterSnapshot(scan->xs_snapshot);
457 : :
458 : : /* Release the scan data structure itself */
9761 tgl@sss.pgh.pa.us 459 : 8676690 : IndexScanEnd(scan);
11030 scrappy@hub.org 460 : 8676690 : }
461 : :
462 : : /* ----------------
463 : : * index_markpos - mark a scan position
464 : : * ----------------
465 : : */
466 : : void
467 : 86087 : index_markpos(IndexScanDesc scan)
468 : : {
10605 bruce@momjian.us 469 [ - + - + : 86087 : SCAN_CHECKS;
- + ]
3899 tgl@sss.pgh.pa.us 470 [ - + - - ]: 86087 : CHECK_SCAN_PROCEDURE(ammarkpos);
471 : :
2799 andres@anarazel.de 472 : 86087 : scan->indexRelation->rd_indam->ammarkpos(scan);
11030 scrappy@hub.org 473 : 86087 : }
474 : :
475 : : /* ----------------
476 : : * index_restrpos - restore a scan position
477 : : *
478 : : * NOTE: this only restores the internal scan state of the index AM. See
479 : : * comments for ExecRestrPos().
480 : : *
481 : : * NOTE: For heap, in the presence of HOT chains, mark/restore only works
482 : : * correctly if the scan's snapshot is MVCC-safe; that ensures that there's at
483 : : * most one returnable tuple in each HOT chain, and so restoring the prior
484 : : * state at the granularity of the index AM is sufficient. Since the only
485 : : * current user of mark/restore functionality is nodeMergejoin.c, this
486 : : * effectively means that merge-join plans only work for MVCC snapshots. This
487 : : * could be fixed if necessary, but for now it seems unimportant.
488 : : * ----------------
489 : : */
490 : : void
491 : 36030 : index_restrpos(IndexScanDesc scan)
492 : : {
191 andres@anarazel.de 493 [ - + - - ]: 36030 : Assert(IsMVCCLikeSnapshot(scan->xs_snapshot));
494 : :
10605 bruce@momjian.us 495 [ - + - + : 36030 : SCAN_CHECKS;
- + ]
3899 tgl@sss.pgh.pa.us 496 [ - + - - ]: 36030 : CHECK_SCAN_PROCEDURE(amrestrpos);
497 : :
498 : : /* reset table AM state for restoring the marked position */
5 pg@bowt.ie 499 [ + - ]:GNC 36030 : if (scan->xs_table_opaque)
500 : 36030 : table_index_scan_reset(scan);
501 : :
3378 tgl@sss.pgh.pa.us 502 :CBC 36030 : scan->kill_prior_tuple = false; /* for safety */
2750 andres@anarazel.de 503 : 36030 : scan->xs_heap_continue = false;
504 : :
2799 505 : 36030 : scan->indexRelation->rd_indam->amrestrpos(scan);
11030 scrappy@hub.org 506 : 36030 : }
507 : :
508 : : /*
509 : : * Estimates the shared memory needed for parallel scan, including any
510 : : * AM-specific parallel scan state.
511 : : */
512 : : Size
897 pg@bowt.ie 513 : 42 : index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys,
514 : : Snapshot snapshot)
515 : : {
516 : : Size nbytes;
517 : :
3526 rhaas@postgresql.org 518 [ - + - + : 42 : RELATION_CHECKS;
- + - - ]
519 : :
520 : 42 : nbytes = offsetof(ParallelIndexScanDescData, ps_snapshot_data);
521 : 42 : nbytes = add_size(nbytes, EstimateSnapshotSpace(snapshot));
522 : 42 : nbytes = MAXALIGN(nbytes);
523 : :
524 : : /*
525 : : * If parallel scan index AM interface can't be used (or index AM provides
526 : : * no such interface), assume there is no AM-specific data needed
527 : : */
167 melanieplageman@gmai 528 [ + - ]: 42 : if (indexRelation->rd_indam->amestimateparallelscan != NULL)
3526 rhaas@postgresql.org 529 : 42 : nbytes = add_size(nbytes,
534 pg@bowt.ie 530 : 42 : indexRelation->rd_indam->amestimateparallelscan(indexRelation,
531 : : nkeys,
532 : : norderbys));
533 : :
3526 rhaas@postgresql.org 534 : 42 : return nbytes;
535 : : }
536 : :
537 : : /*
538 : : * index_parallelscan_initialize - initialize parallel scan
539 : : *
540 : : * We initialize both the ParallelIndexScanDesc proper and the AM-specific
541 : : * information which follows it.
542 : : *
543 : : * This function calls access method specific initialization routine to
544 : : * initialize am specific information. Call this just once in the leader
545 : : * process; then, individual workers attach via index_beginscan_parallel.
546 : : */
547 : : void
548 : 42 : index_parallelscan_initialize(Relation heapRelation, Relation indexRelation,
549 : : Snapshot snapshot,
550 : : ParallelIndexScanDesc target)
551 : : {
552 : : Size offset;
553 : :
554 [ - + - + : 42 : RELATION_CHECKS;
- + - - ]
555 : :
556 : 42 : offset = add_size(offsetof(ParallelIndexScanDescData, ps_snapshot_data),
557 : : EstimateSnapshotSpace(snapshot));
558 : 42 : offset = MAXALIGN(offset);
559 : :
730 tgl@sss.pgh.pa.us 560 : 42 : target->ps_locator = heapRelation->rd_locator;
561 : 42 : target->ps_indexlocator = indexRelation->rd_locator;
558 pg@bowt.ie 562 : 42 : target->ps_offset_am = 0;
3526 rhaas@postgresql.org 563 : 42 : SerializeSnapshot(snapshot, target->ps_snapshot_data);
564 : :
565 : : /* aminitparallelscan is optional; assume no-op if not provided by AM */
167 melanieplageman@gmai 566 [ + - ]: 42 : if (indexRelation->rd_indam->aminitparallelscan != NULL)
567 : : {
568 : : void *amtarget;
569 : :
558 pg@bowt.ie 570 : 42 : target->ps_offset_am = offset;
571 : 42 : amtarget = OffsetToPointer(target, target->ps_offset_am);
2799 andres@anarazel.de 572 : 42 : indexRelation->rd_indam->aminitparallelscan(amtarget);
573 : : }
3526 rhaas@postgresql.org 574 : 42 : }
575 : :
576 : : /* ----------------
577 : : * index_parallelrescan - (re)start a parallel scan of an index
578 : : * ----------------
579 : : */
580 : : void
581 : 16 : index_parallelrescan(IndexScanDesc scan)
582 : : {
583 [ - + - + : 16 : SCAN_CHECKS;
- + ]
584 : :
585 : : /* reset table AM state for rescan */
5 pg@bowt.ie 586 [ + - ]:GNC 16 : if (scan->xs_table_opaque)
587 : 16 : table_index_scan_reset(scan);
588 : :
589 : : /* amparallelrescan is optional; assume no-op if not provided by AM */
2799 andres@anarazel.de 590 [ + - ]:CBC 16 : if (scan->indexRelation->rd_indam->amparallelrescan != NULL)
591 : 16 : scan->indexRelation->rd_indam->amparallelrescan(scan);
3526 rhaas@postgresql.org 592 : 16 : }
593 : :
594 : : /*
595 : : * index_beginscan_parallel - join parallel index scan
596 : : *
597 : : * flags is a bitmask of ScanOptions affecting the underlying table scan. No
598 : : * SO_INTERNAL_FLAGS are permitted.
599 : : *
600 : : * Caller must be holding suitable locks on the heap and the index.
601 : : */
602 : : IndexScanDesc
558 pg@bowt.ie 603 : 258 : index_beginscan_parallel(Relation heaprel, Relation indexrel,
604 : : bool index_only_scan,
605 : : IndexScanInstrumentation *instrument,
606 : : int nkeys, int norderbys,
607 : : ParallelIndexScanDesc pscan,
608 : : uint32 flags)
609 : : {
610 : : Snapshot snapshot;
611 : :
730 tgl@sss.pgh.pa.us 612 [ + - + - : 258 : Assert(RelFileLocatorEquals(heaprel->rd_locator, pscan->ps_locator));
- + ]
613 [ + - + - : 258 : Assert(RelFileLocatorEquals(indexrel->rd_locator, pscan->ps_indexlocator));
- + ]
5 pg@bowt.ie 614 [ - + ]:GNC 258 : pg_assume(heaprel != NULL);
615 : :
3526 rhaas@postgresql.org 616 :CBC 258 : snapshot = RestoreSnapshot(pscan->ps_snapshot_data);
617 : 258 : RegisterSnapshot(snapshot);
618 : :
5 pg@bowt.ie 619 :GNC 258 : return index_beginscan_internal(indexrel, heaprel, nkeys, norderbys,
620 : : snapshot, pscan, instrument,
621 : : index_only_scan, true, flags);
622 : : }
623 : :
624 : : /* ----------------
625 : : * index_getbitmap - get all tuples at once from an index scan
626 : : *
627 : : * Adds the TIDs of all heap tuples satisfying the scan keys to a bitmap.
628 : : * Since there's no interlock between the index scan and the eventual heap
629 : : * access, this is only safe to use with MVCC-based snapshots: the heap
630 : : * item slot could have been replaced by a newer tuple by the time we get
631 : : * to it.
632 : : *
633 : : * Returns the number of matching tuples found. (Note: this might be only
634 : : * approximate, so it should only be used for statistical purposes.)
635 : : * ----------------
636 : : */
637 : : int64
6737 tgl@sss.pgh.pa.us 638 :CBC 14598 : index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
639 : : {
640 : : int64 ntids;
641 : :
7847 642 [ - + - + : 14598 : SCAN_CHECKS;
- + ]
3899 643 [ - + - - ]: 14598 : CHECK_SCAN_PROCEDURE(amgetbitmap);
644 : :
645 : : /* just make sure this is false... */
7847 646 : 14598 : scan->kill_prior_tuple = false;
647 : :
648 : : /*
649 : : * have the am's getbitmap proc do all the work.
650 : : */
2799 andres@anarazel.de 651 : 14598 : ntids = scan->indexRelation->rd_indam->amgetbitmap(scan, bitmap);
652 : :
6737 tgl@sss.pgh.pa.us 653 [ - + - - : 14598 : pgstat_count_index_tuples(scan->indexRelation, ntids);
+ - - + ]
654 : :
655 : 14598 : return ntids;
656 : : }
657 : :
658 : : /* ----------------
659 : : * index_bulk_delete - do mass deletion of index entries
660 : : *
661 : : * callback routine tells whether a given main-heap tuple is
662 : : * to be deleted
663 : : *
664 : : * return value is an optional palloc'd struct of statistics
665 : : * ----------------
666 : : */
667 : : IndexBulkDeleteResult *
7446 668 : 2545 : index_bulk_delete(IndexVacuumInfo *info,
669 : : IndexBulkDeleteResult *istat,
670 : : IndexBulkDeleteCallback callback,
671 : : void *callback_state)
672 : : {
673 : 2545 : Relation indexRelation = info->index;
674 : :
9198 675 [ - + - + : 2545 : RELATION_CHECKS;
- + - - ]
3899 676 [ - + - - ]: 2545 : CHECK_REL_PROCEDURE(ambulkdelete);
677 : :
1994 pg@bowt.ie 678 : 2545 : return indexRelation->rd_indam->ambulkdelete(info, istat,
679 : : callback, callback_state);
680 : : }
681 : :
682 : : /* ----------------
683 : : * index_vacuum_cleanup - do post-deletion cleanup of an index
684 : : *
685 : : * return value is an optional palloc'd struct of statistics
686 : : * ----------------
687 : : */
688 : : IndexBulkDeleteResult *
7446 tgl@sss.pgh.pa.us 689 : 32907 : index_vacuum_cleanup(IndexVacuumInfo *info,
690 : : IndexBulkDeleteResult *istat)
691 : : {
692 : 32907 : Relation indexRelation = info->index;
693 : :
8611 694 [ - + - + : 32907 : RELATION_CHECKS;
- + - - ]
3899 695 [ - + - - ]: 32907 : CHECK_REL_PROCEDURE(amvacuumcleanup);
696 : :
1994 pg@bowt.ie 697 : 32907 : return indexRelation->rd_indam->amvacuumcleanup(info, istat);
698 : : }
699 : :
700 : : /* ----------------
701 : : * index_can_return
702 : : *
703 : : * Does the index access method support index-only scans for the given
704 : : * column?
705 : : * ----------------
706 : : */
707 : : bool
4196 heikki.linnakangas@i 708 : 1094875 : index_can_return(Relation indexRelation, int attno)
709 : : {
5390 tgl@sss.pgh.pa.us 710 [ - + - + : 1094875 : RELATION_CHECKS;
- + - - ]
711 : :
712 : : /* amcanreturn is optional; assume false if not provided by AM */
2799 andres@anarazel.de 713 [ + + ]: 1094875 : if (indexRelation->rd_indam->amcanreturn == NULL)
5390 tgl@sss.pgh.pa.us 714 : 226932 : return false;
715 : :
2799 andres@anarazel.de 716 : 867943 : return indexRelation->rd_indam->amcanreturn(indexRelation, attno);
717 : : }
718 : :
719 : : /* ----------------
720 : : * index_getprocid
721 : : *
722 : : * Index access methods typically require support routines that are
723 : : * not directly the implementation of any WHERE-clause query operator
724 : : * and so cannot be kept in pg_amop. Instead, such routines are kept
725 : : * in pg_amproc. These registered procedure OIDs are assigned numbers
726 : : * according to a convention established by the access method.
727 : : * The general index code doesn't know anything about the routines
728 : : * involved; it just builds an ordered list of them for
729 : : * each attribute on which an index is defined.
730 : : *
731 : : * As of Postgres 8.3, support routines within an operator family
732 : : * are further subdivided by the "left type" and "right type" of the
733 : : * query operator(s) that they support. The "default" functions for a
734 : : * particular indexed attribute are those with both types equal to
735 : : * the index opclass' opcintype (note that this is subtly different
736 : : * from the indexed attribute's own type: it may be a binary-compatible
737 : : * type instead). Only the default functions are stored in relcache
738 : : * entries --- access methods can use the syscache to look up non-default
739 : : * functions.
740 : : *
741 : : * This routine returns the requested default procedure OID for a
742 : : * particular indexed attribute.
743 : : * ----------------
744 : : */
745 : : RegProcedure
11030 scrappy@hub.org 746 : 1220956 : index_getprocid(Relation irel,
747 : : AttrNumber attnum,
748 : : uint16 procnum)
749 : : {
750 : : RegProcedure *loc;
751 : : int nproc;
752 : : int procindex;
753 : :
2799 andres@anarazel.de 754 : 1220956 : nproc = irel->rd_indam->amsupport;
755 : :
2365 akorotkov@postgresql 756 [ + - - + ]: 1220956 : Assert(procnum > 0 && procnum <= (uint16) nproc);
757 : :
758 : 1220956 : procindex = (nproc * (attnum - 1)) + (procnum - 1);
759 : :
10605 bruce@momjian.us 760 : 1220956 : loc = irel->rd_support;
761 : :
762 [ - + ]: 1220956 : Assert(loc != NULL);
763 : :
9115 tgl@sss.pgh.pa.us 764 : 1220956 : return loc[procindex];
765 : : }
766 : :
767 : : /* ----------------
768 : : * index_getprocinfo
769 : : *
770 : : * This routine allows index AMs to keep fmgr lookup info for
771 : : * support procs in the relcache. As above, only the "default"
772 : : * functions for any particular indexed attribute are cached.
773 : : *
774 : : * Note: the return value points into cached data that will be lost during
775 : : * any relcache rebuild! Therefore, either use the callinfo right away,
776 : : * or save it only after having acquired some type of lock on the index rel.
777 : : * ----------------
778 : : */
779 : : FmgrInfo *
780 : 30384364 : index_getprocinfo(Relation irel,
781 : : AttrNumber attnum,
782 : : uint16 procnum)
783 : : {
784 : : FmgrInfo *locinfo;
785 : : int nproc;
786 : : int optsproc;
787 : : int procindex;
788 : :
2799 andres@anarazel.de 789 : 30384364 : nproc = irel->rd_indam->amsupport;
2365 akorotkov@postgresql 790 : 30384364 : optsproc = irel->rd_indam->amoptsprocnum;
791 : :
792 [ + - - + ]: 30384364 : Assert(procnum > 0 && procnum <= (uint16) nproc);
793 : :
794 : 30384364 : procindex = (nproc * (attnum - 1)) + (procnum - 1);
795 : :
9115 tgl@sss.pgh.pa.us 796 : 30384364 : locinfo = irel->rd_supportinfo;
797 : :
798 [ - + ]: 30384364 : Assert(locinfo != NULL);
799 : :
800 : 30384364 : locinfo += procindex;
801 : :
802 : : /* Initialize the lookup info if first time through */
803 [ + + ]: 30384364 : if (locinfo->fn_oid == InvalidOid)
804 : : {
805 : 684103 : RegProcedure *loc = irel->rd_support;
806 : : RegProcedure procId;
807 : :
808 [ - + ]: 684103 : Assert(loc != NULL);
809 : :
8922 810 : 684103 : procId = loc[procindex];
811 : :
812 : : /*
813 : : * Complain if function was not found during IndexSupportInitialize.
814 : : * This should not happen unless the system tables contain bogus
815 : : * entries for the index opclass. (If an AM wants to allow a support
816 : : * function to be optional, it can use index_getprocid.)
817 : : */
818 [ - + ]: 684103 : if (!RegProcedureIsValid(procId))
8462 tgl@sss.pgh.pa.us 819 [ # # ]:UBC 0 : elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
820 : : procnum, attnum, RelationGetRelationName(irel));
821 : :
8922 tgl@sss.pgh.pa.us 822 :CBC 684103 : fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
823 : :
2365 akorotkov@postgresql 824 [ + + ]: 684103 : if (procnum != optsproc)
825 : : {
826 : : /* Initialize locinfo->fn_expr with opclass options Const */
827 : 682662 : bytea **attoptions = RelationGetIndexAttOptions(irel, false);
828 : 682662 : MemoryContext oldcxt = MemoryContextSwitchTo(irel->rd_indexcxt);
829 : :
830 : 682662 : set_fn_opclass_options(locinfo, attoptions[attnum - 1]);
831 : :
832 : 682662 : MemoryContextSwitchTo(oldcxt);
833 : : }
834 : : }
835 : :
9115 tgl@sss.pgh.pa.us 836 : 30384364 : return locinfo;
837 : : }
838 : :
839 : : /* ----------------
840 : : * index_store_float8_orderby_distances
841 : : *
842 : : * Convert AM distance function's results (that can be inexact)
843 : : * to ORDER BY types and save them into xs_orderbyvals/xs_orderbynulls
844 : : * for a possible recheck.
845 : : * ----------------
846 : : */
847 : : void
2923 akorotkov@postgresql 848 : 242999 : index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
849 : : IndexOrderByDistance *distances,
850 : : bool recheckOrderBy)
851 : : {
852 : : int i;
853 : :
2557 854 [ + + - + ]: 242999 : Assert(distances || !recheckOrderBy);
855 : :
856 : 242999 : scan->xs_recheckorderby = recheckOrderBy;
857 : :
2923 858 [ + + ]: 486010 : for (i = 0; i < scan->numberOfOrderBys; i++)
859 : : {
860 [ + + ]: 243011 : if (orderByTypes[i] == FLOAT8OID)
861 : : {
2557 862 [ + + + + ]: 242946 : if (distances && !distances[i].isnull)
863 : : {
2558 864 : 242906 : scan->xs_orderbyvals[i] = Float8GetDatum(distances[i].value);
2557 865 : 242906 : scan->xs_orderbynulls[i] = false;
866 : : }
867 : : else
868 : : {
869 : 40 : scan->xs_orderbyvals[i] = (Datum) 0;
870 : 40 : scan->xs_orderbynulls[i] = true;
871 : : }
872 : : }
2923 873 [ + + ]: 65 : else if (orderByTypes[i] == FLOAT4OID)
874 : : {
875 : : /* convert distance function's result to ORDER BY type */
2557 876 [ + - + - ]: 35 : if (distances && !distances[i].isnull)
877 : : {
2558 878 : 35 : scan->xs_orderbyvals[i] = Float4GetDatum((float4) distances[i].value);
2557 879 : 35 : scan->xs_orderbynulls[i] = false;
880 : : }
881 : : else
882 : : {
2557 akorotkov@postgresql 883 :UBC 0 : scan->xs_orderbyvals[i] = (Datum) 0;
884 : 0 : scan->xs_orderbynulls[i] = true;
885 : : }
886 : : }
887 : : else
888 : : {
889 : : /*
890 : : * If the ordering operator's return value is anything else, we
891 : : * don't know how to convert the float8 bound calculated by the
892 : : * distance function to that. The executor won't actually need
893 : : * the order by values we return here, if there are no lossy
894 : : * results, so only insist on converting if the *recheck flag is
895 : : * set.
896 : : */
2923 akorotkov@postgresql 897 [ - + ]:CBC 30 : if (scan->xs_recheckorderby)
2923 akorotkov@postgresql 898 [ # # ]:UBC 0 : elog(ERROR, "ORDER BY operator must return float8 or float4 if the distance function is lossy");
2923 akorotkov@postgresql 899 :CBC 30 : scan->xs_orderbynulls[i] = true;
900 : : }
901 : : }
902 : 242999 : }
903 : :
904 : : /* ----------------
905 : : * index_opclass_options
906 : : *
907 : : * Parse opclass-specific options for index column.
908 : : * ----------------
909 : : */
910 : : bytea *
2365 911 : 568554 : index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
912 : : bool validate)
913 : : {
914 : 568554 : int amoptsprocnum = indrel->rd_indam->amoptsprocnum;
915 : 568554 : Oid procid = InvalidOid;
916 : : FmgrInfo *procinfo;
917 : : local_relopts relopts;
918 : :
919 : : /* fetch options support procedure if specified */
920 [ + + ]: 568554 : if (amoptsprocnum != 0)
2320 tgl@sss.pgh.pa.us 921 : 568520 : procid = index_getprocid(indrel, attnum, amoptsprocnum);
922 : :
2365 akorotkov@postgresql 923 [ + + ]: 568554 : if (!OidIsValid(procid))
924 : : {
925 : : Oid opclass;
926 : : Datum indclassDatum;
927 : : oidvector *indclass;
928 : :
929 [ + + ]: 566650 : if (!DatumGetPointer(attoptions))
2320 tgl@sss.pgh.pa.us 930 : 566646 : return NULL; /* ok, no options, no procedure */
931 : :
932 : : /*
933 : : * Report an error if the opclass's options-parsing procedure does not
934 : : * exist but the opclass options are specified.
935 : : */
1275 dgustafsson@postgres 936 : 4 : indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indrel->rd_indextuple,
937 : : Anum_pg_index_indclass);
2365 akorotkov@postgresql 938 : 4 : indclass = (oidvector *) DatumGetPointer(indclassDatum);
939 : 4 : opclass = indclass->values[attnum - 1];
940 : :
941 [ + - ]: 4 : ereport(ERROR,
942 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
943 : : errmsg("operator class %s has no options",
944 : : generate_opclass_name(opclass))));
945 : : }
946 : :
947 : 1904 : init_local_reloptions(&relopts, 0);
948 : :
949 : 1904 : procinfo = index_getprocinfo(indrel, attnum, amoptsprocnum);
950 : :
951 : 1904 : (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
952 : :
953 : 1904 : return build_local_reloptions(&relopts, attoptions, validate);
954 : : }
|