PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
nbtvalidate.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * nbtvalidate.c
4 * Opclass validator for btree.
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/nbtree/nbtvalidate.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include "access/amvalidate.h"
17#include "access/htup_details.h"
18#include "access/nbtree.h"
19#include "access/xact.h"
20#include "catalog/pg_am.h"
21#include "catalog/pg_amop.h"
22#include "catalog/pg_amproc.h"
23#include "catalog/pg_opclass.h"
24#include "catalog/pg_type.h"
25#include "utils/builtins.h"
26#include "utils/lsyscache.h"
27#include "utils/regproc.h"
28#include "utils/syscache.h"
29
30
31/*
32 * Validator for a btree opclass.
33 *
34 * Some of the checks done here cover the whole opfamily, and therefore are
35 * redundant when checking each opclass in a family. But they don't run long
36 * enough to be much of a problem, so we accept the duplication rather than
37 * complicate the amvalidate API.
38 */
39bool
40btvalidate(Oid opclassoid)
41{
42 bool result = true;
43 HeapTuple classtup;
44 Form_pg_opclass classform;
45 Oid opfamilyoid;
46 Oid opcintype;
47 char *opclassname;
48 char *opfamilyname;
49 CatCList *proclist,
50 *oprlist;
51 List *grouplist;
52 OpFamilyOpFuncGroup *opclassgroup;
53 List *familytypes;
54 int usefulgroups;
55 int i;
56 ListCell *lc;
57
58 /* Fetch opclass information */
59 classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
60 if (!HeapTupleIsValid(classtup))
61 elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
62 classform = (Form_pg_opclass) GETSTRUCT(classtup);
63
64 opfamilyoid = classform->opcfamily;
65 opcintype = classform->opcintype;
66 opclassname = NameStr(classform->opcname);
67
68 /* Fetch opfamily information */
69 opfamilyname = get_opfamily_name(opfamilyoid, false);
70
71 /* Fetch all operators and support functions of the opfamily */
72 oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
73 proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
74
75 /* Check individual support functions */
76 for (i = 0; i < proclist->n_members; i++)
77 {
78 HeapTuple proctup = &proclist->members[i]->tuple;
79 Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
80 bool ok;
81
82 /* Check procedure numbers and function signatures */
83 switch (procform->amprocnum)
84 {
85 case BTORDER_PROC:
86 ok = check_amproc_signature(procform->amproc, INT4OID, true,
87 2, 2, procform->amproclefttype,
88 procform->amprocrighttype);
89 break;
91 ok = check_amproc_signature(procform->amproc, VOIDOID, true,
92 1, 1, INTERNALOID);
93 break;
94 case BTINRANGE_PROC:
95 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
96 5, 5,
97 procform->amproclefttype,
98 procform->amproclefttype,
99 procform->amprocrighttype,
100 BOOLOID, BOOLOID);
101 break;
103 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
104 1, 1, OIDOID);
105 break;
106 case BTOPTIONS_PROC:
107 ok = check_amoptsproc_signature(procform->amproc);
108 break;
110 ok = check_amproc_signature(procform->amproc, VOIDOID, true,
111 1, 1, INTERNALOID);
112 break;
113 default:
115 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
116 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
117 opfamilyname, "btree",
118 format_procedure(procform->amproc),
119 procform->amprocnum)));
120 result = false;
121 continue; /* don't want additional message */
122 }
123
124 if (!ok)
125 {
127 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
128 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
129 opfamilyname, "btree",
130 format_procedure(procform->amproc),
131 procform->amprocnum)));
132 result = false;
133 }
134 }
135
136 /* Check individual operators */
137 for (i = 0; i < oprlist->n_members; i++)
138 {
139 HeapTuple oprtup = &oprlist->members[i]->tuple;
140 Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
141
142 /* Check that only allowed strategy numbers exist */
143 if (oprform->amopstrategy < 1 ||
144 oprform->amopstrategy > BTMaxStrategyNumber)
145 {
147 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
148 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
149 opfamilyname, "btree",
150 format_operator(oprform->amopopr),
151 oprform->amopstrategy)));
152 result = false;
153 }
154
155 /* btree doesn't support ORDER BY operators */
156 if (oprform->amoppurpose != AMOP_SEARCH ||
157 OidIsValid(oprform->amopsortfamily))
158 {
160 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
161 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
162 opfamilyname, "btree",
163 format_operator(oprform->amopopr))));
164 result = false;
165 }
166
167 /* Check operator signature --- same for all btree strategies */
168 if (!check_amop_signature(oprform->amopopr, BOOLOID,
169 oprform->amoplefttype,
170 oprform->amoprighttype))
171 {
173 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
174 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
175 opfamilyname, "btree",
176 format_operator(oprform->amopopr))));
177 result = false;
178 }
179 }
180
181 /* Now check for inconsistent groups of operators/functions */
182 grouplist = identify_opfamily_groups(oprlist, proclist);
183 usefulgroups = 0;
184 opclassgroup = NULL;
185 familytypes = NIL;
186 foreach(lc, grouplist)
187 {
189
190 /*
191 * It is possible for an in_range support function to have a RHS type
192 * that is otherwise irrelevant to the opfamily --- for instance, SQL
193 * requires the datetime_ops opclass to have range support with an
194 * interval offset. So, if this group appears to contain only an
195 * in_range function, ignore it: it doesn't represent a pair of
196 * supported types.
197 */
198 if (thisgroup->operatorset == 0 &&
199 thisgroup->functionset == (1 << BTINRANGE_PROC))
200 continue;
201
202 /* Else count it as a relevant group */
203 usefulgroups++;
204
205 /* Remember the group exactly matching the test opclass */
206 if (thisgroup->lefttype == opcintype &&
207 thisgroup->righttype == opcintype)
208 opclassgroup = thisgroup;
209
210 /*
211 * Identify all distinct data types handled in this opfamily. This
212 * implementation is O(N^2), but there aren't likely to be enough
213 * types in the family for it to matter.
214 */
215 familytypes = list_append_unique_oid(familytypes, thisgroup->lefttype);
216 familytypes = list_append_unique_oid(familytypes, thisgroup->righttype);
217
218 /*
219 * Complain if there seems to be an incomplete set of either operators
220 * or support functions for this datatype pair. The sortsupport,
221 * in_range, and equalimage functions are considered optional.
222 */
223 if (thisgroup->operatorset !=
224 ((1 << BTLessStrategyNumber) |
226 (1 << BTEqualStrategyNumber) |
229 {
231 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
232 errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
233 opfamilyname, "btree",
234 format_type_be(thisgroup->lefttype),
235 format_type_be(thisgroup->righttype))));
236 result = false;
237 }
238 if ((thisgroup->functionset & (1 << BTORDER_PROC)) == 0)
239 {
241 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
242 errmsg("operator family \"%s\" of access method %s is missing support function for types %s and %s",
243 opfamilyname, "btree",
244 format_type_be(thisgroup->lefttype),
245 format_type_be(thisgroup->righttype))));
246 result = false;
247 }
248 }
249
250 /* Check that the originally-named opclass is supported */
251 /* (if group is there, we already checked it adequately above) */
252 if (!opclassgroup)
253 {
255 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
256 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
257 opclassname, "btree")));
258 result = false;
259 }
260
261 /*
262 * Complain if the opfamily doesn't have entries for all possible
263 * combinations of its supported datatypes. While missing cross-type
264 * operators are not fatal, they do limit the planner's ability to derive
265 * additional qual clauses from equivalence classes, so it seems
266 * reasonable to insist that all built-in btree opfamilies be complete.
267 */
268 if (usefulgroups != (list_length(familytypes) * list_length(familytypes)))
269 {
271 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
272 errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
273 opfamilyname, "btree")));
274 result = false;
275 }
276
277 ReleaseCatCacheList(proclist);
278 ReleaseCatCacheList(oprlist);
279 ReleaseSysCache(classtup);
280
281 return result;
282}
283
284/*
285 * Prechecking function for adding operators/functions to a btree opfamily.
286 */
287void
289 Oid opclassoid,
290 List *operators,
292{
293 Oid opcintype;
294 ListCell *lc;
295
296 /*
297 * Btree operators and comparison support functions are always "loose"
298 * members of the opfamily if they are cross-type. If they are not
299 * cross-type, we prefer to tie them to the appropriate opclass ... but if
300 * the user hasn't created one, we can't do that, and must fall back to
301 * using the opfamily dependency. (We mustn't force creation of an
302 * opclass in such a case, as leaving an incomplete opclass laying about
303 * would be bad. Throwing an error is another undesirable alternative.)
304 *
305 * This behavior results in a bit of a dump/reload hazard, in that the
306 * order of restoring objects could affect what dependencies we end up
307 * with. pg_dump's existing behavior will preserve the dependency choices
308 * in most cases, but not if a cross-type operator has been bound tightly
309 * into an opclass. That's a mistake anyway, so silently "fixing" it
310 * isn't awful.
311 *
312 * Optional support functions are always "loose" family members.
313 *
314 * To avoid repeated lookups, we remember the most recently used opclass's
315 * input type.
316 */
317 if (OidIsValid(opclassoid))
318 {
319 /* During CREATE OPERATOR CLASS, need CCI to see the pg_opclass row */
321 opcintype = get_opclass_input_type(opclassoid);
322 }
323 else
324 opcintype = InvalidOid;
325
326 /*
327 * We handle operators and support functions almost identically, so rather
328 * than duplicate this code block, just join the lists.
329 */
330 foreach(lc, list_concat_copy(operators, functions))
331 {
333
334 if (op->is_func && op->number != BTORDER_PROC)
335 {
336 /* Optional support proc, so always a soft family dependency */
337 op->ref_is_hard = false;
338 op->ref_is_family = true;
339 op->refobjid = opfamilyoid;
340 }
341 else if (op->lefttype != op->righttype)
342 {
343 /* Cross-type, so always a soft family dependency */
344 op->ref_is_hard = false;
345 op->ref_is_family = true;
346 op->refobjid = opfamilyoid;
347 }
348 else
349 {
350 /* Not cross-type; is there a suitable opclass? */
351 if (op->lefttype != opcintype)
352 {
353 /* Avoid repeating this expensive lookup, even if it fails */
354 opcintype = op->lefttype;
355 opclassoid = opclass_for_family_datatype(BTREE_AM_OID,
356 opfamilyoid,
357 opcintype);
358 }
359 if (OidIsValid(opclassoid))
360 {
361 /* Hard dependency on opclass */
362 op->ref_is_hard = true;
363 op->ref_is_family = false;
364 op->refobjid = opclassoid;
365 }
366 else
367 {
368 /* We're stuck, so make a soft dependency on the opfamily */
369 op->ref_is_hard = false;
370 op->ref_is_family = true;
371 op->refobjid = opfamilyoid;
372 }
373 }
374 }
375}
Oid opclass_for_family_datatype(Oid amoid, Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:236
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 check_amoptsproc_signature(Oid funcid)
Definition: amvalidate.c:192
#define NameStr(name)
Definition: c.h:717
#define OidIsValid(objectId)
Definition: c.h:746
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:2093
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#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
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:77
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:598
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1304
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1393
#define BTEQUALIMAGE_PROC
Definition: nbtree.h:720
#define BTORDER_PROC
Definition: nbtree.h:717
#define BTSKIPSUPPORT_PROC
Definition: nbtree.h:722
#define BTSORTSUPPORT_PROC
Definition: nbtree.h:718
#define BTINRANGE_PROC
Definition: nbtree.h:719
#define BTOPTIONS_PROC
Definition: nbtree.h:721
bool btvalidate(Oid opclassoid)
Definition: nbtvalidate.c:40
void btadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: nbtvalidate.c:288
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
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
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 BTGreaterStrategyNumber
Definition: stratnum.h:33
#define BTMaxStrategyNumber
Definition: stratnum.h:35
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
Definition: pg_list.h:54
Oid refobjid
Definition: amapi.h:96
Oid lefttype
Definition: amapi.h:91
bool ref_is_family
Definition: amapi.h:95
Oid righttype
Definition: amapi.h:92
int number
Definition: amapi.h:90
bool is_func
Definition: amapi.h:88
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
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
void CommandCounterIncrement(void)
Definition: xact.c:1100