PostgreSQL Source Code git master
spgvalidate.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * spgvalidate.c
4 * Opclass validator for SP-GiST.
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/spgist/spgvalidate.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include "access/amvalidate.h"
17#include "access/htup_details.h"
18#include "access/spgist.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/builtins.h"
24#include "utils/lsyscache.h"
25#include "utils/regproc.h"
26#include "utils/syscache.h"
27
28
29/*
30 * Validator for an SP-GiST opclass.
31 *
32 * Some of the checks done here cover the whole opfamily, and therefore are
33 * redundant when checking each opclass in a family. But they don't run long
34 * enough to be much of a problem, so we accept the duplication rather than
35 * complicate the amvalidate API.
36 */
37bool
38spgvalidate(Oid opclassoid)
39{
40 bool result = true;
41 HeapTuple classtup;
42 Form_pg_opclass classform;
43 Oid opfamilyoid;
44 Oid opcintype;
45 Oid opckeytype;
46 char *opclassname;
47 char *opfamilyname;
48 CatCList *proclist,
49 *oprlist;
50 List *grouplist;
51 OpFamilyOpFuncGroup *opclassgroup;
52 int i;
53 ListCell *lc;
54 spgConfigIn configIn;
55 spgConfigOut configOut;
56 Oid configOutLefttype = InvalidOid;
57 Oid configOutRighttype = InvalidOid;
58 Oid configOutLeafType = InvalidOid;
59
60 /* Fetch opclass information */
61 classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
62 if (!HeapTupleIsValid(classtup))
63 elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
64 classform = (Form_pg_opclass) GETSTRUCT(classtup);
65
66 opfamilyoid = classform->opcfamily;
67 opcintype = classform->opcintype;
68 opckeytype = classform->opckeytype;
69 opclassname = NameStr(classform->opcname);
70
71 /* Fetch opfamily information */
72 opfamilyname = get_opfamily_name(opfamilyoid, false);
73
74 /* Fetch all operators and support functions of the opfamily */
75 oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
76 proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
77 grouplist = identify_opfamily_groups(oprlist, proclist);
78
79 /* Check individual support functions */
80 for (i = 0; i < proclist->n_members; i++)
81 {
82 HeapTuple proctup = &proclist->members[i]->tuple;
83 Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
84 bool ok;
85
86 /*
87 * All SP-GiST support functions should be registered with matching
88 * left/right types
89 */
90 if (procform->amproclefttype != procform->amprocrighttype)
91 {
93 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
94 errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
95 opfamilyname, "spgist",
96 format_procedure(procform->amproc))));
97 result = false;
98 }
99
100 /* Check procedure numbers and function signatures */
101 switch (procform->amprocnum)
102 {
104 ok = check_amproc_signature(procform->amproc, VOIDOID, true,
105 2, 2, INTERNALOID, INTERNALOID);
106 configIn.attType = procform->amproclefttype;
107 memset(&configOut, 0, sizeof(configOut));
108
109 OidFunctionCall2(procform->amproc,
110 PointerGetDatum(&configIn),
111 PointerGetDatum(&configOut));
112
113 configOutLefttype = procform->amproclefttype;
114 configOutRighttype = procform->amprocrighttype;
115
116 /* Default leaf type is opckeytype or input type */
117 if (OidIsValid(opckeytype))
118 configOutLeafType = opckeytype;
119 else
120 configOutLeafType = procform->amproclefttype;
121
122 /* If some other leaf datum type is specified, warn */
123 if (OidIsValid(configOut.leafType) &&
124 configOutLeafType != configOut.leafType)
125 {
127 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
128 errmsg("SP-GiST leaf data type %s does not match declared type %s",
129 format_type_be(configOut.leafType),
130 format_type_be(configOutLeafType))));
131 result = false;
132 configOutLeafType = configOut.leafType;
133 }
134
135 /*
136 * When leaf and attribute types are the same, compress
137 * function is not required and we set corresponding bit in
138 * functionset for later group consistency check.
139 */
140 if (configOutLeafType == configIn.attType)
141 {
142 foreach(lc, grouplist)
143 {
144 OpFamilyOpFuncGroup *group = lfirst(lc);
145
146 if (group->lefttype == procform->amproclefttype &&
147 group->righttype == procform->amprocrighttype)
148 {
149 group->functionset |=
151 break;
152 }
153 }
154 }
155 break;
159 ok = check_amproc_signature(procform->amproc, VOIDOID, true,
160 2, 2, INTERNALOID, INTERNALOID);
161 break;
163 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
164 2, 2, INTERNALOID, INTERNALOID);
165 break;
167 if (configOutLefttype != procform->amproclefttype ||
168 configOutRighttype != procform->amprocrighttype)
169 ok = false;
170 else
171 ok = check_amproc_signature(procform->amproc,
172 configOutLeafType, true,
173 1, 1, procform->amproclefttype);
174 break;
176 ok = check_amoptsproc_signature(procform->amproc);
177 break;
178 default:
180 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
181 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
182 opfamilyname, "spgist",
183 format_procedure(procform->amproc),
184 procform->amprocnum)));
185 result = false;
186 continue; /* don't want additional message */
187 }
188
189 if (!ok)
190 {
192 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
193 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
194 opfamilyname, "spgist",
195 format_procedure(procform->amproc),
196 procform->amprocnum)));
197 result = false;
198 }
199 }
200
201 /* Check individual operators */
202 for (i = 0; i < oprlist->n_members; i++)
203 {
204 HeapTuple oprtup = &oprlist->members[i]->tuple;
205 Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
206 Oid op_rettype;
207
208 /* TODO: Check that only allowed strategy numbers exist */
209 if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
210 {
212 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
213 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
214 opfamilyname, "spgist",
215 format_operator(oprform->amopopr),
216 oprform->amopstrategy)));
217 result = false;
218 }
219
220 /* spgist supports ORDER BY operators */
221 if (oprform->amoppurpose != AMOP_SEARCH)
222 {
223 /* ... and operator result must match the claimed btree opfamily */
224 op_rettype = get_op_rettype(oprform->amopopr);
225 if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
226 {
228 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
229 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
230 opfamilyname, "spgist",
231 format_operator(oprform->amopopr))));
232 result = false;
233 }
234 }
235 else
236 op_rettype = BOOLOID;
237
238 /* Check operator signature --- same for all spgist strategies */
239 if (!check_amop_signature(oprform->amopopr, op_rettype,
240 oprform->amoplefttype,
241 oprform->amoprighttype))
242 {
244 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
245 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
246 opfamilyname, "spgist",
247 format_operator(oprform->amopopr))));
248 result = false;
249 }
250 }
251
252 /* Now check for inconsistent groups of operators/functions */
253 opclassgroup = NULL;
254 foreach(lc, grouplist)
255 {
257
258 /* Remember the group exactly matching the test opclass */
259 if (thisgroup->lefttype == opcintype &&
260 thisgroup->righttype == opcintype)
261 opclassgroup = thisgroup;
262
263 /*
264 * Complain if there are any datatype pairs with functions but no
265 * operators. This is about the best we can do for now to detect
266 * missing operators.
267 */
268 if (thisgroup->operatorset == 0)
269 {
271 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
272 errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
273 opfamilyname, "spgist",
274 format_type_be(thisgroup->lefttype),
275 format_type_be(thisgroup->righttype))));
276 result = false;
277 }
278
279 /*
280 * Complain if we're missing functions for any datatype, remembering
281 * that SP-GiST doesn't use cross-type support functions.
282 */
283 if (thisgroup->lefttype != thisgroup->righttype)
284 continue;
285
286 for (i = 1; i <= SPGISTNProc; i++)
287 {
288 if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
289 continue; /* got it */
290 if (i == SPGIST_OPTIONS_PROC)
291 continue; /* optional method */
293 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
294 errmsg("operator family \"%s\" of access method %s is missing support function %d for type %s",
295 opfamilyname, "spgist", i,
296 format_type_be(thisgroup->lefttype))));
297 result = false;
298 }
299 }
300
301 /* Check that the originally-named opclass is supported */
302 /* (if group is there, we already checked it adequately above) */
303 if (!opclassgroup)
304 {
306 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
307 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
308 opclassname, "spgist")));
309 result = false;
310 }
311
312 ReleaseCatCacheList(proclist);
313 ReleaseCatCacheList(oprlist);
314 ReleaseSysCache(classtup);
315
316 return result;
317}
318
319/*
320 * Prechecking function for adding operators/functions to an SP-GiST opfamily.
321 */
322void
324 Oid opclassoid,
325 List *operators,
327{
328 ListCell *lc;
329
330 /*
331 * Operator members of an SP-GiST opfamily should never have hard
332 * dependencies, since their connection to the opfamily depends only on
333 * what the support functions think, and that can be altered. For
334 * consistency, we make all soft dependencies point to the opfamily,
335 * though a soft dependency on the opclass would work as well in the
336 * CREATE OPERATOR CLASS case.
337 */
338 foreach(lc, operators)
339 {
341
342 op->ref_is_hard = false;
343 op->ref_is_family = true;
344 op->refobjid = opfamilyoid;
345 }
346
347 /*
348 * Required support functions should have hard dependencies. Preferably
349 * those are just dependencies on the opclass, but if we're in ALTER
350 * OPERATOR FAMILY, we leave the dependency pointing at the whole
351 * opfamily. (Given that SP-GiST opclasses generally don't share
352 * opfamilies, it seems unlikely to be worth working harder.)
353 */
354 foreach(lc, functions)
355 {
357
358 switch (op->number)
359 {
365 /* Required support function */
366 op->ref_is_hard = true;
367 break;
370 /* Optional, so force it to be a soft family dependency */
371 op->ref_is_hard = false;
372 op->ref_is_family = true;
373 op->refobjid = opfamilyoid;
374 break;
375 default:
377 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
378 errmsg("support function number %d is invalid for access method %s",
379 op->number, "spgist")));
380 break;
381 }
382 }
383}
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:152
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:206
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:43
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:271
bool check_amoptsproc_signature(Oid funcid)
Definition: amvalidate.c:192
#define NameStr(name)
Definition: c.h:703
uint64_t uint64
Definition: c.h:489
#define OidIsValid(objectId)
Definition: c.h:732
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:2071
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define INFO
Definition: elog.h:34
#define ereport(elevel,...)
Definition: elog.h:149
#define OidFunctionCall2(functionId, arg1, arg2)
Definition: fmgr.h:681
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
int i
Definition: isn.c:72
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1360
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1280
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:68
#define lfirst(lc)
Definition: pg_list.h:172
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
static const struct fns functions
Definition: regcomp.c:358
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:299
char * format_operator(Oid operator_oid)
Definition: regproc.c:793
#define SPGIST_OPTIONS_PROC
Definition: spgist.h:29
#define SPGIST_COMPRESS_PROC
Definition: spgist.h:28
#define SPGIST_CHOOSE_PROC
Definition: spgist.h:24
#define SPGIST_LEAF_CONSISTENT_PROC
Definition: spgist.h:27
#define SPGISTNProc
Definition: spgist.h:31
#define SPGIST_CONFIG_PROC
Definition: spgist.h:23
#define SPGIST_PICKSPLIT_PROC
Definition: spgist.h:25
#define SPGIST_INNER_CONSISTENT_PROC
Definition: spgist.h:26
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:38
void spgadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: spgvalidate.c:323
Definition: pg_list.h:54
Oid refobjid
Definition: amapi.h:96
bool ref_is_family
Definition: amapi.h:95
int number
Definition: amapi.h:90
bool ref_is_hard
Definition: amapi.h:94
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:180
int n_members
Definition: catcache.h:178
HeapTupleData tuple
Definition: catcache.h:123
Oid attType
Definition: spgist.h:38
Oid leafType
Definition: spgist.h:45
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:127