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