Line data Source code
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
38 6 : spgvalidate(Oid opclassoid)
39 : {
40 6 : 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 6 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
58 6 : if (!HeapTupleIsValid(classtup))
59 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
60 6 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
61 :
62 6 : opfamilyoid = classform->opcfamily;
63 6 : opcintype = classform->opcintype;
64 6 : opclassname = NameStr(classform->opcname);
65 :
66 : /* Fetch opfamily information */
67 6 : familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
68 6 : if (!HeapTupleIsValid(familytup))
69 0 : elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
70 6 : familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
71 :
72 6 : opfamilyname = NameStr(familyform->opfname);
73 :
74 : /* Fetch all operators and support functions of the opfamily */
75 6 : oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
76 6 : proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
77 :
78 : /* Check individual support functions */
79 36 : for (i = 0; i < proclist->n_members; i++)
80 : {
81 30 : HeapTuple proctup = &proclist->members[i]->tuple;
82 30 : 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 30 : 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, "spgist",
95 : format_procedure(procform->amproc))));
96 0 : result = false;
97 : }
98 :
99 : /* Check procedure numbers and function signatures */
100 30 : 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 24 : ok = check_amproc_signature(procform->amproc, VOIDOID, true,
107 : 2, 2, INTERNALOID, INTERNALOID);
108 24 : break;
109 : case SPGIST_LEAF_CONSISTENT_PROC:
110 6 : ok = check_amproc_signature(procform->amproc, BOOLOID, true,
111 : 2, 2, INTERNALOID, INTERNALOID);
112 6 : break;
113 : default:
114 0 : 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 0 : result = false;
121 0 : continue; /* don't want additional message */
122 : }
123 :
124 30 : if (!ok)
125 : {
126 0 : 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 0 : result = false;
133 : }
134 : }
135 :
136 : /* Check individual operators */
137 60 : for (i = 0; i < oprlist->n_members; i++)
138 : {
139 54 : HeapTuple oprtup = &oprlist->members[i]->tuple;
140 54 : Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
141 :
142 : /* TODO: Check that only allowed strategy numbers exist */
143 54 : if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
144 : {
145 0 : 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 0 : result = false;
152 : }
153 :
154 : /* spgist doesn't support ORDER BY operators */
155 108 : if (oprform->amoppurpose != AMOP_SEARCH ||
156 54 : OidIsValid(oprform->amopsortfamily))
157 : {
158 0 : 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 0 : result = false;
164 : }
165 :
166 : /* Check operator signature --- same for all spgist strategies */
167 54 : if (!check_amop_signature(oprform->amopopr, BOOLOID,
168 : oprform->amoplefttype,
169 : oprform->amoprighttype))
170 : {
171 0 : 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 0 : result = false;
177 : }
178 : }
179 :
180 : /* Now check for inconsistent groups of operators/functions */
181 6 : grouplist = identify_opfamily_groups(oprlist, proclist);
182 6 : opclassgroup = NULL;
183 15 : foreach(lc, grouplist)
184 : {
185 9 : OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
186 :
187 : /* Remember the group exactly matching the test opclass */
188 18 : if (thisgroup->lefttype == opcintype &&
189 9 : thisgroup->righttype == opcintype)
190 6 : 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 9 : if (thisgroup->operatorset == 0)
198 : {
199 0 : 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 0 : 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 9 : if (thisgroup->lefttype != thisgroup->righttype)
213 3 : continue;
214 :
215 36 : for (i = 1; i <= SPGISTNProc; i++)
216 : {
217 30 : if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
218 30 : continue; /* got it */
219 0 : 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 0 : 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 6 : if (!opclassgroup)
231 : {
232 0 : 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 0 : result = false;
237 : }
238 :
239 6 : ReleaseCatCacheList(proclist);
240 6 : ReleaseCatCacheList(oprlist);
241 6 : ReleaseSysCache(familytup);
242 6 : ReleaseSysCache(classtup);
243 :
244 6 : return result;
245 : }
|