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-2024, 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"
17 #include "access/brin_internal.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/builtins.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  */
36 bool
37 brinvalidate(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  HeapTuple familytup;
46  Form_pg_opfamily familyform;
47  char *opfamilyname;
48  CatCList *proclist,
49  *oprlist;
50  uint64 allfuncs = 0;
51  uint64 allops = 0;
52  List *grouplist;
53  OpFamilyOpFuncGroup *opclassgroup;
54  int i;
55  ListCell *lc;
56 
57  /* Fetch opclass information */
58  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
59  if (!HeapTupleIsValid(classtup))
60  elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
61  classform = (Form_pg_opclass) GETSTRUCT(classtup);
62 
63  opfamilyoid = classform->opcfamily;
64  opcintype = classform->opcintype;
65  opclassname = NameStr(classform->opcname);
66 
67  /* Fetch opfamily information */
68  familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
69  if (!HeapTupleIsValid(familytup))
70  elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
71  familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
72 
73  opfamilyname = NameStr(familyform->opfname);
74 
75  /* Fetch all operators and support functions of the opfamily */
76  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
77  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
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  /* Check procedure numbers and function signatures */
87  switch (procform->amprocnum)
88  {
90  ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
91  1, 1, INTERNALOID);
92  break;
94  ok = check_amproc_signature(procform->amproc, BOOLOID, true,
95  4, 4, INTERNALOID, INTERNALOID,
96  INTERNALOID, INTERNALOID);
97  break;
99  ok = check_amproc_signature(procform->amproc, BOOLOID, true,
100  3, 4, INTERNALOID, INTERNALOID,
101  INTERNALOID, INT4OID);
102  break;
103  case BRIN_PROCNUM_UNION:
104  ok = check_amproc_signature(procform->amproc, BOOLOID, true,
105  3, 3, INTERNALOID, INTERNALOID,
106  INTERNALOID);
107  break;
109  ok = check_amoptsproc_signature(procform->amproc);
110  break;
111  default:
112  /* Complain if it's not a valid optional proc number */
113  if (procform->amprocnum < BRIN_FIRST_OPTIONAL_PROCNUM ||
114  procform->amprocnum > BRIN_LAST_OPTIONAL_PROCNUM)
115  {
116  ereport(INFO,
117  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
118  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
119  opfamilyname, "brin",
120  format_procedure(procform->amproc),
121  procform->amprocnum)));
122  result = false;
123  continue; /* omit bad proc numbers from allfuncs */
124  }
125  /* Can't check signatures of optional procs, so assume OK */
126  ok = true;
127  break;
128  }
129 
130  if (!ok)
131  {
132  ereport(INFO,
133  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
134  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
135  opfamilyname, "brin",
136  format_procedure(procform->amproc),
137  procform->amprocnum)));
138  result = false;
139  }
140 
141  /* Track all valid procedure numbers seen in opfamily */
142  allfuncs |= ((uint64) 1) << procform->amprocnum;
143  }
144 
145  /* Check individual operators */
146  for (i = 0; i < oprlist->n_members; i++)
147  {
148  HeapTuple oprtup = &oprlist->members[i]->tuple;
149  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
150 
151  /* Check that only allowed strategy numbers exist */
152  if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
153  {
154  ereport(INFO,
155  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
156  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
157  opfamilyname, "brin",
158  format_operator(oprform->amopopr),
159  oprform->amopstrategy)));
160  result = false;
161  }
162  else
163  {
164  /*
165  * The set of operators supplied varies across BRIN opfamilies.
166  * Our plan is to identify all operator strategy numbers used in
167  * the opfamily and then complain about datatype combinations that
168  * are missing any operator(s). However, consider only numbers
169  * that appear in some non-cross-type case, since cross-type
170  * operators may have unique strategies. (This is not a great
171  * heuristic, in particular an erroneous number used in a
172  * cross-type operator will not get noticed; but the core BRIN
173  * opfamilies are messy enough to make it necessary.)
174  */
175  if (oprform->amoplefttype == oprform->amoprighttype)
176  allops |= ((uint64) 1) << oprform->amopstrategy;
177  }
178 
179  /* brin doesn't support ORDER BY operators */
180  if (oprform->amoppurpose != AMOP_SEARCH ||
181  OidIsValid(oprform->amopsortfamily))
182  {
183  ereport(INFO,
184  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
185  errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
186  opfamilyname, "brin",
187  format_operator(oprform->amopopr))));
188  result = false;
189  }
190 
191  /* Check operator signature --- same for all brin strategies */
192  if (!check_amop_signature(oprform->amopopr, BOOLOID,
193  oprform->amoplefttype,
194  oprform->amoprighttype))
195  {
196  ereport(INFO,
197  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
198  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
199  opfamilyname, "brin",
200  format_operator(oprform->amopopr))));
201  result = false;
202  }
203  }
204 
205  /* Now check for inconsistent groups of operators/functions */
206  grouplist = identify_opfamily_groups(oprlist, proclist);
207  opclassgroup = NULL;
208  foreach(lc, grouplist)
209  {
210  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
211 
212  /* Remember the group exactly matching the test opclass */
213  if (thisgroup->lefttype == opcintype &&
214  thisgroup->righttype == opcintype)
215  opclassgroup = thisgroup;
216 
217  /*
218  * Some BRIN opfamilies expect cross-type support functions to exist,
219  * and some don't. We don't know exactly which are which, so if we
220  * find a cross-type operator for which there are no support functions
221  * at all, let it pass. (Don't expect that all operators exist for
222  * such cross-type cases, either.)
223  */
224  if (thisgroup->functionset == 0 &&
225  thisgroup->lefttype != thisgroup->righttype)
226  continue;
227 
228  /*
229  * Else complain if there seems to be an incomplete set of either
230  * operators or support functions for this datatype pair.
231  */
232  if (thisgroup->operatorset != allops)
233  {
234  ereport(INFO,
235  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
236  errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
237  opfamilyname, "brin",
238  format_type_be(thisgroup->lefttype),
239  format_type_be(thisgroup->righttype))));
240  result = false;
241  }
242  if (thisgroup->functionset != allfuncs)
243  {
244  ereport(INFO,
245  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
246  errmsg("operator family \"%s\" of access method %s is missing support function(s) for types %s and %s",
247  opfamilyname, "brin",
248  format_type_be(thisgroup->lefttype),
249  format_type_be(thisgroup->righttype))));
250  result = false;
251  }
252  }
253 
254  /* Check that the originally-named opclass is complete */
255  if (!opclassgroup || opclassgroup->operatorset != allops)
256  {
257  ereport(INFO,
258  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
259  errmsg("operator class \"%s\" of access method %s is missing operator(s)",
260  opclassname, "brin")));
261  result = false;
262  }
263  for (i = 1; i <= BRIN_MANDATORY_NPROCS; i++)
264  {
265  if (opclassgroup &&
266  (opclassgroup->functionset & (((int64) 1) << i)) != 0)
267  continue; /* got it */
268  ereport(INFO,
269  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
270  errmsg("operator class \"%s\" of access method %s is missing support function %d",
271  opclassname, "brin", i)));
272  result = false;
273  }
274 
275  ReleaseCatCacheList(proclist);
276  ReleaseCatCacheList(oprlist);
277  ReleaseSysCache(familytup);
278  ReleaseSysCache(classtup);
279 
280  return result;
281 }
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:733
#define OidIsValid(objectId)
Definition: c.h:762
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1889
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#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
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
int i
Definition: isn.c:73
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
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
unsigned int Oid
Definition: postgres_ext.h:31
char * format_operator(Oid operator_oid)
Definition: regproc.c:793
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:299
Definition: pg_list.h:54
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
int n_members
Definition: catcache.h:176
HeapTupleData tuple
Definition: catcache.h:121
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:122