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-2024, 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 1264 : identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
44 : {
45 1264 : List *result = NIL;
46 : OpFamilyOpFuncGroup *thisgroup;
47 : Form_pg_amop oprform;
48 : Form_pg_amproc procform;
49 : int io,
50 : ip;
51 :
52 : /* We need the lists to be ordered; should be true in normal operation */
53 1264 : 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 1264 : thisgroup = NULL;
62 1264 : io = ip = 0;
63 1264 : if (io < oprlist->n_members)
64 : {
65 1264 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
66 1264 : io++;
67 : }
68 : else
69 0 : oprform = NULL;
70 1264 : if (ip < proclist->n_members)
71 : {
72 1264 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
73 1264 : ip++;
74 : }
75 : else
76 0 : procform = NULL;
77 :
78 26358 : while (oprform || procform)
79 : {
80 25094 : if (oprform && thisgroup &&
81 19002 : oprform->amoplefttype == thisgroup->lefttype &&
82 17440 : oprform->amoprighttype == thisgroup->righttype)
83 : {
84 : /* Operator belongs to current group; include it and advance */
85 :
86 : /* Ignore strategy numbers outside supported range */
87 13416 : if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
88 13410 : thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;
89 :
90 13416 : if (io < oprlist->n_members)
91 : {
92 12152 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
93 12152 : io++;
94 : }
95 : else
96 1264 : oprform = NULL;
97 13416 : continue;
98 : }
99 :
100 11678 : if (procform && thisgroup &&
101 10314 : procform->amproclefttype == thisgroup->lefttype &&
102 9182 : procform->amprocrighttype == thisgroup->righttype)
103 : {
104 : /* Procedure belongs to current group; include it and advance */
105 :
106 : /* Ignore function numbers outside supported range */
107 7946 : if (procform->amprocnum > 0 && procform->amprocnum < 64)
108 7946 : thisgroup->functionset |= ((uint64) 1) << procform->amprocnum;
109 :
110 7946 : if (ip < proclist->n_members)
111 : {
112 6682 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
113 6682 : ip++;
114 : }
115 : else
116 1264 : procform = NULL;
117 7946 : continue;
118 : }
119 :
120 : /* Time for a new group */
121 3732 : thisgroup = (OpFamilyOpFuncGroup *) palloc(sizeof(OpFamilyOpFuncGroup));
122 3732 : if (oprform &&
123 3606 : (!procform ||
124 3606 : (oprform->amoplefttype < procform->amproclefttype ||
125 3118 : (oprform->amoplefttype == procform->amproclefttype &&
126 3078 : oprform->amoprighttype < procform->amprocrighttype))))
127 : {
128 1162 : thisgroup->lefttype = oprform->amoplefttype;
129 1162 : thisgroup->righttype = oprform->amoprighttype;
130 : }
131 : else
132 : {
133 2570 : thisgroup->lefttype = procform->amproclefttype;
134 2570 : thisgroup->righttype = procform->amprocrighttype;
135 : }
136 3732 : thisgroup->operatorset = thisgroup->functionset = 0;
137 3732 : result = lappend(result, thisgroup);
138 : }
139 :
140 1264 : return result;
141 : }
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 7472 : check_amproc_signature(Oid funcid, Oid restype, bool exact,
153 : int minargs, int maxargs,...)
154 : {
155 7472 : bool result = true;
156 : HeapTuple tp;
157 : Form_pg_proc procform;
158 : va_list ap;
159 : int i;
160 :
161 7472 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
162 7472 : if (!HeapTupleIsValid(tp))
163 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
164 7472 : procform = (Form_pg_proc) GETSTRUCT(tp);
165 :
166 7472 : if (procform->prorettype != restype || procform->proretset ||
167 7472 : procform->pronargs < minargs || procform->pronargs > maxargs)
168 0 : result = false;
169 :
170 7472 : va_start(ap, maxargs);
171 26168 : for (i = 0; i < maxargs; i++)
172 : {
173 18696 : Oid argtype = va_arg(ap, Oid);
174 :
175 18696 : if (i >= procform->pronargs)
176 560 : continue;
177 21504 : if (exact ? (argtype != procform->proargtypes.values[i]) :
178 3368 : !IsBinaryCoercible(argtype, procform->proargtypes.values[i]))
179 0 : result = false;
180 : }
181 7472 : va_end(ap);
182 :
183 7472 : ReleaseSysCache(tp);
184 7472 : return result;
185 : }
186 :
187 : /*
188 : * Validate the signature of an opclass options support function, that should
189 : * be 'void(internal)'.
190 : */
191 : bool
192 444 : check_amoptsproc_signature(Oid funcid)
193 : {
194 444 : 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 13416 : check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
207 : {
208 13416 : bool result = true;
209 : HeapTuple tp;
210 : Form_pg_operator opform;
211 :
212 13416 : tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
213 13416 : if (!HeapTupleIsValid(tp)) /* shouldn't happen */
214 0 : elog(ERROR, "cache lookup failed for operator %u", opno);
215 13416 : opform = (Form_pg_operator) GETSTRUCT(tp);
216 :
217 13416 : if (opform->oprresult != restype || opform->oprkind != 'b' ||
218 13416 : opform->oprleft != lefttype || opform->oprright != righttype)
219 0 : result = false;
220 :
221 13416 : ReleaseSysCache(tp);
222 13416 : return result;
223 : }
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 138 : opclass_for_family_datatype(Oid amoid, Oid opfamilyoid, Oid datatypeoid)
237 : {
238 138 : Oid result = InvalidOid;
239 : CatCList *opclist;
240 : 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 138 : opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(amoid));
248 :
249 3280 : for (i = 0; i < opclist->n_members; i++)
250 : {
251 3234 : HeapTuple classtup = &opclist->members[i]->tuple;
252 3234 : Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup);
253 :
254 3234 : if (classform->opcfamily == opfamilyoid &&
255 244 : classform->opcintype == datatypeoid)
256 : {
257 92 : result = classform->oid;
258 92 : break;
259 : }
260 : }
261 :
262 138 : ReleaseCatCacheList(opclist);
263 :
264 138 : return result;
265 : }
266 :
267 : /*
268 : * Is the datatype a legitimate input type for the btree opfamily?
269 : */
270 : bool
271 92 : opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
272 : {
273 92 : return OidIsValid(opclass_for_family_datatype(BTREE_AM_OID,
274 : opfamilyoid,
275 : datatypeoid));
276 : }
|