Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * hashvalidate.c
4 : * Opclass validator for hash.
5 : *
6 : * Portions Copyright (c) 1996-2017, 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
44 39 : hashvalidate(Oid opclassoid)
45 : {
46 39 : 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 39 : List *hashabletypes = NIL;
60 : int i;
61 : ListCell *lc;
62 :
63 : /* Fetch opclass information */
64 39 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
65 39 : if (!HeapTupleIsValid(classtup))
66 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
67 39 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
68 :
69 39 : opfamilyoid = classform->opcfamily;
70 39 : opcintype = classform->opcintype;
71 39 : opclassname = NameStr(classform->opcname);
72 :
73 : /* Fetch opfamily information */
74 39 : familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
75 39 : if (!HeapTupleIsValid(familytup))
76 0 : elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
77 39 : familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
78 :
79 39 : opfamilyname = NameStr(familyform->opfname);
80 :
81 : /* Fetch all operators and support functions of the opfamily */
82 39 : oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
83 39 : proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
84 :
85 : /* Check individual support functions */
86 133 : for (i = 0; i < proclist->n_members; i++)
87 : {
88 94 : HeapTuple proctup = &proclist->members[i]->tuple;
89 94 : 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 94 : if (procform->amproclefttype != procform->amprocrighttype)
96 : {
97 0 : ereport(INFO,
98 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
99 : errmsg("operator family \"%s\" of access method %s contains support procedure %s with different left and right input types",
100 : opfamilyname, "hash",
101 : format_procedure(procform->amproc))));
102 0 : result = false;
103 : }
104 :
105 : /* Check procedure numbers and function signatures */
106 94 : switch (procform->amprocnum)
107 : {
108 : case HASHSTANDARD_PROC:
109 : case HASHEXTENDED_PROC:
110 94 : if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
111 : procform->amproclefttype))
112 : {
113 0 : 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 0 : result = false;
120 : }
121 : else
122 : {
123 : /* Remember which types we can hash */
124 94 : hashabletypes =
125 94 : list_append_unique_oid(hashabletypes,
126 : procform->amproclefttype);
127 : }
128 94 : break;
129 : default:
130 0 : 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 0 : result = false;
137 0 : break;
138 : }
139 : }
140 :
141 : /* Check individual operators */
142 108 : for (i = 0; i < oprlist->n_members; i++)
143 : {
144 69 : HeapTuple oprtup = &oprlist->members[i]->tuple;
145 69 : Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
146 :
147 : /* Check that only allowed strategy numbers exist */
148 138 : if (oprform->amopstrategy < 1 ||
149 69 : oprform->amopstrategy > HTMaxStrategyNumber)
150 : {
151 0 : 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 0 : result = false;
158 : }
159 :
160 : /* hash doesn't support ORDER BY operators */
161 138 : if (oprform->amoppurpose != AMOP_SEARCH ||
162 69 : OidIsValid(oprform->amopsortfamily))
163 : {
164 0 : 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 0 : result = false;
170 : }
171 :
172 : /* Check operator signature --- same for all hash strategies */
173 69 : if (!check_amop_signature(oprform->amopopr, BOOLOID,
174 : oprform->amoplefttype,
175 : oprform->amoprighttype))
176 : {
177 0 : 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 0 : result = false;
183 : }
184 :
185 : /* There should be relevant hash procedures for each datatype */
186 138 : if (!list_member_oid(hashabletypes, oprform->amoplefttype) ||
187 69 : !list_member_oid(hashabletypes, oprform->amoprighttype))
188 : {
189 0 : 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 0 : result = false;
195 : }
196 : }
197 :
198 : /* Now check for inconsistent groups of operators/functions */
199 39 : grouplist = identify_opfamily_groups(oprlist, proclist);
200 39 : opclassgroup = NULL;
201 108 : foreach(lc, grouplist)
202 : {
203 69 : OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
204 :
205 : /* Remember the group exactly matching the test opclass */
206 116 : if (thisgroup->lefttype == opcintype &&
207 47 : thisgroup->righttype == opcintype)
208 39 : 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 69 : if (thisgroup->operatorset != (1 << HTEqualStrategyNumber))
216 : {
217 0 : 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 0 : 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 39 : if (!opclassgroup)
230 : {
231 0 : 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 0 : 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 78 : if (list_length(grouplist) !=
245 39 : list_length(hashabletypes) * list_length(hashabletypes))
246 : {
247 0 : 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 0 : result = false;
252 : }
253 :
254 39 : ReleaseCatCacheList(proclist);
255 39 : ReleaseCatCacheList(oprlist);
256 39 : ReleaseSysCache(familytup);
257 39 : ReleaseSysCache(classtup);
258 :
259 39 : 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
268 94 : check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
269 : {
270 94 : bool result = true;
271 : Oid restype;
272 : int16 nargs;
273 : HeapTuple tp;
274 : Form_pg_proc procform;
275 :
276 94 : switch (amprocnum)
277 : {
278 : case HASHSTANDARD_PROC:
279 47 : restype = INT4OID;
280 47 : nargs = 1;
281 47 : break;
282 :
283 : case HASHEXTENDED_PROC:
284 47 : restype = INT8OID;
285 47 : nargs = 2;
286 47 : break;
287 :
288 : default:
289 0 : elog(ERROR, "invalid amprocnum");
290 : }
291 :
292 94 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
293 94 : if (!HeapTupleIsValid(tp))
294 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
295 94 : procform = (Form_pg_proc) GETSTRUCT(tp);
296 :
297 188 : if (procform->prorettype != restype || procform->proretset ||
298 94 : procform->pronargs != nargs)
299 0 : result = false;
300 :
301 94 : 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 26 : if ((funcid == F_HASHINT4 || funcid == F_HASHINT4EXTENDED) &&
313 8 : (argtype == DATEOID ||
314 6 : argtype == ABSTIMEOID || argtype == RELTIMEOID ||
315 2 : argtype == XIDOID || argtype == CIDOID))
316 : /* okay, allowed use of hashint4() */ ;
317 6 : else if ((funcid == F_TIMESTAMP_HASH ||
318 2 : funcid == F_TIMESTAMP_HASH_EXTENDED) &&
319 : argtype == TIMESTAMPTZOID)
320 : /* okay, allowed use of timestamp_hash() */ ;
321 4 : else if ((funcid == F_HASHCHAR || funcid == F_HASHCHAREXTENDED) &&
322 : argtype == BOOLOID)
323 : /* okay, allowed use of hashchar() */ ;
324 2 : else if ((funcid == F_HASHVARLENA || funcid == F_HASHVARLENAEXTENDED) &&
325 : argtype == BYTEAOID)
326 : /* okay, allowed use of hashvarlena() */ ;
327 : else
328 0 : result = false;
329 : }
330 :
331 : /* If function takes a second argument, it must be for a 64-bit salt. */
332 94 : if (nargs == 2 && procform->proargtypes.values[1] != INT8OID)
333 0 : result = false;
334 :
335 94 : ReleaseSysCache(tp);
336 94 : return result;
337 : }
|