1/*-------------------------------------------------------------------------
2 *
3 * hashvalidate.c
4 * Opclass validator for hash.
5 *
6 * Portions Copyright (c) 1996-2019, 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
32static 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 */
43bool
44hashvalidate(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 */
267static bool
268check_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 == XIDOID || argtype == CIDOID))
315 /* okay, allowed use of hashint4() */ ;
316 else if ((funcid == F_TIMESTAMP_HASH ||
317 funcid == F_TIMESTAMP_HASH_EXTENDED) &&
318 argtype == TIMESTAMPTZOID)
319 /* okay, allowed use of timestamp_hash() */ ;
320 else if ((funcid == F_HASHCHAR || funcid == F_HASHCHAREXTENDED) &&
321 argtype == BOOLOID)
322 /* okay, allowed use of hashchar() */ ;
323 else if ((funcid == F_HASHVARLENA || funcid == F_HASHVARLENAEXTENDED) &&
324 argtype == BYTEAOID)
325 /* okay, allowed use of hashvarlena() */ ;
326 else
327 result = false;
328 }
329
330 /* If function takes a second argument, it must be for a 64-bit salt. */
331 if (nargs == 2 && procform->proargtypes.values[1] != INT8OID)
332 result = false;
333
334 ReleaseSysCache(tp);
335 return result;
336}
337