PostgreSQL Source Code  git master
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-2024, 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 "access/xact.h"
20 #include "catalog/pg_am.h"
21 #include "catalog/pg_amop.h"
22 #include "catalog/pg_amproc.h"
23 #include "catalog/pg_opclass.h"
24 #include "catalog/pg_opfamily.h"
25 #include "catalog/pg_type.h"
26 #include "utils/builtins.h"
27 #include "utils/lsyscache.h"
28 #include "utils/regproc.h"
29 #include "utils/syscache.h"
30 
31 
32 /*
33  * Validator for a hash opclass.
34  *
35  * Some of the checks done here cover the whole opfamily, and therefore are
36  * redundant when checking each opclass in a family. But they don't run long
37  * enough to be much of a problem, so we accept the duplication rather than
38  * complicate the amvalidate API.
39  */
40 bool
41 hashvalidate(Oid opclassoid)
42 {
43  bool result = true;
44  HeapTuple classtup;
45  Form_pg_opclass classform;
46  Oid opfamilyoid;
47  Oid opcintype;
48  char *opclassname;
49  HeapTuple familytup;
50  Form_pg_opfamily familyform;
51  char *opfamilyname;
52  CatCList *proclist,
53  *oprlist;
54  List *grouplist;
55  OpFamilyOpFuncGroup *opclassgroup;
56  List *hashabletypes = NIL;
57  int i;
58  ListCell *lc;
59 
60  /* Fetch opclass information */
61  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
62  if (!HeapTupleIsValid(classtup))
63  elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
64  classform = (Form_pg_opclass) GETSTRUCT(classtup);
65 
66  opfamilyoid = classform->opcfamily;
67  opcintype = classform->opcintype;
68  opclassname = NameStr(classform->opcname);
69 
70  /* Fetch opfamily information */
71  familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
72  if (!HeapTupleIsValid(familytup))
73  elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
74  familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
75 
76  opfamilyname = NameStr(familyform->opfname);
77 
78  /* Fetch all operators and support functions of the opfamily */
79  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
80  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
81 
82  /* Check individual support functions */
83  for (i = 0; i < proclist->n_members; i++)
84  {
85  HeapTuple proctup = &proclist->members[i]->tuple;
86  Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
87  bool ok;
88 
89  /*
90  * All hash functions should be registered with matching left/right
91  * types
92  */
93  if (procform->amproclefttype != procform->amprocrighttype)
94  {
95  ereport(INFO,
96  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
97  errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
98  opfamilyname, "hash",
99  format_procedure(procform->amproc))));
100  result = false;
101  }
102 
103  /* Check procedure numbers and function signatures */
104  switch (procform->amprocnum)
105  {
106  case HASHSTANDARD_PROC:
107  ok = check_amproc_signature(procform->amproc, INT4OID, true,
108  1, 1, procform->amproclefttype);
109  break;
110  case HASHEXTENDED_PROC:
111  ok = check_amproc_signature(procform->amproc, INT8OID, true,
112  2, 2, procform->amproclefttype, INT8OID);
113  break;
114  case HASHOPTIONS_PROC:
115  ok = check_amoptsproc_signature(procform->amproc);
116  break;
117  default:
118  ereport(INFO,
119  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
120  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
121  opfamilyname, "hash",
122  format_procedure(procform->amproc),
123  procform->amprocnum)));
124  result = false;
125  continue; /* don't want additional message */
126  }
127 
128  if (!ok)
129  {
130  ereport(INFO,
131  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
132  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
133  opfamilyname, "hash",
134  format_procedure(procform->amproc),
135  procform->amprocnum)));
136  result = false;
137  }
138 
139  /* Remember which types we can hash */
140  if (ok && (procform->amprocnum == HASHSTANDARD_PROC || procform->amprocnum == HASHEXTENDED_PROC))
141  {
142  hashabletypes = list_append_unique_oid(hashabletypes, procform->amproclefttype);
143  }
144  }
145 
146  /* Check individual operators */
147  for (i = 0; i < oprlist->n_members; i++)
148  {
149  HeapTuple oprtup = &oprlist->members[i]->tuple;
150  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
151 
152  /* Check that only allowed strategy numbers exist */
153  if (oprform->amopstrategy < 1 ||
154  oprform->amopstrategy > HTMaxStrategyNumber)
155  {
156  ereport(INFO,
157  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
158  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
159  opfamilyname, "hash",
160  format_operator(oprform->amopopr),
161  oprform->amopstrategy)));
162  result = false;
163  }
164 
165  /* hash doesn't support ORDER BY operators */
166  if (oprform->amoppurpose != AMOP_SEARCH ||
167  OidIsValid(oprform->amopsortfamily))
168  {
169  ereport(INFO,
170  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
171  errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
172  opfamilyname, "hash",
173  format_operator(oprform->amopopr))));
174  result = false;
175  }
176 
177  /* Check operator signature --- same for all hash strategies */
178  if (!check_amop_signature(oprform->amopopr, BOOLOID,
179  oprform->amoplefttype,
180  oprform->amoprighttype))
181  {
182  ereport(INFO,
183  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
184  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
185  opfamilyname, "hash",
186  format_operator(oprform->amopopr))));
187  result = false;
188  }
189 
190  /* There should be relevant hash functions for each datatype */
191  if (!list_member_oid(hashabletypes, oprform->amoplefttype) ||
192  !list_member_oid(hashabletypes, oprform->amoprighttype))
193  {
194  ereport(INFO,
195  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
196  errmsg("operator family \"%s\" of access method %s lacks support function for operator %s",
197  opfamilyname, "hash",
198  format_operator(oprform->amopopr))));
199  result = false;
200  }
201  }
202 
203  /* Now check for inconsistent groups of operators/functions */
204  grouplist = identify_opfamily_groups(oprlist, proclist);
205  opclassgroup = NULL;
206  foreach(lc, grouplist)
207  {
208  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
209 
210  /* Remember the group exactly matching the test opclass */
211  if (thisgroup->lefttype == opcintype &&
212  thisgroup->righttype == opcintype)
213  opclassgroup = thisgroup;
214 
215  /*
216  * Complain if there seems to be an incomplete set of operators for
217  * this datatype pair (implying that we have a hash function but no
218  * operator).
219  */
220  if (thisgroup->operatorset != (1 << HTEqualStrategyNumber))
221  {
222  ereport(INFO,
223  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
224  errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
225  opfamilyname, "hash",
226  format_type_be(thisgroup->lefttype),
227  format_type_be(thisgroup->righttype))));
228  result = false;
229  }
230  }
231 
232  /* Check that the originally-named opclass is supported */
233  /* (if group is there, we already checked it adequately above) */
234  if (!opclassgroup)
235  {
236  ereport(INFO,
237  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
238  errmsg("operator class \"%s\" of access method %s is missing operator(s)",
239  opclassname, "hash")));
240  result = false;
241  }
242 
243  /*
244  * Complain if the opfamily doesn't have entries for all possible
245  * combinations of its supported datatypes. While missing cross-type
246  * operators are not fatal, it seems reasonable to insist that all
247  * built-in hash opfamilies be complete.
248  */
249  if (list_length(grouplist) !=
250  list_length(hashabletypes) * list_length(hashabletypes))
251  {
252  ereport(INFO,
253  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
254  errmsg("operator family \"%s\" of access method %s is missing cross-type operator(s)",
255  opfamilyname, "hash")));
256  result = false;
257  }
258 
259  ReleaseCatCacheList(proclist);
260  ReleaseCatCacheList(oprlist);
261  ReleaseSysCache(familytup);
262  ReleaseSysCache(classtup);
263 
264  return result;
265 }
266 
267 
268 /*
269  * Prechecking function for adding operators/functions to a hash opfamily.
270  */
271 void
272 hashadjustmembers(Oid opfamilyoid,
273  Oid opclassoid,
274  List *operators,
275  List *functions)
276 {
277  Oid opcintype;
278  ListCell *lc;
279 
280  /*
281  * Hash operators and required support functions are always "loose"
282  * members of the opfamily if they are cross-type. If they are not
283  * cross-type, we prefer to tie them to the appropriate opclass ... but if
284  * the user hasn't created one, we can't do that, and must fall back to
285  * using the opfamily dependency. (We mustn't force creation of an
286  * opclass in such a case, as leaving an incomplete opclass laying about
287  * would be bad. Throwing an error is another undesirable alternative.)
288  *
289  * This behavior results in a bit of a dump/reload hazard, in that the
290  * order of restoring objects could affect what dependencies we end up
291  * with. pg_dump's existing behavior will preserve the dependency choices
292  * in most cases, but not if a cross-type operator has been bound tightly
293  * into an opclass. That's a mistake anyway, so silently "fixing" it
294  * isn't awful.
295  *
296  * Optional support functions are always "loose" family members.
297  *
298  * To avoid repeated lookups, we remember the most recently used opclass's
299  * input type.
300  */
301  if (OidIsValid(opclassoid))
302  {
303  /* During CREATE OPERATOR CLASS, need CCI to see the pg_opclass row */
305  opcintype = get_opclass_input_type(opclassoid);
306  }
307  else
308  opcintype = InvalidOid;
309 
310  /*
311  * We handle operators and support functions almost identically, so rather
312  * than duplicate this code block, just join the lists.
313  */
314  foreach(lc, list_concat_copy(operators, functions))
315  {
316  OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
317 
318  if (op->is_func && op->number != HASHSTANDARD_PROC)
319  {
320  /* Optional support proc, so always a soft family dependency */
321  op->ref_is_hard = false;
322  op->ref_is_family = true;
323  op->refobjid = opfamilyoid;
324  }
325  else if (op->lefttype != op->righttype)
326  {
327  /* Cross-type, so always a soft family dependency */
328  op->ref_is_hard = false;
329  op->ref_is_family = true;
330  op->refobjid = opfamilyoid;
331  }
332  else
333  {
334  /* Not cross-type; is there a suitable opclass? */
335  if (op->lefttype != opcintype)
336  {
337  /* Avoid repeating this expensive lookup, even if it fails */
338  opcintype = op->lefttype;
339  opclassoid = opclass_for_family_datatype(HASH_AM_OID,
340  opfamilyoid,
341  opcintype);
342  }
343  if (OidIsValid(opclassoid))
344  {
345  /* Hard dependency on opclass */
346  op->ref_is_hard = true;
347  op->ref_is_family = false;
348  op->refobjid = opclassoid;
349  }
350  else
351  {
352  /* We're stuck, so make a soft dependency on the opfamily */
353  op->ref_is_hard = false;
354  op->ref_is_family = true;
355  op->refobjid = opfamilyoid;
356  }
357  }
358  }
359 }
Oid opclass_for_family_datatype(Oid amoid, Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:236
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:152
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:206
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:43
bool check_amoptsproc_signature(Oid funcid)
Definition: amvalidate.c:192
#define NameStr(name)
Definition: c.h:737
#define OidIsValid(objectId)
Definition: c.h:766
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1985
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define INFO
Definition: elog.h:34
#define ereport(elevel,...)
Definition: elog.h:149
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
#define HASHSTANDARD_PROC
Definition: hash.h:355
#define HASHEXTENDED_PROC
Definition: hash.h:356
#define HASHOPTIONS_PROC
Definition: hash.h:357
void hashadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: hashvalidate.c:272
bool hashvalidate(Oid opclassoid)
Definition: hashvalidate.c:41
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
int i
Definition: isn.c:73
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:598
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
Oid get_opclass_input_type(Oid opclass)
Definition: lsyscache.c:1212
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:68
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
static const struct fns functions
Definition: regcomp.c:356
char * format_operator(Oid operator_oid)
Definition: regproc.c:793
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:299
#define HTMaxStrategyNumber
Definition: stratnum.h:43
#define HTEqualStrategyNumber
Definition: stratnum.h:41
Definition: pg_list.h:54
Oid refobjid
Definition: amapi.h:90
Oid lefttype
Definition: amapi.h:85
bool ref_is_family
Definition: amapi.h:89
Oid righttype
Definition: amapi.h:86
int number
Definition: amapi.h:84
bool is_func
Definition: amapi.h:82
bool ref_is_hard
Definition: amapi.h:88
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:180
int n_members
Definition: catcache.h:178
HeapTupleData tuple
Definition: catcache.h:123
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:127
void CommandCounterIncrement(void)
Definition: xact.c:1099