Age Owner Branch data TLA 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 *
3006 tgl@sss.pgh.pa.us 43 :CBC 632 : identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
44 : : {
45 : 632 : 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 [ + - - + ]: 632 : if (!oprlist->ordered || !proclist->ordered)
3006 tgl@sss.pgh.pa.us 54 [ # # ]:UBC 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 : : */
3006 tgl@sss.pgh.pa.us 61 :CBC 632 : thisgroup = NULL;
62 : 632 : io = ip = 0;
63 [ + - ]: 632 : if (io < oprlist->n_members)
64 : : {
65 : 632 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
66 : 632 : io++;
67 : : }
68 : : else
3006 tgl@sss.pgh.pa.us 69 :UBC 0 : oprform = NULL;
3006 tgl@sss.pgh.pa.us 70 [ + - ]:CBC 632 : if (ip < proclist->n_members)
71 : : {
72 : 632 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
73 : 632 : ip++;
74 : : }
75 : : else
3006 tgl@sss.pgh.pa.us 76 :UBC 0 : procform = NULL;
77 : :
3006 tgl@sss.pgh.pa.us 78 [ + + + + ]:CBC 13179 : while (oprform || procform)
79 : : {
80 [ + + + + ]: 12547 : if (oprform && thisgroup &&
81 [ + + ]: 9501 : oprform->amoplefttype == thisgroup->lefttype &&
82 [ + + ]: 8720 : oprform->amoprighttype == thisgroup->righttype)
83 : : {
84 : : /* Operator belongs to current group; include it and advance */
85 : :
86 : : /* Ignore strategy numbers outside supported range */
87 [ + - + + ]: 6708 : if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
88 : 6705 : thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;
89 : :
90 [ + + ]: 6708 : if (io < oprlist->n_members)
91 : : {
92 : 6076 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
93 : 6076 : io++;
94 : : }
95 : : else
96 : 632 : oprform = NULL;
97 : 6708 : continue;
98 : : }
99 : :
100 [ + + + + ]: 5839 : if (procform && thisgroup &&
101 [ + + ]: 5157 : procform->amproclefttype == thisgroup->lefttype &&
102 [ + + ]: 4591 : procform->amprocrighttype == thisgroup->righttype)
103 : : {
104 : : /* Procedure belongs to current group; include it and advance */
105 : :
106 : : /* Ignore function numbers outside supported range */
107 [ + - + - ]: 3973 : if (procform->amprocnum > 0 && procform->amprocnum < 64)
108 : 3973 : thisgroup->functionset |= ((uint64) 1) << procform->amprocnum;
109 : :
110 [ + + ]: 3973 : if (ip < proclist->n_members)
111 : : {
112 : 3341 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
113 : 3341 : ip++;
114 : : }
115 : : else
116 : 632 : procform = NULL;
117 : 3973 : continue;
118 : : }
119 : :
120 : : /* Time for a new group */
121 : 1866 : thisgroup = (OpFamilyOpFuncGroup *) palloc(sizeof(OpFamilyOpFuncGroup));
122 [ + + + + ]: 1866 : if (oprform &&
123 : 1803 : (!procform ||
124 [ + + ]: 1803 : (oprform->amoplefttype < procform->amproclefttype ||
125 [ + + ]: 1559 : (oprform->amoplefttype == procform->amproclefttype &&
126 [ + + ]: 1539 : oprform->amoprighttype < procform->amprocrighttype))))
127 : : {
128 : 581 : thisgroup->lefttype = oprform->amoplefttype;
129 : 581 : thisgroup->righttype = oprform->amoprighttype;
130 : : }
131 : : else
132 : : {
133 : 1285 : thisgroup->lefttype = procform->amproclefttype;
134 : 1285 : thisgroup->righttype = procform->amprocrighttype;
135 : : }
136 : 1866 : thisgroup->operatorset = thisgroup->functionset = 0;
137 : 1866 : result = lappend(result, thisgroup);
138 : : }
139 : :
140 : 632 : 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 : 3348 : check_amproc_signature(Oid funcid, Oid restype, bool exact,
153 : : int minargs, int maxargs,...)
154 : : {
155 : 3348 : bool result = true;
156 : : HeapTuple tp;
157 : : Form_pg_proc procform;
158 : : va_list ap;
159 : : int i;
160 : :
161 : 3348 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
162 [ - + ]: 3348 : if (!HeapTupleIsValid(tp))
3006 tgl@sss.pgh.pa.us 163 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
3006 tgl@sss.pgh.pa.us 164 :CBC 3348 : procform = (Form_pg_proc) GETSTRUCT(tp);
165 : :
166 [ + - + - ]: 3348 : if (procform->prorettype != restype || procform->proretset ||
167 [ + - - + ]: 3348 : procform->pronargs < minargs || procform->pronargs > maxargs)
3006 tgl@sss.pgh.pa.us 168 :UBC 0 : result = false;
169 : :
3006 tgl@sss.pgh.pa.us 170 :CBC 3348 : va_start(ap, maxargs);
171 [ + + ]: 12143 : for (i = 0; i < maxargs; i++)
172 : : {
173 : 8795 : Oid argtype = va_arg(ap, Oid);
174 : :
175 [ + + ]: 8795 : if (i >= procform->pronargs)
176 : 280 : continue;
177 [ + + - + ]: 10199 : if (exact ? (argtype != procform->proargtypes.values[i]) :
178 : 1684 : !IsBinaryCoercible(argtype, procform->proargtypes.values[i]))
3006 tgl@sss.pgh.pa.us 179 :UBC 0 : result = false;
180 : : }
3006 tgl@sss.pgh.pa.us 181 :CBC 3348 : va_end(ap);
182 : :
183 : 3348 : ReleaseSysCache(tp);
184 : 3348 : return result;
185 : : }
186 : :
187 : : /*
188 : : * Validate the signature of an opclass options support function, that should
189 : : * be 'void(internal)'.
190 : : */
191 : : bool
1476 akorotkov@postgresql 192 : 222 : check_amoptsproc_signature(Oid funcid)
193 : : {
194 : 222 : 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
3006 tgl@sss.pgh.pa.us 206 : 6708 : check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
207 : : {
208 : 6708 : bool result = true;
209 : : HeapTuple tp;
210 : : Form_pg_operator opform;
211 : :
212 : 6708 : tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
213 [ - + ]: 6708 : if (!HeapTupleIsValid(tp)) /* shouldn't happen */
3006 tgl@sss.pgh.pa.us 214 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator %u", opno);
3006 tgl@sss.pgh.pa.us 215 :CBC 6708 : opform = (Form_pg_operator) GETSTRUCT(tp);
216 : :
217 [ + - + - ]: 6708 : if (opform->oprresult != restype || opform->oprkind != 'b' ||
218 [ + - - + ]: 6708 : opform->oprleft != lefttype || opform->oprright != righttype)
3006 tgl@sss.pgh.pa.us 219 :UBC 0 : result = false;
220 : :
3006 tgl@sss.pgh.pa.us 221 :CBC 6708 : ReleaseSysCache(tp);
222 : 6708 : 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
1352 236 : 66 : opclass_for_family_datatype(Oid amoid, Oid opfamilyoid, Oid datatypeoid)
237 : : {
238 : 66 : 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 : 66 : opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(amoid));
248 : :
3006 249 [ + + ]: 1508 : for (i = 0; i < opclist->n_members; i++)
250 : : {
251 : 1488 : HeapTuple classtup = &opclist->members[i]->tuple;
252 : 1488 : Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup);
253 : :
254 [ + + ]: 1488 : if (classform->opcfamily == opfamilyoid &&
255 [ + + ]: 113 : classform->opcintype == datatypeoid)
256 : : {
1352 257 : 46 : result = classform->oid;
3006 258 : 46 : break;
259 : : }
260 : : }
261 : :
262 : 66 : ReleaseCatCacheList(opclist);
263 : :
264 : 66 : return result;
265 : : }
266 : :
267 : : /*
268 : : * Is the datatype a legitimate input type for the btree opfamily?
269 : : */
270 : : bool
1352 271 : 46 : opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
272 : : {
273 : 46 : return OidIsValid(opclass_for_family_datatype(BTREE_AM_OID,
274 : : opfamilyoid,
275 : : datatypeoid));
276 : : }
|