PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2016, 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/builtins.h"
25 #include "utils/lsyscache.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("gist opfamily %s contains support procedure %s with cross-type registration",
93  opfamilyname,
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,
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,
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,
141  break;
142  default:
143  ereport(INFO,
144  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
145  errmsg("gist opfamily %s contains function %s with invalid support number %d",
146  opfamilyname,
147  format_procedure(procform->amproc),
148  procform->amprocnum)));
149  result = false;
150  continue; /* don't want additional message */
151  }
152 
153  if (!ok)
154  {
155  ereport(INFO,
156  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
157  errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
158  opfamilyname,
159  format_procedure(procform->amproc),
160  procform->amprocnum)));
161  result = false;
162  }
163  }
164 
165  /* Check individual operators */
166  for (i = 0; i < oprlist->n_members; i++)
167  {
168  HeapTuple oprtup = &oprlist->members[i]->tuple;
169  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
170  Oid op_rettype;
171 
172  /* TODO: Check that only allowed strategy numbers exist */
173  if (oprform->amopstrategy < 1)
174  {
175  ereport(INFO,
176  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
177  errmsg("gist opfamily %s contains operator %s with invalid strategy number %d",
178  opfamilyname,
179  format_operator(oprform->amopopr),
180  oprform->amopstrategy)));
181  result = false;
182  }
183 
184  /* GiST supports ORDER BY operators */
185  if (oprform->amoppurpose != AMOP_SEARCH)
186  {
187  /* ... but must have matching distance proc */
188  if (!OidIsValid(get_opfamily_proc(opfamilyoid,
189  oprform->amoplefttype,
190  oprform->amoplefttype,
192  {
193  ereport(INFO,
194  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
195  errmsg("gist opfamily %s contains unsupported ORDER BY specification for operator %s",
196  opfamilyname,
197  format_operator(oprform->amopopr))));
198  result = false;
199  }
200  /* ... and operator result must match the claimed btree opfamily */
201  op_rettype = get_op_rettype(oprform->amopopr);
202  if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
203  {
204  ereport(INFO,
205  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
206  errmsg("gist opfamily %s contains incorrect ORDER BY opfamily specification for operator %s",
207  opfamilyname,
208  format_operator(oprform->amopopr))));
209  result = false;
210  }
211  }
212  else
213  {
214  /* Search operators must always return bool */
215  op_rettype = BOOLOID;
216  }
217 
218  /* Check operator signature */
219  if (!check_amop_signature(oprform->amopopr, op_rettype,
220  oprform->amoplefttype,
221  oprform->amoprighttype))
222  {
223  ereport(INFO,
224  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
225  errmsg("gist opfamily %s contains operator %s with wrong signature",
226  opfamilyname,
227  format_operator(oprform->amopopr))));
228  result = false;
229  }
230  }
231 
232  /* Now check for inconsistent groups of operators/functions */
233  grouplist = identify_opfamily_groups(oprlist, proclist);
234  opclassgroup = NULL;
235  foreach(lc, grouplist)
236  {
237  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
238 
239  /* Remember the group exactly matching the test opclass */
240  if (thisgroup->lefttype == opcintype &&
241  thisgroup->righttype == opcintype)
242  opclassgroup = thisgroup;
243 
244  /*
245  * There is not a lot we can do to check the operator sets, since each
246  * GiST opclass is more or less a law unto itself, and some contain
247  * only operators that are binary-compatible with the opclass datatype
248  * (meaning that empty operator sets can be OK). That case also means
249  * that we shouldn't insist on nonempty function sets except for the
250  * opclass's own group.
251  */
252  }
253 
254  /* Check that the originally-named opclass is complete */
255  for (i = 1; i <= GISTNProcs; i++)
256  {
257  if (opclassgroup &&
258  (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
259  continue; /* got it */
260  if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC)
261  continue; /* optional methods */
262  ereport(INFO,
263  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
264  errmsg("gist opclass %s is missing support function %d",
265  opclassname, i)));
266  result = false;
267  }
268 
269  ReleaseCatCacheList(proclist);
270  ReleaseCatCacheList(oprlist);
271  ReleaseSysCache(familytup);
272  ReleaseSysCache(classtup);
273 
274  return result;
275 }
int n_members
Definition: catcache.h:149
#define GIST_FETCH_PROC
Definition: gist.h:36
#define GETSTRUCT(TUP)
Definition: htup_details.h:631
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:217
#define AMOP_SEARCH
Definition: pg_amop.h:69
#define OIDOID
Definition: pg_type.h:328
#define GIST_EQUAL_PROC
Definition: gist.h:34
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:59
int errcode(int sqlerrcode)
Definition: elog.c:575
#define INFO
Definition: elog.h:33
char * format_operator(Oid operator_oid)
Definition: regproc.c:902
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:530
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1110
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:141
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1672
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:150
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:150
#define GIST_PICKSPLIT_PROC
Definition: gist.h:33
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:43
#define GIST_COMPRESS_PROC
Definition: gist.h:30
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:41
#define INT2OID
Definition: pg_type.h:308
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:186
#define ereport(elevel, rest)
Definition: elog.h:122
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:44
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:990
#define INTERNALOID
Definition: pg_type.h:686
#define GIST_CONSISTENT_PROC
Definition: gist.h:28
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:365
#define GIST_UNION_PROC
Definition: gist.h:29
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:226
#define lfirst(lc)
Definition: pg_list.h:106
Oid get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
Definition: lsyscache.c:744
#define GIST_PENALTY_PROC
Definition: gist.h:32
#define FLOAT8OID
Definition: pg_type.h:411
#define GIST_DISTANCE_PROC
Definition: gist.h:35
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:194
#define BOOLOID
Definition: pg_type.h:288
#define GISTNProcs
Definition: gist.h:37
bool gistvalidate(Oid opclassoid)
Definition: gistvalidate.c:33
int errmsg(const char *fmt,...)
Definition: elog.c:797
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:77
int i
#define NameStr(name)
Definition: c.h:494
HeapTupleData tuple
Definition: catcache.h:111
#define GIST_DECOMPRESS_PROC
Definition: gist.h:31
#define elog
Definition: elog.h:218
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:68
Definition: pg_list.h:45