PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2017, 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 
86 static IndexAMProperty
87 lookup_prop_name(const char *name)
88 {
89  int i;
90 
91  for (i = 0; i < lengthof(am_propnames); i++)
92  {
93  if (pg_strcasecmp(am_propnames[i].name, name) == 0)
94  return am_propnames[i].prop;
95  }
96 
97  /* We do not throw an error, so that AMs can define their own properties */
98  return AMPROP_UNKNOWN;
99 }
100 
101 /*
102  * Common code for properties that are just bit tests of indoptions.
103  *
104  * relid/attno: identify the index column to test the indoptions of.
105  * guard: if false, a boolean false result is forced (saves code in caller).
106  * iopt_mask: mask for interesting indoption bit.
107  * iopt_expect: value for a "true" result (should be 0 or iopt_mask).
108  *
109  * Returns false to indicate a NULL result (for "unknown/inapplicable"),
110  * otherwise sets *res to the boolean value to return.
111  */
112 static bool
113 test_indoption(Oid relid, int attno, bool guard,
114  int16 iopt_mask, int16 iopt_expect,
115  bool *res)
116 {
117  HeapTuple tuple;
119  Datum datum;
120  bool isnull;
121  int2vector *indoption;
122  int16 indoption_val;
123 
124  if (!guard)
125  {
126  *res = false;
127  return true;
128  }
129 
131  if (!HeapTupleIsValid(tuple))
132  return false;
133  rd_index = (Form_pg_index) GETSTRUCT(tuple);
134 
135  Assert(relid == rd_index->indexrelid);
136  Assert(attno > 0 && attno <= rd_index->indnatts);
137 
138  datum = SysCacheGetAttr(INDEXRELID, tuple,
139  Anum_pg_index_indoption, &isnull);
140  Assert(!isnull);
141 
142  indoption = ((int2vector *) DatumGetPointer(datum));
143  indoption_val = indoption->values[attno - 1];
144 
145  *res = (indoption_val & iopt_mask) == iopt_expect;
146 
147  ReleaseSysCache(tuple);
148 
149  return true;
150 }
151 
152 
153 /*
154  * Test property of an index AM, index, or index column.
155  *
156  * This is common code for different SQL-level funcs, so the amoid and
157  * index_oid parameters are mutually exclusive; we look up the amoid from the
158  * index_oid if needed, or if no index oid is given, we're looking at AM-wide
159  * properties.
160  */
161 static Datum
163  const char *propname,
164  Oid amoid, Oid index_oid, int attno)
165 {
166  bool res = false;
167  bool isnull = false;
168  int natts = 0;
170  IndexAmRoutine *routine;
171 
172  /* Try to convert property name to enum (no error if not known) */
173  prop = lookup_prop_name(propname);
174 
175  /* If we have an index OID, look up the AM, and get # of columns too */
176  if (OidIsValid(index_oid))
177  {
178  HeapTuple tuple;
179  Form_pg_class rd_rel;
180 
181  Assert(!OidIsValid(amoid));
182  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(index_oid));
183  if (!HeapTupleIsValid(tuple))
184  PG_RETURN_NULL();
185  rd_rel = (Form_pg_class) GETSTRUCT(tuple);
186  if (rd_rel->relkind != RELKIND_INDEX)
187  {
188  ReleaseSysCache(tuple);
189  PG_RETURN_NULL();
190  }
191  amoid = rd_rel->relam;
192  natts = rd_rel->relnatts;
193  ReleaseSysCache(tuple);
194  }
195 
196  /*
197  * At this point, either index_oid == InvalidOid or it's a valid index
198  * OID. Also, after this test, either attno == 0 for index-wide or
199  * AM-wide tests, or it's a valid column number in a valid index.
200  */
201  if (attno < 0 || attno > natts)
202  PG_RETURN_NULL();
203 
204  /*
205  * Get AM information. If we don't have a valid AM OID, return NULL.
206  */
207  routine = GetIndexAmRoutineByAmId(amoid, true);
208  if (routine == NULL)
209  PG_RETURN_NULL();
210 
211  /*
212  * If there's an AM property routine, give it a chance to override the
213  * generic logic. Proceed if it returns false.
214  */
215  if (routine->amproperty &&
216  routine->amproperty(index_oid, attno, prop, propname,
217  &res, &isnull))
218  {
219  if (isnull)
220  PG_RETURN_NULL();
221  PG_RETURN_BOOL(res);
222  }
223 
224  if (attno > 0)
225  {
226  /* Handle column-level properties */
227  switch (prop)
228  {
229  case AMPROP_ASC:
230  if (test_indoption(index_oid, attno, routine->amcanorder,
231  INDOPTION_DESC, 0, &res))
232  PG_RETURN_BOOL(res);
233  PG_RETURN_NULL();
234 
235  case AMPROP_DESC:
236  if (test_indoption(index_oid, attno, routine->amcanorder,
238  PG_RETURN_BOOL(res);
239  PG_RETURN_NULL();
240 
241  case AMPROP_NULLS_FIRST:
242  if (test_indoption(index_oid, attno, routine->amcanorder,
244  PG_RETURN_BOOL(res);
245  PG_RETURN_NULL();
246 
247  case AMPROP_NULLS_LAST:
248  if (test_indoption(index_oid, attno, routine->amcanorder,
249  INDOPTION_NULLS_FIRST, 0, &res))
250  PG_RETURN_BOOL(res);
251  PG_RETURN_NULL();
252 
253  case AMPROP_ORDERABLE:
254  PG_RETURN_BOOL(routine->amcanorder);
255 
257 
258  /*
259  * The conditions for whether a column is distance-orderable
260  * are really up to the AM (at time of writing, only GiST
261  * supports it at all). The planner has its own idea based on
262  * whether it finds an operator with amoppurpose 'o', but
263  * getting there from just the index column type seems like a
264  * lot of work. So instead we expect the AM to handle this in
265  * its amproperty routine. The generic result is to return
266  * false if the AM says it never supports this, and null
267  * otherwise (meaning we don't know).
268  */
269  if (!routine->amcanorderbyop)
270  PG_RETURN_BOOL(false);
271  PG_RETURN_NULL();
272 
273  case AMPROP_RETURNABLE:
274  if (!routine->amcanreturn)
275  PG_RETURN_BOOL(false);
276 
277  /*
278  * If possible, the AM should handle this test in its
279  * amproperty function without opening the rel. But this is
280  * the generic fallback if it does not.
281  */
282  {
283  Relation indexrel = index_open(index_oid, AccessShareLock);
284 
285  res = index_can_return(indexrel, attno);
286  index_close(indexrel, AccessShareLock);
287  }
288 
289  PG_RETURN_BOOL(res);
290 
291  case AMPROP_SEARCH_ARRAY:
292  PG_RETURN_BOOL(routine->amsearcharray);
293 
294  case AMPROP_SEARCH_NULLS:
295  PG_RETURN_BOOL(routine->amsearchnulls);
296 
297  default:
298  PG_RETURN_NULL();
299  }
300  }
301 
302  if (OidIsValid(index_oid))
303  {
304  /*
305  * Handle index-level properties. Currently, these only depend on the
306  * AM, but that might not be true forever, so we make users name an
307  * index not just an AM.
308  */
309  switch (prop)
310  {
311  case AMPROP_CLUSTERABLE:
312  PG_RETURN_BOOL(routine->amclusterable);
313 
314  case AMPROP_INDEX_SCAN:
315  PG_RETURN_BOOL(routine->amgettuple ? true : false);
316 
317  case AMPROP_BITMAP_SCAN:
318  PG_RETURN_BOOL(routine->amgetbitmap ? true : false);
319 
321  PG_RETURN_BOOL(routine->amcanbackward);
322 
323  default:
324  PG_RETURN_NULL();
325  }
326  }
327 
328  /*
329  * Handle AM-level properties (those that control what you can say in
330  * CREATE INDEX).
331  */
332  switch (prop)
333  {
334  case AMPROP_CAN_ORDER:
335  PG_RETURN_BOOL(routine->amcanorder);
336 
337  case AMPROP_CAN_UNIQUE:
338  PG_RETURN_BOOL(routine->amcanunique);
339 
341  PG_RETURN_BOOL(routine->amcanmulticol);
342 
343  case AMPROP_CAN_EXCLUDE:
344  PG_RETURN_BOOL(routine->amgettuple ? true : false);
345 
346  default:
347  PG_RETURN_NULL();
348  }
349 }
350 
351 /*
352  * Test property of an AM specified by AM OID
353  */
354 Datum
356 {
357  Oid amoid = PG_GETARG_OID(0);
358  char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
359 
360  return indexam_property(fcinfo, propname, amoid, InvalidOid, 0);
361 }
362 
363 /*
364  * Test property of an index specified by index OID
365  */
366 Datum
368 {
369  Oid relid = PG_GETARG_OID(0);
370  char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1));
371 
372  return indexam_property(fcinfo, propname, InvalidOid, relid, 0);
373 }
374 
375 /*
376  * Test property of an index column specified by index OID and column number
377  */
378 Datum
380 {
381  Oid relid = PG_GETARG_OID(0);
382  int32 attno = PG_GETARG_INT32(1);
383  char *propname = text_to_cstring(PG_GETARG_TEXT_PP(2));
384 
385  /* Reject attno 0 immediately, so that attno > 0 identifies this case */
386  if (attno <= 0)
387  PG_RETURN_NULL();
388 
389  return indexam_property(fcinfo, propname, InvalidOid, relid, attno);
390 }
static IndexAMProperty lookup_prop_name(const char *name)
Definition: amutils.c:87
signed short int16
Definition: c.h:255
#define INDOPTION_NULLS_FIRST
Definition: pg_index.h:100
#define PG_GETARG_INT32(n)
Definition: fmgr.h:234
bool amcanmulticol
Definition: amapi.h:179
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
IndexAMProperty
Definition: amapi.h:34
amgettuple_function amgettuple
Definition: amapi.h:210
bool amcanorderbyop
Definition: amapi.h:173
amproperty_function amproperty
Definition: amapi.h:206
static const struct am_propname am_propnames[]
Definition: amutils.c:31
#define AccessShareLock
Definition: lockdefs.h:36
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define lengthof(array)
Definition: c.h:562
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:538
Datum pg_index_has_property(PG_FUNCTION_ARGS)
Definition: amutils.c:367
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:156
signed int int32
Definition: c.h:256
#define PG_GETARG_TEXT_PP(n)
Definition: fmgr.h:273
bool index_can_return(Relation indexRelation, int attno)
Definition: indexam.c:783
amgetbitmap_function amgetbitmap
Definition: amapi.h:211
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
IndexAmRoutine * GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
Definition: amapi.c:56
bool amcanunique
Definition: amapi.h:177
#define PG_GETARG_OID(n)
Definition: fmgr.h:240
bool amcanbackward
Definition: amapi.h:175
static Datum indexam_property(FunctionCallInfo fcinfo, const char *propname, Oid amoid, Oid index_oid, int attno)
Definition: amutils.c:162
FormData_pg_index * Form_pg_index
Definition: pg_index.h:67
Datum pg_indexam_has_property(PG_FUNCTION_ARGS)
Definition: amutils.c:355
bool amsearchnulls
Definition: amapi.h:185
static bool test_indoption(Oid relid, int attno, bool guard, int16 iopt_mask, int16 iopt_expect, bool *res)
Definition: amutils.c:113
Definition: c.h:467
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:319
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1117
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1279
bool amclusterable
Definition: amapi.h:189
bool amsearcharray
Definition: amapi.h:183
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:676
#define INDOPTION_DESC
Definition: pg_index.h:99
IndexAMProperty prop
Definition: amutils.c:28
bool amcanorder
Definition: amapi.h:171
int16 values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:475
const char * name
Definition: encode.c:521
Datum pg_index_column_has_property(PG_FUNCTION_ARGS)
Definition: amutils.c:379
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:176
#define DatumGetPointer(X)
Definition: postgres.h:555
const char * name
Definition: amutils.c:27
char * text_to_cstring(const text *t)
Definition: varlena.c:182
FormData_pg_class * Form_pg_class
Definition: pg_class.h:95
int i
#define RELKIND_INDEX
Definition: pg_class.h:161
#define PG_FUNCTION_ARGS
Definition: fmgr.h:158
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:991
amcanreturn_function amcanreturn
Definition: amapi.h:203
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:151
#define PG_RETURN_NULL()
Definition: fmgr.h:305
#define Anum_pg_index_indoption
Definition: pg_index.h:90