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