Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * amvalidate.c
4 : * Support routines for index access methods' amvalidate functions.
5 : *
6 : * Copyright (c) 2016-2017, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/access/index/amvalidate.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "access/amvalidate.h"
17 : #include "access/htup_details.h"
18 : #include "catalog/pg_am.h"
19 : #include "catalog/pg_amop.h"
20 : #include "catalog/pg_amproc.h"
21 : #include "catalog/pg_opclass.h"
22 : #include "catalog/pg_operator.h"
23 : #include "catalog/pg_proc.h"
24 : #include "parser/parse_coerce.h"
25 : #include "utils/syscache.h"
26 :
27 :
28 : /*
29 : * identify_opfamily_groups() returns a List of OpFamilyOpFuncGroup structs,
30 : * one for each combination of lefttype/righttype present in the family's
31 : * operator and support function lists. If amopstrategy K is present for
32 : * this datatype combination, we set bit 1 << K in operatorset, and similarly
33 : * for the support functions. With uint64 fields we can handle operator and
34 : * function numbers up to 63, which is plenty for the foreseeable future.
35 : *
36 : * The given CatCLists are expected to represent a single opfamily fetched
37 : * from the AMOPSTRATEGY and AMPROCNUM caches, so that they will be in
38 : * order by those caches' second and third cache keys, namely the datatypes.
39 : */
40 : List *
41 133 : identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
42 : {
43 133 : List *result = NIL;
44 : OpFamilyOpFuncGroup *thisgroup;
45 : Form_pg_amop oprform;
46 : Form_pg_amproc procform;
47 : int io,
48 : ip;
49 :
50 : /* We need the lists to be ordered; should be true in normal operation */
51 133 : if (!oprlist->ordered || !proclist->ordered)
52 0 : elog(ERROR, "cannot validate operator family without ordered data");
53 :
54 : /*
55 : * Advance through the lists concurrently. Thanks to the ordering, we
56 : * should see all operators and functions of a given datatype pair
57 : * consecutively.
58 : */
59 133 : thisgroup = NULL;
60 133 : io = ip = 0;
61 133 : if (io < oprlist->n_members)
62 : {
63 133 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
64 133 : io++;
65 : }
66 : else
67 0 : oprform = NULL;
68 133 : if (ip < proclist->n_members)
69 : {
70 133 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
71 133 : ip++;
72 : }
73 : else
74 0 : procform = NULL;
75 :
76 2389 : while (oprform || procform)
77 : {
78 3757 : if (oprform && thisgroup &&
79 3162 : oprform->amoplefttype == thisgroup->lefttype &&
80 1528 : oprform->amoprighttype == thisgroup->righttype)
81 : {
82 : /* Operator belongs to current group; include it and advance */
83 :
84 : /* Ignore strategy numbers outside supported range */
85 1149 : if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
86 1148 : thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;
87 :
88 1149 : if (io < oprlist->n_members)
89 : {
90 1016 : oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
91 1016 : io++;
92 : }
93 : else
94 133 : oprform = NULL;
95 1149 : continue;
96 : }
97 :
98 1808 : if (procform && thisgroup &&
99 1621 : procform->amproclefttype == thisgroup->lefttype &&
100 787 : procform->amprocrighttype == thisgroup->righttype)
101 : {
102 : /* Procedure belongs to current group; include it and advance */
103 :
104 : /* Ignore function numbers outside supported range */
105 688 : if (procform->amprocnum > 0 && procform->amprocnum < 64)
106 688 : thisgroup->functionset |= ((uint64) 1) << procform->amprocnum;
107 :
108 688 : if (ip < proclist->n_members)
109 : {
110 555 : procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
111 555 : ip++;
112 : }
113 : else
114 133 : procform = NULL;
115 688 : continue;
116 : }
117 :
118 : /* Time for a new group */
119 286 : thisgroup = (OpFamilyOpFuncGroup *) palloc(sizeof(OpFamilyOpFuncGroup));
120 286 : if (oprform &&
121 279 : (!procform ||
122 547 : (oprform->amoplefttype < procform->amproclefttype ||
123 536 : (oprform->amoplefttype == procform->amproclefttype &&
124 268 : oprform->amoprighttype < procform->amprocrighttype))))
125 : {
126 37 : thisgroup->lefttype = oprform->amoplefttype;
127 37 : thisgroup->righttype = oprform->amoprighttype;
128 : }
129 : else
130 : {
131 249 : thisgroup->lefttype = procform->amproclefttype;
132 249 : thisgroup->righttype = procform->amprocrighttype;
133 : }
134 286 : thisgroup->operatorset = thisgroup->functionset = 0;
135 286 : result = lappend(result, thisgroup);
136 : }
137 :
138 133 : return result;
139 : }
140 :
141 : /*
142 : * Validate the signature (argument and result types) of an opclass support
143 : * function. Return TRUE if OK, FALSE if not.
144 : *
145 : * The "..." represents maxargs argument-type OIDs. If "exact" is TRUE, they
146 : * must match the function arg types exactly, else only binary-coercibly.
147 : * In any case the function result type must match restype exactly.
148 : */
149 : bool
150 586 : check_amproc_signature(Oid funcid, Oid restype, bool exact,
151 : int minargs, int maxargs,...)
152 : {
153 586 : bool result = true;
154 : HeapTuple tp;
155 : Form_pg_proc procform;
156 : va_list ap;
157 : int i;
158 :
159 586 : tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
160 586 : if (!HeapTupleIsValid(tp))
161 0 : elog(ERROR, "cache lookup failed for function %u", funcid);
162 586 : procform = (Form_pg_proc) GETSTRUCT(tp);
163 :
164 1172 : if (procform->prorettype != restype || procform->proretset ||
165 1172 : procform->pronargs < minargs || procform->pronargs > maxargs)
166 0 : result = false;
167 :
168 586 : va_start(ap, maxargs);
169 2078 : for (i = 0; i < maxargs; i++)
170 : {
171 1492 : Oid argtype = va_arg(ap, Oid);
172 :
173 1492 : if (i >= procform->pronargs)
174 0 : continue;
175 1697 : if (exact ? (argtype != procform->proargtypes.values[i]) :
176 205 : !IsBinaryCoercible(argtype, procform->proargtypes.values[i]))
177 0 : result = false;
178 : }
179 586 : va_end(ap);
180 :
181 586 : ReleaseSysCache(tp);
182 586 : return result;
183 : }
184 :
185 : /*
186 : * Validate the signature (argument and result types) of an opclass operator.
187 : * Return TRUE if OK, FALSE if not.
188 : *
189 : * Currently, we can hard-wire this as accepting only binary operators. Also,
190 : * we can insist on exact type matches, since the given lefttype/righttype
191 : * come from pg_amop and should always match the operator exactly.
192 : */
193 : bool
194 1149 : check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
195 : {
196 1149 : bool result = true;
197 : HeapTuple tp;
198 : Form_pg_operator opform;
199 :
200 1149 : tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
201 1149 : if (!HeapTupleIsValid(tp)) /* shouldn't happen */
202 0 : elog(ERROR, "cache lookup failed for operator %u", opno);
203 1149 : opform = (Form_pg_operator) GETSTRUCT(tp);
204 :
205 2298 : if (opform->oprresult != restype || opform->oprkind != 'b' ||
206 2298 : opform->oprleft != lefttype || opform->oprright != righttype)
207 0 : result = false;
208 :
209 1149 : ReleaseSysCache(tp);
210 1149 : return result;
211 : }
212 :
213 : /*
214 : * Is the datatype a legitimate input type for the btree opfamily?
215 : */
216 : bool
217 3 : opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
218 : {
219 3 : bool result = false;
220 : CatCList *opclist;
221 : int i;
222 :
223 : /*
224 : * We search through all btree opclasses to see if one matches. This is a
225 : * bit inefficient but there is no better index available. It also saves
226 : * making an explicit check that the opfamily belongs to btree.
227 : */
228 3 : opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(BTREE_AM_OID));
229 :
230 39 : for (i = 0; i < opclist->n_members; i++)
231 : {
232 39 : HeapTuple classtup = &opclist->members[i]->tuple;
233 39 : Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup);
234 :
235 45 : if (classform->opcfamily == opfamilyoid &&
236 6 : classform->opcintype == datatypeoid)
237 : {
238 3 : result = true;
239 3 : break;
240 : }
241 : }
242 :
243 3 : ReleaseCatCacheList(opclist);
244 :
245 3 : return result;
246 : }
|