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