1/*-------------------------------------------------------------------------
2 *
3 * nbtvalidate.c
4 * Opclass validator for btree.
5 *
6 * Portions Copyright (c) 1996-2019, 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 */
37bool
38btvalidate(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) |
219 (1 << BTLessEqualStrategyNumber) |
220 (1 << BTEqualStrategyNumber) |
221 (1 << BTGreaterEqualStrategyNumber) |
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}
278