PostgreSQL Source Code  git master
nbtvalidate.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * nbtvalidate.c
4  * Opclass validator for btree.
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/nbtree/nbtvalidate.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/amvalidate.h"
17 #include "access/htup_details.h"
18 #include "access/nbtree.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 /*
30  * Validator for a btree opclass.
31  *
32  * Some of the checks done here cover the whole opfamily, and therefore are
33  * redundant when checking each opclass in a family. But they don't run long
34  * enough to be much of a problem, so we accept the duplication rather than
35  * complicate the amvalidate API.
36  */
37 bool
38 btvalidate(Oid opclassoid)
39 {
40  bool result = true;
41  HeapTuple classtup;
42  Form_pg_opclass classform;
43  Oid opfamilyoid;
44  Oid opcintype;
45  char *opclassname;
46  HeapTuple familytup;
47  Form_pg_opfamily familyform;
48  char *opfamilyname;
49  CatCList *proclist,
50  *oprlist;
51  List *grouplist;
52  OpFamilyOpFuncGroup *opclassgroup;
53  List *familytypes;
54  int usefulgroups;
55  int i;
56  ListCell *lc;
57 
58  /* Fetch opclass information */
59  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
60  if (!HeapTupleIsValid(classtup))
61  elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
62  classform = (Form_pg_opclass) GETSTRUCT(classtup);
63 
64  opfamilyoid = classform->opcfamily;
65  opcintype = classform->opcintype;
66  opclassname = NameStr(classform->opcname);
67 
68  /* Fetch opfamily information */
69  familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
70  if (!HeapTupleIsValid(familytup))
71  elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
72  familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
73 
74  opfamilyname = NameStr(familyform->opfname);
75 
76  /* Fetch all operators and support functions of the opfamily */
77  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
78  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
79 
80  /* Check individual support functions */
81  for (i = 0; i < proclist->n_members; i++)
82  {
83  HeapTuple proctup = &proclist->members[i]->tuple;
84  Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
85  bool ok;
86 
87  /* Check procedure numbers and function signatures */
88  switch (procform->amprocnum)
89  {
90  case BTORDER_PROC:
91  ok = check_amproc_signature(procform->amproc, INT4OID, true,
92  2, 2, procform->amproclefttype,
93  procform->amprocrighttype);
94  break;
95  case BTSORTSUPPORT_PROC:
96  ok = check_amproc_signature(procform->amproc, VOIDOID, true,
97  1, 1, INTERNALOID);
98  break;
99  case BTINRANGE_PROC:
100  ok = check_amproc_signature(procform->amproc, BOOLOID, true,
101  5, 5,
102  procform->amproclefttype,
103  procform->amproclefttype,
104  procform->amprocrighttype,
105  BOOLOID, BOOLOID);
106  break;
107  default:
108  ereport(INFO,
109  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
110  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
111  opfamilyname, "btree",
112  format_procedure(procform->amproc),
113  procform->amprocnum)));
114  result = false;
115  continue; /* don't want additional message */
116  }
117 
118  if (!ok)
119  {
120  ereport(INFO,
121  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
122  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
123  opfamilyname, "btree",
124  format_procedure(procform->amproc),
125  procform->amprocnum)));
126  result = false;
127  }
128  }
129 
130  /* Check individual operators */
131  for (i = 0; i < oprlist->n_members; i++)
132  {
133  HeapTuple oprtup = &oprlist->members[i]->tuple;
134  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
135 
136  /* Check that only allowed strategy numbers exist */
137  if (oprform->amopstrategy < 1 ||
138  oprform->amopstrategy > BTMaxStrategyNumber)
139  {
140  ereport(INFO,
141  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
142  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
143  opfamilyname, "btree",
144  format_operator(oprform->amopopr),
145  oprform->amopstrategy)));
146  result = false;
147  }
148 
149  /* btree doesn't support ORDER BY operators */
150  if (oprform->amoppurpose != AMOP_SEARCH ||
151  OidIsValid(oprform->amopsortfamily))
152  {
153  ereport(INFO,
154  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
155  errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
156  opfamilyname, "btree",
157  format_operator(oprform->amopopr))));
158  result = false;
159  }
160 
161  /* Check operator signature --- same for all btree strategies */
162  if (!check_amop_signature(oprform->amopopr, BOOLOID,
163  oprform->amoplefttype,
164  oprform->amoprighttype))
165  {
166  ereport(INFO,
167  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
168  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
169  opfamilyname, "btree",
170  format_operator(oprform->amopopr))));
171  result = false;
172  }
173  }
174 
175  /* Now check for inconsistent groups of operators/functions */
176  grouplist = identify_opfamily_groups(oprlist, proclist);
177  usefulgroups = 0;
178  opclassgroup = NULL;
179  familytypes = NIL;
180  foreach(lc, grouplist)
181  {
182  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
183 
184  /*
185  * It is possible for an in_range support function to have a RHS type
186  * that is otherwise irrelevant to the opfamily --- for instance, SQL
187  * requires the datetime_ops opclass to have range support with an
188  * interval offset. So, if this group appears to contain only an
189  * in_range function, ignore it: it doesn't represent a pair of
190  * supported types.
191  */
192  if (thisgroup->operatorset == 0 &&
193  thisgroup->functionset == (1 << BTINRANGE_PROC))
194  continue;
195 
196  /* Else count it as a relevant group */
197  usefulgroups++;
198 
199  /* Remember the group exactly matching the test opclass */
200  if (thisgroup->lefttype == opcintype &&
201  thisgroup->righttype == opcintype)
202  opclassgroup = thisgroup;
203 
204  /*
205  * Identify all distinct data types handled in this opfamily. This
206  * implementation is O(N^2), but there aren't likely to be enough
207  * types in the family for it to matter.
208  */
209  familytypes = list_append_unique_oid(familytypes, thisgroup->lefttype);
210  familytypes = list_append_unique_oid(familytypes, thisgroup->righttype);
211 
212  /*
213  * Complain if there seems to be an incomplete set of either operators
214  * or support functions for this datatype pair. The only things
215  * considered optional are the sortsupport and in_range functions.
216  */
217  if (thisgroup->operatorset !=
218  ((1 << BTLessStrategyNumber) |
220  (1 << BTEqualStrategyNumber) |
222  (1 << BTGreaterStrategyNumber)))
223  {
224  ereport(INFO,
225  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
226  errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
227  opfamilyname, "btree",
228  format_type_be(thisgroup->lefttype),
229  format_type_be(thisgroup->righttype))));
230  result = false;
231  }
232  if ((thisgroup->functionset & (1 << BTORDER_PROC)) == 0)
233  {
234  ereport(INFO,
235  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
236  errmsg("operator family \"%s\" of access method %s is missing support function for types %s and %s",
237  opfamilyname, "btree",
238  format_type_be(thisgroup->lefttype),
239  format_type_be(thisgroup->righttype))));
240  result = false;
241  }
242  }
243 
244  /* Check that the originally-named opclass is supported */
245  /* (if group is there, we already checked it adequately above) */
246  if (!opclassgroup)
247  {
248  ereport(INFO,
249  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
250  errmsg("operator class \"%s\" of access method %s is missing operator(s)",
251  opclassname, "btree")));
252  result = false;
253  }
254 
255  /*
256  * Complain if the opfamily doesn't have entries for all possible
257  * combinations of its supported datatypes. While missing cross-type
258  * operators are not fatal, they do limit the planner's ability to derive
259  * additional qual clauses from equivalence classes, so it seems
260  * reasonable to insist that all built-in btree opfamilies be complete.
261  */
262  if (usefulgroups != (list_length(familytypes) * list_length(familytypes)))
263  {
264  ereport(INFO,
265  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
266  errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
267  opfamilyname, "btree")));
268  result = false;
269  }
270 
271  ReleaseCatCacheList(proclist);
272  ReleaseCatCacheList(oprlist);
273  ReleaseSysCache(familytup);
274  ReleaseSysCache(classtup);
275 
276  return result;
277 }
#define NIL
Definition: pg_list.h:69
int n_members
Definition: catcache.h:176
#define BTORDER_PROC
Definition: nbtree.h:276
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:999
#define GETSTRUCT(TUP)
Definition: htup_details.h:673
#define BTSORTSUPPORT_PROC
Definition: nbtree.h:277
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
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:605
bool btvalidate(Oid opclassoid)
Definition: nbtvalidate.c:38
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
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 BTINRANGE_PROC
Definition: nbtree.h:278
#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
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
static int list_length(const List *l)
Definition: pg_list.h:89
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:194
int errmsg(const char *fmt,...)
Definition: elog.c:797
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:86
int i
#define NameStr(name)
Definition: c.h:576
#define BTMaxStrategyNumber
Definition: stratnum.h:35
HeapTupleData tuple
Definition: catcache.h:121
#define elog
Definition: elog.h:219
#define BTLessStrategyNumber
Definition: stratnum.h:29
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:81
Definition: pg_list.h:45
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32