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 3184478 : GetIndexAmRoutine(Oid amhandler)
34 : {
35 : Datum datum;
36 : IndexAmRoutine *routine;
37 :
38 3184478 : datum = OidFunctionCall0(amhandler);
39 3184478 : routine = (IndexAmRoutine *) DatumGetPointer(datum);
40 :
41 3184478 : 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 3184478 : return routine;
46 : }
47 :
48 : /*
49 : * GetIndexAmRoutineByAmId - look up the handler of the index access method
50 : * with the given OID, and get its IndexAmRoutine struct.
51 : *
52 : * If the given OID isn't a valid index access method, returns NULL if
53 : * noerror is true, else throws error.
54 : */
55 : IndexAmRoutine *
56 180758 : GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
57 : {
58 : HeapTuple tuple;
59 : Form_pg_am amform;
60 : regproc amhandler;
61 :
62 : /* Get handler function OID for the access method */
63 180758 : tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
64 180758 : if (!HeapTupleIsValid(tuple))
65 : {
66 0 : if (noerror)
67 0 : return NULL;
68 0 : elog(ERROR, "cache lookup failed for access method %u",
69 : amoid);
70 : }
71 180758 : amform = (Form_pg_am) GETSTRUCT(tuple);
72 :
73 : /* Check if it's an index access method as opposed to some other AM */
74 180758 : if (amform->amtype != AMTYPE_INDEX)
75 : {
76 0 : if (noerror)
77 : {
78 0 : ReleaseSysCache(tuple);
79 0 : return NULL;
80 : }
81 0 : ereport(ERROR,
82 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
83 : errmsg("access method \"%s\" is not of type %s",
84 : NameStr(amform->amname), "INDEX")));
85 : }
86 :
87 180758 : amhandler = amform->amhandler;
88 :
89 : /* Complain if handler OID is invalid */
90 180758 : if (!RegProcedureIsValid(amhandler))
91 : {
92 0 : if (noerror)
93 : {
94 0 : ReleaseSysCache(tuple);
95 0 : return NULL;
96 : }
97 0 : ereport(ERROR,
98 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
99 : errmsg("index access method \"%s\" does not have a handler",
100 : NameStr(amform->amname))));
101 : }
102 :
103 180758 : ReleaseSysCache(tuple);
104 :
105 : /* And finally, call the handler function to get the API struct. */
106 180758 : return GetIndexAmRoutine(amhandler);
107 : }
108 :
109 :
110 : /*
111 : * IndexAmTranslateStrategy - given an access method and strategy, get the
112 : * corresponding compare type.
113 : *
114 : * If missing_ok is false, throw an error if no compare type is found. If
115 : * true, just return COMPARE_INVALID.
116 : */
117 : CompareType
118 3145176 : IndexAmTranslateStrategy(StrategyNumber strategy, Oid amoid, Oid opfamily, bool missing_ok)
119 : {
120 : CompareType result;
121 : IndexAmRoutine *amroutine;
122 :
123 : /* shortcut for common case */
124 3145176 : if (amoid == BTREE_AM_OID &&
125 3145176 : (strategy > InvalidStrategy && strategy <= BTMaxStrategyNumber))
126 3145176 : return (CompareType) strategy;
127 :
128 0 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
129 0 : if (amroutine->amtranslatestrategy)
130 0 : result = amroutine->amtranslatestrategy(strategy, opfamily);
131 : else
132 0 : result = COMPARE_INVALID;
133 :
134 0 : if (!missing_ok && result == COMPARE_INVALID)
135 0 : elog(ERROR, "could not translate strategy number %d for index AM %u", strategy, amoid);
136 :
137 0 : return result;
138 : }
139 :
140 : /*
141 : * IndexAmTranslateCompareType - given an access method and compare type, get
142 : * the corresponding strategy number.
143 : *
144 : * If missing_ok is false, throw an error if no strategy is found correlating
145 : * to the given cmptype. If true, just return InvalidStrategy.
146 : */
147 : StrategyNumber
148 2900962 : IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
149 : {
150 : StrategyNumber result;
151 : IndexAmRoutine *amroutine;
152 :
153 : /* shortcut for common case */
154 2900962 : if (amoid == BTREE_AM_OID &&
155 2898632 : (cmptype > COMPARE_INVALID && cmptype <= COMPARE_GT))
156 2898632 : return (StrategyNumber) cmptype;
157 :
158 2330 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
159 2330 : if (amroutine->amtranslatecmptype)
160 2330 : result = amroutine->amtranslatecmptype(cmptype, opfamily);
161 : else
162 0 : result = InvalidStrategy;
163 :
164 2330 : if (!missing_ok && result == InvalidStrategy)
165 0 : elog(ERROR, "could not translate compare type %u for index AM %u", cmptype, amoid);
166 :
167 2330 : return result;
168 : }
169 :
170 : /*
171 : * Ask appropriate access method to validate the specified opclass.
172 : */
173 : Datum
174 1264 : amvalidate(PG_FUNCTION_ARGS)
175 : {
176 1264 : Oid opclassoid = PG_GETARG_OID(0);
177 : bool result;
178 : HeapTuple classtup;
179 : Form_pg_opclass classform;
180 : Oid amoid;
181 : IndexAmRoutine *amroutine;
182 :
183 1264 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
184 1264 : if (!HeapTupleIsValid(classtup))
185 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
186 1264 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
187 :
188 1264 : amoid = classform->opcmethod;
189 :
190 1264 : ReleaseSysCache(classtup);
191 :
192 1264 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
193 :
194 1264 : if (amroutine->amvalidate == NULL)
195 0 : elog(ERROR, "function amvalidate is not defined for index access method %u",
196 : amoid);
197 :
198 1264 : result = amroutine->amvalidate(opclassoid);
199 :
200 1264 : pfree(amroutine);
201 :
202 1264 : PG_RETURN_BOOL(result);
203 : }
|