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  */
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 			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