Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * gistvalidate.c
4 : : * Opclass validator for GiST.
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/gist/gistvalidate.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "access/amvalidate.h"
17 : : #include "access/gist_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/lsyscache.h"
25 : : #include "utils/regproc.h"
26 : : #include "utils/syscache.h"
27 : :
28 : :
29 : : /*
30 : : * Validator for a GiST opclass.
31 : : */
32 : : bool
3010 tgl@sss.pgh.pa.us 33 :CBC 61 : gistvalidate(Oid opclassoid)
34 : : {
3006 35 : 61 : 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 */
3010 53 : 61 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
54 [ - + ]: 61 : if (!HeapTupleIsValid(classtup))
3010 tgl@sss.pgh.pa.us 55 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
3010 tgl@sss.pgh.pa.us 56 :CBC 61 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
57 : :
58 : 61 : opfamilyoid = classform->opcfamily;
59 : 61 : opcintype = classform->opcintype;
3006 60 : 61 : opckeytype = classform->opckeytype;
61 [ + + ]: 61 : if (!OidIsValid(opckeytype))
62 : 12 : opckeytype = opcintype;
63 : 61 : opclassname = NameStr(classform->opcname);
64 : :
65 : : /* Fetch opfamily information */
66 : 61 : familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
67 [ - + ]: 61 : if (!HeapTupleIsValid(familytup))
3006 tgl@sss.pgh.pa.us 68 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
3006 tgl@sss.pgh.pa.us 69 :CBC 61 : familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
70 : :
71 : 61 : opfamilyname = NameStr(familyform->opfname);
72 : :
73 : : /* Fetch all operators and support functions of the opfamily */
3010 74 : 61 : oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
75 : 61 : proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
76 : :
77 : : /* Check individual support functions */
78 [ + + ]: 565 : for (i = 0; i < proclist->n_members; i++)
79 : : {
80 : 504 : HeapTuple proctup = &proclist->members[i]->tuple;
81 : 504 : Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
82 : : bool ok;
83 : :
84 : : /*
85 : : * All GiST support functions should be registered with matching
86 : : * left/right types
87 : : */
3006 88 [ - + ]: 504 : if (procform->amproclefttype != procform->amprocrighttype)
89 : : {
3006 tgl@sss.pgh.pa.us 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, "gist",
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 : : */
3006 tgl@sss.pgh.pa.us 102 [ - + ]:CBC 504 : if (procform->amproclefttype != opcintype)
3006 tgl@sss.pgh.pa.us 103 :UBC 0 : continue;
104 : :
105 : : /* Check procedure numbers and function signatures */
3006 tgl@sss.pgh.pa.us 106 [ + + + + :CBC 504 : switch (procform->amprocnum)
+ + + + +
+ - ]
107 : : {
108 : 61 : case GIST_CONSISTENT_PROC:
109 : 61 : ok = check_amproc_signature(procform->amproc, BOOLOID, false,
110 : : 5, 5, INTERNALOID, opcintype,
111 : : INT2OID, OIDOID, INTERNALOID);
112 : 61 : break;
113 : 61 : case GIST_UNION_PROC:
114 : 61 : ok = check_amproc_signature(procform->amproc, opckeytype, false,
115 : : 2, 2, INTERNALOID, INTERNALOID);
116 : 61 : break;
117 : 117 : case GIST_COMPRESS_PROC:
118 : : case GIST_DECOMPRESS_PROC:
119 : : case GIST_FETCH_PROC:
120 : 117 : ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
121 : : 1, 1, INTERNALOID);
122 : 117 : break;
123 : 61 : case GIST_PENALTY_PROC:
124 : 61 : ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
125 : : 3, 3, INTERNALOID,
126 : : INTERNALOID, INTERNALOID);
127 : 61 : break;
128 : 61 : case GIST_PICKSPLIT_PROC:
129 : 61 : ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
130 : : 2, 2, INTERNALOID, INTERNALOID);
131 : 61 : break;
132 : 61 : case GIST_EQUAL_PROC:
133 : 61 : ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
134 : : 3, 3, opckeytype, opckeytype,
135 : : INTERNALOID);
136 : 61 : break;
137 : 26 : case GIST_DISTANCE_PROC:
138 : 26 : ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
139 : : 5, 5, INTERNALOID, opcintype,
140 : : INT2OID, OIDOID, INTERNALOID);
141 : 26 : break;
1476 akorotkov@postgresql 142 : 9 : case GIST_OPTIONS_PROC:
143 : 9 : ok = check_amoptsproc_signature(procform->amproc);
144 : 9 : break;
1305 heikki.linnakangas@i 145 : 3 : case GIST_SORTSUPPORT_PROC:
146 : 3 : ok = check_amproc_signature(procform->amproc, VOIDOID, true,
147 : : 1, 1, INTERNALOID);
148 : 3 : break;
86 peter@eisentraut.org 149 :GNC 44 : case GIST_STRATNUM_PROC:
150 : 44 : ok = check_amproc_signature(procform->amproc, INT2OID, true,
151 : : 1, 1, INT2OID);
152 : 44 : break;
3006 tgl@sss.pgh.pa.us 153 :UBC 0 : default:
154 [ # # ]: 0 : ereport(INFO,
155 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
156 : : errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
157 : : opfamilyname, "gist",
158 : : format_procedure(procform->amproc),
159 : : procform->amprocnum)));
160 : 0 : result = false;
161 : 0 : continue; /* don't want additional message */
162 : : }
163 : :
3006 tgl@sss.pgh.pa.us 164 [ - + ]:CBC 504 : if (!ok)
165 : : {
3006 tgl@sss.pgh.pa.us 166 [ # # ]:UBC 0 : ereport(INFO,
167 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
168 : : errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
169 : : opfamilyname, "gist",
170 : : format_procedure(procform->amproc),
171 : : procform->amprocnum)));
172 : 0 : result = false;
173 : : }
174 : : }
175 : :
176 : : /* Check individual operators */
3010 tgl@sss.pgh.pa.us 177 [ + + ]:CBC 589 : for (i = 0; i < oprlist->n_members; i++)
178 : : {
179 : 528 : HeapTuple oprtup = &oprlist->members[i]->tuple;
180 : 528 : Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
181 : : Oid op_rettype;
182 : :
183 : : /* TODO: Check that only allowed strategy numbers exist */
184 [ - + ]: 528 : if (oprform->amopstrategy < 1)
185 : : {
3006 tgl@sss.pgh.pa.us 186 [ # # ]:UBC 0 : ereport(INFO,
187 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
188 : : errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
189 : : opfamilyname, "gist",
190 : : format_operator(oprform->amopopr),
191 : : oprform->amopstrategy)));
192 : 0 : result = false;
193 : : }
194 : :
195 : : /* GiST supports ORDER BY operators */
3006 tgl@sss.pgh.pa.us 196 [ + + ]:CBC 528 : if (oprform->amoppurpose != AMOP_SEARCH)
197 : : {
198 : : /* ... but must have matching distance proc */
199 [ - + ]: 31 : if (!OidIsValid(get_opfamily_proc(opfamilyoid,
200 : : oprform->amoplefttype,
201 : : oprform->amoplefttype,
202 : : GIST_DISTANCE_PROC)))
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 unsupported ORDER BY specification for operator %s",
207 : : opfamilyname, "gist",
208 : : format_operator(oprform->amopopr))));
209 : 0 : result = false;
210 : : }
211 : : /* ... and operator result must match the claimed btree opfamily */
3006 tgl@sss.pgh.pa.us 212 :CBC 31 : op_rettype = get_op_rettype(oprform->amopopr);
213 [ - + ]: 31 : if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
214 : : {
3006 tgl@sss.pgh.pa.us 215 [ # # ]:UBC 0 : ereport(INFO,
216 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
217 : : errmsg("operator family \"%s\" of access method %s contains incorrect ORDER BY opfamily specification for operator %s",
218 : : opfamilyname, "gist",
219 : : format_operator(oprform->amopopr))));
220 : 0 : result = false;
221 : : }
222 : : }
223 : : else
224 : : {
225 : : /* Search operators must always return bool */
3006 tgl@sss.pgh.pa.us 226 :CBC 497 : op_rettype = BOOLOID;
227 : : }
228 : :
229 : : /* Check operator signature */
230 [ - + ]: 528 : if (!check_amop_signature(oprform->amopopr, op_rettype,
231 : : oprform->amoplefttype,
232 : : oprform->amoprighttype))
233 : : {
3006 tgl@sss.pgh.pa.us 234 [ # # ]:UBC 0 : ereport(INFO,
235 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
236 : : errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
237 : : opfamilyname, "gist",
238 : : format_operator(oprform->amopopr))));
239 : 0 : result = false;
240 : : }
241 : : }
242 : :
243 : : /* Now check for inconsistent groups of operators/functions */
3006 tgl@sss.pgh.pa.us 244 :CBC 61 : grouplist = identify_opfamily_groups(oprlist, proclist);
245 : 61 : opclassgroup = NULL;
246 [ + - + + : 177 : foreach(lc, grouplist)
+ + ]
247 : : {
248 : 116 : OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
249 : :
250 : : /* Remember the group exactly matching the test opclass */
251 [ + + ]: 116 : if (thisgroup->lefttype == opcintype &&
252 [ + + ]: 106 : thisgroup->righttype == opcintype)
253 : 61 : opclassgroup = thisgroup;
254 : :
255 : : /*
256 : : * There is not a lot we can do to check the operator sets, since each
257 : : * GiST opclass is more or less a law unto itself, and some contain
258 : : * only operators that are binary-compatible with the opclass datatype
259 : : * (meaning that empty operator sets can be OK). That case also means
260 : : * that we shouldn't insist on nonempty function sets except for the
261 : : * opclass's own group.
262 : : */
263 : : }
264 : :
265 : : /* Check that the originally-named opclass is complete */
3010 266 [ + + ]: 793 : for (i = 1; i <= GISTNProcs; i++)
267 : : {
3006 268 [ + - ]: 732 : if (opclassgroup &&
269 [ + + ]: 732 : (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
3010 270 : 504 : continue; /* got it */
2399 271 [ + + + + : 228 : if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
+ + ]
1476 akorotkov@postgresql 272 [ + + + + ]: 153 : i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
86 peter@eisentraut.org 273 [ + + + - ]:GNC 75 : i == GIST_OPTIONS_PROC || i == GIST_SORTSUPPORT_PROC ||
274 : : i == GIST_STRATNUM_PROC)
3010 tgl@sss.pgh.pa.us 275 :CBC 228 : continue; /* optional methods */
3006 tgl@sss.pgh.pa.us 276 [ # # ]:UBC 0 : ereport(INFO,
277 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
278 : : errmsg("operator class \"%s\" of access method %s is missing support function %d",
279 : : opclassname, "gist", i)));
280 : 0 : result = false;
281 : : }
282 : :
3010 tgl@sss.pgh.pa.us 283 :CBC 61 : ReleaseCatCacheList(proclist);
284 : 61 : ReleaseCatCacheList(oprlist);
3006 285 : 61 : ReleaseSysCache(familytup);
286 : 61 : ReleaseSysCache(classtup);
287 : :
288 : 61 : return result;
289 : : }
290 : :
291 : : /*
292 : : * Prechecking function for adding operators/functions to a GiST opfamily.
293 : : */
294 : : void
1352 295 : 124 : gistadjustmembers(Oid opfamilyoid,
296 : : Oid opclassoid,
297 : : List *operators,
298 : : List *functions)
299 : : {
300 : : ListCell *lc;
301 : :
302 : : /*
303 : : * Operator members of a GiST opfamily should never have hard
304 : : * dependencies, since their connection to the opfamily depends only on
305 : : * what the support functions think, and that can be altered. For
306 : : * consistency, we make all soft dependencies point to the opfamily,
307 : : * though a soft dependency on the opclass would work as well in the
308 : : * CREATE OPERATOR CLASS case.
309 : : */
310 [ + + + + : 486 : foreach(lc, operators)
+ + ]
311 : : {
312 : 362 : OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
313 : :
314 : 362 : op->ref_is_hard = false;
315 : 362 : op->ref_is_family = true;
316 : 362 : op->refobjid = opfamilyoid;
317 : : }
318 : :
319 : : /*
320 : : * Required support functions should have hard dependencies. Preferably
321 : : * those are just dependencies on the opclass, but if we're in ALTER
322 : : * OPERATOR FAMILY, we leave the dependency pointing at the whole
323 : : * opfamily. (Given that GiST opclasses generally don't share opfamilies,
324 : : * it seems unlikely to be worth working harder.)
325 : : */
326 [ + + + + : 533 : foreach(lc, functions)
+ + ]
327 : : {
328 : 409 : OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
329 : :
330 [ + + - ]: 409 : switch (op->number)
331 : : {
332 : 240 : case GIST_CONSISTENT_PROC:
333 : : case GIST_UNION_PROC:
334 : : case GIST_PENALTY_PROC:
335 : : case GIST_PICKSPLIT_PROC:
336 : : case GIST_EQUAL_PROC:
337 : : /* Required support function */
338 : 240 : op->ref_is_hard = true;
339 : 240 : break;
340 : 169 : case GIST_COMPRESS_PROC:
341 : : case GIST_DECOMPRESS_PROC:
342 : : case GIST_DISTANCE_PROC:
343 : : case GIST_FETCH_PROC:
344 : : case GIST_OPTIONS_PROC:
345 : : case GIST_SORTSUPPORT_PROC:
346 : : case GIST_STRATNUM_PROC:
347 : : /* Optional, so force it to be a soft family dependency */
348 : 169 : op->ref_is_hard = false;
349 : 169 : op->ref_is_family = true;
350 : 169 : op->refobjid = opfamilyoid;
351 : 169 : break;
1352 tgl@sss.pgh.pa.us 352 :UBC 0 : default:
353 [ # # ]: 0 : ereport(ERROR,
354 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
355 : : errmsg("support function number %d is invalid for access method %s",
356 : : op->number, "gist")));
357 : : break;
358 : : }
359 : : }
1352 tgl@sss.pgh.pa.us 360 :CBC 124 : }
|