1 /*-------------------------------------------------------------------------
2 *
3 * ginvalidate.c
4 * Opclass validator for GIN.
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/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/syscache.h"
27
28
29 /*
30 * Validator for a GIN opclass.
31 */
32 bool
ginvalidate(Oid opclassoid)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 procedure %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;
112 case GIN_EXTRACTVALUE_PROC:
113 /* Some opclasses omit nullFlags */
114 ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
115 2, 3, opcintype, INTERNALOID,
116 INTERNALOID);
117 break;
118 case GIN_EXTRACTQUERY_PROC:
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;
133 case GIN_COMPARE_PARTIAL_PROC:
134 ok = check_amproc_signature(procform->amproc, INT4OID, false,
135 4, 4, opckeytype, opckeytype,
136 INT2OID, INTERNALOID);
137 break;
138 case GIN_TRICONSISTENT_PROC:
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 */
240 if (i == GIN_COMPARE_PARTIAL_PROC)
241 continue; /* optional method */
242 if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
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",
258 GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC)));
259 result = false;
260 }
261
262
263 ReleaseCatCacheList(proclist);
264 ReleaseCatCacheList(oprlist);
265 ReleaseSysCache(familytup);
266 ReleaseSysCache(classtup);
267
268 return result;
269 }
270