mirror of https://github.com/postgres/postgres
Per discussion, we should provide such functions to replace the lost ability to discover AM properties by inspecting pg_am (cf commitpull/31/head65c5fcd35
). The added functionality is also meant to displace any code that was looking directly at pg_index.indoption, since we'd rather not believe that the bit meanings in that field are part of any client API contract. As future-proofing, define the SQL API to not assume that properties that are currently AM-wide or index-wide will remain so unless they logically must be; instead, expose them only when inquiring about a specific index or even specific index column. Also provide the ability for an index AM to override the behavior. In passing, document pg_am.amtype, overlooked in commit473b93287
. Andrew Gierth, with kibitzing by me and others Discussion: <87mvl5on7n.fsf@news-spur.riddles.org.uk>
parent
4997878193
commit
ed0097e4f9
@ -0,0 +1,390 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* amutils.c |
||||
* SQL-level APIs related to index access methods. |
||||
* |
||||
* Copyright (c) 2016, PostgreSQL Global Development Group |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/backend/utils/adt/amutils.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/amapi.h" |
||||
#include "access/htup_details.h" |
||||
#include "catalog/pg_class.h" |
||||
#include "catalog/pg_index.h" |
||||
#include "utils/builtins.h" |
||||
#include "utils/syscache.h" |
||||
|
||||
|
||||
/* Convert string property name to enum, for efficiency */ |
||||
struct am_propname |
||||
{ |
||||
const char *name; |
||||
IndexAMProperty prop; |
||||
}; |
||||
|
||||
static const struct am_propname am_propnames[] = |
||||
{ |
||||
{ |
||||
"asc", AMPROP_ASC |
||||
}, |
||||
{ |
||||
"desc", AMPROP_DESC |
||||
}, |
||||
{ |
||||
"nulls_first", AMPROP_NULLS_FIRST |
||||
}, |
||||
{ |
||||
"nulls_last", AMPROP_NULLS_LAST |
||||
}, |
||||
{ |
||||
"orderable", AMPROP_ORDERABLE |
||||
}, |
||||
{ |
||||
"distance_orderable", AMPROP_DISTANCE_ORDERABLE |
||||
}, |
||||
{ |
||||
"returnable", AMPROP_RETURNABLE |
||||
}, |
||||
{ |
||||
"search_array", AMPROP_SEARCH_ARRAY |
||||
}, |
||||
{ |
||||
"search_nulls", AMPROP_SEARCH_NULLS |
||||
}, |
||||
{ |
||||
"clusterable", AMPROP_CLUSTERABLE |
||||
}, |
||||
{ |
||||
"index_scan", AMPROP_INDEX_SCAN |
||||
}, |
||||
{ |
||||
"bitmap_scan", AMPROP_BITMAP_SCAN |
||||
}, |
||||
{ |
||||
"backward_scan", AMPROP_BACKWARD_SCAN |
||||
}, |
||||
{ |
||||
"can_order", AMPROP_CAN_ORDER |
||||
}, |
||||
{ |
||||
"can_unique", AMPROP_CAN_UNIQUE |
||||
}, |
||||
{ |
||||
"can_multi_col", AMPROP_CAN_MULTI_COL |
||||
}, |
||||
{ |
||||
"can_exclude", AMPROP_CAN_EXCLUDE |
||||
} |
||||
}; |
||||
|
||||
static IndexAMProperty |
||||
lookup_prop_name(const char *name) |
||||
{ |
||||
int i; |
||||
|
||||
for (i = 0; i < lengthof(am_propnames); i++) |
||||
{ |
||||
if (pg_strcasecmp(am_propnames[i].name, name) == 0) |
||||
return am_propnames[i].prop; |
||||
} |
||||
|
||||
/* We do not throw an error, so that AMs can define their own properties */ |
||||
return AMPROP_UNKNOWN; |
||||
} |
||||
|
||||
/*
|
||||
* Common code for properties that are just bit tests of indoptions. |
||||
* |
||||
* relid/attno: identify the index column to test the indoptions of. |
||||
* guard: if false, a boolean false result is forced (saves code in caller). |
||||
* iopt_mask: mask for interesting indoption bit. |
||||
* iopt_expect: value for a "true" result (should be 0 or iopt_mask). |
||||
* |
||||
* Returns false to indicate a NULL result (for "unknown/inapplicable"), |
||||
* otherwise sets *res to the boolean value to return. |
||||
*/ |
||||
static bool |
||||
test_indoption(Oid relid, int attno, bool guard, |
||||
int16 iopt_mask, int16 iopt_expect, |
||||
bool *res) |
||||
{ |
||||
HeapTuple tuple; |
||||
Form_pg_index rd_index; |
||||
Datum datum; |
||||
bool isnull; |
||||
int2vector *indoption; |
||||
int16 indoption_val; |
||||
|
||||
if (!guard) |
||||
{ |
||||
*res = false; |
||||
return true; |
||||
} |
||||
|
||||
tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relid)); |
||||
if (!HeapTupleIsValid(tuple)) |
||||
return false; |
||||
rd_index = (Form_pg_index) GETSTRUCT(tuple); |
||||
|
||||
Assert(relid == rd_index->indexrelid); |
||||
Assert(attno > 0 && attno <= rd_index->indnatts); |
||||
|
||||
datum = SysCacheGetAttr(INDEXRELID, tuple, |
||||
Anum_pg_index_indoption, &isnull); |
||||
Assert(!isnull); |
||||
|
||||
indoption = ((int2vector *) DatumGetPointer(datum)); |
||||
indoption_val = indoption->values[attno - 1]; |
||||
|
||||
*res = (indoption_val & iopt_mask) == iopt_expect; |
||||
|
||||
ReleaseSysCache(tuple); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* Test property of an index AM, index, or index column. |
||||
* |
||||
* This is common code for different SQL-level funcs, so the amoid and |
||||
* index_oid parameters are mutually exclusive; we look up the amoid from the |
||||
* index_oid if needed, or if no index oid is given, we're looking at AM-wide |
||||
* properties. |
||||
*/ |
||||
static Datum |
||||
indexam_property(FunctionCallInfo fcinfo, |
||||
const char *propname, |
||||
Oid amoid, Oid index_oid, int attno) |
||||
{ |
||||
bool res = false; |
||||
bool isnull = false; |
||||
int natts = 0; |
||||
IndexAMProperty prop; |
||||
IndexAmRoutine *routine; |
||||
|
||||
/* Try to convert property name to enum (no error if not known) */ |
||||
prop = lookup_prop_name(propname); |
||||
|
||||
/* If we have an index OID, look up the AM, and get # of columns too */ |
||||
if (OidIsValid(index_oid)) |
||||
{ |
||||
HeapTuple tuple; |
||||
Form_pg_class rd_rel; |
||||
|
||||
Assert(!OidIsValid(amoid)); |
||||
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(index_oid)); |
||||
if (!HeapTupleIsValid(tuple)) |
||||
PG_RETURN_NULL(); |
||||
rd_rel = (Form_pg_class) GETSTRUCT(tuple); |
||||
if (rd_rel->relkind != RELKIND_INDEX) |
||||
{ |
||||
ReleaseSysCache(tuple); |
||||
PG_RETURN_NULL(); |
||||
} |
||||
amoid = rd_rel->relam; |
||||
natts = rd_rel->relnatts; |
||||
ReleaseSysCache(tuple); |
||||
} |
||||
|
||||
/*
|
||||
* At this point, either index_oid == InvalidOid or it's a valid index |
||||
* OID. Also, after this test, either attno == 0 for index-wide or |
||||
* AM-wide tests, or it's a valid column number in a valid index. |
||||
*/ |
||||
if (attno < 0 || attno > natts) |
||||
PG_RETURN_NULL(); |
||||
|
||||
/*
|
||||
* Get AM information. If we don't have a valid AM OID, return NULL. |
||||
*/ |
||||
routine = GetIndexAmRoutineByAmId(amoid, true); |
||||
if (routine == NULL) |
||||
PG_RETURN_NULL(); |
||||
|
||||
/*
|
||||
* If there's an AM property routine, give it a chance to override the |
||||
* generic logic. Proceed if it returns false. |
||||
*/ |
||||
if (routine->amproperty && |
||||
routine->amproperty(index_oid, attno, prop, propname, |
||||
&res, &isnull)) |
||||
{ |
||||
if (isnull) |
||||
PG_RETURN_NULL(); |
||||
PG_RETURN_BOOL(res); |
||||
} |
||||
|
||||
if (attno > 0) |
||||
{ |
||||
/* Handle column-level properties */ |
||||
switch (prop) |
||||
{ |
||||
case AMPROP_ASC: |
||||
if (test_indoption(index_oid, attno, routine->amcanorder, |
||||
INDOPTION_DESC, 0, &res)) |
||||
PG_RETURN_BOOL(res); |
||||
PG_RETURN_NULL(); |
||||
|
||||
case AMPROP_DESC: |
||||
if (test_indoption(index_oid, attno, routine->amcanorder, |
||||
INDOPTION_DESC, INDOPTION_DESC, &res)) |
||||
PG_RETURN_BOOL(res); |
||||
PG_RETURN_NULL(); |
||||
|
||||
case AMPROP_NULLS_FIRST: |
||||
if (test_indoption(index_oid, attno, routine->amcanorder, |
||||
INDOPTION_NULLS_FIRST, INDOPTION_NULLS_FIRST, &res)) |
||||
PG_RETURN_BOOL(res); |
||||
PG_RETURN_NULL(); |
||||
|
||||
case AMPROP_NULLS_LAST: |
||||
if (test_indoption(index_oid, attno, routine->amcanorder, |
||||
INDOPTION_NULLS_FIRST, 0, &res)) |
||||
PG_RETURN_BOOL(res); |
||||
PG_RETURN_NULL(); |
||||
|
||||
case AMPROP_ORDERABLE: |
||||
PG_RETURN_BOOL(routine->amcanorder); |
||||
|
||||
case AMPROP_DISTANCE_ORDERABLE: |
||||
|
||||
/*
|
||||
* The conditions for whether a column is distance-orderable |
||||
* are really up to the AM (at time of writing, only GiST |
||||
* supports it at all). The planner has its own idea based on |
||||
* whether it finds an operator with amoppurpose 'o', but |
||||
* getting there from just the index column type seems like a |
||||
* lot of work. So instead we expect the AM to handle this in |
||||
* its amproperty routine. The generic result is to return |
||||
* false if the AM says it never supports this, and null |
||||
* otherwise (meaning we don't know). |
||||
*/ |
||||
if (!routine->amcanorderbyop) |
||||
PG_RETURN_BOOL(false); |
||||
PG_RETURN_NULL(); |
||||
|
||||
case AMPROP_RETURNABLE: |
||||
if (!routine->amcanreturn) |
||||
PG_RETURN_BOOL(false); |
||||
|
||||
/*
|
||||
* If possible, the AM should handle this test in its |
||||
* amproperty function without opening the rel. But this is |
||||
* the generic fallback if it does not. |
||||
*/ |
||||
{ |
||||
Relation indexrel = index_open(index_oid, AccessShareLock); |
||||
|
||||
res = index_can_return(indexrel, attno); |
||||
index_close(indexrel, AccessShareLock); |
||||
} |
||||
|
||||
PG_RETURN_BOOL(res); |
||||
|
||||
case AMPROP_SEARCH_ARRAY: |
||||
PG_RETURN_BOOL(routine->amsearcharray); |
||||
|
||||
case AMPROP_SEARCH_NULLS: |
||||
PG_RETURN_BOOL(routine->amsearchnulls); |
||||
|
||||
default: |
||||
PG_RETURN_NULL(); |
||||
} |
||||
} |
||||
|
||||
if (OidIsValid(index_oid)) |
||||
{ |
||||
/*
|
||||
* Handle index-level properties. Currently, these only depend on the |
||||
* AM, but that might not be true forever, so we make users name an |
||||
* index not just an AM. |
||||
*/ |
||||
switch (prop) |
||||
{ |
||||
case AMPROP_CLUSTERABLE: |
||||
PG_RETURN_BOOL(routine->amclusterable); |
||||
|
||||
case AMPROP_INDEX_SCAN: |
||||
PG_RETURN_BOOL(routine->amgettuple ? true : false); |
||||
|
||||
case AMPROP_BITMAP_SCAN: |
||||
PG_RETURN_BOOL(routine->amgetbitmap ? true : false); |
||||
|
||||
case AMPROP_BACKWARD_SCAN: |
||||
PG_RETURN_BOOL(routine->amcanbackward); |
||||
|
||||
default: |
||||
PG_RETURN_NULL(); |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* Handle AM-level properties (those that control what you can say in |
||||
* CREATE INDEX). |
||||
*/ |
||||
switch (prop) |
||||
{ |
||||
case AMPROP_CAN_ORDER: |
||||
PG_RETURN_BOOL(routine->amcanorder); |
||||
|
||||
case AMPROP_CAN_UNIQUE: |
||||
PG_RETURN_BOOL(routine->amcanunique); |
||||
|
||||
case AMPROP_CAN_MULTI_COL: |
||||
PG_RETURN_BOOL(routine->amcanmulticol); |
||||
|
||||
case AMPROP_CAN_EXCLUDE: |
||||
PG_RETURN_BOOL(routine->amgettuple ? true : false); |
||||
|
||||
default: |
||||
PG_RETURN_NULL(); |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* Test property of an AM specified by AM OID |
||||
*/ |
||||
Datum |
||||
pg_indexam_has_property(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid amoid = PG_GETARG_OID(0); |
||||
char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1)); |
||||
|
||||
return indexam_property(fcinfo, propname, amoid, InvalidOid, 0); |
||||
} |
||||
|
||||
/*
|
||||
* Test property of an index specified by index OID |
||||
*/ |
||||
Datum |
||||
pg_index_has_property(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid relid = PG_GETARG_OID(0); |
||||
char *propname = text_to_cstring(PG_GETARG_TEXT_PP(1)); |
||||
|
||||
return indexam_property(fcinfo, propname, InvalidOid, relid, 0); |
||||
} |
||||
|
||||
/*
|
||||
* Test property of an index column specified by index OID and column number |
||||
*/ |
||||
Datum |
||||
pg_index_column_has_property(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid relid = PG_GETARG_OID(0); |
||||
int32 attno = PG_GETARG_INT32(1); |
||||
char *propname = text_to_cstring(PG_GETARG_TEXT_PP(2)); |
||||
|
||||
/* Reject attno 0 immediately, so that attno > 0 identifies this case */ |
||||
if (attno <= 0) |
||||
PG_RETURN_NULL(); |
||||
|
||||
return indexam_property(fcinfo, propname, InvalidOid, relid, attno); |
||||
} |
@ -0,0 +1,208 @@ |
||||
-- |
||||
-- Test index AM property-reporting functions |
||||
-- |
||||
select prop, |
||||
pg_indexam_has_property(a.oid, prop) as "AM", |
||||
pg_index_has_property('onek_hundred'::regclass, prop) as "Index", |
||||
pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as "Column" |
||||
from pg_am a, |
||||
unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', |
||||
'orderable', 'distance_orderable', 'returnable', |
||||
'search_array', 'search_nulls', |
||||
'clusterable', 'index_scan', 'bitmap_scan', |
||||
'backward_scan', |
||||
'can_order', 'can_unique', 'can_multi_col', |
||||
'can_exclude', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
where a.amname = 'btree' |
||||
order by ord; |
||||
prop | AM | Index | Column |
||||
--------------------+----+-------+-------- |
||||
asc | | | t |
||||
desc | | | f |
||||
nulls_first | | | f |
||||
nulls_last | | | t |
||||
orderable | | | t |
||||
distance_orderable | | | f |
||||
returnable | | | t |
||||
search_array | | | t |
||||
search_nulls | | | t |
||||
clusterable | | t | |
||||
index_scan | | t | |
||||
bitmap_scan | | t | |
||||
backward_scan | | t | |
||||
can_order | t | | |
||||
can_unique | t | | |
||||
can_multi_col | t | | |
||||
can_exclude | t | | |
||||
bogus | | | |
||||
(18 rows) |
||||
|
||||
select prop, |
||||
pg_indexam_has_property(a.oid, prop) as "AM", |
||||
pg_index_has_property('gcircleind'::regclass, prop) as "Index", |
||||
pg_index_column_has_property('gcircleind'::regclass, 1, prop) as "Column" |
||||
from pg_am a, |
||||
unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', |
||||
'orderable', 'distance_orderable', 'returnable', |
||||
'search_array', 'search_nulls', |
||||
'clusterable', 'index_scan', 'bitmap_scan', |
||||
'backward_scan', |
||||
'can_order', 'can_unique', 'can_multi_col', |
||||
'can_exclude', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
where a.amname = 'gist' |
||||
order by ord; |
||||
prop | AM | Index | Column |
||||
--------------------+----+-------+-------- |
||||
asc | | | f |
||||
desc | | | f |
||||
nulls_first | | | f |
||||
nulls_last | | | f |
||||
orderable | | | f |
||||
distance_orderable | | | t |
||||
returnable | | | f |
||||
search_array | | | f |
||||
search_nulls | | | t |
||||
clusterable | | t | |
||||
index_scan | | t | |
||||
bitmap_scan | | t | |
||||
backward_scan | | f | |
||||
can_order | f | | |
||||
can_unique | f | | |
||||
can_multi_col | t | | |
||||
can_exclude | t | | |
||||
bogus | | | |
||||
(18 rows) |
||||
|
||||
select prop, |
||||
pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as btree, |
||||
pg_index_column_has_property('hash_i4_index'::regclass, 1, prop) as hash, |
||||
pg_index_column_has_property('gcircleind'::regclass, 1, prop) as gist, |
||||
pg_index_column_has_property('sp_radix_ind'::regclass, 1, prop) as spgist, |
||||
pg_index_column_has_property('botharrayidx'::regclass, 1, prop) as gin, |
||||
pg_index_column_has_property('brinidx'::regclass, 1, prop) as brin |
||||
from unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', |
||||
'orderable', 'distance_orderable', 'returnable', |
||||
'search_array', 'search_nulls', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
order by ord; |
||||
prop | btree | hash | gist | spgist | gin | brin |
||||
--------------------+-------+------+------+--------+-----+------ |
||||
asc | t | f | f | f | f | f |
||||
desc | f | f | f | f | f | f |
||||
nulls_first | f | f | f | f | f | f |
||||
nulls_last | t | f | f | f | f | f |
||||
orderable | t | f | f | f | f | f |
||||
distance_orderable | f | f | t | f | f | f |
||||
returnable | t | f | f | t | f | f |
||||
search_array | t | f | f | f | f | f |
||||
search_nulls | t | f | t | t | f | t |
||||
bogus | | | | | | |
||||
(10 rows) |
||||
|
||||
select prop, |
||||
pg_index_has_property('onek_hundred'::regclass, prop) as btree, |
||||
pg_index_has_property('hash_i4_index'::regclass, prop) as hash, |
||||
pg_index_has_property('gcircleind'::regclass, prop) as gist, |
||||
pg_index_has_property('sp_radix_ind'::regclass, prop) as spgist, |
||||
pg_index_has_property('botharrayidx'::regclass, prop) as gin, |
||||
pg_index_has_property('brinidx'::regclass, prop) as brin |
||||
from unnest(array['clusterable', 'index_scan', 'bitmap_scan', |
||||
'backward_scan', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
order by ord; |
||||
prop | btree | hash | gist | spgist | gin | brin |
||||
---------------+-------+------+------+--------+-----+------ |
||||
clusterable | t | f | t | f | f | f |
||||
index_scan | t | t | t | t | f | f |
||||
bitmap_scan | t | t | t | t | t | t |
||||
backward_scan | t | t | f | f | f | f |
||||
bogus | | | | | | |
||||
(5 rows) |
||||
|
||||
select amname, prop, pg_indexam_has_property(a.oid, prop) as p |
||||
from pg_am a, |
||||
unnest(array['can_order', 'can_unique', 'can_multi_col', |
||||
'can_exclude', 'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
where amtype = 'i' |
||||
order by amname, ord; |
||||
amname | prop | p |
||||
--------+---------------+--- |
||||
brin | can_order | f |
||||
brin | can_unique | f |
||||
brin | can_multi_col | t |
||||
brin | can_exclude | f |
||||
brin | bogus | |
||||
btree | can_order | t |
||||
btree | can_unique | t |
||||
btree | can_multi_col | t |
||||
btree | can_exclude | t |
||||
btree | bogus | |
||||
gin | can_order | f |
||||
gin | can_unique | f |
||||
gin | can_multi_col | t |
||||
gin | can_exclude | f |
||||
gin | bogus | |
||||
gist | can_order | f |
||||
gist | can_unique | f |
||||
gist | can_multi_col | t |
||||
gist | can_exclude | t |
||||
gist | bogus | |
||||
hash | can_order | f |
||||
hash | can_unique | f |
||||
hash | can_multi_col | f |
||||
hash | can_exclude | t |
||||
hash | bogus | |
||||
spgist | can_order | f |
||||
spgist | can_unique | f |
||||
spgist | can_multi_col | f |
||||
spgist | can_exclude | t |
||||
spgist | bogus | |
||||
(30 rows) |
||||
|
||||
-- |
||||
-- additional checks for pg_index_column_has_property |
||||
-- |
||||
CREATE TEMP TABLE foo (f1 int, f2 int, f3 int, f4 int); |
||||
CREATE INDEX fooindex ON foo (f1 desc, f2 asc, f3 nulls first, f4 nulls last); |
||||
select col, prop, pg_index_column_has_property(o, col, prop) |
||||
from (values ('fooindex'::regclass)) v1(o), |
||||
(values (1,'orderable'),(2,'asc'),(3,'desc'), |
||||
(4,'nulls_first'),(5,'nulls_last'), |
||||
(6, 'bogus')) v2(idx,prop), |
||||
generate_series(1,4) col |
||||
order by col, idx; |
||||
col | prop | pg_index_column_has_property |
||||
-----+-------------+------------------------------ |
||||
1 | orderable | t |
||||
1 | asc | f |
||||
1 | desc | t |
||||
1 | nulls_first | t |
||||
1 | nulls_last | f |
||||
1 | bogus | |
||||
2 | orderable | t |
||||
2 | asc | t |
||||
2 | desc | f |
||||
2 | nulls_first | f |
||||
2 | nulls_last | t |
||||
2 | bogus | |
||||
3 | orderable | t |
||||
3 | asc | t |
||||
3 | desc | f |
||||
3 | nulls_first | t |
||||
3 | nulls_last | f |
||||
3 | bogus | |
||||
4 | orderable | t |
||||
4 | asc | t |
||||
4 | desc | f |
||||
4 | nulls_first | f |
||||
4 | nulls_last | t |
||||
4 | bogus | |
||||
(24 rows) |
||||
|
@ -0,0 +1,87 @@ |
||||
-- |
||||
-- Test index AM property-reporting functions |
||||
-- |
||||
|
||||
select prop, |
||||
pg_indexam_has_property(a.oid, prop) as "AM", |
||||
pg_index_has_property('onek_hundred'::regclass, prop) as "Index", |
||||
pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as "Column" |
||||
from pg_am a, |
||||
unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', |
||||
'orderable', 'distance_orderable', 'returnable', |
||||
'search_array', 'search_nulls', |
||||
'clusterable', 'index_scan', 'bitmap_scan', |
||||
'backward_scan', |
||||
'can_order', 'can_unique', 'can_multi_col', |
||||
'can_exclude', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
where a.amname = 'btree' |
||||
order by ord; |
||||
|
||||
select prop, |
||||
pg_indexam_has_property(a.oid, prop) as "AM", |
||||
pg_index_has_property('gcircleind'::regclass, prop) as "Index", |
||||
pg_index_column_has_property('gcircleind'::regclass, 1, prop) as "Column" |
||||
from pg_am a, |
||||
unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', |
||||
'orderable', 'distance_orderable', 'returnable', |
||||
'search_array', 'search_nulls', |
||||
'clusterable', 'index_scan', 'bitmap_scan', |
||||
'backward_scan', |
||||
'can_order', 'can_unique', 'can_multi_col', |
||||
'can_exclude', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
where a.amname = 'gist' |
||||
order by ord; |
||||
|
||||
select prop, |
||||
pg_index_column_has_property('onek_hundred'::regclass, 1, prop) as btree, |
||||
pg_index_column_has_property('hash_i4_index'::regclass, 1, prop) as hash, |
||||
pg_index_column_has_property('gcircleind'::regclass, 1, prop) as gist, |
||||
pg_index_column_has_property('sp_radix_ind'::regclass, 1, prop) as spgist, |
||||
pg_index_column_has_property('botharrayidx'::regclass, 1, prop) as gin, |
||||
pg_index_column_has_property('brinidx'::regclass, 1, prop) as brin |
||||
from unnest(array['asc', 'desc', 'nulls_first', 'nulls_last', |
||||
'orderable', 'distance_orderable', 'returnable', |
||||
'search_array', 'search_nulls', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
order by ord; |
||||
|
||||
select prop, |
||||
pg_index_has_property('onek_hundred'::regclass, prop) as btree, |
||||
pg_index_has_property('hash_i4_index'::regclass, prop) as hash, |
||||
pg_index_has_property('gcircleind'::regclass, prop) as gist, |
||||
pg_index_has_property('sp_radix_ind'::regclass, prop) as spgist, |
||||
pg_index_has_property('botharrayidx'::regclass, prop) as gin, |
||||
pg_index_has_property('brinidx'::regclass, prop) as brin |
||||
from unnest(array['clusterable', 'index_scan', 'bitmap_scan', |
||||
'backward_scan', |
||||
'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
order by ord; |
||||
|
||||
select amname, prop, pg_indexam_has_property(a.oid, prop) as p |
||||
from pg_am a, |
||||
unnest(array['can_order', 'can_unique', 'can_multi_col', |
||||
'can_exclude', 'bogus']::text[]) |
||||
with ordinality as u(prop,ord) |
||||
where amtype = 'i' |
||||
order by amname, ord; |
||||
|
||||
-- |
||||
-- additional checks for pg_index_column_has_property |
||||
-- |
||||
CREATE TEMP TABLE foo (f1 int, f2 int, f3 int, f4 int); |
||||
|
||||
CREATE INDEX fooindex ON foo (f1 desc, f2 asc, f3 nulls first, f4 nulls last); |
||||
|
||||
select col, prop, pg_index_column_has_property(o, col, prop) |
||||
from (values ('fooindex'::regclass)) v1(o), |
||||
(values (1,'orderable'),(2,'asc'),(3,'desc'), |
||||
(4,'nulls_first'),(5,'nulls_last'), |
||||
(6, 'bogus')) v2(idx,prop), |
||||
generate_series(1,4) col |
||||
order by col, idx; |
Loading…
Reference in new issue