PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
hashvalidate.c File Reference
#include "postgres.h"
#include "access/amvalidate.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "parser/parse_coerce.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
Include dependency graph for hashvalidate.c:

Go to the source code of this file.

Functions

static bool check_hash_func_signature (Oid funcid, int16 amprocnum, Oid argtype)
 
bool hashvalidate (Oid opclassoid)
 

Function Documentation

static bool check_hash_func_signature ( Oid  funcid,
int16  amprocnum,
Oid  argtype 
)
static

Definition at line 268 of file hashvalidate.c.

References ABSTIMEOID, BOOLOID, BYTEAOID, CIDOID, DATEOID, elog, ERROR, GETSTRUCT, HASHEXTENDED_PROC, HASHSTANDARD_PROC, HeapTupleIsValid, INT4OID, INT8OID, IsBinaryCoercible(), ObjectIdGetDatum, PROCOID, ReleaseSysCache(), RELTIMEOID, SearchSysCache1(), TIMESTAMPTZOID, and XIDOID.

Referenced by hashvalidate().

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 
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 == ABSTIMEOID || argtype == RELTIMEOID ||
315  argtype == XIDOID || argtype == CIDOID))
316  /* okay, allowed use of hashint4() */ ;
317  else if ((funcid == F_TIMESTAMP_HASH ||
318  funcid == F_TIMESTAMP_HASH_EXTENDED) &&
319  argtype == TIMESTAMPTZOID)
320  /* okay, allowed use of timestamp_hash() */ ;
321  else if ((funcid == F_HASHCHAR || funcid == F_HASHCHAREXTENDED) &&
322  argtype == BOOLOID)
323  /* okay, allowed use of hashchar() */ ;
324  else if ((funcid == F_HASHVARLENA || funcid == F_HASHVARLENAEXTENDED) &&
325  argtype == BYTEAOID)
326  /* okay, allowed use of hashvarlena() */ ;
327  else
328  result = false;
329  }
330 
331  /* If function takes a second argument, it must be for a 64-bit salt. */
332  if (nargs == 2 && procform->proargtypes.values[1] != INT8OID)
333  result = false;
334 
335  ReleaseSysCache(tp);
336  return result;
337 }
#define TIMESTAMPTZOID
Definition: pg_type.h:525
signed short int16
Definition: c.h:245
#define DATEOID
Definition: pg_type.h:511
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
#define INT4OID
Definition: pg_type.h:316
#define HASHEXTENDED_PROC
Definition: hash.h:352
#define XIDOID
Definition: pg_type.h:336
unsigned int Oid
Definition: postgres_ext.h:31
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
#define HASHSTANDARD_PROC
Definition: hash.h:351
bool IsBinaryCoercible(Oid srctype, Oid targettype)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
#define CIDOID
Definition: pg_type.h:340
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:83
#define INT8OID
Definition: pg_type.h:304
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define BOOLOID
Definition: pg_type.h:288
#define BYTEAOID
Definition: pg_type.h:292
#define elog
Definition: elog.h:219
#define ABSTIMEOID
Definition: pg_type.h:422
#define RELTIMEOID
Definition: pg_type.h:425
bool hashvalidate ( Oid  opclassoid)

Definition at line 44 of file hashvalidate.c.

References AMOP_SEARCH, AMOPSTRATEGY, AMPROCNUM, BOOLOID, check_amop_signature(), check_hash_func_signature(), CLAOID, elog, ereport, errcode(), errmsg(), ERROR, format_operator(), format_procedure(), format_type_be(), GETSTRUCT, HASHEXTENDED_PROC, HASHSTANDARD_PROC, HeapTupleIsValid, HTEqualStrategyNumber, HTMaxStrategyNumber, i, identify_opfamily_groups(), INFO, OpFamilyOpFuncGroup::lefttype, lfirst, list_append_unique_oid(), list_length(), list_member_oid(), catclist::members, catclist::n_members, NameStr, NIL, ObjectIdGetDatum, OidIsValid, OpFamilyOpFuncGroup::operatorset, OPFAMILYOID, ReleaseCatCacheList(), ReleaseSysCache(), OpFamilyOpFuncGroup::righttype, SearchSysCache1(), SearchSysCacheList1, and catctup::tuple.

Referenced by hashhandler().

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 procedure %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 procedures 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 }
#define NIL
Definition: pg_list.h:69
int n_members
Definition: catcache.h:176
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:336
int errcode(int sqlerrcode)
Definition: elog.c:575
#define HASHEXTENDED_PROC
Definition: hash.h:352
#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:820
#define HTMaxStrategyNumber
Definition: hash.h:337
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:532
static bool check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
Definition: hashvalidate.c:268
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1795
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
#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
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:209
#define HASHSTANDARD_PROC
Definition: hash.h:351
#define ereport(elevel, rest)
Definition: elog.h:122
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:44
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:505
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#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
int errmsg(const char *fmt,...)
Definition: elog.c:797
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:77
int i
#define NameStr(name)
Definition: c.h:493
HeapTupleData tuple
Definition: catcache.h:121
#define elog
Definition: elog.h:219
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:68
Definition: pg_list.h:45