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-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
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("hash opfamily %s contains support procedure %s with cross-type registration",
99  opfamilyname,
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("hash opfamily %s contains function %s with wrong signature for support number %d",
114  opfamilyname,
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("hash opfamily %s contains function %s with invalid support number %d",
131  opfamilyname,
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("hash opfamily %s contains operator %s with invalid strategy number %d",
152  opfamilyname,
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("hash opfamily %s contains invalid ORDER BY specification for operator %s",
165  opfamilyname,
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("hash opfamily %s contains operator %s with wrong signature",
178  opfamilyname,
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("hash opfamily %s lacks support function for operator %s",
190  opfamilyname,
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("hash opfamily %s is missing operator(s) for types %s and %s",
218  opfamilyname,
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("hash opclass %s is missing operator(s)",
232  opclassname)));
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("hash opfamily %s is missing cross-type operator(s)",
248  opfamilyname)));
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
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 
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 }
#define TIMESTAMPTZOID
Definition: pg_type.h:513
#define NIL
Definition: pg_list.h:69
#define DATEOID
Definition: pg_type.h:499
int n_members
Definition: catcache.h:149
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:999
#define GETSTRUCT(TUP)
Definition: htup_details.h:631
#define AMOP_SEARCH
Definition: pg_amop.h:69
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:59
#define HTEqualStrategyNumber
Definition: hash.h:233
#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
char * format_operator(Oid operator_oid)
Definition: regproc.c:902
#define XIDOID
Definition: pg_type.h:336
#define HTMaxStrategyNumber
Definition: hash.h:234
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:530
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:141
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1672
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:150
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#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:266
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:186
#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:990
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:83
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:365
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:226
#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:43
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:241
int i
#define NameStr(name)
Definition: c.h:494
HeapTupleData tuple
Definition: catcache.h:111
#define elog
Definition: elog.h:218
#define ABSTIMEOID
Definition: pg_type.h:414
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:68
Definition: pg_list.h:45
#define RELTIMEOID
Definition: pg_type.h:417