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 3159078 : GetIndexAmRoutine(Oid amhandler)
34 : {
35 : Datum datum;
36 : IndexAmRoutine *routine;
37 :
38 3159078 : datum = OidFunctionCall0(amhandler);
39 3159078 : routine = (IndexAmRoutine *) DatumGetPointer(datum);
40 :
41 3159078 : 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 3159078 : 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 376482 : 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 376482 : tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
64 376482 : 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 376482 : amform = (Form_pg_am) GETSTRUCT(tuple);
72 :
73 : /* Check if it's an index access method as opposed to some other AM */
74 376482 : 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 376482 : amhandler = amform->amhandler;
88 :
89 : /* Complain if handler OID is invalid */
90 376482 : 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 376482 : ReleaseSysCache(tuple);
104 :
105 : /* And finally, call the handler function to get the API struct. */
106 376482 : 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 0 : IndexAmTranslateStrategy(StrategyNumber strategy, Oid amoid, Oid opfamily, bool missing_ok)
119 : {
120 : CompareType result;
121 : IndexAmRoutine *amroutine;
122 :
123 0 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
124 0 : if (amroutine->amtranslatestrategy)
125 0 : result = amroutine->amtranslatestrategy(strategy, opfamily);
126 : else
127 0 : result = COMPARE_INVALID;
128 :
129 0 : if (!missing_ok && result == COMPARE_INVALID)
130 0 : elog(ERROR, "could not translate strategy number %d for index AM %u", strategy, amoid);
131 :
132 0 : return result;
133 : }
134 :
135 : /*
136 : * IndexAmTranslateCompareType - given an access method and compare type, get
137 : * the corresponding strategy number.
138 : *
139 : * If missing_ok is false, throw an error if no strategy is found correlating
140 : * to the given cmptype. If true, just return InvalidStrategy.
141 : */
142 : StrategyNumber
143 326024 : IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
144 : {
145 : StrategyNumber result;
146 : IndexAmRoutine *amroutine;
147 :
148 326024 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
149 326024 : if (amroutine->amtranslatecmptype)
150 326024 : result = amroutine->amtranslatecmptype(cmptype, opfamily);
151 : else
152 0 : result = InvalidStrategy;
153 :
154 326024 : if (!missing_ok && result == InvalidStrategy)
155 0 : elog(ERROR, "could not translate compare type %u for index AM %u", cmptype, amoid);
156 :
157 326024 : return result;
158 : }
159 :
160 : /*
161 : * Ask appropriate access method to validate the specified opclass.
162 : */
163 : Datum
164 1264 : amvalidate(PG_FUNCTION_ARGS)
165 : {
166 1264 : Oid opclassoid = PG_GETARG_OID(0);
167 : bool result;
168 : HeapTuple classtup;
169 : Form_pg_opclass classform;
170 : Oid amoid;
171 : IndexAmRoutine *amroutine;
172 :
173 1264 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
174 1264 : if (!HeapTupleIsValid(classtup))
175 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
176 1264 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
177 :
178 1264 : amoid = classform->opcmethod;
179 :
180 1264 : ReleaseSysCache(classtup);
181 :
182 1264 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
183 :
184 1264 : if (amroutine->amvalidate == NULL)
185 0 : elog(ERROR, "function amvalidate is not defined for index access method %u",
186 : amoid);
187 :
188 1264 : result = amroutine->amvalidate(opclassoid);
189 :
190 1264 : pfree(amroutine);
191 :
192 1264 : PG_RETURN_BOOL(result);
193 : }
|