Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * gistvalidate.c
4 : * Opclass validator for 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/gist/gistvalidate.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "access/amvalidate.h"
17 : #include "access/gist_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 : /*
31 : * Validator for a GiST opclass.
32 : */
33 : bool
34 8 : gistvalidate(Oid opclassoid)
35 : {
36 8 : bool result = true;
37 : HeapTuple classtup;
38 : Form_pg_opclass classform;
39 : Oid opfamilyoid;
40 : Oid opcintype;
41 : Oid opckeytype;
42 : char *opclassname;
43 : HeapTuple familytup;
44 : Form_pg_opfamily familyform;
45 : char *opfamilyname;
46 : CatCList *proclist,
47 : *oprlist;
48 : List *grouplist;
49 : OpFamilyOpFuncGroup *opclassgroup;
50 : int i;
51 : ListCell *lc;
52 :
53 : /* Fetch opclass information */
54 8 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
55 8 : if (!HeapTupleIsValid(classtup))
56 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
57 8 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
58 :
59 8 : opfamilyoid = classform->opcfamily;
60 8 : opcintype = classform->opcintype;
61 8 : opckeytype = classform->opckeytype;
62 8 : if (!OidIsValid(opckeytype))
63 3 : opckeytype = opcintype;
64 8 : opclassname = NameStr(classform->opcname);
65 :
66 : /* Fetch opfamily information */
67 8 : familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
68 8 : if (!HeapTupleIsValid(familytup))
69 0 : elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
70 8 : familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
71 :
72 8 : opfamilyname = NameStr(familyform->opfname);
73 :
74 : /* Fetch all operators and support functions of the opfamily */
75 8 : oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
76 8 : proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
77 :
78 : /* Check individual support functions */
79 71 : for (i = 0; i < proclist->n_members; i++)
80 : {
81 63 : HeapTuple proctup = &proclist->members[i]->tuple;
82 63 : Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
83 : bool ok;
84 :
85 : /*
86 : * All GiST support functions should be registered with matching
87 : * left/right types
88 : */
89 63 : if (procform->amproclefttype != procform->amprocrighttype)
90 : {
91 0 : 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, "gist",
95 : format_procedure(procform->amproc))));
96 0 : result = false;
97 : }
98 :
99 : /*
100 : * We can't check signatures except within the specific opclass, since
101 : * we need to know the associated opckeytype in many cases.
102 : */
103 63 : if (procform->amproclefttype != opcintype)
104 0 : continue;
105 :
106 : /* Check procedure numbers and function signatures */
107 63 : switch (procform->amprocnum)
108 : {
109 : case GIST_CONSISTENT_PROC:
110 8 : ok = check_amproc_signature(procform->amproc, BOOLOID, false,
111 : 5, 5, INTERNALOID, opcintype,
112 : INT2OID, OIDOID, INTERNALOID);
113 8 : break;
114 : case GIST_UNION_PROC:
115 8 : ok = check_amproc_signature(procform->amproc, opckeytype, false,
116 : 2, 2, INTERNALOID, INTERNALOID);
117 8 : break;
118 : case GIST_COMPRESS_PROC:
119 : case GIST_DECOMPRESS_PROC:
120 : case GIST_FETCH_PROC:
121 20 : ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
122 : 1, 1, INTERNALOID);
123 20 : break;
124 : case GIST_PENALTY_PROC:
125 8 : ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
126 : 3, 3, INTERNALOID,
127 : INTERNALOID, INTERNALOID);
128 8 : break;
129 : case GIST_PICKSPLIT_PROC:
130 8 : ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
131 : 2, 2, INTERNALOID, INTERNALOID);
132 8 : break;
133 : case GIST_EQUAL_PROC:
134 8 : ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
135 : 3, 3, opckeytype, opckeytype,
136 : INTERNALOID);
137 8 : break;
138 : case GIST_DISTANCE_PROC:
139 3 : ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
140 : 5, 5, INTERNALOID, opcintype,
141 : INT2OID, OIDOID, INTERNALOID);
142 3 : break;
143 : default:
144 0 : ereport(INFO,
145 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
146 : errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
147 : opfamilyname, "gist",
148 : format_procedure(procform->amproc),
149 : procform->amprocnum)));
150 0 : result = false;
151 0 : continue; /* don't want additional message */
152 : }
153 :
154 63 : if (!ok)
155 : {
156 0 : ereport(INFO,
157 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
158 : errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
159 : opfamilyname, "gist",
160 : format_procedure(procform->amproc),
161 : procform->amprocnum)));
162 0 : result = false;
163 : }
164 : }
165 :
166 : /* Check individual operators */
167 85 : for (i = 0; i < oprlist->n_members; i++)
168 : {
169 77 : HeapTuple oprtup = &oprlist->members[i]->tuple;
170 77 : Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
171 : Oid op_rettype;
172 :
173 : /* TODO: Check that only allowed strategy numbers exist */
174 77 : if (oprform->amopstrategy < 1)
175 : {
176 0 : ereport(INFO,
177 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
178 : errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
179 : opfamilyname, "gist",
180 : format_operator(oprform->amopopr),
181 : oprform->amopstrategy)));
182 0 : result = false;
183 : }
184 :
185 : /* GiST supports ORDER BY operators */
186 77 : if (oprform->amoppurpose != AMOP_SEARCH)
187 : {
188 : /* ... but must have matching distance proc */
189 3 : if (!OidIsValid(get_opfamily_proc(opfamilyoid,
190 : oprform->amoplefttype,
191 : oprform->amoplefttype,
192 : GIST_DISTANCE_PROC)))
193 : {
194 0 : ereport(INFO,
195 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
196 : errmsg("operator family \"%s\" of access method %s contains unsupported ORDER BY specification for operator %s",
197 : opfamilyname, "gist",
198 : format_operator(oprform->amopopr))));
199 0 : result = false;
200 : }
201 : /* ... and operator result must match the claimed btree opfamily */
202 3 : op_rettype = get_op_rettype(oprform->amopopr);
203 3 : if (!opfamily_can_sort_type(oprform->amopsortfamily, op_rettype))
204 : {
205 0 : ereport(INFO,
206 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
207 : errmsg("operator family \"%s\" of access method %s contains incorrect ORDER BY opfamily specification for operator %s",
208 : opfamilyname, "gist",
209 : format_operator(oprform->amopopr))));
210 0 : result = false;
211 : }
212 : }
213 : else
214 : {
215 : /* Search operators must always return bool */
216 74 : op_rettype = BOOLOID;
217 : }
218 :
219 : /* Check operator signature */
220 77 : if (!check_amop_signature(oprform->amopopr, op_rettype,
221 : oprform->amoplefttype,
222 : oprform->amoprighttype))
223 : {
224 0 : ereport(INFO,
225 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
226 : errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
227 : opfamilyname, "gist",
228 : format_operator(oprform->amopopr))));
229 0 : result = false;
230 : }
231 : }
232 :
233 : /* Now check for inconsistent groups of operators/functions */
234 8 : grouplist = identify_opfamily_groups(oprlist, proclist);
235 8 : opclassgroup = NULL;
236 23 : foreach(lc, grouplist)
237 : {
238 15 : OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
239 :
240 : /* Remember the group exactly matching the test opclass */
241 30 : if (thisgroup->lefttype == opcintype &&
242 15 : thisgroup->righttype == opcintype)
243 8 : opclassgroup = thisgroup;
244 :
245 : /*
246 : * There is not a lot we can do to check the operator sets, since each
247 : * GiST opclass is more or less a law unto itself, and some contain
248 : * only operators that are binary-compatible with the opclass datatype
249 : * (meaning that empty operator sets can be OK). That case also means
250 : * that we shouldn't insist on nonempty function sets except for the
251 : * opclass's own group.
252 : */
253 : }
254 :
255 : /* Check that the originally-named opclass is complete */
256 80 : for (i = 1; i <= GISTNProcs; i++)
257 : {
258 144 : if (opclassgroup &&
259 72 : (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
260 63 : continue; /* got it */
261 9 : if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC)
262 9 : continue; /* optional methods */
263 0 : ereport(INFO,
264 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
265 : errmsg("operator class \"%s\" of access method %s is missing support function %d",
266 : opclassname, "gist", i)));
267 0 : result = false;
268 : }
269 :
270 8 : ReleaseCatCacheList(proclist);
271 8 : ReleaseCatCacheList(oprlist);
272 8 : ReleaseSysCache(familytup);
273 8 : ReleaseSysCache(classtup);
274 :
275 8 : return result;
276 : }
|