Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * blvalidate.c
4 : * Opclass validator for bloom.
5 : *
6 : * Copyright (c) 2016-2024, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * contrib/bloom/blvalidate.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
15 : #include "access/amvalidate.h"
16 : #include "access/htup_details.h"
17 : #include "bloom.h"
18 : #include "catalog/pg_amop.h"
19 : #include "catalog/pg_amproc.h"
20 : #include "catalog/pg_opclass.h"
21 : #include "catalog/pg_opfamily.h"
22 : #include "catalog/pg_type.h"
23 : #include "utils/regproc.h"
24 : #include "utils/syscache.h"
25 :
26 : /*
27 : * Validator for a bloom opclass.
28 : */
29 : bool
30 4 : blvalidate(Oid opclassoid)
31 : {
32 4 : bool result = true;
33 : HeapTuple classtup;
34 : Form_pg_opclass classform;
35 : Oid opfamilyoid;
36 : Oid opcintype;
37 : Oid opckeytype;
38 : char *opclassname;
39 : HeapTuple familytup;
40 : Form_pg_opfamily familyform;
41 : char *opfamilyname;
42 : CatCList *proclist,
43 : *oprlist;
44 : List *grouplist;
45 : OpFamilyOpFuncGroup *opclassgroup;
46 : int i;
47 : ListCell *lc;
48 :
49 : /* Fetch opclass information */
50 4 : classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
51 4 : if (!HeapTupleIsValid(classtup))
52 0 : elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
53 4 : classform = (Form_pg_opclass) GETSTRUCT(classtup);
54 :
55 4 : opfamilyoid = classform->opcfamily;
56 4 : opcintype = classform->opcintype;
57 4 : opckeytype = classform->opckeytype;
58 4 : if (!OidIsValid(opckeytype))
59 4 : opckeytype = opcintype;
60 4 : opclassname = NameStr(classform->opcname);
61 :
62 : /* Fetch opfamily information */
63 4 : familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
64 4 : if (!HeapTupleIsValid(familytup))
65 0 : elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
66 4 : familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
67 :
68 4 : opfamilyname = NameStr(familyform->opfname);
69 :
70 : /* Fetch all operators and support functions of the opfamily */
71 4 : oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
72 4 : proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
73 :
74 : /* Check individual support functions */
75 8 : for (i = 0; i < proclist->n_members; i++)
76 : {
77 4 : HeapTuple proctup = &proclist->members[i]->tuple;
78 4 : Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
79 : bool ok;
80 :
81 : /*
82 : * All bloom support functions should be registered with matching
83 : * left/right types
84 : */
85 4 : if (procform->amproclefttype != procform->amprocrighttype)
86 : {
87 0 : ereport(INFO,
88 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
89 : errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
90 : opfamilyname,
91 : format_procedure(procform->amproc))));
92 0 : result = false;
93 : }
94 :
95 : /*
96 : * We can't check signatures except within the specific opclass, since
97 : * we need to know the associated opckeytype in many cases.
98 : */
99 4 : if (procform->amproclefttype != opcintype)
100 0 : continue;
101 :
102 : /* Check procedure numbers and function signatures */
103 4 : switch (procform->amprocnum)
104 : {
105 4 : case BLOOM_HASH_PROC:
106 4 : ok = check_amproc_signature(procform->amproc, INT4OID, false,
107 : 1, 1, opckeytype);
108 4 : break;
109 0 : case BLOOM_OPTIONS_PROC:
110 0 : ok = check_amoptsproc_signature(procform->amproc);
111 0 : break;
112 0 : default:
113 0 : ereport(INFO,
114 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
115 : errmsg("bloom opfamily %s contains function %s with invalid support number %d",
116 : opfamilyname,
117 : format_procedure(procform->amproc),
118 : procform->amprocnum)));
119 0 : result = false;
120 0 : continue; /* don't want additional message */
121 : }
122 :
123 4 : if (!ok)
124 : {
125 0 : ereport(INFO,
126 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
127 : errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
128 : opfamilyname,
129 : format_procedure(procform->amproc),
130 : procform->amprocnum)));
131 0 : result = false;
132 : }
133 : }
134 :
135 : /* Check individual operators */
136 8 : for (i = 0; i < oprlist->n_members; i++)
137 : {
138 4 : HeapTuple oprtup = &oprlist->members[i]->tuple;
139 4 : Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
140 :
141 : /* Check it's allowed strategy for bloom */
142 4 : if (oprform->amopstrategy < 1 ||
143 4 : oprform->amopstrategy > BLOOM_NSTRATEGIES)
144 : {
145 0 : ereport(INFO,
146 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
147 : errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
148 : opfamilyname,
149 : format_operator(oprform->amopopr),
150 : oprform->amopstrategy)));
151 0 : result = false;
152 : }
153 :
154 : /* bloom doesn't support ORDER BY operators */
155 4 : if (oprform->amoppurpose != AMOP_SEARCH ||
156 4 : OidIsValid(oprform->amopsortfamily))
157 : {
158 0 : ereport(INFO,
159 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
160 : errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
161 : opfamilyname,
162 : format_operator(oprform->amopopr))));
163 0 : result = false;
164 : }
165 :
166 : /* Check operator signature --- same for all bloom strategies */
167 4 : 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("bloom opfamily %s contains operator %s with wrong signature",
174 : opfamilyname,
175 : format_operator(oprform->amopopr))));
176 0 : result = false;
177 : }
178 : }
179 :
180 : /* Now check for inconsistent groups of operators/functions */
181 4 : grouplist = identify_opfamily_groups(oprlist, proclist);
182 4 : opclassgroup = NULL;
183 8 : foreach(lc, grouplist)
184 : {
185 4 : OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
186 :
187 : /* Remember the group exactly matching the test opclass */
188 4 : if (thisgroup->lefttype == opcintype &&
189 4 : thisgroup->righttype == opcintype)
190 4 : opclassgroup = thisgroup;
191 :
192 : /*
193 : * There is not a lot we can do to check the operator sets, since each
194 : * bloom opclass is more or less a law unto itself, and some contain
195 : * only operators that are binary-compatible with the opclass datatype
196 : * (meaning that empty operator sets can be OK). That case also means
197 : * that we shouldn't insist on nonempty function sets except for the
198 : * opclass's own group.
199 : */
200 : }
201 :
202 : /* Check that the originally-named opclass is complete */
203 12 : for (i = 1; i <= BLOOM_NPROC; i++)
204 : {
205 8 : if (opclassgroup &&
206 8 : (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
207 4 : continue; /* got it */
208 4 : if (i == BLOOM_OPTIONS_PROC)
209 4 : continue; /* optional method */
210 0 : ereport(INFO,
211 : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
212 : errmsg("bloom opclass %s is missing support function %d",
213 : opclassname, i)));
214 0 : result = false;
215 : }
216 :
217 4 : ReleaseCatCacheList(proclist);
218 4 : ReleaseCatCacheList(oprlist);
219 4 : ReleaseSysCache(familytup);
220 4 : ReleaseSysCache(classtup);
221 :
222 4 : return result;
223 : }
|