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-2019, 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_private.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/lsyscache.h"
26 #include "utils/regproc.h"
27 #include "utils/syscache.h"
28 
29 
30 /*
31  * Validator for an SP-GiST opclass.
32  *
33  * Some of the checks done here cover the whole opfamily, and therefore are
34  * redundant when checking each opclass in a family. But they don't run long
35  * enough to be much of a problem, so we accept the duplication rather than
36  * complicate the amvalidate API.
37  */
38 bool
39 spgvalidate(Oid opclassoid)
40 {
41  bool result = true;
42  HeapTuple classtup;
43  Form_pg_opclass classform;
44  Oid opfamilyoid;
45  Oid opcintype;
46  char *opclassname;
47  HeapTuple familytup;
48  Form_pg_opfamily familyform;
49  char *opfamilyname;
50  CatCList *proclist,
51  *oprlist;
52  List *grouplist;
53  OpFamilyOpFuncGroup *opclassgroup;
54  int i;
55  ListCell *lc;
56  spgConfigIn configIn;
57  spgConfigOut configOut;
58  Oid configOutLefttype = InvalidOid;
59  Oid configOutRighttype = InvalidOid;
60 
61  /* Fetch opclass information */
62  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
63  if (!HeapTupleIsValid(classtup))
64  elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
65  classform = (Form_pg_opclass) GETSTRUCT(classtup);
66 
67  opfamilyoid = classform->opcfamily;
68  opcintype = classform->opcintype;
69  opclassname = NameStr(classform->opcname);
70 
71  /* Fetch opfamily information */
72  familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
73  if (!HeapTupleIsValid(familytup))
74  elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
75  familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
76 
77  opfamilyname = NameStr(familyform->opfname);
78 
79  /* Fetch all operators and support functions of the opfamily */
80  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
81  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
82  grouplist = identify_opfamily_groups(oprlist, proclist);
83 
84  /* Check individual support functions */
85  for (i = 0; i < proclist->n_members; i++)
86  {
87  HeapTuple proctup = &proclist->members[i]->tuple;
88  Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
89  bool ok;
90 
91  /*
92  * All SP-GiST support functions should be registered with matching
93  * left/right types
94  */
95  if (procform->amproclefttype != procform->amprocrighttype)
96  {
97  ereport(INFO,
98  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
99  errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
100  opfamilyname, "spgist",
101  format_procedure(procform->amproc))));
102  result = false;
103  }
104 
105  /* Check procedure numbers and function signatures */
106  switch (procform->amprocnum)
107  {
108  case SPGIST_CONFIG_PROC:
109  ok = check_amproc_signature(procform->amproc, VOIDOID, true,
110  2, 2, INTERNALOID, INTERNALOID);
111  configIn.attType = procform->amproclefttype;
112  memset(&configOut, 0, sizeof(configOut));
113 
114  OidFunctionCall2(procform->amproc,
115  PointerGetDatum(&configIn),
116  PointerGetDatum(&configOut));
117 
118  configOutLefttype = procform->amproclefttype;
119  configOutRighttype = procform->amprocrighttype;
120 
121  /*
122  * When leaf and attribute types are the same, compress
123  * function is not required and we set corresponding bit in
124  * functionset for later group consistency check.
125  */
126  if (!OidIsValid(configOut.leafType) ||
127  configOut.leafType == configIn.attType)
128  {
129  foreach(lc, grouplist)
130  {
131  OpFamilyOpFuncGroup *group = lfirst(lc);
132 
133  if (group->lefttype == procform->amproclefttype &&
134  group->righttype == procform->amprocrighttype)
135  {
136  group->functionset |=
137  ((uint64) 1) << SPGIST_COMPRESS_PROC;
138  break;
139  }
140  }
141  }
142  break;
143  case SPGIST_CHOOSE_PROC:
146  ok = check_amproc_signature(procform->amproc, VOIDOID, true,
147  2, 2, INTERNALOID, INTERNALOID);
148  break;
150  ok = check_amproc_signature(procform->amproc, BOOLOID, true,
151  2, 2, INTERNALOID, INTERNALOID);
152  break;
154  if (configOutLefttype != procform->amproclefttype ||
155  configOutRighttype != procform->amprocrighttype)
156  ok = false;
157  else
158  ok = check_amproc_signature(procform->amproc,
159  configOut.leafType, true,
160  1, 1, procform->amproclefttype);
161  break;
162  default:
163  ereport(INFO,
164  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
165  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
166  opfamilyname, "spgist",
167  format_procedure(procform->amproc),
168  procform->amprocnum)));
169  result = false;
170  continue; /* don't want additional message */
171  }
172 
173  if (!ok)
174  {
175  ereport(INFO,
176  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
177  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
178  opfamilyname, "spgist",
179  format_procedure(procform->amproc),
180  procform->amprocnum)));
181  result = false;
182  }
183  }
184 
185  /* Check individual operators */
186  for (i = 0; i < oprlist->n_members; i++)
187  {
188  HeapTuple oprtup = &oprlist->members[i]->tuple;
189  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
190  Oid op_rettype;
191 
192  /* TODO: Check that only allowed strategy numbers exist */
193  if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
194  {
195  ereport(INFO,
196  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
197  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
198  opfamilyname, "spgist",
199  format_operator(oprform->amopopr),
200  oprform->amopstrategy)));
201  result = false;
202  }
203 
204  /* spgist supports ORDER BY operators */
205  if (oprform->amoppurpose != AMOP_SEARCH)
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 invalid ORDER BY specification for operator %s",
214  opfamilyname, "spgist",
215  format_operator(oprform->amopopr))));
216  result = false;
217  }
218  }
219  else
220  op_rettype = BOOLOID;
221 
222  /* Check operator signature --- same for all spgist strategies */
223  if (!check_amop_signature(oprform->amopopr, op_rettype,
224  oprform->amoplefttype,
225  oprform->amoprighttype))
226  {
227  ereport(INFO,
228  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
229  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
230  opfamilyname, "spgist",
231  format_operator(oprform->amopopr))));
232  result = false;
233  }
234  }
235 
236  /* Now check for inconsistent groups of operators/functions */
237  opclassgroup = NULL;
238  foreach(lc, grouplist)
239  {
240  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
241 
242  /* Remember the group exactly matching the test opclass */
243  if (thisgroup->lefttype == opcintype &&
244  thisgroup->righttype == opcintype)
245  opclassgroup = thisgroup;
246 
247  /*
248  * Complain if there are any datatype pairs with functions but no
249  * operators. This is about the best we can do for now to detect
250  * missing operators.
251  */
252  if (thisgroup->operatorset == 0)
253  {
254  ereport(INFO,
255  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
256  errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
257  opfamilyname, "spgist",
258  format_type_be(thisgroup->lefttype),
259  format_type_be(thisgroup->righttype))));
260  result = false;
261  }
262 
263  /*
264  * Complain if we're missing functions for any datatype, remembering
265  * that SP-GiST doesn't use cross-type support functions.
266  */
267  if (thisgroup->lefttype != thisgroup->righttype)
268  continue;
269 
270  for (i = 1; i <= SPGISTNProc; i++)
271  {
272  if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
273  continue; /* got it */
274  ereport(INFO,
275  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
276  errmsg("operator family \"%s\" of access method %s is missing support function %d for type %s",
277  opfamilyname, "spgist", i,
278  format_type_be(thisgroup->lefttype))));
279  result = false;
280  }
281  }
282 
283  /* Check that the originally-named opclass is supported */
284  /* (if group is there, we already checked it adequately above) */
285  if (!opclassgroup)
286  {
287  ereport(INFO,
288  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
289  errmsg("operator class \"%s\" of access method %s is missing operator(s)",
290  opclassname, "spgist")));
291  result = false;
292  }
293 
294  ReleaseCatCacheList(proclist);
295  ReleaseCatCacheList(oprlist);
296  ReleaseSysCache(familytup);
297  ReleaseSysCache(classtup);
298 
299  return result;
300 }
int n_members
Definition: catcache.h:176
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
#define SPGIST_LEAF_CONSISTENT_PROC
Definition: spgist.h:31
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:217
#define PointerGetDatum(X)
Definition: postgres.h:556
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:68
int errcode(int sqlerrcode)
Definition: elog.c:570
#define INFO
Definition: elog.h:33
char * format_type_be(Oid type_oid)
Definition: format_type.c:326
char * format_operator(Oid operator_oid)
Definition: regproc.c:820
Oid attType
Definition: spgist.h:41
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1140
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1782
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:150
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:41
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:210
#define ereport(elevel, rest)
Definition: elog.h:141
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1124
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
#define InvalidOid
Definition: postgres_ext.h:36
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
#define SPGIST_COMPRESS_PROC
Definition: spgist.h:32
#define SPGIST_CONFIG_PROC
Definition: spgist.h:27
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:194
#define OidFunctionCall2(functionId, arg1, arg2)
Definition: fmgr.h:657
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define SPGISTNProc
Definition: spgist.h:34
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:39
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
#define elog(elevel,...)
Definition: elog.h:226
int i
#define NameStr(name)
Definition: c.h:609
Oid leafType
Definition: spgist.h:48
#define SPGIST_INNER_CONSISTENT_PROC
Definition: spgist.h:30
HeapTupleData tuple
Definition: catcache.h:121
#define SPGIST_CHOOSE_PROC
Definition: spgist.h:28
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
#define SPGIST_PICKSPLIT_PROC
Definition: spgist.h:29
Definition: pg_list.h:50