On 02/23/2017 04:41 PM, Tom Lane wrote: > Andrew Dunstan <andrew.duns...@2ndquadrant.com> writes: >> I'm not entirely sure how I should replace those DirectFunctionCall2 calls. > Basically what we want is for the called function to be able to use the > fcinfo->flinfo->fn_extra and fcinfo->flinfo->fn_mcxt fields of the > FmgrInfo struct that GIN has set up for the btree_gin support function. > > The fast but somewhat scary way to do it would just be to pass through > the flinfo pointer as-is. Imagine that fmgr.c grows a set of functions > like > > [...]
Here's a POC for btree_gin based on my original patch. I just made your function static in btree_gin.c at least for now. I stayed with the DirectFunctionCall2 use in the type-specific routines where calling context wasn't needed (i.e. everything except enums). But it looks more than ugly and highly invasive for btree_gist, and sadly that's my main use case, exclusion constraints with enum fields. cheers andrew -- Andrew Dunstan https://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/contrib/btree_gin/Makefile b/contrib/btree_gin/Makefile index 0492091..c6aae26 100644 --- a/contrib/btree_gin/Makefile +++ b/contrib/btree_gin/Makefile @@ -4,13 +4,13 @@ MODULE_big = btree_gin OBJS = btree_gin.o $(WIN32RES) EXTENSION = btree_gin -DATA = btree_gin--1.0.sql btree_gin--unpackaged--1.0.sql +DATA = btree_gin--1.0.sql btree_gin--unpackaged--1.0.sql btree_gin--1.0--1.1.sql PGFILEDESC = "btree_gin - B-tree equivalent GIN operator classes" REGRESS = install_btree_gin int2 int4 int8 float4 float8 money oid \ timestamp timestamptz time timetz date interval \ macaddr inet cidr text varchar char bytea bit varbit \ - numeric + numeric enum ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/btree_gin/btree_gin--1.0--1.1.sql b/contrib/btree_gin/btree_gin--1.0--1.1.sql new file mode 100644 index 0000000..3c40ccd --- /dev/null +++ b/contrib/btree_gin/btree_gin--1.0--1.1.sql @@ -0,0 +1,47 @@ +/* contrib/btree_gin/btree_gin--1.0--1.1.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gin UPDATE TO '1.1'" to load this file. \quit + +-- +-- +-- +-- enum ops +-- +-- + + +CREATE FUNCTION gin_extract_value_anyenum(anyenum, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_compare_prefix_anyenum(anyenum, anyenum, int2, internal) +RETURNS int4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_extract_query_anyenum(anyenum, internal, int2, internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_enum_cmp(anyenum, anyenum) +RETURNS int4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE OPERATOR CLASS enum_ops +DEFAULT FOR TYPE anyenum USING gin +AS + OPERATOR 1 <, + OPERATOR 2 <=, + OPERATOR 3 =, + OPERATOR 4 >=, + OPERATOR 5 >, + FUNCTION 1 gin_enum_cmp(anyenum,anyenum), + FUNCTION 2 gin_extract_value_anyenum(anyenum, internal), + FUNCTION 3 gin_extract_query_anyenum(anyenum, internal, int2, internal, internal), + FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal), + FUNCTION 5 gin_compare_prefix_anyenum(anyenum,anyenum,int2, internal), +STORAGE anyenum; diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c index 030b610..4694275 100644 --- a/contrib/btree_gin/btree_gin.c +++ b/contrib/btree_gin/btree_gin.c @@ -25,6 +25,35 @@ typedef struct QueryInfo Datum (*typecmp) (FunctionCallInfo); } QueryInfo; +/* + * Routine to provide context to a data type comparison call. + * Needed for enum support. + */ + +static Datum +CallerContextFunctionCall2(PGFunction func, + FmgrInfo *flinfo, Oid collation, + Datum arg1, Datum arg2) +{ + FunctionCallInfoData fcinfo; + Datum result; + + InitFunctionCallInfoData(fcinfo, flinfo, 2, collation, NULL, NULL); + + fcinfo.arg[0] = arg1; + fcinfo.arg[1] = arg2; + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = false; + + result = (*func) (&fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (fcinfo.isnull) + elog(ERROR, "function %p returned NULL", (void *) func); + + return result; +} + /*** GIN support functions shared by all datatypes ***/ @@ -112,13 +141,14 @@ gin_btree_compare_prefix(FunctionCallInfo fcinfo) int32 res, cmp; - cmp = DatumGetInt32(DirectFunctionCall2Coll( - data->typecmp, - PG_GET_COLLATION(), - (data->strategy == BTLessStrategyNumber || - data->strategy == BTLessEqualStrategyNumber) - ? data->datum : a, - b)); + cmp = DatumGetInt32(CallerContextFunctionCall2( + data->typecmp, + fcinfo->flinfo, + PG_GET_COLLATION(), + (data->strategy == BTLessStrategyNumber || + data->strategy == BTLessEqualStrategyNumber) + ? data->datum : a, + b)); switch (data->strategy) { @@ -416,3 +446,50 @@ leftmostvalue_numeric(void) } GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp) + +/* + * Use a similar trick to that used for numeric for enums, since we don't + * actually know the leftmost value of any enum without knowing the concrete + * type, so we use a dummy leftmost value of InvalidOid. + */ + + +#define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid) + +PG_FUNCTION_INFO_V1(gin_enum_cmp); + +Datum +gin_enum_cmp(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + int res = 0; + + if (ENUM_IS_LEFTMOST(a)) + { + res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1; + } + else if (ENUM_IS_LEFTMOST(b)) + { + res = 1; + } + else + { + res = DatumGetInt32(CallerContextFunctionCall2( + enum_cmp, + fcinfo->flinfo, + PG_GET_COLLATION(), + ObjectIdGetDatum(a), + ObjectIdGetDatum(b))); + } + + PG_RETURN_INT32(res); +} + +static Datum +leftmostvalue_enum(void) +{ + return ObjectIdGetDatum(InvalidOid); +} + +GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp) diff --git a/contrib/btree_gin/btree_gin.control b/contrib/btree_gin/btree_gin.control index 3b2cb2d..d96436e 100644 --- a/contrib/btree_gin/btree_gin.control +++ b/contrib/btree_gin/btree_gin.control @@ -1,5 +1,5 @@ # btree_gin extension comment = 'support for indexing common datatypes in GIN' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/btree_gin' relocatable = true diff --git a/contrib/btree_gin/expected/enum.out b/contrib/btree_gin/expected/enum.out new file mode 100644 index 0000000..bad1cc0 --- /dev/null +++ b/contrib/btree_gin/expected/enum.out @@ -0,0 +1,63 @@ +set enable_seqscan=off; +CREATE TYPE rainbow AS ENUM ('r','o','y','g','b','i','v'); +CREATE TABLE test_enum ( + i rainbow +); +INSERT INTO test_enum VALUES ('v'),('y'),('r'),('g'),('o'),('i'),('b'); +CREATE INDEX idx_enum ON test_enum USING gin (i); +SELECT * FROM test_enum WHERE i<'g'::rainbow ORDER BY i; + i +--- + r + o + y +(3 rows) + +SELECT * FROM test_enum WHERE i<='g'::rainbow ORDER BY i; + i +--- + r + o + y + g +(4 rows) + +SELECT * FROM test_enum WHERE i='g'::rainbow ORDER BY i; + i +--- + g +(1 row) + +SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; + i +--- + g + b + i + v +(4 rows) + +SELECT * FROM test_enum WHERE i>'g'::rainbow ORDER BY i; + i +--- + b + i + v +(3 rows) + +explain (costs off) SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; + QUERY PLAN +----------------------------------------------- + Sort + Sort Key: i + -> Bitmap Heap Scan on test_enum + Recheck Cond: (i >= 'g'::rainbow) + -> Bitmap Index Scan on idx_enum + Index Cond: (i >= 'g'::rainbow) +(6 rows) + +-- make sure we handle the non-evenly-numbered oid case for enums +create type e as enum ('0', '2', '3'); +alter type e add value '1' after '0'; +create table t as select (i % 4)::text::e from generate_series(0, 100000) as i; +create index on t using gin (e); diff --git a/contrib/btree_gin/sql/enum.sql b/contrib/btree_gin/sql/enum.sql new file mode 100644 index 0000000..6db2d76 --- /dev/null +++ b/contrib/btree_gin/sql/enum.sql @@ -0,0 +1,26 @@ +set enable_seqscan=off; + +CREATE TYPE rainbow AS ENUM ('r','o','y','g','b','i','v'); + +CREATE TABLE test_enum ( + i rainbow +); + +INSERT INTO test_enum VALUES ('v'),('y'),('r'),('g'),('o'),('i'),('b'); + +CREATE INDEX idx_enum ON test_enum USING gin (i); + +SELECT * FROM test_enum WHERE i<'g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i<='g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i='g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i>'g'::rainbow ORDER BY i; + +explain (costs off) SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; + + +-- make sure we handle the non-evenly-numbered oid case for enums +create type e as enum ('0', '2', '3'); +alter type e add value '1' after '0'; +create table t as select (i % 4)::text::e from generate_series(0, 100000) as i; +create index on t using gin (e); diff --git a/doc/src/sgml/btree-gin.sgml b/doc/src/sgml/btree-gin.sgml index 2b081db..f407e92 100644 --- a/doc/src/sgml/btree-gin.sgml +++ b/doc/src/sgml/btree-gin.sgml @@ -16,7 +16,8 @@ <type>time without time zone</>, <type>date</>, <type>interval</>, <type>oid</>, <type>money</>, <type>"char"</>, <type>varchar</>, <type>text</>, <type>bytea</>, <type>bit</>, - <type>varbit</>, <type>macaddr</>, <type>inet</>, and <type>cidr</>. + <type>varbit</>, <type>macaddr</>, <type>inet</>, <type>cidr</>, + and all <type>enum</> types. </para> <para>
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers