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