Branch data Line data Source code
1 : : /*--------------------------------------------------------------------------
2 : : *
3 : : * test_indexscan.c
4 : : * Test helpers for low-level index scan behavior.
5 : : *
6 : : * Copyright (c) 2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * src/test/modules/index/test_indexscan.c
10 : : *
11 : : * -------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : :
15 : : #include "access/amapi.h"
16 : : #include "access/genam.h"
17 : : #include "access/relscan.h"
18 : : #include "access/table.h"
19 : : #include "access/tableam.h"
20 : : #include "catalog/index.h"
21 : : #include "catalog/pg_class.h"
22 : : #include "executor/tuptable.h"
23 : : #include "fmgr.h"
24 : : #include "funcapi.h"
25 : : #include "miscadmin.h"
26 : : #include "storage/itemptr.h"
27 : : #include "utils/builtins.h"
28 : : #include "utils/rel.h"
29 : : #include "utils/snapmgr.h"
30 : : #include "utils/tuplestore.h"
31 : :
32 : 1 : PG_MODULE_MAGIC;
33 : :
34 : 2 : PG_FUNCTION_INFO_V1(index_scan_tids);
35 : : Datum
36 : 7 : index_scan_tids(PG_FUNCTION_ARGS)
37 : : {
38 : 7 : Oid indexoid = PG_GETARG_OID(0);
39 : 7 : char *snaptype = text_to_cstring(PG_GETARG_TEXT_PP(1));
40 : 7 : bool forwardscan = PG_GETARG_BOOL(2);
41 : 7 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
42 : : Oid heapoid;
43 : : Relation heaprel;
44 : : Relation indexrel;
45 [ + + ]: 7 : ScanDirection dir = forwardscan ? ForwardScanDirection : BackwardScanDirection;
46 : : SnapshotData snapdata;
47 : : Snapshot snapshot;
48 : : IndexScanDesc scan;
49 : : TupleTableSlot *slot;
50 : :
51 [ - + ]: 7 : if (!superuser())
52 [ # # ]: 0 : ereport(ERROR,
53 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
54 : : errmsg("must be superuser to use index scan test functions")));
55 : :
56 : 7 : InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
57 : :
58 : : /*
59 : : * Lock the heap before the index to avoid deadlock. IndexGetRelation()
60 : : * runs without a lock, so if the OID isn't an index it returns
61 : : * InvalidOid; defer the complaint to index_open() below, which gives a
62 : : * better message.
63 : : */
64 : 7 : heapoid = IndexGetRelation(indexoid, true);
65 [ + - ]: 7 : if (OidIsValid(heapoid))
66 : 7 : heaprel = table_open(heapoid, AccessShareLock);
67 : : else
68 : 0 : heaprel = NULL;
69 : :
70 : 7 : indexrel = index_open(indexoid, AccessShareLock);
71 : :
72 : : /*
73 : : * Since the IndexGetRelation() call above ran without a lock, recheck now
74 : : * that both relations are locked: a concurrent drop and recreate could
75 : : * have left us with the wrong heap.
76 : : */
77 [ + - - + ]: 7 : if (heaprel == NULL || heapoid != IndexGetRelation(indexoid, false))
78 [ # # ]: 0 : ereport(ERROR,
79 : : (errcode(ERRCODE_UNDEFINED_TABLE),
80 : : errmsg("could not open parent table of index \"%s\"",
81 : : RelationGetRelationName(indexrel))));
82 : :
83 [ - + ]: 7 : if (indexrel->rd_rel->relkind != RELKIND_INDEX)
84 [ # # ]: 0 : ereport(ERROR,
85 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
86 : : errmsg("\"%s\" is not a regular index",
87 : : RelationGetRelationName(indexrel))));
88 : :
89 [ - + ]: 7 : if (indexrel->rd_indam->amgettuple == NULL)
90 [ # # ]: 0 : ereport(ERROR,
91 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
92 : : errmsg("index \"%s\" does not support plain index scans",
93 : : RelationGetRelationName(indexrel))));
94 : :
95 [ + + ]: 7 : if (strcmp(snaptype, "mvcc") == 0)
96 : 2 : snapshot = GetActiveSnapshot();
97 [ + + ]: 5 : else if (strcmp(snaptype, "any") == 0)
98 : 2 : snapshot = SnapshotAny;
99 [ - + ]: 3 : else if (strcmp(snaptype, "self") == 0)
100 : 0 : snapshot = SnapshotSelf;
101 [ + + ]: 3 : else if (strcmp(snaptype, "dirty") == 0)
102 : : {
103 : 1 : InitDirtySnapshot(snapdata);
104 : 1 : snapshot = &snapdata;
105 : : }
106 [ + - ]: 2 : else if (strcmp(snaptype, "nonvacuumable") == 0)
107 : : {
108 : 2 : InitNonVacuumableSnapshot(snapdata, GlobalVisTestFor(heaprel));
109 : 2 : snapshot = &snapdata;
110 : : }
111 : : else
112 [ # # ]: 0 : ereport(ERROR,
113 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
114 : : errmsg("invalid snapshot type \"%s\"", snaptype)));
115 : :
116 : 7 : slot = table_slot_create(heaprel, NULL);
117 : :
118 : 7 : scan = index_beginscan(heaprel, indexrel, snapshot, NULL,
119 : : 0, 0, SO_NONE);
120 : 7 : index_rescan(scan, NULL, 0, NULL, 0);
121 : :
122 [ + + ]: 30 : while (index_getnext_slot(scan, dir, slot))
123 : : {
124 : 23 : ItemPointerData tid = slot->tts_tid;
125 : : Datum values[1];
126 : : bool nulls[1];
127 : :
128 [ - + ]: 23 : if (scan->xs_recheck)
129 [ # # ]: 0 : elog(ERROR, "unexpected recheck request");
130 : :
131 : 23 : values[0] = ItemPointerGetDatum(&tid);
132 : 23 : nulls[0] = false;
133 : 23 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
134 : : values, nulls);
135 : : }
136 : :
137 : 7 : index_endscan(scan);
138 : 7 : ExecDropSingleTupleTableSlot(slot);
139 : 7 : index_close(indexrel, AccessShareLock);
140 : 7 : table_close(heaprel, AccessShareLock);
141 : :
142 : 7 : return (Datum) 0;
143 : : }
|