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-2018, 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 procedure %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 function
123  * is not required and we set corresponding bit in functionset
124  * 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 
191  /* TODO: Check that only allowed strategy numbers exist */
192  if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
193  {
194  ereport(INFO,
195  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
196  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
197  opfamilyname, "spgist",
198  format_operator(oprform->amopopr),
199  oprform->amopstrategy)));
200  result = false;
201  }
202 
203  /* spgist doesn't support ORDER BY operators */
204  if (oprform->amoppurpose != AMOP_SEARCH ||
205  OidIsValid(oprform->amopsortfamily))
206  {
207  ereport(INFO,
208  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
209  errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
210  opfamilyname, "spgist",
211  format_operator(oprform->amopopr))));
212  result = false;
213  }
214 
215  /* Check operator signature --- same for all spgist strategies */
216  if (!check_amop_signature(oprform->amopopr, BOOLOID,
217  oprform->amoplefttype,
218  oprform->amoprighttype))
219  {
220  ereport(INFO,
221  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
222  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
223  opfamilyname, "spgist",
224  format_operator(oprform->amopopr))));
225  result = false;
226  }
227  }
228 
229  /* Now check for inconsistent groups of operators/functions */
230  opclassgroup = NULL;
231  foreach(lc, grouplist)
232  {
233  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
234 
235  /* Remember the group exactly matching the test opclass */
236  if (thisgroup->lefttype == opcintype &&
237  thisgroup->righttype == opcintype)
238  opclassgroup = thisgroup;
239 
240  /*
241  * Complain if there are any datatype pairs with functions but no
242  * operators. This is about the best we can do for now to detect
243  * missing operators.
244  */
245  if (thisgroup->operatorset == 0)
246  {
247  ereport(INFO,
248  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
249  errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
250  opfamilyname, "spgist",
251  format_type_be(thisgroup->lefttype),
252  format_type_be(thisgroup->righttype))));
253  result = false;
254  }
255 
256  /*
257  * Complain if we're missing functions for any datatype, remembering
258  * that SP-GiST doesn't use cross-type support functions.
259  */
260  if (thisgroup->lefttype != thisgroup->righttype)
261  continue;
262 
263  for (i = 1; i <= SPGISTNProc; i++)
264  {
265  if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
266  continue; /* got it */
267  ereport(INFO,
268  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
269  errmsg("operator family \"%s\" of access method %s is missing support function %d for type %s",
270  opfamilyname, "spgist", i,
271  format_type_be(thisgroup->lefttype))));
272  result = false;
273  }
274  }
275 
276  /* Check that the originally-named opclass is supported */
277  /* (if group is there, we already checked it adequately above) */
278  if (!opclassgroup)
279  {
280  ereport(INFO,
281  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
282  errmsg("operator class \"%s\" of access method %s is missing operator(s)",
283  opclassname, "spgist")));
284  result = false;
285  }
286 
287  ReleaseCatCacheList(proclist);
288  ReleaseCatCacheList(oprlist);
289  ReleaseSysCache(familytup);
290  ReleaseSysCache(classtup);
291 
292  return result;
293 }
int n_members
Definition: catcache.h:176
#define GETSTRUCT(TUP)
Definition: htup_details.h:673
#define SPGIST_LEAF_CONSISTENT_PROC
Definition: spgist.h:32
#define PointerGetDatum(X)
Definition: postgres.h:539
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:66
int errcode(int sqlerrcode)
Definition: elog.c:575
#define INFO
Definition: elog.h:33
char * format_type_be(Oid type_oid)
Definition: format_type.c:328
char * format_operator(Oid operator_oid)
Definition: regproc.c:820
Oid attType
Definition: spgist.h:42
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:605
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1800
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:490
#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:209
#define ereport(elevel, rest)
Definition: elog.h:122
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:49
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
#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:106
#define SPGIST_COMPRESS_PROC
Definition: spgist.h:33
#define SPGIST_CONFIG_PROC
Definition: spgist.h:28
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:194
#define OidFunctionCall2(functionId, arg1, arg2)
Definition: fmgr.h:630
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define SPGISTNProc
Definition: spgist.h:35
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:39
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:86
int i
#define NameStr(name)
Definition: c.h:576
Oid leafType
Definition: spgist.h:49
#define SPGIST_INNER_CONSISTENT_PROC
Definition: spgist.h:31
HeapTupleData tuple
Definition: catcache.h:121
#define elog
Definition: elog.h:219
#define SPGIST_CHOOSE_PROC
Definition: spgist.h:29
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:81
#define SPGIST_PICKSPLIT_PROC
Definition: spgist.h:30
Definition: pg_list.h:45