diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index c70f17869a..af651205ec 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -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)) diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c new file mode 100644 index 0000000000..5e46e782be --- /dev/null +++ b/contrib/btree_gist/btree_enum.c @@ -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); +} diff --git a/contrib/btree_gist/btree_gist--1.4--1.5.sql b/contrib/btree_gist/btree_gist--1.4--1.5.sql new file mode 100644 index 0000000000..cf974c2f53 --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.4--1.5.sql @@ -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) ; diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index fdf0e6ad9e..81c850905c 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -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 diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index f759299bb2..011285abce 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -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 diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c index e30924ba1d..d4fee91ee1 100644 --- a/contrib/btree_gist/btree_utils_num.c +++ b/contrib/btree_gist/btree_utils_num.c @@ -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: diff --git a/contrib/btree_gist/data/enum.data b/contrib/btree_gist/data/enum.data new file mode 100644 index 0000000000..d03bfced98 --- /dev/null +++ b/contrib/btree_gist/data/enum.data @@ -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 diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out new file mode 100644 index 0000000000..c4b769dd4b --- /dev/null +++ b/contrib/btree_gist/expected/enum.out @@ -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) + diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql new file mode 100644 index 0000000000..476211e979 --- /dev/null +++ b/contrib/btree_gist/sql/enum.sql @@ -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; diff --git a/doc/src/sgml/btree-gist.sgml b/doc/src/sgml/btree-gist.sgml index cfdd5be84a..f3c639c2f3 100644 --- a/doc/src/sgml/btree-gist.sgml +++ b/doc/src/sgml/btree-gist.sgml @@ -17,7 +17,7 @@ oid, money, char, varchar, text, bytea, bit, varbit, macaddr, macaddr8, inet, - cidr and uuid. + cidr, uuid, and all enum types.