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-2019, 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  default:
146  ereport(INFO,
147  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
148  errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
149  opfamilyname, "gin",
150  format_procedure(procform->amproc),
151  procform->amprocnum)));
152  result = false;
153  continue; /* don't want additional message */
154  }
155 
156  if (!ok)
157  {
158  ereport(INFO,
159  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
160  errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
161  opfamilyname, "gin",
162  format_procedure(procform->amproc),
163  procform->amprocnum)));
164  result = false;
165  }
166  }
167 
168  /* Check individual operators */
169  for (i = 0; i < oprlist->n_members; i++)
170  {
171  HeapTuple oprtup = &oprlist->members[i]->tuple;
172  Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
173 
174  /* TODO: Check that only allowed strategy numbers exist */
175  if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
176  {
177  ereport(INFO,
178  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
179  errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
180  opfamilyname, "gin",
181  format_operator(oprform->amopopr),
182  oprform->amopstrategy)));
183  result = false;
184  }
185 
186  /* gin doesn't support ORDER BY operators */
187  if (oprform->amoppurpose != AMOP_SEARCH ||
188  OidIsValid(oprform->amopsortfamily))
189  {
190  ereport(INFO,
191  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
192  errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
193  opfamilyname, "gin",
194  format_operator(oprform->amopopr))));
195  result = false;
196  }
197 
198  /* Check operator signature --- same for all gin strategies */
199  if (!check_amop_signature(oprform->amopopr, BOOLOID,
200  oprform->amoplefttype,
201  oprform->amoprighttype))
202  {
203  ereport(INFO,
204  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
205  errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
206  opfamilyname, "gin",
207  format_operator(oprform->amopopr))));
208  result = false;
209  }
210  }
211 
212  /* Now check for inconsistent groups of operators/functions */
213  grouplist = identify_opfamily_groups(oprlist, proclist);
214  opclassgroup = NULL;
215  foreach(lc, grouplist)
216  {
217  OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
218 
219  /* Remember the group exactly matching the test opclass */
220  if (thisgroup->lefttype == opcintype &&
221  thisgroup->righttype == opcintype)
222  opclassgroup = thisgroup;
223 
224  /*
225  * There is not a lot we can do to check the operator sets, since each
226  * GIN opclass is more or less a law unto itself, and some contain
227  * only operators that are binary-compatible with the opclass datatype
228  * (meaning that empty operator sets can be OK). That case also means
229  * that we shouldn't insist on nonempty function sets except for the
230  * opclass's own group.
231  */
232  }
233 
234  /* Check that the originally-named opclass is complete */
235  for (i = 1; i <= GINNProcs; i++)
236  {
237  if (opclassgroup &&
238  (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
239  continue; /* got it */
241  continue; /* optional method */
243  continue; /* don't need both, see check below loop */
244  ereport(INFO,
245  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
246  errmsg("operator class \"%s\" of access method %s is missing support function %d",
247  opclassname, "gin", i)));
248  result = false;
249  }
250  if (!opclassgroup ||
251  ((opclassgroup->functionset & (1 << GIN_CONSISTENT_PROC)) == 0 &&
252  (opclassgroup->functionset & (1 << GIN_TRICONSISTENT_PROC)) == 0))
253  {
254  ereport(INFO,
255  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
256  errmsg("operator class \"%s\" of access method %s is missing support function %d or %d",
257  opclassname, "gin",
259  result = false;
260  }
261 
262 
263  ReleaseCatCacheList(proclist);
264  ReleaseCatCacheList(oprlist);
265  ReleaseSysCache(familytup);
266  ReleaseSysCache(classtup);
267 
268  return result;
269 }
int n_members
Definition: catcache.h:176
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
#define GIN_EXTRACTQUERY_PROC
Definition: gin.h:24
FormData_pg_amproc * Form_pg_amproc
Definition: pg_amproc.h:68
int errcode(int sqlerrcode)
Definition: elog.c:608
#define INFO
Definition: elog.h:33
#define GIN_TRICONSISTENT_PROC
Definition: gin.h:27
char * format_operator(Oid operator_oid)
Definition: regproc.c:820
#define GIN_COMPARE_PARTIAL_PROC
Definition: gin.h:26
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1782
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
bool check_amproc_signature(Oid funcid, Oid restype, bool exact, int minargs, int maxargs,...)
Definition: amvalidate.c:150
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
List * identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
Definition: amvalidate.c:41
bool ginvalidate(Oid opclassoid)
Definition: ginvalidate.c:33
#define GIN_CONSISTENT_PROC
Definition: gin.h:25
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:210
#define GINNProcs
Definition: gin.h:28
#define ereport(elevel, rest)
Definition: elog.h:141
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
FormData_pg_opfamily * Form_pg_opfamily
Definition: pg_opfamily.h:51
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GIN_EXTRACTVALUE_PROC
Definition: gin.h:23
#define lfirst(lc)
Definition: pg_list.h:190
bool check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
Definition: amvalidate.c:194
int errmsg(const char *fmt,...)
Definition: elog.c:822
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
#define elog(elevel,...)
Definition: elog.h:228
int i
#define NameStr(name)
Definition: c.h:616
HeapTupleData tuple
Definition: catcache.h:121
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
Definition: pg_list.h:50
#define GIN_COMPARE_PROC
Definition: gin.h:22