1 /*-------------------------------------------------------------------------
2 *
3 * nbtvalidate.c
4 * Opclass validator for btree.
5 *
6 * Portions Copyright (c) 1996-2020, 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
btvalidate(Oid opclassoid)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 case BTEQUALIMAGE_PROC:
108 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
109 1, 1, OIDOID);
110 break;
111 case BTOPTIONS_PROC:
112 ok = check_amoptsproc_signature(procform->amproc);
113 break;
114 default:
115 ereport(INFO,
116 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
117 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
118 opfamilyname, "btree",
119 format_procedure(procform->amproc),
120 procform->amprocnum)));
121 result = false;
122 continue; /* don't want additional message */
123 }
124
125 if (!ok)
126 {
127 ereport(INFO,
128 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
129 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
130 opfamilyname, "btree",
131 format_procedure(procform->amproc),
132 procform->amprocnum)));
133 result = false;
134 }
135 }
136
137 /* Check individual operators */
138 for (i = 0; i < oprlist->n_members; i++)
139 {
140 HeapTuple oprtup = &oprlist->members[i]->tuple;
141 Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
142
143 /* Check that only allowed strategy numbers exist */
144 if (oprform->amopstrategy < 1 ||
145 oprform->amopstrategy > BTMaxStrategyNumber)
146 {
147 ereport(INFO,
148 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
149 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
150 opfamilyname, "btree",
151 format_operator(oprform->amopopr),
152 oprform->amopstrategy)));
153 result = false;
154 }
155
156 /* btree doesn't support ORDER BY operators */
157 if (oprform->amoppurpose != AMOP_SEARCH ||
158 OidIsValid(oprform->amopsortfamily))
159 {
160 ereport(INFO,
161 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
162 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
163 opfamilyname, "btree",
164 format_operator(oprform->amopopr))));
165 result = false;
166 }
167
168 /* Check operator signature --- same for all btree strategies */
169 if (!check_amop_signature(oprform->amopopr, BOOLOID,
170 oprform->amoplefttype,
171 oprform->amoprighttype))
172 {
173 ereport(INFO,
174 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
175 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
176 opfamilyname, "btree",
177 format_operator(oprform->amopopr))));
178 result = false;
179 }
180 }
181
182 /* Now check for inconsistent groups of operators/functions */
183 grouplist = identify_opfamily_groups(oprlist, proclist);
184 usefulgroups = 0;
185 opclassgroup = NULL;
186 familytypes = NIL;
187 foreach(lc, grouplist)
188 {
189 OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
190
191 /*
192 * It is possible for an in_range support function to have a RHS type
193 * that is otherwise irrelevant to the opfamily --- for instance, SQL
194 * requires the datetime_ops opclass to have range support with an
195 * interval offset. So, if this group appears to contain only an
196 * in_range function, ignore it: it doesn't represent a pair of
197 * supported types.
198 */
199 if (thisgroup->operatorset == 0 &&
200 thisgroup->functionset == (1 << BTINRANGE_PROC))
201 continue;
202
203 /* Else count it as a relevant group */
204 usefulgroups++;
205
206 /* Remember the group exactly matching the test opclass */
207 if (thisgroup->lefttype == opcintype &&
208 thisgroup->righttype == opcintype)
209 opclassgroup = thisgroup;
210
211 /*
212 * Identify all distinct data types handled in this opfamily. This
213 * implementation is O(N^2), but there aren't likely to be enough
214 * types in the family for it to matter.
215 */
216 familytypes = list_append_unique_oid(familytypes, thisgroup->lefttype);
217 familytypes = list_append_unique_oid(familytypes, thisgroup->righttype);
218
219 /*
220 * Complain if there seems to be an incomplete set of either operators
221 * or support functions for this datatype pair. The sortsupport,
222 * in_range, and equalimage functions are considered optional.
223 */
224 if (thisgroup->operatorset !=
225 ((1 << BTLessStrategyNumber) |
226 (1 << BTLessEqualStrategyNumber) |
227 (1 << BTEqualStrategyNumber) |
228 (1 << BTGreaterEqualStrategyNumber) |
229 (1 << BTGreaterStrategyNumber)))
230 {
231 ereport(INFO,
232 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
233 errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
234 opfamilyname, "btree",
235 format_type_be(thisgroup->lefttype),
236 format_type_be(thisgroup->righttype))));
237 result = false;
238 }
239 if ((thisgroup->functionset & (1 << BTORDER_PROC)) == 0)
240 {
241 ereport(INFO,
242 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
243 errmsg("operator family \"%s\" of access method %s is missing support function for types %s and %s",
244 opfamilyname, "btree",
245 format_type_be(thisgroup->lefttype),
246 format_type_be(thisgroup->righttype))));
247 result = false;
248 }
249 }
250
251 /* Check that the originally-named opclass is supported */
252 /* (if group is there, we already checked it adequately above) */
253 if (!opclassgroup)
254 {
255 ereport(INFO,
256 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
257 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
258 opclassname, "btree")));
259 result = false;
260 }
261
262 /*
263 * Complain if the opfamily doesn't have entries for all possible
264 * combinations of its supported datatypes. While missing cross-type
265 * operators are not fatal, they do limit the planner's ability to derive
266 * additional qual clauses from equivalence classes, so it seems
267 * reasonable to insist that all built-in btree opfamilies be complete.
268 */
269 if (usefulgroups != (list_length(familytypes) * list_length(familytypes)))
270 {
271 ereport(INFO,
272 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
273 errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
274 opfamilyname, "btree")));
275 result = false;
276 }
277
278 ReleaseCatCacheList(proclist);
279 ReleaseCatCacheList(oprlist);
280 ReleaseSysCache(familytup);
281 ReleaseSysCache(classtup);
282
283 return result;
284 }
285