Add btree_gist support for enum types.

This will allow enums to be used in exclusion constraints.

The code uses the new CallerFInfoFunctionCall infrastructure in fmgr,
and the support for it added to btree_gist in commit 393bb504d7.

Reviewed by Tom Lane and Anastasia Lubennikova

Discussion:  http://postgr.es/m/56EA8A71.8060107@dunslane.net
This commit is contained in:
Andrew Dunstan 2017-03-21 10:19:03 -04:00
parent 65a9138b9b
commit f7946a92b6
10 changed files with 991 additions and 7 deletions

View File

@ -5,18 +5,19 @@ MODULE_big = btree_gist
OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
btree_int4.o btree_int8.o btree_float4.o btree_float8.o btree_cash.o \
btree_oid.o btree_ts.o btree_time.o btree_date.o btree_interval.o \
btree_macaddr.o btree_macaddr8.o btree_inet.o btree_text.o btree_bytea.o \
btree_bit.o btree_numeric.o btree_uuid.o $(WIN32RES)
btree_macaddr.o btree_macaddr8.o btree_inet.o btree_text.o \
btree_bytea.o btree_bit.o btree_numeric.o btree_uuid.o \
btree_enum.o $(WIN32RES)
EXTENSION = btree_gist
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
btree_gist--1.3--1.4.sql
btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
time timetz date interval macaddr macaddr8 inet cidr text varchar char \
bytea bit varbit numeric uuid not_equal
bytea bit varbit numeric uuid not_equal enum
SHLIB_LINK += $(filter -lm, $(LIBS))

View File

@ -0,0 +1,187 @@
/*
* contrib/btree_gist/btree_enum.c
*/
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
#include "btree_gist.h"
#include "btree_utils_num.h"
/* enums are really Oids, so we just use the same structure */
typedef struct
{
Oid lower;
Oid upper;
} oidKEY;
/*
** enum ops
*/
PG_FUNCTION_INFO_V1(gbt_enum_compress);
PG_FUNCTION_INFO_V1(gbt_enum_fetch);
PG_FUNCTION_INFO_V1(gbt_enum_union);
PG_FUNCTION_INFO_V1(gbt_enum_picksplit);
PG_FUNCTION_INFO_V1(gbt_enum_consistent);
PG_FUNCTION_INFO_V1(gbt_enum_penalty);
PG_FUNCTION_INFO_V1(gbt_enum_same);
static bool
gbt_enumgt(const void *a, const void *b, FmgrInfo *flinfo)
{
return DatumGetBool(
CallerFInfoFunctionCall2(enum_gt, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b)))
);
}
static bool
gbt_enumge(const void *a, const void *b, FmgrInfo *flinfo)
{
return DatumGetBool(
CallerFInfoFunctionCall2(enum_ge, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b)))
);
}
static bool
gbt_enumeq(const void *a, const void *b, FmgrInfo *flinfo)
{
return (*((const Oid *) a) == *((const Oid *) b));
}
static bool
gbt_enumle(const void *a, const void *b, FmgrInfo *flinfo)
{
return DatumGetBool(
CallerFInfoFunctionCall2(enum_le, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b)))
);
}
static bool
gbt_enumlt(const void *a, const void *b, FmgrInfo *flinfo)
{
return DatumGetBool(
CallerFInfoFunctionCall2(enum_lt, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b)))
);
}
static int
gbt_enumkey_cmp(const void *a, const void *b, FmgrInfo *flinfo)
{
oidKEY *ia = (oidKEY *) (((const Nsrt *) a)->t);
oidKEY *ib = (oidKEY *) (((const Nsrt *) b)->t);
if (ia->lower == ib->lower)
{
if (ia->upper == ib->upper)
return 0;
return DatumGetInt32(
CallerFInfoFunctionCall2(enum_cmp, flinfo, InvalidOid, ObjectIdGetDatum(ia->upper), ObjectIdGetDatum(ib->upper))
);
}
return DatumGetInt32(
CallerFInfoFunctionCall2(enum_cmp, flinfo, InvalidOid, ObjectIdGetDatum(ia->lower), ObjectIdGetDatum(ib->lower))
);
}
static const gbtree_ninfo tinfo =
{
gbt_t_enum,
sizeof(Oid),
8, /* sizeof(gbtreekey8) */
gbt_enumgt,
gbt_enumge,
gbt_enumeq,
gbt_enumle,
gbt_enumlt,
gbt_enumkey_cmp,
NULL /* no KNN support at least for now */
};
/**************************************************
* Enum ops
**************************************************/
Datum
gbt_enum_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
PG_RETURN_POINTER(gbt_num_compress(entry, &tinfo));
}
Datum
gbt_enum_fetch(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
PG_RETURN_POINTER(gbt_num_fetch(entry, &tinfo));
}
Datum
gbt_enum_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
Oid query = PG_GETARG_OID(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
oidKEY *kkk = (oidKEY *) DatumGetPointer(entry->key);
GBT_NUMKEY_R key;
/* All cases served by this function are exact */
*recheck = false;
key.lower = (GBT_NUMKEY *) &kkk->lower;
key.upper = (GBT_NUMKEY *) &kkk->upper;
PG_RETURN_BOOL(
gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo)
);
}
Datum
gbt_enum_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
void *out = palloc(sizeof(oidKEY));
*(int *) PG_GETARG_POINTER(1) = sizeof(oidKEY);
PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo));
}
Datum
gbt_enum_penalty(PG_FUNCTION_ARGS)
{
oidKEY *origentry = (oidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
oidKEY *newentry = (oidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *result = (float *) PG_GETARG_POINTER(2);
penalty_num(result, origentry->lower, origentry->upper, newentry->lower, newentry->upper);
PG_RETURN_POINTER(result);
}
Datum
gbt_enum_picksplit(PG_FUNCTION_ARGS)
{
PG_RETURN_POINTER(gbt_num_picksplit(
(GistEntryVector *) PG_GETARG_POINTER(0),
(GIST_SPLITVEC *) PG_GETARG_POINTER(1),
&tinfo, fcinfo->flinfo
));
}
Datum
gbt_enum_same(PG_FUNCTION_ARGS)
{
oidKEY *b1 = (oidKEY *) PG_GETARG_POINTER(0);
oidKEY *b2 = (oidKEY *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
*result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo);
PG_RETURN_POINTER(result);
}

View File

@ -0,0 +1,69 @@
/* contrib/btree_gist/btree_gist--1.4--1.5.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.5'" to load this file. \quit
--
--
--
-- enum ops
--
--
--
-- define the GiST support methods
CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_enum_compress(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_enum_fetch(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_enum_penalty(internal,internal,internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_enum_picksplit(internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_enum_union(internal, internal)
RETURNS gbtreekey8
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
-- Create the operator class
CREATE OPERATOR CLASS gist_enum_ops
DEFAULT FOR TYPE anyenum USING gist
AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal),
FUNCTION 2 gbt_enum_union (internal, internal),
FUNCTION 3 gbt_enum_compress (internal),
FUNCTION 4 gbt_decompress (internal),
FUNCTION 5 gbt_enum_penalty (internal, internal, internal),
FUNCTION 6 gbt_enum_picksplit (internal, internal),
FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal),
STORAGE gbtreekey8;
ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
OPERATOR 6 <> (anyenum, anyenum) ,
FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ;

View File

@ -1,5 +1,5 @@
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
default_version = '1.4'
default_version = '1.5'
module_pathname = '$libdir/btree_gist'
relocatable = true

View File

@ -33,7 +33,8 @@ enum gbtree_type
gbt_t_bytea,
gbt_t_bit,
gbt_t_inet,
gbt_t_uuid
gbt_t_uuid,
gbt_t_enum
};
#endif

View File

@ -48,6 +48,7 @@ gbt_num_compress(GISTENTRY *entry, const gbtree_ninfo *tinfo)
leaf = &v.i8;
break;
case gbt_t_oid:
case gbt_t_enum:
v.i4 = DatumGetObjectId(entry->key);
leaf = &v.i4;
break;
@ -122,6 +123,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo)
datum = Int64GetDatum(*(int64 *) entry->key);
break;
case gbt_t_oid:
case gbt_t_enum:
datum = ObjectIdGetDatum(*(Oid *) entry->key);
break;
case gbt_t_float4:

View File

@ -0,0 +1,595 @@
r
v
i
b
r
\N
y
v
g
o
y
b
o
o
o
o
v
r
i
o
b
r
g
b
i
o
r
r
r
\N
o
b
v
y
o
\N
i
o
o
g
g
b
y
v
g
g
\N
v
g
i
i
\N
v
y
i
r
\N
r
\N
g
\N
g
\N
v
g
y
v
r
v
r
v
y
i
i
v
y
v
i
b
i
i
r
r
\N
\N
y
r
g
i
y
i
i
r
g
y
\N
i
o
r
y
y
g
o
o
g
y
r
g
v
r
i
r
i
r
y
v
b
i
o
r
\N
o
i
v
o
b
\N
b
g
y
o
v
b
i
v
v
o
y
i
i
i
g
b
b
g
r
i
y
o
\N
r
\N
i
i
g
v
o
y
y
o
i
b
r
y
y
o
g
g
g
\N
y
o
v
g
y
g
v
\N
i
o
v
b
b
\N
y
v
\N
v
\N
i
\N
r
b
r
o
r
b
o
g
i
r
b
g
g
y
b
b
g
y
g
v
v
b
\N
i
v
y
b
b
o
g
b
v
g
g
b
\N
y
r
r
b
\N
r
g
i
o
v
\N
o
r
b
o
b
i
\N
\N
y
b
y
\N
i
i
i
o
y
o
i
b
o
g
r
\N
b
y
\N
g
b
y
y
o
o
b
g
i
i
v
b
o
o
v
i
g
i
o
r
o
i
i
r
b
g
o
o
y
v
g
g
g
r
o
i
i
g
\N
o
v
b
b
v
i
g
y
i
i
g
r
y
i
b
\N
g
y
o
\N
i
i
b
v
o
b
v
r
g
o
v
v
y
r
v
g
\N
v
v
b
y
o
g
i
o
b
r
y
r
v
b
b
\N
i
v
y
r
b
i
y
g
\N
g
r
y
y
g
b
o
v
r
i
g
r
b
b
b
\N
y
y
y
i
o
r
g
g
i
y
g
y
v
o
o
g
\N
b
v
o
y
r
\N
o
i
g
\N
i
i
i
o
b
\N
\N
b
\N
v
v
r
\N
o
b
r
o
b
o
r
y
\N
r
i
b
b
y
v
r
g
r
r
\N
g
\N
v
v
y
r
o
r
o
i
o
\N
r
\N
i
v
b
v
\N
b
r
v
o
\N
i
r
b
g
o
\N
o
g
r
v
y
g
v
r
b
r
v
o
g
i
i
g
i
y
b
i
y
r
y
o
r
b
y
y
b
y
g
b
\N
r
g
b
o
y
o
g
r
g
b
\N
v
v
v
g
b
y
v
o
v
g
o
g
i
b
v
i
r
r
i
b
i
b
o
\N
\N
y
r
g
v
o
y
\N
g
v
o
b
v
v
\N
r
v
y
g
b
o
v
b
v
b
r
r
i
r
v
y
v
y
o
v
g
i
r
o
o
i
y
r
\N
y
r
b
y
y
\N
b
\N
\N
i
v

View File

@ -0,0 +1,91 @@
-- enum check
create type rainbow as enum ('r','o','y','g','b','i','v');
CREATE TABLE enumtmp (a rainbow);
\copy enumtmp from 'data/enum.data'
SET enable_seqscan=on;
select a, count(*) from enumtmp group by a order by 1;
a | count
---+-------
r | 76
o | 78
y | 73
g | 75
b | 77
i | 78
v | 75
| 63
(8 rows)
SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow;
count
-------
227
(1 row)
SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow;
count
-------
302
(1 row)
SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow;
count
-------
75
(1 row)
SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow;
count
-------
305
(1 row)
SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow;
count
-------
230
(1 row)
CREATE INDEX enumidx ON enumtmp USING gist ( a );
SET enable_seqscan=off;
SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow;
count
-------
227
(1 row)
SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow;
count
-------
302
(1 row)
SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow;
count
-------
75
(1 row)
SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow;
count
-------
305
(1 row)
SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow;
count
-------
230
(1 row)
EXPLAIN (COSTS OFF)
SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow;
QUERY PLAN
-----------------------------------------------
Aggregate
-> Bitmap Heap Scan on enumtmp
Recheck Cond: (a >= 'g'::rainbow)
-> Bitmap Index Scan on enumidx
Index Cond: (a >= 'g'::rainbow)
(5 rows)

View File

@ -0,0 +1,38 @@
-- enum check
create type rainbow as enum ('r','o','y','g','b','i','v');
CREATE TABLE enumtmp (a rainbow);
\copy enumtmp from 'data/enum.data'
SET enable_seqscan=on;
select a, count(*) from enumtmp group by a order by 1;
SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow;
CREATE INDEX enumidx ON enumtmp USING gist ( a );
SET enable_seqscan=off;
SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow;
SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow;

View File

@ -17,7 +17,7 @@
<type>oid</>, <type>money</>, <type>char</>,
<type>varchar</>, <type>text</>, <type>bytea</>, <type>bit</>,
<type>varbit</>, <type>macaddr</>, <type>macaddr8</>, <type>inet</>,
<type>cidr</> and <type>uuid</>.
<type>cidr</>, <type>uuid</>, and all <type>enum</> types.
</para>
<para>