PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
hashvalidate.c
Go to the documentation of this file.
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, Oid restype, 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 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("hash operator family \"%s\" contains support procedure %s with cross-type registration",
100  opfamilyname,
101  format_procedure(procform->amproc))));
102  result = false;
103  }
104 
105  /* Check procedure numbers and function signatures */
106  switch (procform->amprocnum)
107  {
108  case HASHPROC:
109  if (!check_hash_func_signature(procform->amproc, INT4OID,
110  procform->amproclefttype))
111  {
112  ereport(INFO,
113  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
114  errmsg("hash operator family \"%s\" contains function %s with wrong signature for support number %d",
115  opfamilyname,
116  format_procedure(procform->amproc),
117  procform->amprocnum)));
118  result = false;
119  }
120  else
121  {
122  /* Remember which types we can hash */
123  hashabletypes =
124  list_append_unique_oid(hashabletypes,
125  procform->amproclefttype);
126  }
127  break;
128  default:
129  ereport(INFO,
130  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
131  errmsg("hash operator family \"%s\" contains function %s with invalid support number %d",
132  opfamilyname,
133  format_procedure(procform->amproc),
134  procform->amprocnum)));
135  result = false;
136  break;
137  }
138  }
139 
140  /* Check individual operators */
141  for (i = 0; i < oprlist->n_members; i++)
142  {
143  HeapTuple oprtup = &oprlist->members[i]->tuple;
144  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
145 
146  /* Check that only allowed strategy numbers exist */
147  if (oprform->amopstrategy < 1 ||
148  oprform->amopstrategy > HTMaxStrategyNumber)
149  {
150  ereport(INFO,
151  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
152  errmsg("hash operator family \"%s\" contains operator %s with invalid strategy number %d",
153  opfamilyname,
154  format_operator(oprform->amopopr),
155  oprform->amopstrategy)));
156  result = false;
157  }
158 
159  /* hash doesn't support ORDER BY operators */
160  if (oprform->amoppurpose != AMOP_SEARCH ||
161  OidIsValid(oprform->amopsortfamily))
162  {
163  ereport(INFO,
164  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
165  errmsg("hash operator family \"%s\" contains invalid ORDER BY specification for operator %s",
166  opfamilyname,
167  format_operator(oprform->amopopr))));
168  result = false;
169  }
170 
171  /* Check operator signature --- same for all hash strategies */
172  if (!check_amop_signature(oprform->amopopr, BOOLOID,
173  oprform->amoplefttype,
174  oprform->amoprighttype))
175  {
176  ereport(INFO,
177  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
178  errmsg("hash operator family \"%s\" contains operator %s with wrong signature",
179  opfamilyname,
180  format_operator(oprform->amopopr))));
181  result = false;
182  }
183 
184  /* There should be relevant hash procedures for each datatype */
185  if (!list_member_oid(hashabletypes, oprform->amoplefttype) ||
186  !list_member_oid(hashabletypes, oprform->amoprighttype))
187  {
188  ereport(INFO,
189  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
190  errmsg("hash operator family \"%s\" lacks support function for operator %s",
191  opfamilyname,
192  format_operator(oprform->amopopr))));
193  result = false;
194  }
195  }
196 
197  /* Now check for inconsistent groups of operators/functions */
198  grouplist = identify_opfamily_groups(oprlist, proclist);
199  opclassgroup = NULL;
200  foreach(lc, grouplist)
201  {
202  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
203 
204  /* Remember the group exactly matching the test opclass */
205  if (thisgroup->lefttype == opcintype &&
206  thisgroup->righttype == opcintype)
207  opclassgroup = thisgroup;
208 
209  /*
210  * Complain if there seems to be an incomplete set of operators for
211  * this datatype pair (implying that we have a hash function but no
212  * operator).
213  */
214  if (thisgroup->operatorset != (1 << HTEqualStrategyNumber))
215  {
216  ereport(INFO,
217  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
218  errmsg("hash operator family \"%s\" is missing operator(s) for types %s and %s",
219  opfamilyname,
220  format_type_be(thisgroup->lefttype),
221  format_type_be(thisgroup->righttype))));
222  result = false;
223  }
224  }
225 
226  /* Check that the originally-named opclass is supported */
227  /* (if group is there, we already checked it adequately above) */
228  if (!opclassgroup)
229  {
230  ereport(INFO,
231  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
232  errmsg("hash operator class \"%s\" is missing operator(s)",
233  opclassname)));
234  result = false;
235  }
236 
237  /*
238  * Complain if the opfamily doesn't have entries for all possible
239  * combinations of its supported datatypes. While missing cross-type
240  * operators are not fatal, it seems reasonable to insist that all
241  * built-in hash opfamilies be complete.
242  */
243  if (list_length(grouplist) !=
244  list_length(hashabletypes) * list_length(hashabletypes))
245  {
246  ereport(INFO,
247  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
248  errmsg("hash operator family \"%s\" is missing cross-type operator(s)",
249  opfamilyname)));
250  result = false;
251  }
252 
253  ReleaseCatCacheList(proclist);
254  ReleaseCatCacheList(oprlist);
255  ReleaseSysCache(familytup);
256  ReleaseSysCache(classtup);
257 
258  return result;
259 }
260 
261 
262 /*
263  * We need a custom version of check_amproc_signature because of assorted
264  * hacks in the core hash opclass definitions.
265  */
266 static bool
267 check_hash_func_signature(Oid funcid, Oid restype, Oid argtype)
268 {
269  bool result = true;
270  HeapTuple tp;
271  Form_pg_proc procform;
272 
274  if (!HeapTupleIsValid(tp))
275  elog(ERROR, "cache lookup failed for function %u", funcid);
276  procform = (Form_pg_proc) GETSTRUCT(tp);
277 
278  if (procform->prorettype != restype || procform->proretset ||
279  procform->pronargs != 1)
280  result = false;
281 
282  if (!IsBinaryCoercible(argtype, procform->proargtypes.values[0]))
283  {
284  /*
285  * Some of the built-in hash opclasses cheat by using hash functions
286  * that are different from but physically compatible with the opclass
287  * datatype. In some of these cases, even a "binary coercible" check
288  * fails because there's no relevant cast. For the moment, fix it by
289  * having a whitelist of allowed cases. Test the specific function
290  * identity, not just its input type, because hashvarlena() takes
291  * INTERNAL and allowing any such function seems too scary.
292  */
293  if (funcid == F_HASHINT4 &&
294  (argtype == DATEOID ||
295  argtype == ABSTIMEOID || argtype == RELTIMEOID ||
296  argtype == XIDOID || argtype == CIDOID))
297  /* okay, allowed use of hashint4() */ ;
298  else if (funcid == F_TIMESTAMP_HASH &&
299  argtype == TIMESTAMPTZOID)
300  /* okay, allowed use of timestamp_hash() */ ;
301  else if (funcid == F_HASHCHAR &&
302  argtype == BOOLOID)
303  /* okay, allowed use of hashchar() */ ;
304  else if (funcid == F_HASHVARLENA &&
305  argtype == BYTEAOID)
306  /* okay, allowed use of hashvarlena() */ ;
307  else
308  result = false;
309  }
310 
311  ReleaseSysCache(tp);
312  return result;
313 }
#define TIMESTAMPTZOID
Definition: pg_type.h:521
#define NIL
Definition: pg_list.h:69
#define DATEOID
Definition: pg_type.h:507
int n_members
Definition: catcache.h:154
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:999
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define AMOP_SEARCH
Definition: pg_amop.h:69
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:59
#define HTEqualStrategyNumber
Definition: hash.h:273
#define INT4OID
Definition: pg_type.h:316
int errcode(int sqlerrcode)
Definition: elog.c:575
#define INFO
Definition: elog.h:33
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
return result
Definition: formatting.c:1618
char * format_operator(Oid operator_oid)
Definition: regproc.c:904
#define XIDOID
Definition: pg_type.h:336
#define HTMaxStrategyNumber
Definition: hash.h:274
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:538
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:152
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1665
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:155
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:41
static bool check_hash_func_signature(Oid funcid, Oid restype, Oid argtype)
Definition: hashvalidate.c:267
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:197
#define ereport(elevel, rest)
Definition: elog.h:122
bool IsBinaryCoercible(Oid srctype, Oid targettype)
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:44
#define CIDOID
Definition: pg_type.h:340
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1116
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:83
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:367
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:505
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:194
#define BOOLOID
Definition: pg_type.h:288
#define BYTEAOID
Definition: pg_type.h:292
bool hashvalidate(Oid opclassoid)
Definition: hashvalidate.c:44
int errmsg(const char *fmt,...)
Definition: elog.c:797
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:77
#define HASHPROC
Definition: hash.h:281
int i
#define NameStr(name)
Definition: c.h:499
HeapTupleData tuple
Definition: catcache.h:116
#define elog
Definition: elog.h:219
#define ABSTIMEOID
Definition: pg_type.h:418
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:68
Definition: pg_list.h:45
#define RELTIMEOID
Definition: pg_type.h:421