Age Owner Branch data TLA 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 *
3520 tgl@sss.pgh.pa.us 33 :CBC 1409341 : GetIndexAmRoutine(Oid amhandler)
34 : : {
35 : : Datum datum;
36 : : IndexAmRoutine *routine;
37 : :
38 : 1409341 : datum = OidFunctionCall0(amhandler);
39 : 1409341 : routine = (IndexAmRoutine *) DatumGetPointer(datum);
40 : :
41 [ + - - + ]: 1409341 : if (routine == NULL || !IsA(routine, IndexAmRoutine))
3520 tgl@sss.pgh.pa.us 42 [ # # ]:UBC 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. */
41 michael@paquier.xyz 46 [ - + ]:GNC 1409341 : Assert(routine->ambuild != NULL);
47 [ - + ]: 1409341 : Assert(routine->ambuildempty != NULL);
48 [ - + ]: 1409341 : Assert(routine->aminsert != NULL);
49 [ - + ]: 1409341 : Assert(routine->ambulkdelete != NULL);
50 [ - + ]: 1409341 : Assert(routine->amvacuumcleanup != NULL);
51 [ - + ]: 1409341 : Assert(routine->amcostestimate != NULL);
52 [ - + ]: 1409341 : Assert(routine->amoptions != NULL);
53 [ - + ]: 1409341 : Assert(routine->amvalidate != NULL);
54 [ - + ]: 1409341 : Assert(routine->ambeginscan != NULL);
55 [ - + ]: 1409341 : Assert(routine->amrescan != NULL);
56 [ - + ]: 1409341 : Assert(routine->amendscan != NULL);
57 : :
3520 tgl@sss.pgh.pa.us 58 :CBC 1409341 : 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 *
3311 69 : 92382 : 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 */
3520 76 : 92382 : tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
77 [ - + ]: 92382 : if (!HeapTupleIsValid(tuple))
78 : : {
3311 tgl@sss.pgh.pa.us 79 [ # # ]:UBC 0 : if (noerror)
80 : 0 : return NULL;
3520 81 [ # # ]: 0 : elog(ERROR, "cache lookup failed for access method %u",
82 : : amoid);
83 : : }
3520 tgl@sss.pgh.pa.us 84 :CBC 92382 : amform = (Form_pg_am) GETSTRUCT(tuple);
85 : :
86 : : /* Check if it's an index access method as opposed to some other AM */
3454 alvherre@alvh.no-ip. 87 [ - + ]: 92382 : if (amform->amtype != AMTYPE_INDEX)
88 : : {
3311 tgl@sss.pgh.pa.us 89 [ # # ]:UBC 0 : if (noerror)
90 : : {
91 : 0 : ReleaseSysCache(tuple);
92 : 0 : return NULL;
93 : : }
3454 alvherre@alvh.no-ip. 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 : :
3520 tgl@sss.pgh.pa.us 100 :CBC 92382 : amhandler = amform->amhandler;
101 : :
102 : : /* Complain if handler OID is invalid */
103 [ - + ]: 92382 : if (!RegProcedureIsValid(amhandler))
104 : : {
3311 tgl@sss.pgh.pa.us 105 [ # # ]:UBC 0 : if (noerror)
106 : : {
107 : 0 : ReleaseSysCache(tuple);
108 : 0 : return NULL;
109 : : }
3520 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 : :
3520 tgl@sss.pgh.pa.us 116 :CBC 92382 : ReleaseSysCache(tuple);
117 : :
118 : : /* And finally, call the handler function to get the API struct. */
119 : 92382 : 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
197 peter@eisentraut.org 131 : 1591554 : IndexAmTranslateStrategy(StrategyNumber strategy, Oid amoid, Oid opfamily, bool missing_ok)
132 : : {
133 : : CompareType result;
134 : : IndexAmRoutine *amroutine;
135 : :
136 : : /* shortcut for common case */
153 137 [ + - + - ]: 1591554 : if (amoid == BTREE_AM_OID &&
138 [ + - ]: 1591554 : (strategy > InvalidStrategy && strategy <= BTMaxStrategyNumber))
139 : 1591554 : return (CompareType) strategy;
140 : :
216 peter@eisentraut.org 141 :UBC 0 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
142 [ # # ]: 0 : if (amroutine->amtranslatestrategy)
197 143 : 0 : result = amroutine->amtranslatestrategy(strategy, opfamily);
144 : : else
216 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
197 peter@eisentraut.org 161 :CBC 1409922 : IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
162 : : {
163 : : StrategyNumber result;
164 : : IndexAmRoutine *amroutine;
165 : :
166 : : /* shortcut for common case */
153 167 [ + + + - ]: 1409922 : if (amoid == BTREE_AM_OID &&
168 [ + - ]: 1408783 : (cmptype > COMPARE_INVALID && cmptype <= COMPARE_GT))
169 : 1408783 : return (StrategyNumber) cmptype;
170 : :
216 171 : 1139 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
172 [ + - ]: 1139 : if (amroutine->amtranslatecmptype)
197 173 : 1139 : result = amroutine->amtranslatecmptype(cmptype, opfamily);
174 : : else
216 peter@eisentraut.org 175 :UBC 0 : result = InvalidStrategy;
176 : :
216 peter@eisentraut.org 177 [ + + - + ]:CBC 1139 : if (!missing_ok && result == InvalidStrategy)
216 peter@eisentraut.org 178 [ # # ]:UBC 0 : elog(ERROR, "could not translate compare type %u for index AM %u", cmptype, amoid);
179 : :
216 peter@eisentraut.org 180 :CBC 1139 : return result;
181 : : }
182 : :
183 : : /*
184 : : * Ask appropriate access method to validate the specified opclass.
185 : : */
186 : : Datum
3520 tgl@sss.pgh.pa.us 187 : 632 : amvalidate(PG_FUNCTION_ARGS)
188 : : {
189 : 632 : 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 : 632 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
197 [ - + ]: 632 : if (!HeapTupleIsValid(classtup))
3520 tgl@sss.pgh.pa.us 198 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
3520 tgl@sss.pgh.pa.us 199 :CBC 632 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
200 : :
201 : 632 : amoid = classform->opcmethod;
202 : :
203 : 632 : ReleaseSysCache(classtup);
204 : :
3311 205 : 632 : amroutine = GetIndexAmRoutineByAmId(amoid, false);
206 : :
3520 207 [ - + ]: 632 : if (amroutine->amvalidate == NULL)
3520 tgl@sss.pgh.pa.us 208 [ # # ]:UBC 0 : elog(ERROR, "function amvalidate is not defined for index access method %u",
209 : : amoid);
210 : :
3520 tgl@sss.pgh.pa.us 211 :CBC 632 : result = amroutine->amvalidate(opclassoid);
212 : :
213 : 632 : pfree(amroutine);
214 : :
215 : 632 : PG_RETURN_BOOL(result);
216 : : }
|