1 /*-------------------------------------------------------------------------
2  *
3  * hashvalidate.c
4  *	  Opclass validator for hash.
5  *
6  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *	  src/backend/access/hash/hashvalidate.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/amvalidate.h"
17 #include "access/hash.h"
18 #include "access/htup_details.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_proc.h"
24 #include "catalog/pg_type.h"
25 #include "parser/parse_coerce.h"
26 #include "utils/builtins.h"
27 #include "utils/fmgroids.h"
28 #include "utils/regproc.h"
29 #include "utils/syscache.h"
30 
31 
32 static bool check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype);
33 
34 
35 /*
36  * Validator for a hash opclass.
37  *
38  * Some of the checks done here cover the whole opfamily, and therefore are
39  * redundant when checking each opclass in a family.  But they don't run long
40  * enough to be much of a problem, so we accept the duplication rather than
41  * complicate the amvalidate API.
42  */
43 bool
hashvalidate(Oid opclassoid)44 hashvalidate(Oid opclassoid)
45 {
46 	bool		result = true;
47 	HeapTuple	classtup;
48 	Form_pg_opclass classform;
49 	Oid			opfamilyoid;
50 	Oid			opcintype;
51 	char	   *opclassname;
52 	HeapTuple	familytup;
53 	Form_pg_opfamily familyform;
54 	char	   *opfamilyname;
55 	CatCList   *proclist,
56 			   *oprlist;
57 	List	   *grouplist;
58 	OpFamilyOpFuncGroup *opclassgroup;
59 	List	   *hashabletypes = NIL;
60 	int			i;
61 	ListCell   *lc;
62 
63 	/* Fetch opclass information */
64 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
65 	if (!HeapTupleIsValid(classtup))
66 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
67 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
68 
69 	opfamilyoid = classform->opcfamily;
70 	opcintype = classform->opcintype;
71 	opclassname = NameStr(classform->opcname);
72 
73 	/* Fetch opfamily information */
74 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
75 	if (!HeapTupleIsValid(familytup))
76 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
77 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
78 
79 	opfamilyname = NameStr(familyform->opfname);
80 
81 	/* Fetch all operators and support functions of the opfamily */
82 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
83 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
84 
85 	/* Check individual support functions */
86 	for (i = 0; i < proclist->n_members; i++)
87 	{
88 		HeapTuple	proctup = &proclist->members[i]->tuple;
89 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
90 
91 		/*
92 		 * All hash functions should be registered with matching left/right
93 		 * types
94 		 */
95 		if (procform->amproclefttype != procform->amprocrighttype)
96 		{
97 			ereport(INFO,
98 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
99 					 errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
100 							opfamilyname, "hash",
101 							format_procedure(procform->amproc))));
102 			result = false;
103 		}
104 
105 		/* Check procedure numbers and function signatures */
106 		switch (procform->amprocnum)
107 		{
108 			case HASHSTANDARD_PROC:
109 			case HASHEXTENDED_PROC:
110 				if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
111 											   procform->amproclefttype))
112 				{
113 					ereport(INFO,
114 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
115 							 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
116 									opfamilyname, "hash",
117 									format_procedure(procform->amproc),
118 									procform->amprocnum)));
119 					result = false;
120 				}
121 				else
122 				{
123 					/* Remember which types we can hash */
124 					hashabletypes =
125 						list_append_unique_oid(hashabletypes,
126 											   procform->amproclefttype);
127 				}
128 				break;
129 			default:
130 				ereport(INFO,
131 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
132 						 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
133 								opfamilyname, "hash",
134 								format_procedure(procform->amproc),
135 								procform->amprocnum)));
136 				result = false;
137 				break;
138 		}
139 	}
140 
141 	/* Check individual operators */
142 	for (i = 0; i < oprlist->n_members; i++)
143 	{
144 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
145 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
146 
147 		/* Check that only allowed strategy numbers exist */
148 		if (oprform->amopstrategy < 1 ||
149 			oprform->amopstrategy > HTMaxStrategyNumber)
150 		{
151 			ereport(INFO,
152 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
153 					 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
154 							opfamilyname, "hash",
155 							format_operator(oprform->amopopr),
156 							oprform->amopstrategy)));
157 			result = false;
158 		}
159 
160 		/* hash doesn't support ORDER BY operators */
161 		if (oprform->amoppurpose != AMOP_SEARCH ||
162 			OidIsValid(oprform->amopsortfamily))
163 		{
164 			ereport(INFO,
165 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
166 					 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
167 							opfamilyname, "hash",
168 							format_operator(oprform->amopopr))));
169 			result = false;
170 		}
171 
172 		/* Check operator signature --- same for all hash strategies */
173 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
174 								  oprform->amoplefttype,
175 								  oprform->amoprighttype))
176 		{
177 			ereport(INFO,
178 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
179 					 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
180 							opfamilyname, "hash",
181 							format_operator(oprform->amopopr))));
182 			result = false;
183 		}
184 
185 		/* There should be relevant hash functions for each datatype */
186 		if (!list_member_oid(hashabletypes, oprform->amoplefttype) ||
187 			!list_member_oid(hashabletypes, oprform->amoprighttype))
188 		{
189 			ereport(INFO,
190 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
191 					 errmsg("operator family \"%s\" of access method %s lacks support function for operator %s",
192 							opfamilyname, "hash",
193 							format_operator(oprform->amopopr))));
194 			result = false;
195 		}
196 	}
197 
198 	/* Now check for inconsistent groups of operators/functions */
199 	grouplist = identify_opfamily_groups(oprlist, proclist);
200 	opclassgroup = NULL;
201 	foreach(lc, grouplist)
202 	{
203 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
204 
205 		/* Remember the group exactly matching the test opclass */
206 		if (thisgroup->lefttype == opcintype &&
207 			thisgroup->righttype == opcintype)
208 			opclassgroup = thisgroup;
209 
210 		/*
211 		 * Complain if there seems to be an incomplete set of operators for
212 		 * this datatype pair (implying that we have a hash function but no
213 		 * operator).
214 		 */
215 		if (thisgroup->operatorset != (1 << HTEqualStrategyNumber))
216 		{
217 			ereport(INFO,
218 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
219 					 errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
220 							opfamilyname, "hash",
221 							format_type_be(thisgroup->lefttype),
222 							format_type_be(thisgroup->righttype))));
223 			result = false;
224 		}
225 	}
226 
227 	/* Check that the originally-named opclass is supported */
228 	/* (if group is there, we already checked it adequately above) */
229 	if (!opclassgroup)
230 	{
231 		ereport(INFO,
232 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
233 				 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
234 						opclassname, "hash")));
235 		result = false;
236 	}
237 
238 	/*
239 	 * Complain if the opfamily doesn't have entries for all possible
240 	 * combinations of its supported datatypes.  While missing cross-type
241 	 * operators are not fatal, it seems reasonable to insist that all
242 	 * built-in hash opfamilies be complete.
243 	 */
244 	if (list_length(grouplist) !=
245 		list_length(hashabletypes) * list_length(hashabletypes))
246 	{
247 		ereport(INFO,
248 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
249 				 errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
250 						opfamilyname, "hash")));
251 		result = false;
252 	}
253 
254 	ReleaseCatCacheList(proclist);
255 	ReleaseCatCacheList(oprlist);
256 	ReleaseSysCache(familytup);
257 	ReleaseSysCache(classtup);
258 
259 	return result;
260 }
261 
262 
263 /*
264  * We need a custom version of check_amproc_signature because of assorted
265  * hacks in the core hash opclass definitions.
266  */
267 static bool
check_hash_func_signature(Oid funcid,int16 amprocnum,Oid argtype)268 check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
269 {
270 	bool		result = true;
271 	Oid			restype;
272 	int16		nargs;
273 	HeapTuple	tp;
274 	Form_pg_proc procform;
275 
276 	switch (amprocnum)
277 	{
278 		case HASHSTANDARD_PROC:
279 			restype = INT4OID;
280 			nargs = 1;
281 			break;
282 
283 		case HASHEXTENDED_PROC:
284 			restype = INT8OID;
285 			nargs = 2;
286 			break;
287 
288 		default:
289 			elog(ERROR, "invalid amprocnum");
290 	}
291 
292 	tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
293 	if (!HeapTupleIsValid(tp))
294 		elog(ERROR, "cache lookup failed for function %u", funcid);
295 	procform = (Form_pg_proc) GETSTRUCT(tp);
296 
297 	if (procform->prorettype != restype || procform->proretset ||
298 		procform->pronargs != nargs)
299 		result = false;
300 
301 	if (!IsBinaryCoercible(argtype, procform->proargtypes.values[0]))
302 	{
303 		/*
304 		 * Some of the built-in hash opclasses cheat by using hash functions
305 		 * that are different from but physically compatible with the opclass
306 		 * datatype.  In some of these cases, even a "binary coercible" check
307 		 * fails because there's no relevant cast.  For the moment, fix it by
308 		 * having a whitelist of allowed cases.  Test the specific function
309 		 * identity, not just its input type, because hashvarlena() takes
310 		 * INTERNAL and allowing any such function seems too scary.
311 		 */
312 		if ((funcid == F_HASHINT4 || funcid == F_HASHINT4EXTENDED) &&
313 			(argtype == DATEOID ||
314 			 argtype == ABSTIMEOID || argtype == RELTIMEOID ||
315 			 argtype == XIDOID || argtype == CIDOID))
316 			 /* okay, allowed use of hashint4() */ ;
317 		else if ((funcid == F_TIMESTAMP_HASH ||
318 				  funcid == F_TIMESTAMP_HASH_EXTENDED) &&
319 				 argtype == TIMESTAMPTZOID)
320 			 /* okay, allowed use of timestamp_hash() */ ;
321 		else if ((funcid == F_HASHCHAR || funcid == F_HASHCHAREXTENDED) &&
322 				 argtype == BOOLOID)
323 			 /* okay, allowed use of hashchar() */ ;
324 		else if ((funcid == F_HASHVARLENA || funcid == F_HASHVARLENAEXTENDED) &&
325 				 argtype == BYTEAOID)
326 			 /* okay, allowed use of hashvarlena() */ ;
327 		else
328 			result = false;
329 	}
330 
331 	/* If function takes a second argument, it must be for a 64-bit salt. */
332 	if (nargs == 2 && procform->proargtypes.values[1] != INT8OID)
333 		result = false;
334 
335 	ReleaseSysCache(tp);
336 	return result;
337 }
338