Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * amvalidate.c
4 : : * Support routines for index access methods' amvalidate and
5 : : * amadjustmembers functions.
6 : : *
7 : : * Copyright (c) 2016-2026, PostgreSQL Global Development Group
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/access/index/amvalidate.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : : #include "postgres.h"
16 : :
17 : : #include "access/amvalidate.h"
18 : : #include "access/htup_details.h"
19 : : #include "catalog/pg_am.h"
20 : : #include "catalog/pg_amop.h"
21 : : #include "catalog/pg_amproc.h"
22 : : #include "catalog/pg_opclass.h"
23 : : #include "catalog/pg_operator.h"
24 : : #include "catalog/pg_proc.h"
25 : : #include "catalog/pg_type.h"
26 : : #include "parser/parse_coerce.h"
27 : : #include "utils/syscache.h"
28 : :
29 : :
30 : : /*
31 : : * identify_opfamily_groups() returns a List of OpFamilyOpFuncGroup structs,
32 : : * one for each combination of lefttype/righttype present in the family's
33 : : * operator and support function lists. If amopstrategy K is present for
34 : : * this datatype combination, we set bit 1 << K in operatorset, and similarly
35 : : * for the support functions. With uint64 fields we can handle operator and
36 : : * function numbers up to 63, which is plenty for the foreseeable future.
37 : : *
38 : : * The given CatCLists are expected to represent a single opfamily fetched
39 : : * from the AMOPSTRATEGY and AMPROCNUM caches, so that they will be in
40 : : * order by those caches' second and third cache keys, namely the datatypes.
41 : : */
42 : : List *
43 : 181 : identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
44 : : {
45 : 181 : List *result = NIL;
46 : 181 : OpFamilyOpFuncGroup *thisgroup;
47 : 181 : Form_pg_amop oprform;
48 : 181 : Form_pg_amproc procform;
49 : 181 : int io,
50 : : ip;
51 : :
52 : : /* We need the lists to be ordered; should be true in normal operation */
53 [ + - ]: 181 : if (!oprlist->ordered || !proclist->ordered)
54 [ # # # # ]: 0 : elog(ERROR, "cannot validate operator family without ordered data");
55 : :
56 : : /*
57 : : * Advance through the lists concurrently. Thanks to the ordering, we
58 : : * should see all operators and functions of a given datatype pair
59 : : * consecutively.
60 : : */
61 : 181 : thisgroup = NULL;
62 : 181 : io = ip = 0;
63 [ + - ]: 181 : if (io < oprlist->n_members)
64 : : {
65 : 181 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
66 : 181 : io++;
67 : 181 : }
68 : : else
69 : 0 : oprform = NULL;
70 [ + - ]: 181 : if (ip < proclist->n_members)
71 : : {
72 : 181 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
73 : 181 : ip++;
74 : 181 : }
75 : : else
76 : 0 : procform = NULL;
77 : :
78 [ + + + + ]: 3371 : while (oprform || procform)
79 : : {
80 [ + + + + ]: 3190 : if (oprform && thisgroup &&
81 [ + + + + ]: 2336 : oprform->amoplefttype == thisgroup->lefttype &&
82 : 2144 : oprform->amoprighttype == thisgroup->righttype)
83 : : {
84 : : /* Operator belongs to current group; include it and advance */
85 : :
86 : : /* Ignore strategy numbers outside supported range */
87 [ + - + + ]: 1649 : if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
88 : 1648 : thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;
89 : :
90 [ + + ]: 1649 : if (io < oprlist->n_members)
91 : : {
92 : 1468 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
93 : 1468 : io++;
94 : 1468 : }
95 : : else
96 : 181 : oprform = NULL;
97 : 1649 : continue;
98 : : }
99 : :
100 [ + + + + ]: 1541 : if (procform && thisgroup &&
101 [ + + + + ]: 1350 : procform->amproclefttype == thisgroup->lefttype &&
102 : 1220 : procform->amprocrighttype == thisgroup->righttype)
103 : : {
104 : : /* Procedure belongs to current group; include it and advance */
105 : :
106 : : /* Ignore function numbers outside supported range */
107 [ + - - + ]: 1096 : if (procform->amprocnum > 0 && procform->amprocnum < 64)
108 : 1096 : thisgroup->functionset |= ((uint64) 1) << procform->amprocnum;
109 : :
110 [ + + ]: 1096 : if (ip < proclist->n_members)
111 : : {
112 : 915 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
113 : 915 : ip++;
114 : 915 : }
115 : : else
116 : 181 : procform = NULL;
117 : 1096 : continue;
118 : : }
119 : :
120 : : /* Time for a new group */
121 : 445 : thisgroup = palloc_object(OpFamilyOpFuncGroup);
122 [ + + + + ]: 810 : if (oprform &&
123 [ + + ]: 437 : (!procform ||
124 [ + + ]: 427 : (oprform->amoplefttype < procform->amproclefttype ||
125 [ + + ]: 373 : (oprform->amoplefttype == procform->amproclefttype &&
126 : 365 : oprform->amoprighttype < procform->amprocrighttype))))
127 : : {
128 : 131 : thisgroup->lefttype = oprform->amoplefttype;
129 : 131 : thisgroup->righttype = oprform->amoprighttype;
130 : 131 : }
131 : : else
132 : : {
133 : 314 : thisgroup->lefttype = procform->amproclefttype;
134 : 314 : thisgroup->righttype = procform->amprocrighttype;
135 : : }
136 : 445 : thisgroup->operatorset = thisgroup->functionset = 0;
137 : 445 : result = lappend(result, thisgroup);
138 : : }
139 : :
140 : 362 : return result;
141 : 181 : }
142 : :
143 : : /*
144 : : * Validate the signature (argument and result types) of an opclass support
145 : : * function. Return true if OK, false if not.
146 : : *
147 : : * The "..." represents maxargs argument-type OIDs. If "exact" is true, they
148 : : * must match the function arg types exactly, else only binary-coercibly.
149 : : * In any case the function result type must match restype exactly.
150 : : */
151 : : bool
152 : 1011 : check_amproc_signature(Oid funcid, Oid restype, bool exact,
153 : : int minargs, int maxargs,...)
154 : : {
155 : 1011 : bool result = true;
156 : 1011 : HeapTuple tp;
157 : 1011 : Form_pg_proc procform;
158 : 1011 : va_list ap;
159 : 1011 : int i;
160 : :
161 : 1011 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
162 [ + - ]: 1011 : if (!HeapTupleIsValid(tp))
163 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
164 : 1011 : procform = (Form_pg_proc) GETSTRUCT(tp);
165 : :
166 [ + - + - ]: 1011 : if (procform->prorettype != restype || procform->proretset ||
167 [ + - - + ]: 1011 : procform->pronargs < minargs || procform->pronargs > maxargs)
168 : 0 : result = false;
169 : :
170 : 1011 : va_start(ap, maxargs);
171 [ + + ]: 3480 : for (i = 0; i < maxargs; i++)
172 : : {
173 : 2469 : Oid argtype = va_arg(ap, Oid);
174 : :
175 [ + + ]: 2469 : if (i >= procform->pronargs)
176 : 43 : continue;
177 [ + + - + ]: 2426 : if (exact ? (argtype != procform->proargtypes.values[i]) :
178 : 220 : !IsBinaryCoercible(argtype, procform->proargtypes.values[i]))
179 : 0 : result = false;
180 [ - + + ]: 2469 : }
181 : 1011 : va_end(ap);
182 : :
183 : 1011 : ReleaseSysCache(tp);
184 : 2022 : return result;
185 : 1011 : }
186 : :
187 : : /*
188 : : * Validate the signature of an opclass options support function, that should
189 : : * be 'void(internal)'.
190 : : */
191 : : bool
192 : 72 : check_amoptsproc_signature(Oid funcid)
193 : : {
194 : 72 : return check_amproc_signature(funcid, VOIDOID, true, 1, 1, INTERNALOID);
195 : : }
196 : :
197 : : /*
198 : : * Validate the signature (argument and result types) of an opclass operator.
199 : : * Return true if OK, false if not.
200 : : *
201 : : * Currently, we can hard-wire this as accepting only binary operators. Also,
202 : : * we can insist on exact type matches, since the given lefttype/righttype
203 : : * come from pg_amop and should always match the operator exactly.
204 : : */
205 : : bool
206 : 1649 : check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
207 : : {
208 : 1649 : bool result = true;
209 : 1649 : HeapTuple tp;
210 : 1649 : Form_pg_operator opform;
211 : :
212 : 1649 : tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
213 [ + - ]: 1649 : if (!HeapTupleIsValid(tp)) /* shouldn't happen */
214 [ # # # # ]: 0 : elog(ERROR, "cache lookup failed for operator %u", opno);
215 : 1649 : opform = (Form_pg_operator) GETSTRUCT(tp);
216 : :
217 [ + - + - ]: 1649 : if (opform->oprresult != restype || opform->oprkind != 'b' ||
218 [ + - - + ]: 1649 : opform->oprleft != lefttype || opform->oprright != righttype)
219 : 0 : result = false;
220 : :
221 : 1649 : ReleaseSysCache(tp);
222 : 3298 : return result;
223 : 1649 : }
224 : :
225 : : /*
226 : : * Get the OID of the opclass belonging to an opfamily and accepting
227 : : * the specified type as input type. Returns InvalidOid if no such opclass.
228 : : *
229 : : * If there is more than one such opclass, you get a random one of them.
230 : : * Since that shouldn't happen, we don't waste cycles checking.
231 : : *
232 : : * We could look up the AM's OID from the opfamily, but all existing callers
233 : : * know that or can get it without an extra lookup, so we make them pass it.
234 : : */
235 : : Oid
236 : 16 : opclass_for_family_datatype(Oid amoid, Oid opfamilyoid, Oid datatypeoid)
237 : : {
238 : 16 : Oid result = InvalidOid;
239 : 16 : CatCList *opclist;
240 : 16 : int i;
241 : :
242 : : /*
243 : : * We search through all the AM's opclasses to see if one matches. This
244 : : * is a bit inefficient but there is no better index available. It also
245 : : * saves making an explicit check that the opfamily belongs to the AM.
246 : : */
247 : 16 : opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(amoid));
248 : :
249 [ + + ]: 429 : for (i = 0; i < opclist->n_members; i++)
250 : : {
251 : 422 : HeapTuple classtup = &opclist->members[i]->tuple;
252 : 422 : Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup);
253 : :
254 [ + + + + ]: 422 : if (classform->opcfamily == opfamilyoid &&
255 : 30 : classform->opcintype == datatypeoid)
256 : : {
257 : 9 : result = classform->oid;
258 : 9 : break;
259 : : }
260 [ - + + ]: 422 : }
261 : :
262 : 16 : ReleaseCatCacheList(opclist);
263 : :
264 : 32 : return result;
265 : 16 : }
266 : :
267 : : /*
268 : : * Is the datatype a legitimate input type for the btree opfamily?
269 : : */
270 : : bool
271 : 9 : opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
272 : : {
273 : 9 : return OidIsValid(opclass_for_family_datatype(BTREE_AM_OID,
274 : : opfamilyoid,
275 : : datatypeoid));
276 : : }
|