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