1 /*-------------------------------------------------------------------------
2  *
3  * nbtvalidate.c
4  *	  Opclass validator for btree.
5  *
6  * Portions Copyright (c) 1996-2021, 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 "access/xact.h"
20 #include "catalog/pg_am.h"
21 #include "catalog/pg_amop.h"
22 #include "catalog/pg_amproc.h"
23 #include "catalog/pg_opclass.h"
24 #include "catalog/pg_opfamily.h"
25 #include "catalog/pg_type.h"
26 #include "utils/builtins.h"
27 #include "utils/lsyscache.h"
28 #include "utils/regproc.h"
29 #include "utils/syscache.h"
30 
31 
32 /*
33  * Validator for a btree opclass.
34  *
35  * Some of the checks done here cover the whole opfamily, and therefore are
36  * redundant when checking each opclass in a family.  But they don't run long
37  * enough to be much of a problem, so we accept the duplication rather than
38  * complicate the amvalidate API.
39  */
40 bool
btvalidate(Oid opclassoid)41 btvalidate(Oid opclassoid)
42 {
43 	bool		result = true;
44 	HeapTuple	classtup;
45 	Form_pg_opclass classform;
46 	Oid			opfamilyoid;
47 	Oid			opcintype;
48 	char	   *opclassname;
49 	HeapTuple	familytup;
50 	Form_pg_opfamily familyform;
51 	char	   *opfamilyname;
52 	CatCList   *proclist,
53 			   *oprlist;
54 	List	   *grouplist;
55 	OpFamilyOpFuncGroup *opclassgroup;
56 	List	   *familytypes;
57 	int			usefulgroups;
58 	int			i;
59 	ListCell   *lc;
60 
61 	/* Fetch opclass information */
62 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
63 	if (!HeapTupleIsValid(classtup))
64 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
65 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
66 
67 	opfamilyoid = classform->opcfamily;
68 	opcintype = classform->opcintype;
69 	opclassname = NameStr(classform->opcname);
70 
71 	/* Fetch opfamily information */
72 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
73 	if (!HeapTupleIsValid(familytup))
74 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
75 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
76 
77 	opfamilyname = NameStr(familyform->opfname);
78 
79 	/* Fetch all operators and support functions of the opfamily */
80 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
81 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
82 
83 	/* Check individual support functions */
84 	for (i = 0; i < proclist->n_members; i++)
85 	{
86 		HeapTuple	proctup = &proclist->members[i]->tuple;
87 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
88 		bool		ok;
89 
90 		/* Check procedure numbers and function signatures */
91 		switch (procform->amprocnum)
92 		{
93 			case BTORDER_PROC:
94 				ok = check_amproc_signature(procform->amproc, INT4OID, true,
95 											2, 2, procform->amproclefttype,
96 											procform->amprocrighttype);
97 				break;
98 			case BTSORTSUPPORT_PROC:
99 				ok = check_amproc_signature(procform->amproc, VOIDOID, true,
100 											1, 1, INTERNALOID);
101 				break;
102 			case BTINRANGE_PROC:
103 				ok = check_amproc_signature(procform->amproc, BOOLOID, true,
104 											5, 5,
105 											procform->amproclefttype,
106 											procform->amproclefttype,
107 											procform->amprocrighttype,
108 											BOOLOID, BOOLOID);
109 				break;
110 			case BTEQUALIMAGE_PROC:
111 				ok = check_amproc_signature(procform->amproc, BOOLOID, true,
112 											1, 1, OIDOID);
113 				break;
114 			case BTOPTIONS_PROC:
115 				ok = check_amoptsproc_signature(procform->amproc);
116 				break;
117 			default:
118 				ereport(INFO,
119 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
120 						 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
121 								opfamilyname, "btree",
122 								format_procedure(procform->amproc),
123 								procform->amprocnum)));
124 				result = false;
125 				continue;		/* don't want additional message */
126 		}
127 
128 		if (!ok)
129 		{
130 			ereport(INFO,
131 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
132 					 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
133 							opfamilyname, "btree",
134 							format_procedure(procform->amproc),
135 							procform->amprocnum)));
136 			result = false;
137 		}
138 	}
139 
140 	/* Check individual operators */
141 	for (i = 0; i < oprlist->n_members; i++)
142 	{
143 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
144 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
145 
146 		/* Check that only allowed strategy numbers exist */
147 		if (oprform->amopstrategy < 1 ||
148 			oprform->amopstrategy > BTMaxStrategyNumber)
149 		{
150 			ereport(INFO,
151 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
152 					 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
153 							opfamilyname, "btree",
154 							format_operator(oprform->amopopr),
155 							oprform->amopstrategy)));
156 			result = false;
157 		}
158 
159 		/* btree doesn't support ORDER BY operators */
160 		if (oprform->amoppurpose != AMOP_SEARCH ||
161 			OidIsValid(oprform->amopsortfamily))
162 		{
163 			ereport(INFO,
164 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
165 					 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
166 							opfamilyname, "btree",
167 							format_operator(oprform->amopopr))));
168 			result = false;
169 		}
170 
171 		/* Check operator signature --- same for all btree strategies */
172 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
173 								  oprform->amoplefttype,
174 								  oprform->amoprighttype))
175 		{
176 			ereport(INFO,
177 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
178 					 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
179 							opfamilyname, "btree",
180 							format_operator(oprform->amopopr))));
181 			result = false;
182 		}
183 	}
184 
185 	/* Now check for inconsistent groups of operators/functions */
186 	grouplist = identify_opfamily_groups(oprlist, proclist);
187 	usefulgroups = 0;
188 	opclassgroup = NULL;
189 	familytypes = NIL;
190 	foreach(lc, grouplist)
191 	{
192 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
193 
194 		/*
195 		 * It is possible for an in_range support function to have a RHS type
196 		 * that is otherwise irrelevant to the opfamily --- for instance, SQL
197 		 * requires the datetime_ops opclass to have range support with an
198 		 * interval offset.  So, if this group appears to contain only an
199 		 * in_range function, ignore it: it doesn't represent a pair of
200 		 * supported types.
201 		 */
202 		if (thisgroup->operatorset == 0 &&
203 			thisgroup->functionset == (1 << BTINRANGE_PROC))
204 			continue;
205 
206 		/* Else count it as a relevant group */
207 		usefulgroups++;
208 
209 		/* Remember the group exactly matching the test opclass */
210 		if (thisgroup->lefttype == opcintype &&
211 			thisgroup->righttype == opcintype)
212 			opclassgroup = thisgroup;
213 
214 		/*
215 		 * Identify all distinct data types handled in this opfamily.  This
216 		 * implementation is O(N^2), but there aren't likely to be enough
217 		 * types in the family for it to matter.
218 		 */
219 		familytypes = list_append_unique_oid(familytypes, thisgroup->lefttype);
220 		familytypes = list_append_unique_oid(familytypes, thisgroup->righttype);
221 
222 		/*
223 		 * Complain if there seems to be an incomplete set of either operators
224 		 * or support functions for this datatype pair.  The sortsupport,
225 		 * in_range, and equalimage functions are considered optional.
226 		 */
227 		if (thisgroup->operatorset !=
228 			((1 << BTLessStrategyNumber) |
229 			 (1 << BTLessEqualStrategyNumber) |
230 			 (1 << BTEqualStrategyNumber) |
231 			 (1 << BTGreaterEqualStrategyNumber) |
232 			 (1 << BTGreaterStrategyNumber)))
233 		{
234 			ereport(INFO,
235 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
236 					 errmsg("operator family \"%s\" of access method %s is missing operator(s) 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 		if ((thisgroup->functionset & (1 << BTORDER_PROC)) == 0)
243 		{
244 			ereport(INFO,
245 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
246 					 errmsg("operator family \"%s\" of access method %s is missing support function for types %s and %s",
247 							opfamilyname, "btree",
248 							format_type_be(thisgroup->lefttype),
249 							format_type_be(thisgroup->righttype))));
250 			result = false;
251 		}
252 	}
253 
254 	/* Check that the originally-named opclass is supported */
255 	/* (if group is there, we already checked it adequately above) */
256 	if (!opclassgroup)
257 	{
258 		ereport(INFO,
259 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
260 				 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
261 						opclassname, "btree")));
262 		result = false;
263 	}
264 
265 	/*
266 	 * Complain if the opfamily doesn't have entries for all possible
267 	 * combinations of its supported datatypes.  While missing cross-type
268 	 * operators are not fatal, they do limit the planner's ability to derive
269 	 * additional qual clauses from equivalence classes, so it seems
270 	 * reasonable to insist that all built-in btree opfamilies be complete.
271 	 */
272 	if (usefulgroups != (list_length(familytypes) * list_length(familytypes)))
273 	{
274 		ereport(INFO,
275 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
276 				 errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
277 						opfamilyname, "btree")));
278 		result = false;
279 	}
280 
281 	ReleaseCatCacheList(proclist);
282 	ReleaseCatCacheList(oprlist);
283 	ReleaseSysCache(familytup);
284 	ReleaseSysCache(classtup);
285 
286 	return result;
287 }
288 
289 /*
290  * Prechecking function for adding operators/functions to a btree opfamily.
291  */
292 void
btadjustmembers(Oid opfamilyoid,Oid opclassoid,List * operators,List * functions)293 btadjustmembers(Oid opfamilyoid,
294 				Oid opclassoid,
295 				List *operators,
296 				List *functions)
297 {
298 	Oid			opcintype;
299 	ListCell   *lc;
300 
301 	/*
302 	 * Btree operators and comparison support functions are always "loose"
303 	 * members of the opfamily if they are cross-type.  If they are not
304 	 * cross-type, we prefer to tie them to the appropriate opclass ... but if
305 	 * the user hasn't created one, we can't do that, and must fall back to
306 	 * using the opfamily dependency.  (We mustn't force creation of an
307 	 * opclass in such a case, as leaving an incomplete opclass laying about
308 	 * would be bad.  Throwing an error is another undesirable alternative.)
309 	 *
310 	 * This behavior results in a bit of a dump/reload hazard, in that the
311 	 * order of restoring objects could affect what dependencies we end up
312 	 * with.  pg_dump's existing behavior will preserve the dependency choices
313 	 * in most cases, but not if a cross-type operator has been bound tightly
314 	 * into an opclass.  That's a mistake anyway, so silently "fixing" it
315 	 * isn't awful.
316 	 *
317 	 * Optional support functions are always "loose" family members.
318 	 *
319 	 * To avoid repeated lookups, we remember the most recently used opclass's
320 	 * input type.
321 	 */
322 	if (OidIsValid(opclassoid))
323 	{
324 		/* During CREATE OPERATOR CLASS, need CCI to see the pg_opclass row */
325 		CommandCounterIncrement();
326 		opcintype = get_opclass_input_type(opclassoid);
327 	}
328 	else
329 		opcintype = InvalidOid;
330 
331 	/*
332 	 * We handle operators and support functions almost identically, so rather
333 	 * than duplicate this code block, just join the lists.
334 	 */
335 	foreach(lc, list_concat_copy(operators, functions))
336 	{
337 		OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
338 
339 		if (op->is_func && op->number != BTORDER_PROC)
340 		{
341 			/* Optional support proc, so always a soft family dependency */
342 			op->ref_is_hard = false;
343 			op->ref_is_family = true;
344 			op->refobjid = opfamilyoid;
345 		}
346 		else if (op->lefttype != op->righttype)
347 		{
348 			/* Cross-type, so always a soft family dependency */
349 			op->ref_is_hard = false;
350 			op->ref_is_family = true;
351 			op->refobjid = opfamilyoid;
352 		}
353 		else
354 		{
355 			/* Not cross-type; is there a suitable opclass? */
356 			if (op->lefttype != opcintype)
357 			{
358 				/* Avoid repeating this expensive lookup, even if it fails */
359 				opcintype = op->lefttype;
360 				opclassoid = opclass_for_family_datatype(BTREE_AM_OID,
361 														 opfamilyoid,
362 														 opcintype);
363 			}
364 			if (OidIsValid(opclassoid))
365 			{
366 				/* Hard dependency on opclass */
367 				op->ref_is_hard = true;
368 				op->ref_is_family = false;
369 				op->refobjid = opclassoid;
370 			}
371 			else
372 			{
373 				/* We're stuck, so make a soft dependency on the opfamily */
374 				op->ref_is_hard = false;
375 				op->ref_is_family = true;
376 				op->refobjid = opfamilyoid;
377 			}
378 		}
379 	}
380 }
381