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