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