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