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