1/*-------------------------------------------------------------------------
2 *
3 * amvalidate.c
4 * Support routines for index access methods' amvalidate functions.
5 *
6 * Copyright (c) 2016-2019, 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 */
40List *
41identify_opfamily_groups(CatCList *oprlist, CatCList *proclist)
42{
43 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 if (!oprlist->ordered || !proclist->ordered)
52 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 thisgroup = NULL;
60 io = ip = 0;
61 if (io < oprlist->n_members)
62 {
63 oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
64 io++;
65 }
66 else
67 oprform = NULL;
68 if (ip < proclist->n_members)
69 {
70 procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
71 ip++;
72 }
73 else
74 procform = NULL;
75
76 while (oprform || procform)
77 {
78 if (oprform && thisgroup &&
79 oprform->amoplefttype == thisgroup->lefttype &&
80 oprform->amoprighttype == thisgroup->righttype)
81 {
82 /* Operator belongs to current group; include it and advance */
83
84 /* Ignore strategy numbers outside supported range */
85 if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
86 thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;
87
88 if (io < oprlist->n_members)
89 {
90 oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple);
91 io++;
92 }
93 else
94 oprform = NULL;
95 continue;
96 }
97
98 if (procform && thisgroup &&
99 procform->amproclefttype == thisgroup->lefttype &&
100 procform->amprocrighttype == thisgroup->righttype)
101 {
102 /* Procedure belongs to current group; include it and advance */
103
104 /* Ignore function numbers outside supported range */
105 if (procform->amprocnum > 0 && procform->amprocnum < 64)
106 thisgroup->functionset |= ((uint64) 1) << procform->amprocnum;
107
108 if (ip < proclist->n_members)
109 {
110 procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple);
111 ip++;
112 }
113 else
114 procform = NULL;
115 continue;
116 }
117
118 /* Time for a new group */
119 thisgroup = (OpFamilyOpFuncGroup *) palloc(sizeof(OpFamilyOpFuncGroup));
120 if (oprform &&
121 (!procform ||
122 (oprform->amoplefttype < procform->amproclefttype ||
123 (oprform->amoplefttype == procform->amproclefttype &&
124 oprform->amoprighttype < procform->amprocrighttype))))
125 {
126 thisgroup->lefttype = oprform->amoplefttype;
127 thisgroup->righttype = oprform->amoprighttype;
128 }
129 else
130 {
131 thisgroup->lefttype = procform->amproclefttype;
132 thisgroup->righttype = procform->amprocrighttype;
133 }
134 thisgroup->operatorset = thisgroup->functionset = 0;
135 result = lappend(result, thisgroup);
136 }
137
138 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 */
149bool
150check_amproc_signature(Oid funcid, Oid restype, bool exact,
151 int minargs, int maxargs,...)
152{
153 bool result = true;
154 HeapTuple tp;
155 Form_pg_proc procform;
156 va_list ap;
157 int i;
158
159 tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
160 if (!HeapTupleIsValid(tp))
161 elog(ERROR, "cache lookup failed for function %u", funcid);
162 procform = (Form_pg_proc) GETSTRUCT(tp);
163
164 if (procform->prorettype != restype || procform->proretset ||
165 procform->pronargs < minargs || procform->pronargs > maxargs)
166 result = false;
167
168 va_start(ap, maxargs);
169 for (i = 0; i < maxargs; i++)
170 {
171 Oid argtype = va_arg(ap, Oid);
172
173 if (i >= procform->pronargs)
174 continue;
175 if (exact ? (argtype != procform->proargtypes.values[i]) :
176 !IsBinaryCoercible(argtype, procform->proargtypes.values[i]))
177 result = false;
178 }
179 va_end(ap);
180
181 ReleaseSysCache(tp);
182 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 */
193bool
194check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype)
195{
196 bool result = true;
197 HeapTuple tp;
198 Form_pg_operator opform;
199
200 tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
201 if (!HeapTupleIsValid(tp)) /* shouldn't happen */
202 elog(ERROR, "cache lookup failed for operator %u", opno);
203 opform = (Form_pg_operator) GETSTRUCT(tp);
204
205 if (opform->oprresult != restype || opform->oprkind != 'b' ||
206 opform->oprleft != lefttype || opform->oprright != righttype)
207 result = false;
208
209 ReleaseSysCache(tp);
210 return result;
211}
212
213/*
214 * Is the datatype a legitimate input type for the btree opfamily?
215 */
216bool
217opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
218{
219 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 opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(BTREE_AM_OID));
229
230 for (i = 0; i < opclist->n_members; i++)
231 {
232 HeapTuple classtup = &opclist->members[i]->tuple;
233 Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup);
234
235 if (classform->opcfamily == opfamilyoid &&
236 classform->opcintype == datatypeoid)
237 {
238 result = true;
239 break;
240 }
241 }
242
243 ReleaseCatCacheList(opclist);
244
245 return result;
246}
247