Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * amapi.c
4 : * Support routines for API for Postgres index access methods.
5 : *
6 : * Copyright (c) 2015-2025, 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 will be palloc'd in the caller's context.
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 : IndexAmRoutine *
33 3369478 : GetIndexAmRoutine(Oid amhandler)
34 : {
35 : Datum datum;
36 : IndexAmRoutine *routine;
37 :
38 3369478 : datum = OidFunctionCall0(amhandler);
39 3369478 : routine = (IndexAmRoutine *) DatumGetPointer(datum);
40 :
41 3369478 : 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 : Assert(routine->ambuild != NULL);
47 : Assert(routine->ambuildempty != NULL);
48 : Assert(routine->aminsert != NULL);
49 : Assert(routine->ambulkdelete != NULL);
50 : Assert(routine->amvacuumcleanup != NULL);
51 : Assert(routine->amcostestimate != NULL);
52 : Assert(routine->amoptions != NULL);
53 : Assert(routine->amvalidate != NULL);
54 : Assert(routine->ambeginscan != NULL);
55 : Assert(routine->amrescan != NULL);
56 : Assert(routine->amendscan != NULL);
57 :
58 3369478 : return routine;
59 : }
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 : IndexAmRoutine *
69 184222 : GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
70 : {
71 : HeapTuple tuple;
72 : Form_pg_am amform;
73 : regproc amhandler;
74 :
75 : /* Get handler function OID for the access method */
76 184222 : tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
77 184222 : 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 : }
84 184222 : amform = (Form_pg_am) GETSTRUCT(tuple);
85 :
86 : /* Check if it's an index access method as opposed to some other AM */
87 184222 : 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 : }
99 :
100 184222 : amhandler = amform->amhandler;
101 :
102 : /* Complain if handler OID is invalid */
103 184222 : 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 : }
115 :
116 184222 : ReleaseSysCache(tuple);
117 :
118 : /* And finally, call the handler function to get the API struct. */
119 184222 : return GetIndexAmRoutine(amhandler);
120 : }
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 3147552 : IndexAmTranslateStrategy(StrategyNumber strategy, Oid amoid, Oid opfamily, bool missing_ok)
132 : {
133 : CompareType result;
134 : IndexAmRoutine *amroutine;
135 :
136 : /* shortcut for common case */
137 3147552 : if (amoid == BTREE_AM_OID &&
138 3147552 : (strategy > InvalidStrategy && strategy <= BTMaxStrategyNumber))
139 3147552 : 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 : }
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 2894960 : IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
162 : {
163 : StrategyNumber result;
164 : IndexAmRoutine *amroutine;
165 :
166 : /* shortcut for common case */
167 2894960 : if (amoid == BTREE_AM_OID &&
168 2892630 : (cmptype > COMPARE_INVALID && cmptype <= COMPARE_GT))
169 2892630 : return (StrategyNumber) cmptype;
170 :
171 2330 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
172 2330 : if (amroutine->amtranslatecmptype)
173 2330 : result = amroutine->amtranslatecmptype(cmptype, opfamily);
174 : else
175 0 : result = InvalidStrategy;
176 :
177 2330 : if (!missing_ok && result == InvalidStrategy)
178 0 : elog(ERROR, "could not translate compare type %u for index AM %u", cmptype, amoid);
179 :
180 2330 : return result;
181 : }
182 :
183 : /*
184 : * Ask appropriate access method to validate the specified opclass.
185 : */
186 : Datum
187 1264 : amvalidate(PG_FUNCTION_ARGS)
188 : {
189 1264 : Oid opclassoid = PG_GETARG_OID(0);
190 : bool result;
191 : HeapTuple classtup;
192 : Form_pg_opclass classform;
193 : Oid amoid;
194 : IndexAmRoutine *amroutine;
195 :
196 1264 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
197 1264 : if (!HeapTupleIsValid(classtup))
198 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
199 1264 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
200 :
201 1264 : amoid = classform->opcmethod;
202 :
203 1264 : ReleaseSysCache(classtup);
204 :
205 1264 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
206 :
207 1264 : if (amroutine->amvalidate == NULL)
208 0 : elog(ERROR, "function amvalidate is not defined for index access method %u",
209 : amoid);
210 :
211 1264 : result = amroutine->amvalidate(opclassoid);
212 :
213 1264 : pfree(amroutine);
214 :
215 1264 : PG_RETURN_BOOL(result);
216 : }
|