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