PostgreSQL Source Code  git master
ginvalidate.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * ginvalidate.c
4  * Opclass validator for GIN.
5  *
6  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/backend/access/gin/ginvalidate.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/amvalidate.h"
17 #include "access/gin_private.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_type.h"
24 #include "utils/builtins.h"
25 #include "utils/lsyscache.h"
26 #include "utils/regproc.h"
27 #include "utils/syscache.h"
28 
29 /*
30  * Validator for a GIN opclass.
31  */
32 bool
33 ginvalidate(Oid opclassoid)
34 {
35  bool result = true;
36  HeapTuple classtup;
37  Form_pg_opclass classform;
38  Oid opfamilyoid;
39  Oid opcintype;
40  Oid opckeytype;
41  char *opclassname;
42  HeapTuple familytup;
43  Form_pg_opfamily familyform;
44  char *opfamilyname;
45  CatCList *proclist,
46  *oprlist;
47  List *grouplist;
48  OpFamilyOpFuncGroup *opclassgroup;
49  int i;
50  ListCell *lc;
51 
52  /* Fetch opclass information */
53  classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
54  if (!HeapTupleIsValid(classtup))
55  elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
56  classform = (Form_pg_opclass) GETSTRUCT(classtup);
57 
58  opfamilyoid = classform->opcfamily;
59  opcintype = classform->opcintype;
60  opckeytype = classform->opckeytype;
61  if (!OidIsValid(opckeytype))
62  opckeytype = opcintype;
63  opclassname = NameStr(classform->opcname);
64 
65  /* Fetch opfamily information */
66  familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
67  if (!HeapTupleIsValid(familytup))
68  elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
69  familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
70 
71  opfamilyname = NameStr(familyform->opfname);
72 
73  /* Fetch all operators and support functions of the opfamily */
74  oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
75  proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
76 
77  /* Check individual support functions */
78  for (i = 0; i < proclist->n_members; i++)
79  {
80  HeapTuple proctup = &proclist->members[i]->tuple;
81  Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
82  bool ok;
83 
84  /*
85  * All GIN support functions should be registered with matching
86  * left/right types
87  */
88  if (procform->amproclefttype != procform->amprocrighttype)
89  {
90  ereport(INFO,
91  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
92  errmsg("operator family \"%s\" of access method %s contains support function %s with different left and right input types",
93  opfamilyname, "gin",
94  format_procedure(procform->amproc))));
95  result = false;
96  }
97 
98  /*
99  * We can't check signatures except within the specific opclass, since
100  * we need to know the associated opckeytype in many cases.
101  */
102  if (procform->amproclefttype != opcintype)
103  continue;
104 
105  /* Check procedure numbers and function signatures */
106  switch (procform->amprocnum)
107  {
108  case GIN_COMPARE_PROC:
109  ok = check_amproc_signature(procform->amproc, INT4OID, false,
110  2, 2, opckeytype, opckeytype);
111  break;
113  /* Some opclasses omit nullFlags */
114  ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
115  2, 3, opcintype, INTERNALOID,
116  INTERNALOID);
117  break;
119  /* Some opclasses omit nullFlags and searchMode */
120  ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
121  5, 7, opcintype, INTERNALOID,
122  INT2OID, INTERNALOID, INTERNALOID,
123  INTERNALOID, INTERNALOID);
124  break;
125  case GIN_CONSISTENT_PROC:
126  /* Some opclasses omit queryKeys and nullFlags */
127  ok = check_amproc_signature(procform->amproc, BOOLOID, false,
128  6, 8, INTERNALOID, INT2OID,
129  opcintype, INT4OID,
130  INTERNALOID, INTERNALOID,
131  INTERNALOID, INTERNALOID);
132  break;
134  ok = check_amproc_signature(procform->amproc, INT4OID, false,
135  4, 4, opckeytype, opckeytype,
136  INT2OID, INTERNALOID);
137  break;
139  ok = check_amproc_signature(procform->amproc, CHAROID, false,
140  7, 7, INTERNALOID, INT2OID,
141  opcintype, INT4OID,
142  INTERNALOID, INTERNALOID,
143  INTERNALOID);
144  break;
145  case GIN_OPTIONS_PROC:
146  ok = check_amoptsproc_signature(procform->amproc);
147  break;
148  default:
149  ereport(INFO,
150  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
151  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
152  opfamilyname, "gin",
153  format_procedure(procform->amproc),
154  procform->amprocnum)));
155  result = false;
156  continue; /* don't want additional message */
157  }
158 
159  if (!ok)
160  {
161  ereport(INFO,
162  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
163  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
164  opfamilyname, "gin",
165  format_procedure(procform->amproc),
166  procform->amprocnum)));
167  result = false;
168  }
169  }
170 
171  /* Check individual operators */
172  for (i = 0; i < oprlist->n_members; i++)
173  {
174  HeapTuple oprtup = &oprlist->members[i]->tuple;
175  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
176 
177  /* TODO: Check that only allowed strategy numbers exist */
178  if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
179  {
180  ereport(INFO,
181  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
182  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
183  opfamilyname, "gin",
184  format_operator(oprform->amopopr),
185  oprform->amopstrategy)));
186  result = false;
187  }
188 
189  /* gin doesn't support ORDER BY operators */
190  if (oprform->amoppurpose != AMOP_SEARCH ||
191  OidIsValid(oprform->amopsortfamily))
192  {
193  ereport(INFO,
194  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
195  errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
196  opfamilyname, "gin",
197  format_operator(oprform->amopopr))));
198  result = false;
199  }
200 
201  /* Check operator signature --- same for all gin strategies */
202  if (!check_amop_signature(oprform->amopopr, BOOLOID,
203  oprform->amoplefttype,
204  oprform->amoprighttype))
205  {
206  ereport(INFO,
207  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
208  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
209  opfamilyname, "gin",
210  format_operator(oprform->amopopr))));
211  result = false;
212  }
213  }
214 
215  /* Now check for inconsistent groups of operators/functions */
216  grouplist = identify_opfamily_groups(oprlist, proclist);
217  opclassgroup = NULL;
218  foreach(lc, grouplist)
219  {
220  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
221 
222  /* Remember the group exactly matching the test opclass */
223  if (thisgroup->lefttype == opcintype &&
224  thisgroup->righttype == opcintype)
225  opclassgroup = thisgroup;
226 
227  /*
228  * There is not a lot we can do to check the operator sets, since each
229  * GIN opclass is more or less a law unto itself, and some contain
230  * only operators that are binary-compatible with the opclass datatype
231  * (meaning that empty operator sets can be OK). That case also means
232  * that we shouldn't insist on nonempty function sets except for the
233  * opclass's own group.
234  */
235  }
236 
237  /* Check that the originally-named opclass is complete */
238  for (i = 1; i <= GINNProcs; i++)
239  {
240  if (opclassgroup &&
241  (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
242  continue; /* got it */
244  i == GIN_OPTIONS_PROC)
245  continue; /* optional method */
247  continue; /* don't need both, see check below loop */
248  ereport(INFO,
249  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
250  errmsg("operator class \"%s\" of access method %s is missing support function %d",
251  opclassname, "gin", i)));
252  result = false;
253  }
254  if (!opclassgroup ||
255  ((opclassgroup->functionset & (1 << GIN_CONSISTENT_PROC)) == 0 &&
256  (opclassgroup->functionset & (1 << GIN_TRICONSISTENT_PROC)) == 0))
257  {
258  ereport(INFO,
259  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
260  errmsg("operator class \"%s\" of access method %s is missing support function %d or %d",
261  opclassname, "gin",
263  result = false;
264  }
265 
266 
267  ReleaseCatCacheList(proclist);
268  ReleaseCatCacheList(oprlist);
269  ReleaseSysCache(familytup);
270  ReleaseSysCache(classtup);
271 
272  return result;
273 }
274 
275 /*
276  * Prechecking function for adding operators/functions to a GIN opfamily.
277  */
278 void
279 ginadjustmembers(Oid opfamilyoid,
280  Oid opclassoid,
281  List *operators,
282  List *functions)
283 {
284  ListCell *lc;
285 
286  /*
287  * Operator members of a GIN opfamily should never have hard dependencies,
288  * since their connection to the opfamily depends only on what the support
289  * functions think, and that can be altered. For consistency, we make all
290  * soft dependencies point to the opfamily, though a soft dependency on
291  * the opclass would work as well in the CREATE OPERATOR CLASS case.
292  */
293  foreach(lc, operators)
294  {
295  OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
296 
297  op->ref_is_hard = false;
298  op->ref_is_family = true;
299  op->refobjid = opfamilyoid;
300  }
301 
302  /*
303  * Required support functions should have hard dependencies. Preferably
304  * those are just dependencies on the opclass, but if we're in ALTER
305  * OPERATOR FAMILY, we leave the dependency pointing at the whole
306  * opfamily. (Given that GIN opclasses generally don't share opfamilies,
307  * it seems unlikely to be worth working harder.)
308  */
309  foreach(lc, functions)
310  {
311  OpFamilyMember *op = (OpFamilyMember *) lfirst(lc);
312 
313  switch (op->number)
314  {
317  /* Required support function */
318  op->ref_is_hard = true;
319  break;
320  case GIN_COMPARE_PROC:
321  case GIN_CONSISTENT_PROC:
324  case GIN_OPTIONS_PROC:
325  /* Optional, so force it to be a soft family dependency */
326  op->ref_is_hard = false;
327  op->ref_is_family = true;
328  op->refobjid = opfamilyoid;
329  break;
330  default:
331  ereport(ERROR,
332  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
333  errmsg("support function number %d is invalid for access method %s",
334  op->number, "gin")));
335  break;
336  }
337  }
338 }
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:730
#define OidIsValid(objectId)
Definition: c.h:759
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1776
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define INFO
Definition: elog.h:34
#define ereport(elevel,...)
Definition: elog.h:149
#define GIN_COMPARE_PROC
Definition: gin.h:22
#define GIN_CONSISTENT_PROC
Definition: gin.h:25
#define GIN_EXTRACTQUERY_PROC
Definition: gin.h:24
#define GIN_EXTRACTVALUE_PROC
Definition: gin.h:23
#define GINNProcs
Definition: gin.h:29
#define GIN_TRICONSISTENT_PROC
Definition: gin.h:27
#define GIN_COMPARE_PARTIAL_PROC
Definition: gin.h:26
#define GIN_OPTIONS_PROC
Definition: gin.h:28
bool ginvalidate(Oid opclassoid)
Definition: ginvalidate.c:33
void ginadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: ginvalidate.c:279
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
int i
Definition: isn.c:73
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
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
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
Definition: pg_list.h:54
Oid refobjid
Definition: amapi.h:90
bool ref_is_family
Definition: amapi.h:89
int number
Definition: amapi.h:84
bool ref_is_hard
Definition: amapi.h:88
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
int n_members
Definition: catcache.h:176
HeapTupleData tuple
Definition: catcache.h:121
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:866
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:818
@ OPFAMILYOID
Definition: syscache.h:74
@ CLAOID
Definition: syscache.h:48
@ AMPROCNUM
Definition: syscache.h:39
@ AMOPSTRATEGY
Definition: syscache.h:38
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:218