Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * amapi.c
4 : : * Support routines for API for Postgres index access methods.
5 : : *
6 : : * Copyright (c) 2015-2026, PostgreSQL Global Development Group
7 : : *
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/access/index/amapi.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "access/amapi.h"
17 : : #include "access/htup_details.h"
18 : : #include "catalog/pg_am.h"
19 : : #include "catalog/pg_opclass.h"
20 : : #include "utils/fmgrprotos.h"
21 : : #include "utils/syscache.h"
22 : :
23 : :
24 : : /*
25 : : * GetIndexAmRoutine - call the specified access method handler routine to get
26 : : * its IndexAmRoutine struct, which we expect to be statically allocated.
27 : : *
28 : : * Note that if the amhandler function is built-in, this will not involve
29 : : * any catalog access. It's therefore safe to use this while bootstrapping
30 : : * indexes for the system catalogs. relcache.c relies on that.
31 : : */
32 : : const IndexAmRoutine *
33 : 104442 : GetIndexAmRoutine(Oid amhandler)
34 : : {
35 : 104442 : Datum datum;
36 : 104442 : const IndexAmRoutine *routine;
37 : :
38 : 104442 : datum = OidFunctionCall0(amhandler);
39 : 104442 : routine = (const IndexAmRoutine *) DatumGetPointer(datum);
40 : :
41 [ + - ]: 104442 : if (routine == NULL || !IsA(routine, IndexAmRoutine))
42 [ # # # # ]: 0 : elog(ERROR, "index access method handler function %u did not return an IndexAmRoutine struct",
43 : : amhandler);
44 : :
45 : : /* Assert that all required callbacks are present. */
46 [ + - ]: 104442 : Assert(routine->ambuild != NULL);
47 [ + - ]: 104442 : Assert(routine->ambuildempty != NULL);
48 [ + - ]: 104442 : Assert(routine->aminsert != NULL);
49 [ + - ]: 104442 : Assert(routine->ambulkdelete != NULL);
50 [ + - ]: 104442 : Assert(routine->amvacuumcleanup != NULL);
51 [ + - ]: 104442 : Assert(routine->amcostestimate != NULL);
52 [ + - ]: 104442 : Assert(routine->amoptions != NULL);
53 [ + - ]: 104442 : Assert(routine->amvalidate != NULL);
54 [ + - ]: 104442 : Assert(routine->ambeginscan != NULL);
55 [ + - ]: 104442 : Assert(routine->amrescan != NULL);
56 [ + - ]: 104442 : Assert(routine->amendscan != NULL);
57 : :
58 : 208884 : return routine;
59 : 104442 : }
60 : :
61 : : /*
62 : : * GetIndexAmRoutineByAmId - look up the handler of the index access method
63 : : * with the given OID, and get its IndexAmRoutine struct.
64 : : *
65 : : * If the given OID isn't a valid index access method, returns NULL if
66 : : * noerror is true, else throws error.
67 : : */
68 : : const IndexAmRoutine *
69 : 13684 : GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
70 : : {
71 : 13684 : HeapTuple tuple;
72 : 13684 : Form_pg_am amform;
73 : 13684 : regproc amhandler;
74 : :
75 : : /* Get handler function OID for the access method */
76 : 13684 : tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
77 [ + - ]: 13684 : if (!HeapTupleIsValid(tuple))
78 : : {
79 [ # # ]: 0 : if (noerror)
80 : 0 : return NULL;
81 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for access method %u",
82 : : amoid);
83 : 0 : }
84 : 13684 : amform = (Form_pg_am) GETSTRUCT(tuple);
85 : :
86 : : /* Check if it's an index access method as opposed to some other AM */
87 [ + - ]: 13684 : if (amform->amtype != AMTYPE_INDEX)
88 : : {
89 [ # # ]: 0 : if (noerror)
90 : : {
91 : 0 : ReleaseSysCache(tuple);
92 : 0 : return NULL;
93 : : }
94 [ # # # # ]: 0 : ereport(ERROR,
95 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
96 : : errmsg("access method \"%s\" is not of type %s",
97 : : NameStr(amform->amname), "INDEX")));
98 : 0 : }
99 : :
100 : 13684 : amhandler = amform->amhandler;
101 : :
102 : : /* Complain if handler OID is invalid */
103 [ + - ]: 13684 : if (!RegProcedureIsValid(amhandler))
104 : : {
105 [ # # ]: 0 : if (noerror)
106 : : {
107 : 0 : ReleaseSysCache(tuple);
108 : 0 : return NULL;
109 : : }
110 [ # # # # ]: 0 : ereport(ERROR,
111 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
112 : : errmsg("index access method \"%s\" does not have a handler",
113 : : NameStr(amform->amname))));
114 : 0 : }
115 : :
116 : 13684 : ReleaseSysCache(tuple);
117 : :
118 : : /* And finally, call the handler function to get the API struct. */
119 : 13684 : return GetIndexAmRoutine(amhandler);
120 : 13684 : }
121 : :
122 : :
123 : : /*
124 : : * IndexAmTranslateStrategy - given an access method and strategy, get the
125 : : * corresponding compare type.
126 : : *
127 : : * If missing_ok is false, throw an error if no compare type is found. If
128 : : * true, just return COMPARE_INVALID.
129 : : */
130 : : CompareType
131 : 338850 : IndexAmTranslateStrategy(StrategyNumber strategy, Oid amoid, Oid opfamily, bool missing_ok)
132 : : {
133 : 338850 : CompareType result;
134 : 338850 : const IndexAmRoutine *amroutine;
135 : :
136 : : /* shortcut for common case */
137 [ + - - + ]: 677700 : if (amoid == BTREE_AM_OID &&
138 [ + - ]: 338850 : (strategy > InvalidStrategy && strategy <= BTMaxStrategyNumber))
139 : 338850 : return (CompareType) strategy;
140 : :
141 : 0 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
142 [ # # ]: 0 : if (amroutine->amtranslatestrategy)
143 : 0 : result = amroutine->amtranslatestrategy(strategy, opfamily);
144 : : else
145 : 0 : result = COMPARE_INVALID;
146 : :
147 [ # # # # ]: 0 : if (!missing_ok && result == COMPARE_INVALID)
148 [ # # # # ]: 0 : elog(ERROR, "could not translate strategy number %d for index AM %u", strategy, amoid);
149 : :
150 : 0 : return result;
151 : 338850 : }
152 : :
153 : : /*
154 : : * IndexAmTranslateCompareType - given an access method and compare type, get
155 : : * the corresponding strategy number.
156 : : *
157 : : * If missing_ok is false, throw an error if no strategy is found correlating
158 : : * to the given cmptype. If true, just return InvalidStrategy.
159 : : */
160 : : StrategyNumber
161 : 294739 : IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
162 : : {
163 : 294739 : StrategyNumber result;
164 : 294739 : const IndexAmRoutine *amroutine;
165 : :
166 : : /* shortcut for common case */
167 [ + + - + ]: 589137 : if (amoid == BTREE_AM_OID &&
168 [ + - ]: 294398 : (cmptype > COMPARE_INVALID && cmptype <= COMPARE_GT))
169 : 294398 : return (StrategyNumber) cmptype;
170 : :
171 : 341 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
172 [ + - ]: 341 : if (amroutine->amtranslatecmptype)
173 : 341 : result = amroutine->amtranslatecmptype(cmptype, opfamily);
174 : : else
175 : 0 : result = InvalidStrategy;
176 : :
177 [ - + # # ]: 341 : if (!missing_ok && result == InvalidStrategy)
178 [ # # # # ]: 0 : elog(ERROR, "could not translate compare type %u for index AM %u", cmptype, amoid);
179 : :
180 : 341 : return result;
181 : 294739 : }
182 : :
183 : : /*
184 : : * Ask appropriate access method to validate the specified opclass.
185 : : */
186 : : Datum
187 : 181 : amvalidate(PG_FUNCTION_ARGS)
188 : : {
189 : 181 : Oid opclassoid = PG_GETARG_OID(0);
190 : 181 : bool result;
191 : 181 : HeapTuple classtup;
192 : 181 : Form_pg_opclass classform;
193 : 181 : Oid amoid;
194 : 181 : const IndexAmRoutine *amroutine;
195 : :
196 : 181 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
197 [ + - ]: 181 : if (!HeapTupleIsValid(classtup))
198 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
199 : 181 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
200 : :
201 : 181 : amoid = classform->opcmethod;
202 : :
203 : 181 : ReleaseSysCache(classtup);
204 : :
205 : 181 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
206 : :
207 [ + - ]: 181 : if (amroutine->amvalidate == NULL)
208 [ # # # # ]: 0 : elog(ERROR, "function amvalidate is not defined for index access method %u",
209 : : amoid);
210 : :
211 : 181 : result = amroutine->amvalidate(opclassoid);
212 : :
213 : 362 : PG_RETURN_BOOL(result);
214 : 181 : }
|