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