1 /*-------------------------------------------------------------------------
2 *
3 * spgvalidate.c
4 * Opclass validator for SP-GiST.
5 *
6 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 * IDENTIFICATION
10 * src/backend/access/spgist/spgvalidate.c
11 *
12 *-------------------------------------------------------------------------
13 */
14 #include "postgres.h"
15
16 #include "access/amvalidate.h"
17 #include "access/htup_details.h"
18 #include "access/spgist_private.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/regproc.h"
26 #include "utils/syscache.h"
27
28
29 /*
30 * Validator for an SP-GiST opclass.
31 *
32 * Some of the checks done here cover the whole opfamily, and therefore are
33 * redundant when checking each opclass in a family. But they don't run long
34 * enough to be much of a problem, so we accept the duplication rather than
35 * complicate the amvalidate API.
36 */
37 bool
spgvalidate(Oid opclassoid)38 spgvalidate(Oid opclassoid)
39 {
40 bool result = true;
41 HeapTuple classtup;
42 Form_pg_opclass classform;
43 Oid opfamilyoid;
44 Oid opcintype;
45 char *opclassname;
46 HeapTuple familytup;
47 Form_pg_opfamily familyform;
48 char *opfamilyname;
49 CatCList *proclist,
50 *oprlist;
51 List *grouplist;
52 OpFamilyOpFuncGroup *opclassgroup;
53 int i;
54 ListCell *lc;
55
56 /* Fetch opclass information */
57 classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
58 if (!HeapTupleIsValid(classtup))
59 elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
60 classform = (Form_pg_opclass) GETSTRUCT(classtup);
61
62 opfamilyoid = classform->opcfamily;
63 opcintype = classform->opcintype;
64 opclassname = NameStr(classform->opcname);
65
66 /* Fetch opfamily information */
67 familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
68 if (!HeapTupleIsValid(familytup))
69 elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
70 familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
71
72 opfamilyname = NameStr(familyform->opfname);
73
74 /* Fetch all operators and support functions of the opfamily */
75 oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
76 proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
77
78 /* Check individual support functions */
79 for (i = 0; i < proclist->n_members; i++)
80 {
81 HeapTuple proctup = &proclist->members[i]->tuple;
82 Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
83 bool ok;
84
85 /*
86 * All SP-GiST support functions should be registered with matching
87 * left/right types
88 */
89 if (procform->amproclefttype != procform->amprocrighttype)
90 {
91 ereport(INFO,
92 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
93 errmsg("operator family \"%s\" of access method %s contains support procedure %s with different left and right input types",
94 opfamilyname, "spgist",
95 format_procedure(procform->amproc))));
96 result = false;
97 }
98
99 /* Check procedure numbers and function signatures */
100 switch (procform->amprocnum)
101 {
102 case SPGIST_CONFIG_PROC:
103 case SPGIST_CHOOSE_PROC:
104 case SPGIST_PICKSPLIT_PROC:
105 case SPGIST_INNER_CONSISTENT_PROC:
106 ok = check_amproc_signature(procform->amproc, VOIDOID, true,
107 2, 2, INTERNALOID, INTERNALOID);
108 break;
109 case SPGIST_LEAF_CONSISTENT_PROC:
110 ok = check_amproc_signature(procform->amproc, BOOLOID, true,
111 2, 2, INTERNALOID, INTERNALOID);
112 break;
113 default:
114 ereport(INFO,
115 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
116 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
117 opfamilyname, "spgist",
118 format_procedure(procform->amproc),
119 procform->amprocnum)));
120 result = false;
121 continue; /* don't want additional message */
122 }
123
124 if (!ok)
125 {
126 ereport(INFO,
127 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
128 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
129 opfamilyname, "spgist",
130 format_procedure(procform->amproc),
131 procform->amprocnum)));
132 result = false;
133 }
134 }
135
136 /* Check individual operators */
137 for (i = 0; i < oprlist->n_members; i++)
138 {
139 HeapTuple oprtup = &oprlist->members[i]->tuple;
140 Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
141
142 /* TODO: Check that only allowed strategy numbers exist */
143 if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
144 {
145 ereport(INFO,
146 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
147 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
148 opfamilyname, "spgist",
149 format_operator(oprform->amopopr),
150 oprform->amopstrategy)));
151 result = false;
152 }
153
154 /* spgist doesn't support ORDER BY operators */
155 if (oprform->amoppurpose != AMOP_SEARCH ||
156 OidIsValid(oprform->amopsortfamily))
157 {
158 ereport(INFO,
159 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
160 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
161 opfamilyname, "spgist",
162 format_operator(oprform->amopopr))));
163 result = false;
164 }
165
166 /* Check operator signature --- same for all spgist strategies */
167 if (!check_amop_signature(oprform->amopopr, BOOLOID,
168 oprform->amoplefttype,
169 oprform->amoprighttype))
170 {
171 ereport(INFO,
172 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
173 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
174 opfamilyname, "spgist",
175 format_operator(oprform->amopopr))));
176 result = false;
177 }
178 }
179
180 /* Now check for inconsistent groups of operators/functions */
181 grouplist = identify_opfamily_groups(oprlist, proclist);
182 opclassgroup = NULL;
183 foreach(lc, grouplist)
184 {
185 OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
186
187 /* Remember the group exactly matching the test opclass */
188 if (thisgroup->lefttype == opcintype &&
189 thisgroup->righttype == opcintype)
190 opclassgroup = thisgroup;
191
192 /*
193 * Complain if there are any datatype pairs with functions but no
194 * operators. This is about the best we can do for now to detect
195 * missing operators.
196 */
197 if (thisgroup->operatorset == 0)
198 {
199 ereport(INFO,
200 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
201 errmsg("operator family \"%s\" of access method %s is missing operator(s) for types %s and %s",
202 opfamilyname, "spgist",
203 format_type_be(thisgroup->lefttype),
204 format_type_be(thisgroup->righttype))));
205 result = false;
206 }
207
208 /*
209 * Complain if we're missing functions for any datatype, remembering
210 * that SP-GiST doesn't use cross-type support functions.
211 */
212 if (thisgroup->lefttype != thisgroup->righttype)
213 continue;
214
215 for (i = 1; i <= SPGISTNProc; i++)
216 {
217 if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
218 continue; /* got it */
219 ereport(INFO,
220 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
221 errmsg("operator family \"%s\" of access method %s is missing support function %d for type %s",
222 opfamilyname, "spgist", i,
223 format_type_be(thisgroup->lefttype))));
224 result = false;
225 }
226 }
227
228 /* Check that the originally-named opclass is supported */
229 /* (if group is there, we already checked it adequately above) */
230 if (!opclassgroup)
231 {
232 ereport(INFO,
233 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
234 errmsg("operator class \"%s\" of access method %s is missing operator(s)",
235 opclassname, "spgist")));
236 result = false;
237 }
238
239 ReleaseCatCacheList(proclist);
240 ReleaseCatCacheList(oprlist);
241 ReleaseSysCache(familytup);
242 ReleaseSysCache(classtup);
243
244 return result;
245 }
246