PostgreSQL Source Code  git master
amutils.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * amutils.c
4  * SQL-level APIs related to index access methods.
5  *
6  * Copyright (c) 2016-2024, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  * src/backend/utils/adt/amutils.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/amapi.h"
17 #include "access/htup_details.h"
18 #include "catalog/pg_class.h"
19 #include "catalog/pg_index.h"
20 #include "utils/builtins.h"
21 #include "utils/syscache.h"
22 
23 
24 /* Convert string property name to enum, for efficiency */
26 {
27  const char *name;
29 };
30 
31 static const struct am_propname am_propnames[] =
32 {
33  {
34  "asc", AMPROP_ASC
35  },
36  {
37  "desc", AMPROP_DESC
38  },
39  {
40  "nulls_first", AMPROP_NULLS_FIRST
41  },
42  {
43  "nulls_last", AMPROP_NULLS_LAST
44  },
45  {
46  "orderable", AMPROP_ORDERABLE
47  },
48  {
49  "distance_orderable", AMPROP_DISTANCE_ORDERABLE
50  },
51  {
52  "returnable", AMPROP_RETURNABLE
53  },
54  {
55  "search_array", AMPROP_SEARCH_ARRAY
56  },
57  {
58  "search_nulls", AMPROP_SEARCH_NULLS
59  },
60  {
61  "clusterable", AMPROP_CLUSTERABLE
62  },
63  {
64  "index_scan", AMPROP_INDEX_SCAN
65  },
66  {
67  "bitmap_scan", AMPROP_BITMAP_SCAN
68  },
69  {
70  "backward_scan", AMPROP_BACKWARD_SCAN
71  },
72  {
73  "can_order", AMPROP_CAN_ORDER
74  },
75  {
76  "can_unique", AMPROP_CAN_UNIQUE
77  },
78  {
79  "can_multi_col", AMPROP_CAN_MULTI_COL
80  },
81  {
82  "can_exclude", AMPROP_CAN_EXCLUDE
83  },
84  {
85  "can_include", AMPROP_CAN_INCLUDE
86  },
87 };
88 
89 static IndexAMProperty
90 lookup_prop_name(const char *name)
91 {
92  int i;
93 
94  for (i = 0; i < lengthof(am_propnames); i++)
95  {
96  if (pg_strcasecmp(am_propnames[i].name, name) == 0)
97  return am_propnames[i].prop;
98  }
99 
100  /* We do not throw an error, so that AMs can define their own properties */
101  return AMPROP_UNKNOWN;
102 }
103 
104 /*
105  * Common code for properties that are just bit tests of indoptions.
106  *
107  * tuple: the pg_index heaptuple
108  * attno: identify the index column to test the indoptions of.
109  * guard: if false, a boolean false result is forced (saves code in caller).
110  * iopt_mask: mask for interesting indoption bit.
111  * iopt_expect: value for a "true" result (should be 0 or iopt_mask).
112  *
113  * Returns false to indicate a NULL result (for "unknown/inapplicable"),
114  * otherwise sets *res to the boolean value to return.
115  */
116 static bool
117 test_indoption(HeapTuple tuple, int attno, bool guard,
118  int16 iopt_mask, int16 iopt_expect,
119  bool *res)
120 {
121  Datum datum;
122  int2vector *indoption;
123  int16 indoption_val;
124 
125  if (!guard)
126  {
127  *res = false;
128  return true;
129  }
130 
131  datum = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indoption);
132 
133  indoption = ((int2vector *) DatumGetPointer(datum));
134  indoption_val = indoption->values[attno - 1];
135 
136  *res = (indoption_val & iopt_mask) == iopt_expect;
137 
138  return true;
139 }
140 
141 
142 /*
143  * Test property of an index AM, index, or index column.
144  *
145  * This is common code for different SQL-level funcs, so the amoid and
146  * index_oid parameters are mutually exclusive; we look up the amoid from the
147  * index_oid if needed, or if no index oid is given, we're looking at AM-wide
148  * properties.
149  */
150 static Datum
152  const char *propname,
153  Oid amoid, Oid index_oid, int attno)
154 {
155  bool res = false;
156  bool isnull = false;
157  int natts = 0;
159  IndexAmRoutine *routine;
160 
161  /* Try to convert property name to enum (no error if not known) */
162  prop = lookup_prop_name(propname);
163 
164  /* If we have an index OID, look up the AM, and get # of columns too */
165  if (OidIsValid(index_oid))
166  {
167  HeapTuple tuple;
168  Form_pg_class rd_rel;
169 
170  Assert(!OidIsValid(amoid));
171  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(index_oid));
172  if (!HeapTupleIsValid(tuple))
173  PG_RETURN_NULL();
174  rd_rel = (Form_pg_class) GETSTRUCT(tuple);
175  if (rd_rel->relkind != RELKIND_INDEX &&
176  rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
177  {
178  ReleaseSysCache(tuple);
179  PG_RETURN_NULL();
180  }
181  amoid = rd_rel->relam;
182  natts = rd_rel->relnatts;
183  ReleaseSysCache(tuple);
184  }
185 
186  /*
187  * At this point, either index_oid == InvalidOid or it's a valid index
188  * OID. Also, after this test and the one below, either attno == 0 for
189  * index-wide or AM-wide tests, or it's a valid column number in a valid
190  * index.
191  */
192  if (attno < 0 || attno > natts)
193  PG_RETURN_NULL();
194 
195  /*
196  * Get AM information. If we don't have a valid AM OID, return NULL.
197  */
198  routine = GetIndexAmRoutineByAmId(amoid, true);
199  if (routine == NULL)
200  PG_RETURN_NULL();
201 
202  /*
203  * If there's an AM property routine, give it a chance to override the
204  * generic logic. Proceed if it returns false.
205  */
206  if (routine->amproperty &&
207  routine->amproperty(index_oid, attno, prop, propname,
208  &res, &isnull))
209  {
210  if (isnull)
211  PG_RETURN_NULL();
213  }
214 
215  if (attno > 0)
216  {
217  HeapTuple tuple;
218  Form_pg_index rd_index;
219  bool iskey = true;
220 
221  /*
222  * Handle column-level properties. Many of these need the pg_index row
223  * (which we also need to use to check for nonkey atts) so we fetch
224  * that first.
225  */
226  tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
227  if (!HeapTupleIsValid(tuple))
228  PG_RETURN_NULL();
229  rd_index = (Form_pg_index) GETSTRUCT(tuple);
230 
231  Assert(index_oid == rd_index->indexrelid);
232  Assert(attno > 0 && attno <= rd_index->indnatts);
233 
234  isnull = true;
235 
236  /*
237  * If amcaninclude, we might be looking at an attno for a nonkey
238  * column, for which we (generically) assume that most properties are
239  * null.
240  */
241  if (routine->amcaninclude
242  && attno > rd_index->indnkeyatts)
243  iskey = false;
244 
245  switch (prop)
246  {
247  case AMPROP_ASC:
248  if (iskey &&
249  test_indoption(tuple, attno, routine->amcanorder,
250  INDOPTION_DESC, 0, &res))
251  isnull = false;
252  break;
253 
254  case AMPROP_DESC:
255  if (iskey &&
256  test_indoption(tuple, attno, routine->amcanorder,
257  INDOPTION_DESC, INDOPTION_DESC, &res))
258  isnull = false;
259  break;
260 
261  case AMPROP_NULLS_FIRST:
262  if (iskey &&
263  test_indoption(tuple, attno, routine->amcanorder,
264  INDOPTION_NULLS_FIRST, INDOPTION_NULLS_FIRST, &res))
265  isnull = false;
266  break;
267 
268  case AMPROP_NULLS_LAST:
269  if (iskey &&
270  test_indoption(tuple, attno, routine->amcanorder,
271  INDOPTION_NULLS_FIRST, 0, &res))
272  isnull = false;
273  break;
274 
275  case AMPROP_ORDERABLE:
276 
277  /*
278  * generic assumption is that nonkey columns are not orderable
279  */
280  res = iskey ? routine->amcanorder : false;
281  isnull = false;
282  break;
283 
285 
286  /*
287  * The conditions for whether a column is distance-orderable
288  * are really up to the AM (at time of writing, only GiST
289  * supports it at all). The planner has its own idea based on
290  * whether it finds an operator with amoppurpose 'o', but
291  * getting there from just the index column type seems like a
292  * lot of work. So instead we expect the AM to handle this in
293  * its amproperty routine. The generic result is to return
294  * false if the AM says it never supports this, or if this is
295  * a nonkey column, and null otherwise (meaning we don't
296  * know).
297  */
298  if (!iskey || !routine->amcanorderbyop)
299  {
300  res = false;
301  isnull = false;
302  }
303  break;
304 
305  case AMPROP_RETURNABLE:
306 
307  /* note that we ignore iskey for this property */
308 
309  isnull = false;
310  res = false;
311 
312  if (routine->amcanreturn)
313  {
314  /*
315  * If possible, the AM should handle this test in its
316  * amproperty function without opening the rel. But this
317  * is the generic fallback if it does not.
318  */
319  Relation indexrel = index_open(index_oid, AccessShareLock);
320 
321  res = index_can_return(indexrel, attno);
322  index_close(indexrel, AccessShareLock);
323  }
324  break;
325 
326  case AMPROP_SEARCH_ARRAY:
327  if (iskey)
328  {
329  res = routine->amsearcharray;
330  isnull = false;
331  }
332  break;
333 
334  case AMPROP_SEARCH_NULLS:
335  if (iskey)
336  {
337  res = routine->amsearchnulls;
338  isnull = false;
339  }
340  break;
341 
342  default:
343  break;
344  }
345 
346  ReleaseSysCache(tuple);
347 
348  if (!isnull)
350  PG_RETURN_NULL();
351  }
352 
353  if (OidIsValid(index_oid))
354  {
355  /*
356  * Handle index-level properties. Currently, these only depend on the
357  * AM, but that might not be true forever, so we make users name an
358  * index not just an AM.
359  */
360  switch (prop)
361  {
362  case AMPROP_CLUSTERABLE:
363  PG_RETURN_BOOL(routine->amclusterable);
364 
365  case AMPROP_INDEX_SCAN:
366  PG_RETURN_BOOL(routine->amgettuple ? true : false);
367 
368  case AMPROP_BITMAP_SCAN:
369  PG_RETURN_BOOL(routine->amgetbitmap ? true : false);
370 
372  PG_RETURN_BOOL(routine->amcanbackward);
373 
374  default:
375  PG_RETURN_NULL();
376  }
377  }
378 
379  /*
380  * Handle AM-level properties (those that control what you can say in
381  * CREATE INDEX).
382  */
383  switch (prop)
384  {
385  case AMPROP_CAN_ORDER:
386  PG_RETURN_BOOL(routine->amcanorder);
387 
388  case AMPROP_CAN_UNIQUE:
389  PG_RETURN_BOOL(routine->amcanunique);
390 
392  PG_RETURN_BOOL(routine->amcanmulticol);
393 
394  case AMPROP_CAN_EXCLUDE:
395  PG_RETURN_BOOL(routine->amgettuple ? true : false);
396 
397  case AMPROP_CAN_INCLUDE:
398  PG_RETURN_BOOL(routine->amcaninclude);
399 
400  default:
401  PG_RETURN_NULL();
402  }
403 }
404 
405 /*
406  * Test property of an AM specified by AM OID
407  */
408 Datum
410 {
411  Oid amoid = PG_GETARG_OID(0);
412  char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
413 
414  return indexam_property(fcinfo, propname, amoid, InvalidOid, 0);
415 }
416 
417 /*
418  * Test property of an index specified by index OID
419  */
420 Datum
422 {
423  Oid relid = PG_GETARG_OID(0);
424  char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
425 
426  return indexam_property(fcinfo, propname, InvalidOid, relid, 0);
427 }
428 
429 /*
430  * Test property of an index column specified by index OID and column number
431  */
432 Datum
434 {
435  Oid relid = PG_GETARG_OID(0);
436  int32 attno = PG_GETARG_INT32(1);
437  char *propname = text_to_cstring(PG_GETARG_TEXT_PP(2));
438 
439  /* Reject attno 0 immediately, so that attno > 0 identifies this case */
440  if (attno <= 0)
441  PG_RETURN_NULL();
442 
443  return indexam_property(fcinfo, propname, InvalidOid, relid, attno);
444 }
445 
446 /*
447  * Return the name of the given phase, as used for progress reporting by the
448  * given AM.
449  */
450 Datum
452 {
453  Oid amoid = PG_GETARG_OID(0);
454  int32 phasenum = PG_GETARG_INT32(1);
455  IndexAmRoutine *routine;
456  char *name;
457 
458  routine = GetIndexAmRoutineByAmId(amoid, true);
459  if (routine == NULL || !routine->ambuildphasename)
460  PG_RETURN_NULL();
461 
462  name = routine->ambuildphasename(phasenum);
463  if (!name)
464  PG_RETURN_NULL();
465 
467 }
IndexAmRoutine * GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
Definition: amapi.c:56
IndexAMProperty
Definition: amapi.h:35
@ AMPROP_BACKWARD_SCAN
Definition: amapi.h:49
@ AMPROP_SEARCH_ARRAY
Definition: amapi.h:44
@ AMPROP_CAN_MULTI_COL
Definition: amapi.h:52
@ AMPROP_CAN_ORDER
Definition: amapi.h:50
@ AMPROP_ORDERABLE
Definition: amapi.h:41
@ AMPROP_ASC
Definition: amapi.h:37
@ AMPROP_DISTANCE_ORDERABLE
Definition: amapi.h:42
@ AMPROP_CAN_INCLUDE
Definition: amapi.h:54
@ AMPROP_SEARCH_NULLS
Definition: amapi.h:45
@ AMPROP_CAN_EXCLUDE
Definition: amapi.h:53
@ AMPROP_UNKNOWN
Definition: amapi.h:36
@ AMPROP_CAN_UNIQUE
Definition: amapi.h:51
@ AMPROP_DESC
Definition: amapi.h:38
@ AMPROP_NULLS_LAST
Definition: amapi.h:40
@ AMPROP_INDEX_SCAN
Definition: amapi.h:47
@ AMPROP_NULLS_FIRST
Definition: amapi.h:39
@ AMPROP_CLUSTERABLE
Definition: amapi.h:46
@ AMPROP_RETURNABLE
Definition: amapi.h:43
@ AMPROP_BITMAP_SCAN
Definition: amapi.h:48
static const struct am_propname am_propnames[]
Definition: amutils.c:31
static Datum indexam_property(FunctionCallInfo fcinfo, const char *propname, Oid amoid, Oid index_oid, int attno)
Definition: amutils.c:151
Datum pg_index_column_has_property(PG_FUNCTION_ARGS)
Definition: amutils.c:433
Datum pg_indexam_progress_phasename(PG_FUNCTION_ARGS)
Definition: amutils.c:451
Datum pg_index_has_property(PG_FUNCTION_ARGS)
Definition: amutils.c:421
Datum pg_indexam_has_property(PG_FUNCTION_ARGS)
Definition: amutils.c:409
static IndexAMProperty lookup_prop_name(const char *name)
Definition: amutils.c:90
static bool test_indoption(HeapTuple tuple, int attno, bool guard, int16 iopt_mask, int16 iopt_expect, bool *res)
Definition: amutils.c:117
#define CStringGetTextDatum(s)
Definition: builtins.h:97
signed short int16
Definition: c.h:493
signed int int32
Definition: c.h:494
#define Assert(condition)
Definition: c.h:858
#define lengthof(array)
Definition: c.h:788
#define OidIsValid(objectId)
Definition: c.h:775
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:309
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
bool index_can_return(Relation indexRelation, int attno)
Definition: indexam.c:789
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
int i
Definition: isn.c:73
#define AccessShareLock
Definition: lockdefs.h:36
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
ambuildphasename_function ambuildphasename
Definition: amapi.h:276
bool amclusterable
Definition: amapi.h:241
bool amcanorderbyop
Definition: amapi.h:225
amgettuple_function amgettuple
Definition: amapi.h:281
amcanreturn_function amcanreturn
Definition: amapi.h:272
bool amcanunique
Definition: amapi.h:229
amgetbitmap_function amgetbitmap
Definition: amapi.h:282
amproperty_function amproperty
Definition: amapi.h:275
bool amsearcharray
Definition: amapi.h:235
bool amcanmulticol
Definition: amapi.h:231
bool amcanorder
Definition: amapi.h:223
bool amcanbackward
Definition: amapi.h:227
bool amcaninclude
Definition: amapi.h:249
bool amsearchnulls
Definition: amapi.h:237
IndexAMProperty prop
Definition: amutils.c:28
const char * name
Definition: amutils.c:27
Definition: c.h:715
int16 values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:722
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:510
char * text_to_cstring(const text *t)
Definition: varlena.c:217
const char * name