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