Age Owner 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-2023, 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 *
2635 tgl 43 CBC 631 : identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
44 : {
45 631 : 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 631 : if (!oprlist->ordered || !proclist->ordered)
2635 tgl 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 : */
2635 tgl 61 CBC 631 : thisgroup = NULL;
62 631 : io = ip = 0;
63 631 : if (io < oprlist->n_members)
64 : {
65 631 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
66 631 : io++;
67 : }
68 : else
2635 tgl 69 UBC 0 : oprform = NULL;
2635 tgl 70 CBC 631 : if (ip < proclist->n_members)
71 : {
72 631 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
73 631 : ip++;
74 : }
75 : else
2635 tgl 76 UBC 0 : procform = NULL;
77 :
2635 tgl 78 CBC 13133 : while (oprform || procform)
79 : {
80 12502 : if (oprform && thisgroup &&
81 9496 : oprform->amoplefttype == thisgroup->lefttype &&
82 8716 : oprform->amoprighttype == thisgroup->righttype)
83 : {
84 : /* Operator belongs to current group; include it and advance */
85 :
86 : /* Ignore strategy numbers outside supported range */
87 6707 : if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
88 6704 : thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;
89 :
90 6707 : if (io < oprlist->n_members)
91 : {
92 6076 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
93 6076 : io++;
94 : }
95 : else
96 631 : oprform = NULL;
97 6707 : continue;
98 : }
99 :
100 5795 : if (procform && thisgroup &&
101 5114 : procform->amproclefttype == thisgroup->lefttype &&
102 4548 : procform->amprocrighttype == thisgroup->righttype)
103 : {
104 : /* Procedure belongs to current group; include it and advance */
105 :
106 : /* Ignore function numbers outside supported range */
107 3930 : if (procform->amprocnum > 0 && procform->amprocnum < 64)
108 3930 : thisgroup->functionset |= ((uint64) 1) << procform->amprocnum;
109 :
110 3930 : if (ip < proclist->n_members)
111 : {
112 3299 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
113 3299 : ip++;
114 : }
115 : else
116 631 : procform = NULL;
117 3930 : continue;
118 : }
119 :
120 : /* Time for a new group */
121 1865 : thisgroup = (OpFamilyOpFuncGroup *) palloc(sizeof(OpFamilyOpFuncGroup));
122 1865 : if (oprform &&
123 1802 : (!procform ||
124 1802 : (oprform->amoplefttype < procform->amproclefttype ||
125 1558 : (oprform->amoplefttype == procform->amproclefttype &&
126 1538 : oprform->amoprighttype < procform->amprocrighttype))))
127 : {
128 581 : thisgroup->lefttype = oprform->amoplefttype;
129 581 : thisgroup->righttype = oprform->amoprighttype;
130 : }
131 : else
132 : {
133 1284 : thisgroup->lefttype = procform->amproclefttype;
134 1284 : thisgroup->righttype = procform->amprocrighttype;
135 : }
136 1865 : thisgroup->operatorset = thisgroup->functionset = 0;
137 1865 : result = lappend(result, thisgroup);
138 : }
139 :
140 631 : 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 3307 : check_amproc_signature(Oid funcid, Oid restype, bool exact,
153 : int minargs, int maxargs,...)
154 : {
155 3307 : bool result = true;
156 : HeapTuple tp;
157 : Form_pg_proc procform;
158 : va_list ap;
159 : int i;
160 :
161 3307 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
162 3307 : if (!HeapTupleIsValid(tp))
2635 tgl 163 UBC 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
2635 tgl 164 CBC 3307 : procform = (Form_pg_proc) GETSTRUCT(tp);
165 :
166 3307 : if (procform->prorettype != restype || procform->proretset ||
167 3307 : procform->pronargs < minargs || procform->pronargs > maxargs)
2635 tgl 168 UBC 0 : result = false;
169 :
2635 tgl 170 CBC 3307 : va_start(ap, maxargs);
171 12061 : for (i = 0; i < maxargs; i++)
172 : {
173 8754 : Oid argtype = va_arg(ap, Oid);
174 :
175 8754 : if (i >= procform->pronargs)
176 280 : continue;
177 10158 : if (exact ? (argtype != procform->proargtypes.values[i]) :
178 1684 : !IsBinaryCoercible(argtype, procform->proargtypes.values[i]))
2635 tgl 179 UBC 0 : result = false;
180 : }
2635 tgl 181 CBC 3307 : va_end(ap);
182 :
183 3307 : ReleaseSysCache(tp);
184 3307 : return result;
185 : }
186 :
187 : /*
188 : * Validate the signature of an opclass options support function, that should
189 : * be 'void(internal)'.
190 : */
191 : bool
1105 akorotkov 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
2635 tgl 206 6707 : check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
207 : {
208 6707 : bool result = true;
209 : HeapTuple tp;
210 : Form_pg_operator opform;
211 :
212 6707 : tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
213 6707 : if (!HeapTupleIsValid(tp)) /* shouldn't happen */
2635 tgl 214 UBC 0 : elog(ERROR, "cache lookup failed for operator %u", opno);
2635 tgl 215 CBC 6707 : opform = (Form_pg_operator) GETSTRUCT(tp);
216 :
217 6707 : if (opform->oprresult != restype || opform->oprkind != 'b' ||
218 6707 : opform->oprleft != lefttype || opform->oprright != righttype)
2635 tgl 219 UBC 0 : result = false;
220 :
2635 tgl 221 CBC 6707 : ReleaseSysCache(tp);
222 6707 : 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
981 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 :
2635 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 : {
981 257 46 : result = classform->oid;
2635 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
981 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 : }
|