Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginvalidate.c
4 : * Opclass validator for GIN.
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/backend/access/gin/ginvalidate.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "access/amvalidate.h"
17 : #include "access/gin_private.h"
18 : #include "access/htup_details.h"
19 : #include "catalog/pg_amop.h"
20 : #include "catalog/pg_amproc.h"
21 : #include "catalog/pg_opclass.h"
22 : #include "catalog/pg_opfamily.h"
23 : #include "catalog/pg_type.h"
24 : #include "utils/regproc.h"
25 : #include "utils/syscache.h"
26 :
27 : /*
28 : * Validator for a GIN opclass.
29 : */
30 : bool
31 88 : ginvalidate(Oid opclassoid)
32 : {
33 88 : bool result = true;
34 : HeapTuple classtup;
35 : Form_pg_opclass classform;
36 : Oid opfamilyoid;
37 : Oid opcintype;
38 : Oid opckeytype;
39 : char *opclassname;
40 : HeapTuple familytup;
41 : Form_pg_opfamily familyform;
42 : char *opfamilyname;
43 : CatCList *proclist,
44 : *oprlist;
45 : List *grouplist;
46 : OpFamilyOpFuncGroup *opclassgroup;
47 : int i;
48 : ListCell *lc;
49 :
50 : /* Fetch opclass information */
51 88 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
52 88 : if (!HeapTupleIsValid(classtup))
53 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
54 88 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
55 :
56 88 : opfamilyoid = classform->opcfamily;
57 88 : opcintype = classform->opcintype;
58 88 : opckeytype = classform->opckeytype;
59 88 : if (!OidIsValid(opckeytype))
60 58 : opckeytype = opcintype;
61 88 : opclassname = NameStr(classform->opcname);
62 :
63 : /* Fetch opfamily information */
64 88 : familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
65 88 : if (!HeapTupleIsValid(familytup))
66 0 : elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
67 88 : familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
68 :
69 88 : opfamilyname = NameStr(familyform->opfname);
70 :
71 : /* Fetch all operators and support functions of the opfamily */
72 88 : oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
73 88 : proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
74 :
75 : /* Check individual support functions */
76 524 : for (i = 0; i < proclist->n_members; i++)
77 : {
78 436 : HeapTuple proctup = &proclist->members[i]->tuple;
79 436 : Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
80 : bool ok;
81 :
82 : /*
83 : * All GIN support functions should be registered with matching
84 : * left/right types
85 : */
86 436 : if (procform->amproclefttype != procform->amprocrighttype)
87 : {
88 0 : ereport(INFO,
89 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
90 : errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
91 : opfamilyname, "gin",
92 : format_procedure(procform->amproc))));
93 0 : result = false;
94 : }
95 :
96 : /*
97 : * We can't check signatures except within the specific opclass, since
98 : * we need to know the associated opckeytype in many cases.
99 : */
100 436 : if (procform->amproclefttype != opcintype)
101 0 : continue;
102 :
103 : /* Check procedure numbers and function signatures */
104 436 : switch (procform->amprocnum)
105 : {
106 82 : case GIN_COMPARE_PROC:
107 82 : ok = check_amproc_signature(procform->amproc, INT4OID, false,
108 : 2, 2, opckeytype, opckeytype);
109 82 : break;
110 88 : case GIN_EXTRACTVALUE_PROC:
111 : /* Some opclasses omit nullFlags */
112 88 : ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
113 : 2, 3, opcintype, INTERNALOID,
114 : INTERNALOID);
115 88 : break;
116 88 : case GIN_EXTRACTQUERY_PROC:
117 : /* Some opclasses omit nullFlags and searchMode */
118 88 : ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
119 : 5, 7, opcintype, INTERNALOID,
120 : INT2OID, INTERNALOID, INTERNALOID,
121 : INTERNALOID, INTERNALOID);
122 88 : break;
123 88 : case GIN_CONSISTENT_PROC:
124 : /* Some opclasses omit queryKeys and nullFlags */
125 88 : ok = check_amproc_signature(procform->amproc, BOOLOID, false,
126 : 6, 8, INTERNALOID, INT2OID,
127 : opcintype, INT4OID,
128 : INTERNALOID, INTERNALOID,
129 : INTERNALOID, INTERNALOID);
130 88 : break;
131 64 : case GIN_COMPARE_PARTIAL_PROC:
132 64 : ok = check_amproc_signature(procform->amproc, INT4OID, false,
133 : 4, 4, opckeytype, opckeytype,
134 : INT2OID, INTERNALOID);
135 64 : break;
136 26 : case GIN_TRICONSISTENT_PROC:
137 26 : ok = check_amproc_signature(procform->amproc, CHAROID, false,
138 : 7, 7, INTERNALOID, INT2OID,
139 : opcintype, INT4OID,
140 : INTERNALOID, INTERNALOID,
141 : INTERNALOID);
142 26 : break;
143 0 : case GIN_OPTIONS_PROC:
144 0 : ok = check_amoptsproc_signature(procform->amproc);
145 0 : break;
146 0 : default:
147 0 : ereport(INFO,
148 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
149 : errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
150 : opfamilyname, "gin",
151 : format_procedure(procform->amproc),
152 : procform->amprocnum)));
153 0 : result = false;
154 0 : continue; /* don't want additional message */
155 : }
156 :
157 436 : if (!ok)
158 : {
159 0 : ereport(INFO,
160 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
161 : errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
162 : opfamilyname, "gin",
163 : format_procedure(procform->amproc),
164 : procform->amprocnum)));
165 0 : result = false;
166 : }
167 : }
168 :
169 : /* Check individual operators */
170 502 : for (i = 0; i < oprlist->n_members; i++)
171 : {
172 414 : HeapTuple oprtup = &oprlist->members[i]->tuple;
173 414 : Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
174 :
175 : /* TODO: Check that only allowed strategy numbers exist */
176 414 : if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
177 : {
178 0 : ereport(INFO,
179 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
180 : errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
181 : opfamilyname, "gin",
182 : format_operator(oprform->amopopr),
183 : oprform->amopstrategy)));
184 0 : result = false;
185 : }
186 :
187 : /* gin doesn't support ORDER BY operators */
188 414 : if (oprform->amoppurpose != AMOP_SEARCH ||
189 414 : OidIsValid(oprform->amopsortfamily))
190 : {
191 0 : ereport(INFO,
192 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
193 : errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
194 : opfamilyname, "gin",
195 : format_operator(oprform->amopopr))));
196 0 : result = false;
197 : }
198 :
199 : /* Check operator signature --- same for all gin strategies */
200 414 : if (!check_amop_signature(oprform->amopopr, BOOLOID,
201 : oprform->amoplefttype,
202 : oprform->amoprighttype))
203 : {
204 0 : ereport(INFO,
205 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
206 : errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
207 : opfamilyname, "gin",
208 : format_operator(oprform->amopopr))));
209 0 : result = false;
210 : }
211 : }
212 :
213 : /* Now check for inconsistent groups of operators/functions */
214 88 : grouplist = identify_opfamily_groups(oprlist, proclist);
215 88 : opclassgroup = NULL;
216 218 : foreach(lc, grouplist)
217 : {
218 130 : OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
219 :
220 : /* Remember the group exactly matching the test opclass */
221 130 : if (thisgroup->lefttype == opcintype &&
222 124 : thisgroup->righttype == opcintype)
223 88 : opclassgroup = thisgroup;
224 :
225 : /*
226 : * There is not a lot we can do to check the operator sets, since each
227 : * GIN opclass is more or less a law unto itself, and some contain
228 : * only operators that are binary-compatible with the opclass datatype
229 : * (meaning that empty operator sets can be OK). That case also means
230 : * that we shouldn't insist on nonempty function sets except for the
231 : * opclass's own group.
232 : */
233 : }
234 :
235 : /* Check that the originally-named opclass is complete */
236 704 : for (i = 1; i <= GINNProcs; i++)
237 : {
238 616 : if (opclassgroup &&
239 616 : (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
240 436 : continue; /* got it */
241 180 : if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
242 : i == GIN_OPTIONS_PROC)
243 118 : continue; /* optional method */
244 62 : if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
245 62 : continue; /* don't need both, see check below loop */
246 0 : ereport(INFO,
247 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
248 : errmsg("operator class \"%s\" of access method %s is missing support function %d",
249 : opclassname, "gin", i)));
250 0 : result = false;
251 : }
252 88 : if (!opclassgroup ||
253 88 : ((opclassgroup->functionset & (1 << GIN_CONSISTENT_PROC)) == 0 &&
254 0 : (opclassgroup->functionset & (1 << GIN_TRICONSISTENT_PROC)) == 0))
255 : {
256 0 : ereport(INFO,
257 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
258 : errmsg("operator class \"%s\" of access method %s is missing support function %d or %d",
259 : opclassname, "gin",
260 : GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC)));
261 0 : result = false;
262 : }
263 :
264 :
265 88 : ReleaseCatCacheList(proclist);
266 88 : ReleaseCatCacheList(oprlist);
267 88 : ReleaseSysCache(familytup);
268 88 : ReleaseSysCache(classtup);
269 :
270 88 : return result;
271 : }
272 :
273 : /*
274 : * Prechecking function for adding operators/functions to a GIN opfamily.
275 : */
276 : void
277 86 : ginadjustmembers(Oid opfamilyoid,
278 : Oid opclassoid,
279 : List *operators,
280 : List *functions)
281 : {
282 : ListCell *lc;
283 :
284 : /*
285 : * Operator members of a GIN opfamily should never have hard dependencies,
286 : * since their connection to the opfamily depends only on what the support
287 : * functions think, and that can be altered. For consistency, we make all
288 : * soft dependencies point to the opfamily, though a soft dependency on
289 : * the opclass would work as well in the CREATE OPERATOR CLASS case.
290 : */
291 462 : foreach(lc, operators)
292 : {
293 376 : OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
294 :
295 376 : op->ref_is_hard = false;
296 376 : op->ref_is_family = true;
297 376 : op->refobjid = opfamilyoid;
298 : }
299 :
300 : /*
301 : * Required support functions should have hard dependencies. Preferably
302 : * those are just dependencies on the opclass, but if we're in ALTER
303 : * OPERATOR FAMILY, we leave the dependency pointing at the whole
304 : * opfamily. (Given that GIN opclasses generally don't share opfamilies,
305 : * it seems unlikely to be worth working harder.)
306 : */
307 450 : foreach(lc, functions)
308 : {
309 364 : OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
310 :
311 364 : switch (op->number)
312 : {
313 152 : case GIN_EXTRACTVALUE_PROC:
314 : case GIN_EXTRACTQUERY_PROC:
315 : /* Required support function */
316 152 : op->ref_is_hard = true;
317 152 : break;
318 212 : case GIN_COMPARE_PROC:
319 : case GIN_CONSISTENT_PROC:
320 : case GIN_COMPARE_PARTIAL_PROC:
321 : case GIN_TRICONSISTENT_PROC:
322 : case GIN_OPTIONS_PROC:
323 : /* Optional, so force it to be a soft family dependency */
324 212 : op->ref_is_hard = false;
325 212 : op->ref_is_family = true;
326 212 : op->refobjid = opfamilyoid;
327 212 : break;
328 0 : default:
329 0 : ereport(ERROR,
330 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
331 : errmsg("support function number %d is invalid for access method %s",
332 : op->number, "gin")));
333 : break;
334 : }
335 : }
336 86 : }
|