Age Owner Branch data TLA 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
3010 tgl@sss.pgh.pa.us 31 :CBC 44 : ginvalidate(Oid opclassoid)
32 : : {
3006 33 : 44 : 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 */
3010 51 : 44 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
52 [ - + ]: 44 : if (!HeapTupleIsValid(classtup))
3010 tgl@sss.pgh.pa.us 53 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
3010 tgl@sss.pgh.pa.us 54 :CBC 44 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
55 : :
56 : 44 : opfamilyoid = classform->opcfamily;
57 : 44 : opcintype = classform->opcintype;
3006 58 : 44 : opckeytype = classform->opckeytype;
59 [ + + ]: 44 : if (!OidIsValid(opckeytype))
60 : 29 : opckeytype = opcintype;
61 : 44 : opclassname = NameStr(classform->opcname);
62 : :
63 : : /* Fetch opfamily information */
64 : 44 : familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
65 [ - + ]: 44 : if (!HeapTupleIsValid(familytup))
3006 tgl@sss.pgh.pa.us 66 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
3006 tgl@sss.pgh.pa.us 67 :CBC 44 : familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
68 : :
69 : 44 : opfamilyname = NameStr(familyform->opfname);
70 : :
71 : : /* Fetch all operators and support functions of the opfamily */
3010 72 : 44 : oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
73 : 44 : proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
74 : :
75 : : /* Check individual support functions */
76 [ + + ]: 262 : for (i = 0; i < proclist->n_members; i++)
77 : : {
78 : 218 : HeapTuple proctup = &proclist->members[i]->tuple;
79 : 218 : 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 : : */
3006 86 [ - + ]: 218 : if (procform->amproclefttype != procform->amprocrighttype)
87 : : {
3006 tgl@sss.pgh.pa.us 88 [ # # ]:UBC 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 : : */
3006 tgl@sss.pgh.pa.us 100 [ - + ]:CBC 218 : if (procform->amproclefttype != opcintype)
3006 tgl@sss.pgh.pa.us 101 :UBC 0 : continue;
102 : :
103 : : /* Check procedure numbers and function signatures */
3006 tgl@sss.pgh.pa.us 104 [ + + + + :CBC 218 : switch (procform->amprocnum)
+ + - - ]
105 : : {
106 : 41 : case GIN_COMPARE_PROC:
107 : 41 : ok = check_amproc_signature(procform->amproc, INT4OID, false,
108 : : 2, 2, opckeytype, opckeytype);
109 : 41 : break;
110 : 44 : case GIN_EXTRACTVALUE_PROC:
111 : : /* Some opclasses omit nullFlags */
112 : 44 : ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
113 : : 2, 3, opcintype, INTERNALOID,
114 : : INTERNALOID);
115 : 44 : break;
116 : 44 : case GIN_EXTRACTQUERY_PROC:
117 : : /* Some opclasses omit nullFlags and searchMode */
118 : 44 : ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
119 : : 5, 7, opcintype, INTERNALOID,
120 : : INT2OID, INTERNALOID, INTERNALOID,
121 : : INTERNALOID, INTERNALOID);
122 : 44 : break;
123 : 44 : case GIN_CONSISTENT_PROC:
124 : : /* Some opclasses omit queryKeys and nullFlags */
125 : 44 : ok = check_amproc_signature(procform->amproc, BOOLOID, false,
126 : : 6, 8, INTERNALOID, INT2OID,
127 : : opcintype, INT4OID,
128 : : INTERNALOID, INTERNALOID,
129 : : INTERNALOID, INTERNALOID);
130 : 44 : break;
131 : 32 : case GIN_COMPARE_PARTIAL_PROC:
132 : 32 : ok = check_amproc_signature(procform->amproc, INT4OID, false,
133 : : 4, 4, opckeytype, opckeytype,
134 : : INT2OID, INTERNALOID);
135 : 32 : break;
136 : 13 : case GIN_TRICONSISTENT_PROC:
137 : 13 : ok = check_amproc_signature(procform->amproc, CHAROID, false,
138 : : 7, 7, INTERNALOID, INT2OID,
139 : : opcintype, INT4OID,
140 : : INTERNALOID, INTERNALOID,
141 : : INTERNALOID);
142 : 13 : break;
1476 akorotkov@postgresql 143 :UBC 0 : case GIN_OPTIONS_PROC:
144 : 0 : ok = check_amoptsproc_signature(procform->amproc);
145 : 0 : break;
3006 tgl@sss.pgh.pa.us 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 : :
3006 tgl@sss.pgh.pa.us 157 [ - + ]:CBC 218 : if (!ok)
158 : : {
3006 tgl@sss.pgh.pa.us 159 [ # # ]:UBC 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 */
3010 tgl@sss.pgh.pa.us 170 [ + + ]:CBC 251 : for (i = 0; i < oprlist->n_members; i++)
171 : : {
172 : 207 : HeapTuple oprtup = &oprlist->members[i]->tuple;
173 : 207 : Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
174 : :
175 : : /* TODO: Check that only allowed strategy numbers exist */
3006 176 [ + - - + ]: 207 : if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
177 : : {
3006 tgl@sss.pgh.pa.us 178 [ # # ]:UBC 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 */
3010 tgl@sss.pgh.pa.us 188 [ + - ]:CBC 207 : if (oprform->amoppurpose != AMOP_SEARCH ||
189 [ - + ]: 207 : OidIsValid(oprform->amopsortfamily))
190 : : {
3006 tgl@sss.pgh.pa.us 191 [ # # ]:UBC 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 */
3006 tgl@sss.pgh.pa.us 200 [ - + ]:CBC 207 : if (!check_amop_signature(oprform->amopopr, BOOLOID,
201 : : oprform->amoplefttype,
202 : : oprform->amoprighttype))
203 : : {
3006 tgl@sss.pgh.pa.us 204 [ # # ]:UBC 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 */
3006 tgl@sss.pgh.pa.us 214 :CBC 44 : grouplist = identify_opfamily_groups(oprlist, proclist);
215 : 44 : opclassgroup = NULL;
216 [ + - + + : 109 : foreach(lc, grouplist)
+ + ]
217 : : {
218 : 65 : OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
219 : :
220 : : /* Remember the group exactly matching the test opclass */
221 [ + + ]: 65 : if (thisgroup->lefttype == opcintype &&
222 [ + + ]: 62 : thisgroup->righttype == opcintype)
223 : 44 : 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 */
3010 236 [ + + ]: 352 : for (i = 1; i <= GINNProcs; i++)
237 : : {
3006 238 [ + - ]: 308 : if (opclassgroup &&
239 [ + + ]: 308 : (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
3010 240 : 218 : continue; /* got it */
1476 akorotkov@postgresql 241 [ + + + + : 90 : if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+ + ]
242 : : i == GIN_OPTIONS_PROC)
3010 tgl@sss.pgh.pa.us 243 : 59 : continue; /* optional method */
244 [ + - + - ]: 31 : if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
3006 245 : 31 : continue; /* don't need both, see check below loop */
3006 tgl@sss.pgh.pa.us 246 [ # # ]:UBC 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 : : }
3006 tgl@sss.pgh.pa.us 252 [ + - ]:CBC 44 : if (!opclassgroup ||
253 [ - + ]: 44 : ((opclassgroup->functionset & (1 << GIN_CONSISTENT_PROC)) == 0 &&
3006 tgl@sss.pgh.pa.us 254 [ # # ]:UBC 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 : :
3010 tgl@sss.pgh.pa.us 265 :CBC 44 : ReleaseCatCacheList(proclist);
266 : 44 : ReleaseCatCacheList(oprlist);
3006 267 : 44 : ReleaseSysCache(familytup);
268 : 44 : ReleaseSysCache(classtup);
269 : :
270 : 44 : return result;
271 : : }
272 : :
273 : : /*
274 : : * Prechecking function for adding operators/functions to a GIN opfamily.
275 : : */
276 : : void
1352 277 : 43 : 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 [ + - + + : 231 : foreach(lc, operators)
+ + ]
292 : : {
293 : 188 : OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
294 : :
295 : 188 : op->ref_is_hard = false;
296 : 188 : op->ref_is_family = true;
297 : 188 : 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 [ + + + + : 225 : foreach(lc, functions)
+ + ]
308 : : {
309 : 182 : OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
310 : :
311 [ + + - ]: 182 : switch (op->number)
312 : : {
313 : 76 : case GIN_EXTRACTVALUE_PROC:
314 : : case GIN_EXTRACTQUERY_PROC:
315 : : /* Required support function */
316 : 76 : op->ref_is_hard = true;
317 : 76 : break;
318 : 106 : 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 : 106 : op->ref_is_hard = false;
325 : 106 : op->ref_is_family = true;
326 : 106 : op->refobjid = opfamilyoid;
327 : 106 : break;
1352 tgl@sss.pgh.pa.us 328 :UBC 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 : : }
1352 tgl@sss.pgh.pa.us 336 :CBC 43 : }
|