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