PostgreSQL Source Code git master
brin_validate.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * brin_validate.c
4 * Opclass validator for BRIN.
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/brin/brin_validate.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include "access/amvalidate.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/builtins.h"
24#include "utils/lsyscache.h"
25#include "utils/regproc.h"
26#include "utils/syscache.h"
27
28/*
29 * Validator for a BRIN opclass.
30 *
31 * Some of the checks done here cover the whole opfamily, and therefore are
32 * redundant when checking each opclass in a family. But they don't run long
33 * enough to be much of a problem, so we accept the duplication rather than
34 * complicate the amvalidate API.
35 */
36bool
37brinvalidate(Oid opclassoid)
38{
39 bool result = true;
40 HeapTuple classtup;
41 Form_pg_opclass classform;
42 Oid opfamilyoid;
43 Oid opcintype;
44 char *opclassname;
45 char *opfamilyname;
46 CatCList *proclist,
47 *oprlist;
48 uint64 allfuncs = 0;
49 uint64 allops = 0;
50 List *grouplist;
51 OpFamilyOpFuncGroup *opclassgroup;
52 int i;
53 ListCell *lc;
54
55 /* Fetch opclass information */
56 classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
57 if (!HeapTupleIsValid(classtup))
58 elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
59 classform = (Form_pg_opclass) GETSTRUCT(classtup);
60
61 opfamilyoid = classform->opcfamily;
62 opcintype = classform->opcintype;
63 opclassname = NameStr(classform->opcname);
64
65 /* Fetch opfamily information */
66 opfamilyname = get_opfamily_name(opfamilyoid, false);
67
68 /* Fetch all operators and support functions of the opfamily */
69 oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
70 proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
71
72 /* Check individual support functions */
73 for (i = 0; i < proclist->n_members; i++)
74 {
75 HeapTuple proctup = &proclist->members[i]->tuple;
76 Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
77 bool ok;
78
79 /* Check procedure numbers and function signatures */
80 switch (procform->amprocnum)
81 {
83 ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
84 1, 1, INTERNALOID);
85 break;
87 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
88 4, 4, INTERNALOID, INTERNALOID,
89 INTERNALOID, INTERNALOID);
90 break;
92 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
93 3, 4, INTERNALOID, INTERNALOID,
94 INTERNALOID, INT4OID);
95 break;
97 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
98 3, 3, INTERNALOID, INTERNALOID,
99 INTERNALOID);
100 break;
102 ok = check_amoptsproc_signature(procform->amproc);
103 break;
104 default:
105 /* Complain if it's not a valid optional proc number */
106 if (procform->amprocnum < BRIN_FIRST_OPTIONAL_PROCNUM ||
107 procform->amprocnum > BRIN_LAST_OPTIONAL_PROCNUM)
108 {
110 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
111 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
112 opfamilyname, "brin",
113 format_procedure(procform->amproc),
114 procform->amprocnum)));
115 result = false;
116 continue; /* omit bad proc numbers from allfuncs */
117 }
118 /* Can't check signatures of optional procs, so assume OK */
119 ok = true;
120 break;
121 }
122
123 if (!ok)
124 {
126 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
127 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
128 opfamilyname, "brin",
129 format_procedure(procform->amproc),
130 procform->amprocnum)));
131 result = false;
132 }
133
134 /* Track all valid procedure numbers seen in opfamily */
135 allfuncs |= ((uint64) 1) << procform->amprocnum;
136 }
137
138 /* Check individual operators */
139 for (i = 0; i < oprlist->n_members; i++)
140 {
141 HeapTuple oprtup = &oprlist->members[i]->tuple;
142 Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
143
144 /* Check that only allowed strategy numbers exist */
145 if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
146 {
148 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
149 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
150 opfamilyname, "brin",
151 format_operator(oprform->amopopr),
152 oprform->amopstrategy)));
153 result = false;
154 }
155 else
156 {
157 /*
158 * The set of operators supplied varies across BRIN opfamilies.
159 * Our plan is to identify all operator strategy numbers used in
160 * the opfamily and then complain about datatype combinations that
161 * are missing any operator(s). However, consider only numbers
162 * that appear in some non-cross-type case, since cross-type
163 * operators may have unique strategies. (This is not a great
164 * heuristic, in particular an erroneous number used in a
165 * cross-type operator will not get noticed; but the core BRIN
166 * opfamilies are messy enough to make it necessary.)
167 */
168 if (oprform->amoplefttype == oprform->amoprighttype)
169 allops |= ((uint64) 1) << oprform->amopstrategy;
170 }
171
172 /* brin doesn't support ORDER BY operators */
173 if (oprform->amoppurpose != AMOP_SEARCH ||
174 OidIsValid(oprform->amopsortfamily))
175 {
177 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
178 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
179 opfamilyname, "brin",
180 format_operator(oprform->amopopr))));
181 result = false;
182 }
183
184 /* Check operator signature --- same for all brin strategies */
185 if (!check_amop_signature(oprform->amopopr, BOOLOID,
186 oprform->amoplefttype,
187 oprform->amoprighttype))
188 {
190 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
191 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
192 opfamilyname, "brin",
193 format_operator(oprform->amopopr))));
194 result = false;
195 }
196 }
197
198 /* Now check for inconsistent groups of operators/functions */
199 grouplist = identify_opfamily_groups(oprlist, proclist);
200 opclassgroup = NULL;
201 foreach(lc, grouplist)
202 {
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 * Some BRIN opfamilies expect cross-type support functions to exist,
212 * and some don't. We don't know exactly which are which, so if we
213 * find a cross-type operator for which there are no support functions
214 * at all, let it pass. (Don't expect that all operators exist for
215 * such cross-type cases, either.)
216 */
217 if (thisgroup->functionset == 0 &&
218 thisgroup->lefttype != thisgroup->righttype)
219 continue;
220
221 /*
222 * Else complain if there seems to be an incomplete set of either
223 * operators or support functions for this datatype pair.
224 */
225 if (thisgroup->operatorset != allops)
226 {
228 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
229 errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
230 opfamilyname, "brin",
231 format_type_be(thisgroup->lefttype),
232 format_type_be(thisgroup->righttype))));
233 result = false;
234 }
235 if (thisgroup->functionset != allfuncs)
236 {
238 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
239 errmsg("operator family \"%s\" of access method %s is missing support function(s) for types %s and %s",
240 opfamilyname, "brin",
241 format_type_be(thisgroup->lefttype),
242 format_type_be(thisgroup->righttype))));
243 result = false;
244 }
245 }
246
247 /* Check that the originally-named opclass is complete */
248 if (!opclassgroup || opclassgroup->operatorset != allops)
249 {
251 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
252 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
253 opclassname, "brin")));
254 result = false;
255 }
256 for (i = 1; i <= BRIN_MANDATORY_NPROCS; i++)
257 {
258 if (opclassgroup &&
259 (opclassgroup->functionset & (((int64) 1) << i)) != 0)
260 continue; /* got it */
262 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
263 errmsg("operator class \"%s\" of access method %s is missing support function %d",
264 opclassname, "brin", i)));
265 result = false;
266 }
267
268 ReleaseCatCacheList(proclist);
269 ReleaseCatCacheList(oprlist);
270 ReleaseSysCache(classtup);
271
272 return result;
273}
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 BRIN_LAST_OPTIONAL_PROCNUM
Definition: brin_internal.h:78
#define BRIN_PROCNUM_UNION
Definition: brin_internal.h:73
#define BRIN_MANDATORY_NPROCS
Definition: brin_internal.h:74
#define BRIN_PROCNUM_OPTIONS
Definition: brin_internal.h:75
#define BRIN_FIRST_OPTIONAL_PROCNUM
Definition: brin_internal.h:77
#define BRIN_PROCNUM_OPCINFO
Definition: brin_internal.h:70
#define BRIN_PROCNUM_CONSISTENT
Definition: brin_internal.h:72
#define BRIN_PROCNUM_ADDVALUE
Definition: brin_internal.h:71
bool brinvalidate(Oid opclassoid)
Definition: brin_validate.c:37
#define NameStr(name)
Definition: c.h:703
int64_t int64
Definition: c.h:485
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
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
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 ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
unsigned int Oid
Definition: postgres_ext.h:32
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:299
char * format_operator(Oid operator_oid)
Definition: regproc.c:793
Definition: pg_list.h:54
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