From 585da1d8f6dd8f72a3b5f3d8d6ff0cdb4db90655 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Sat, 3 Feb 2018 02:12:56 +0300
Subject: [PATCH 7/9] Add opclass parameters to contrib/pg_trgm

---
 contrib/pg_trgm/Makefile              |   2 +-
 contrib/pg_trgm/pg_trgm--1.4--1.5.sql |  33 +++++
 contrib/pg_trgm/pg_trgm.control       |   2 +-
 contrib/pg_trgm/trgm.h                |  17 ++-
 contrib/pg_trgm/trgm_gist.c           | 234 +++++++++++++++++++---------------
 doc/src/sgml/pgtrgm.sgml              |   9 ++
 6 files changed, 184 insertions(+), 113 deletions(-)
 create mode 100644 contrib/pg_trgm/pg_trgm--1.4--1.5.sql

diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index dfecc2a..6f452b5 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -4,7 +4,7 @@ MODULE_big = pg_trgm
 OBJS = trgm_op.o trgm_gist.o trgm_gin.o trgm_regexp.o $(WIN32RES)
 
 EXTENSION = pg_trgm
-DATA = pg_trgm--1.3--1.4.sql \
+DATA = pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
 	pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
 	pg_trgm--1.0--1.1.sql pg_trgm--unpackaged--1.0.sql
 PGFILEDESC = "pg_trgm - trigram matching"
diff --git a/contrib/pg_trgm/pg_trgm--1.4--1.5.sql b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
new file mode 100644
index 0000000..9ebb4f1
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
@@ -0,0 +1,33 @@
+/* contrib/pg_trgm/pg_trgm--1.5--1.5.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.5'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already.  In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+  proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+  pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('gtrgm_compress(internal)', '{internal,internal}'),
+('gtrgm_decompress(internal)', '{internal,internal}'),
+('gtrgm_same(gtrgm,gtrgm,internal)', '{gtrgm,gtrgm,internal,internal}'),
+('gtrgm_union(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('gtrgm_picksplit(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_consistent(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}'),
+('gtrgm_distance(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION gtrgm_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'gtrgm_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_trgm_ops USING gist
+ADD FUNCTION 10 (text) gtrgm_options (internal, boolean);
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 3e325dd..d633eac 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,5 +1,5 @@
 # pg_trgm extension
 comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.4'
+default_version = '1.5'
 module_pathname = '$libdir/pg_trgm'
 relocatable = true
diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h
index f0ab50d..acdd83b 100644
--- a/contrib/pg_trgm/trgm.h
+++ b/contrib/pg_trgm/trgm.h
@@ -73,17 +73,16 @@ typedef struct
 #define TRGMHDRSIZE		  (VARHDRSZ + sizeof(uint8))
 
 /* gist */
+#define SIGLEN_DEFAULT	(sizeof(int) * 3)
+#define SIGLEN_MAX		(sizeof(int) * 122)	/* key will toast, so very slow!!! */
 #define BITBYTE 8
-#define SIGLENINT  3			/* >122 => key will toast, so very slow!!! */
-#define SIGLEN	( sizeof(int)*SIGLENINT )
 
-#define SIGLENBIT (SIGLEN*BITBYTE - 1)	/* see makesign */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE - 1)	/* see makesign */
 
-typedef char BITVEC[SIGLEN];
 typedef char *BITVECP;
 
-#define LOOPBYTE \
-			for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+			for (i = 0; i < (siglen); i++)
 
 #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
 #define GETBITBYTE(x,i) ( (((char)(x)) >> (i)) & 0x01 )
@@ -91,8 +90,8 @@ typedef char *BITVECP;
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITBYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 )
 
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
 
 #define ARRKEY			0x01
 #define SIGNKEY			0x02
@@ -102,7 +101,7 @@ typedef char *BITVECP;
 #define ISSIGNKEY(x)	( ((TRGM*)x)->flag & SIGNKEY )
 #define ISALLTRUE(x)	( ((TRGM*)x)->flag & ALLISTRUE )
 
-#define CALCGTSIZE(flag, len) ( TRGMHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(trgm)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( TRGMHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(trgm)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
 #define GETSIGN(x)		( (BITVECP)( (char*)x+TRGMHDRSIZE ) )
 #define GETARR(x)		( (trgm*)( (char*)x+TRGMHDRSIZE ) )
 #define ARRNELEM(x) ( ( VARSIZE(x) - TRGMHDRSIZE )/sizeof(trgm) )
diff --git a/contrib/pg_trgm/trgm_gist.c b/contrib/pg_trgm/trgm_gist.c
index f1e0547..9f653ec 100644
--- a/contrib/pg_trgm/trgm_gist.c
+++ b/contrib/pg_trgm/trgm_gist.c
@@ -5,9 +5,16 @@
 
 #include "trgm.h"
 
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "fmgr.h"
 
+/* gist_trgm_ops opclass options */
+typedef struct TrgmGistOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+} TrgmGistOptions;
 
 typedef struct
 {
@@ -38,6 +45,7 @@ PG_FUNCTION_INFO_V1(gtrgm_union);
 PG_FUNCTION_INFO_V1(gtrgm_same);
 PG_FUNCTION_INFO_V1(gtrgm_penalty);
 PG_FUNCTION_INFO_V1(gtrgm_picksplit);
+PG_FUNCTION_INFO_V1(gtrgm_options);
 
 /* Number of one-bits in an unsigned byte */
 static const uint8 number_of_ones[256] = {
@@ -74,20 +82,41 @@ gtrgm_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+static TRGM *
+gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
+{
+	int			flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
+	int			size = CALCGTSIZE(flag, siglen);
+	TRGM	   *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if (!isalltrue)
+	{
+		if (sign)
+			memcpy(GETSIGN(res), sign, siglen);
+		else
+			memset(GETSIGN(res), 0, siglen);
+	}
+
+	return res;
+}
+
 static void
-makesign(BITVECP sign, TRGM *a)
+makesign(BITVECP sign, TRGM *a, int siglen)
 {
 	int32		k,
 				len = ARRNELEM(a);
 	trgm	   *ptr = GETARR(a);
 	int32		tmp = 0;
 
-	MemSet((void *) sign, 0, sizeof(BITVEC));
-	SETBIT(sign, SIGLENBIT);	/* set last unused bit */
+	MemSet((void *) sign, 0, siglen);
+	SETBIT(sign, SIGLENBIT(siglen));	/* set last unused bit */
 	for (k = 0; k < len; k++)
 	{
 		CPTRGM(((char *) &tmp), ptr + k);
-		HASH(sign, tmp);
+		HASH(sign, tmp, siglen);
 	}
 }
 
@@ -95,6 +124,8 @@ Datum
 gtrgm_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(1);
+	int			siglen = options->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
@@ -111,22 +142,17 @@ gtrgm_compress(PG_FUNCTION_ARGS)
 	else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
 			 !ISALLTRUE(DatumGetPointer(entry->key)))
 	{
-		int32		i,
-					len;
+		int32		i;
 		TRGM	   *res;
 		BITVECP		sign = GETSIGN(DatumGetPointer(entry->key));
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(retval);
 		}
 
-		len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
-		res = (TRGM *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = SIGNKEY | ALLISTRUE;
-
+		res = gtrgm_alloc(true, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -160,7 +186,7 @@ gtrgm_decompress(PG_FUNCTION_ARGS)
 }
 
 static int32
-cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
+cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen)
 {
 	int32		count = 0;
 	int32		k,
@@ -171,7 +197,7 @@ cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
 	for (k = 0; k < len; k++)
 	{
 		CPTRGM(((char *) &tmp), ptr + k);
-		count += GETBIT(sign, HASHVAL(tmp));
+		count += GETBIT(sign, HASHVAL(tmp, siglen));
 	}
 
 	return count;
@@ -186,6 +212,8 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	TRGM	   *key = (TRGM *) DatumGetPointer(entry->key);
 	TRGM	   *qtrg;
 	bool		res;
@@ -313,7 +341,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
 			}
 			else
 			{					/* non-leaf contains signature */
-				int32		count = cnt_sml_sign_common(qtrg, GETSIGN(key));
+				int32		count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
 				int32		len = ARRNELEM(qtrg);
 
 				if (len == 0)
@@ -355,7 +383,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
 				for (k = 0; k < len; k++)
 				{
 					CPTRGM(((char *) &tmp), ptr + k);
-					if (!GETBIT(sign, HASHVAL(tmp)))
+					if (!GETBIT(sign, HASHVAL(tmp, siglen)))
 					{
 						res = false;
 						break;
@@ -408,7 +436,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
 					for (k = 0; k < len; k++)
 					{
 						CPTRGM(((char *) &tmp), ptr + k);
-						check[k] = GETBIT(sign, HASHVAL(tmp));
+						check[k] = GETBIT(sign, HASHVAL(tmp, siglen));
 					}
 					res = trigramsMatchGraph(cache->graph, check);
 					pfree(check);
@@ -438,6 +466,8 @@ gtrgm_distance(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	TRGM	   *key = (TRGM *) DatumGetPointer(entry->key);
 	TRGM	   *qtrg;
 	float8		res;
@@ -495,7 +525,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
 			}
 			else
 			{					/* non-leaf contains signature */
-				int32		count = cnt_sml_sign_common(qtrg, GETSIGN(key));
+				int32		count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
 				int32		len = ARRNELEM(qtrg);
 
 				res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len);
@@ -511,7 +541,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, TRGM *add)
+unionkey(BITVECP sbase, TRGM *add, int siglen)
 {
 	int32		i;
 
@@ -522,7 +552,7 @@ unionkey(BITVECP sbase, TRGM *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -533,7 +563,7 @@ unionkey(BITVECP sbase, TRGM *add)
 		for (i = 0; i < ARRNELEM(add); i++)
 		{
 			CPTRGM(((char *) &tmp), ptr + i);
-			HASH(sbase, tmp);
+			HASH(sbase, tmp, siglen);
 		}
 	}
 	return 0;
@@ -546,29 +576,23 @@ gtrgm_union(PG_FUNCTION_ARGS)
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int32		len = entryvec->n;
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
+	TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	int32		i;
-	int32		flag = 0;
-	TRGM	   *result;
+	TRGM	   *result = gtrgm_alloc(false, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < len; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = ALLISTRUE;
+			result->flag = ALLISTRUE;
+			SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
 			break;
 		}
 	}
 
-	flag |= SIGNKEY;
-	len = CALCGTSIZE(flag, 0);
-	result = (TRGM *) palloc(len);
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!ISALLTRUE(result))
-		memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
-	*size = len;
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -579,6 +603,8 @@ gtrgm_same(PG_FUNCTION_ARGS)
 	TRGM	   *a = (TRGM *) PG_GETARG_POINTER(0);
 	TRGM	   *b = (TRGM *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -595,7 +621,7 @@ gtrgm_same(PG_FUNCTION_ARGS)
 						sb = GETSIGN(b);
 
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -632,24 +658,24 @@ gtrgm_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
 	int32		size = 0,
 				i;
 
-	LOOPBYTE
+	LOOPBYTE(siglen)
 		size += number_of_ones[(unsigned char) sign[i]];
 	return size;
 }
 
 static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
 {
 	int			i,
 				diff,
 				dist = 0;
 
-	LOOPBYTE
+	LOOPBYTE(siglen)
 	{
 		diff = (unsigned char) (a[i] ^ b[i]);
 		dist += number_of_ones[diff];
@@ -658,19 +684,19 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(TRGM *a, TRGM *b)
+hemdist(TRGM *a, TRGM *b, int siglen)
 {
 	if (ISALLTRUE(a))
 	{
 		if (ISALLTRUE(b))
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(GETSIGN(b));
+			return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
 	}
 	else if (ISALLTRUE(b))
-		return SIGLENBIT - sizebitvec(GETSIGN(a));
+		return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
 
-	return hemdistsign(GETSIGN(a), GETSIGN(b));
+	return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
 }
 
 Datum
@@ -679,6 +705,8 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
 	GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
 	GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 	TRGM	   *origval = (TRGM *) DatumGetPointer(origentry->key);
 	TRGM	   *newval = (TRGM *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -688,7 +716,7 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
 	if (ISARRKEY(newval))
 	{
 		char	   *cache = (char *) fcinfo->flinfo->fn_extra;
-		TRGM	   *cachedVal = (TRGM *) (cache + MAXALIGN(sizeof(BITVEC)));
+		TRGM	   *cachedVal = (TRGM *) (cache + MAXALIGN(siglen));
 		Size		newvalsize = VARSIZE(newval);
 		BITVECP		sign;
 
@@ -702,12 +730,12 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
 			char	   *newcache;
 
 			newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
-										  MAXALIGN(sizeof(BITVEC)) +
+										  MAXALIGN(siglen) +
 										  newvalsize);
 
-			makesign((BITVECP) newcache, newval);
+			makesign((BITVECP) newcache, newval, siglen);
 
-			cachedVal = (TRGM *) (newcache + MAXALIGN(sizeof(BITVEC)));
+			cachedVal = (TRGM *) (newcache + MAXALIGN(siglen));
 			memcpy(cachedVal, newval, newvalsize);
 
 			if (cache)
@@ -719,31 +747,32 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
 		sign = (BITVECP) cache;
 
 		if (ISALLTRUE(origval))
-			*penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+			*penalty = ((float) (SIGLENBIT(siglen) - sizebitvec(sign, siglen))) / (float) (SIGLENBIT(siglen) + 1);
 		else
-			*penalty = hemdistsign(sign, orig);
+			*penalty = hemdistsign(sign, orig, siglen);
 	}
 	else
-		*penalty = hemdist(origval, newval);
+		*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
 typedef struct
 {
 	bool		allistrue;
-	BITVEC		sign;
+	BITVECP		sign;
 } CACHESIGN;
 
 static void
-fillcache(CACHESIGN *item, TRGM *key)
+fillcache(CACHESIGN *item, TRGM *key, BITVECP sign, int siglen)
 {
 	item->allistrue = false;
+	item->sign = sign;
 	if (ISARRKEY(key))
-		makesign(item->sign, key);
+		makesign(item->sign, key, siglen);
 	else if (ISALLTRUE(key))
 		item->allistrue = true;
 	else
-		memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC));
+		memcpy((void *) item->sign, (void *) GETSIGN(key), siglen);
 }
 
 #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
@@ -764,19 +793,19 @@ comparecost(const void *a, const void *b)
 
 
 static int
-hemdistcache(CACHESIGN *a, CACHESIGN *b)
+hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
 {
 	if (a->allistrue)
 	{
 		if (b->allistrue)
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(b->sign);
+			return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
 	}
 	else if (b->allistrue)
-		return SIGLENBIT - sizebitvec(a->sign);
+		return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
 
-	return hemdistsign(a->sign, b->sign);
+	return hemdistsign(a->sign, b->sign, siglen);
 }
 
 Datum
@@ -785,6 +814,8 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	OffsetNumber maxoff = entryvec->n - 2;
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	OffsetNumber k,
 				j;
 	TRGM	   *datum_l,
@@ -803,19 +834,23 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 	BITVECP		ptr;
 	int			i;
 	CACHESIGN  *cache;
+	char	   *cache_sign;
 	SPLITCOST  *costvector;
 
 	/* cache the sign data for each existing item */
 	cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
+	cache_sign = palloc(siglen * (maxoff + 2));
+
 	for (k = FirstOffsetNumber; k <= maxoff; k = OffsetNumberNext(k))
-		fillcache(&cache[k], GETENTRY(entryvec, k));
+		fillcache(&cache[k], GETENTRY(entryvec, k), &cache_sign[siglen * k],
+				  siglen);
 
 	/* now find the two furthest-apart items */
 	for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
 	{
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+			size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -840,44 +875,22 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 	v->spl_nright = 0;
 
 	/* form initial .. */
-	if (cache[seed_1].allistrue)
-	{
-		datum_l = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_l->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (TRGM *) palloc(CALCGTSIZE(SIGNKEY, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0));
-		datum_l->flag = SIGNKEY;
-		memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC));
-	}
-	if (cache[seed_2].allistrue)
-	{
-		datum_r = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_r->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (TRGM *) palloc(CALCGTSIZE(SIGNKEY, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0));
-		datum_r->flag = SIGNKEY;
-		memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC));
-	}
+	datum_l = gtrgm_alloc(cache[seed_1].allistrue, siglen, cache[seed_1].sign);
+	datum_r = gtrgm_alloc(cache[seed_2].allistrue, siglen, cache[seed_2].sign);
 
 	union_l = GETSIGN(datum_l);
 	union_r = GETSIGN(datum_r);
 	maxoff = OffsetNumberNext(maxoff);
-	fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff));
+	fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff),
+			  &cache_sign[siglen * maxoff], siglen);
+
 	/* sort before ... */
 	costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
 	for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
 	{
 		costvector[j - 1].pos = j;
-		size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]));
-		size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]));
+		size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
+		size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
 		costvector[j - 1].cost = abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -903,36 +916,36 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_l) && cache[j].allistrue)
 				size_alpha = 0;
 			else
-				size_alpha = SIGLENBIT - sizebitvec(
-													(cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
-					);
+				size_alpha = SIGLENBIT(siglen) -
+					sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign),
+							   siglen);
 		}
 		else
-			size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+			size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
 
 		if (ISALLTRUE(datum_r) || cache[j].allistrue)
 		{
 			if (ISALLTRUE(datum_r) && cache[j].allistrue)
 				size_beta = 0;
 			else
-				size_beta = SIGLENBIT - sizebitvec(
-												   (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
-					);
+				size_beta = SIGLENBIT(siglen) -
+					sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign),
+							   siglen);
 		}
 		else
-			size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+			size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
 		{
 			if (ISALLTRUE(datum_l) || cache[j].allistrue)
 			{
 				if (!ISALLTRUE(datum_l))
-					MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+					MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
 			}
 			else
 			{
 				ptr = cache[j].sign;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -943,12 +956,12 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_r) || cache[j].allistrue)
 			{
 				if (!ISALLTRUE(datum_r))
-					MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC));
+					MemSet((void *) GETSIGN(datum_r), 0xff, siglen);
 			}
 			else
 			{
 				ptr = cache[j].sign;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -962,3 +975,20 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(v);
 }
+
+Datum
+gtrgm_options(PG_FUNCTION_ARGS)
+{
+	Datum		raw_options = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+	relopt_int	siglen =
+		{ {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+			SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(TrgmGistOptions, siglen) };
+	TrgmGistOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(TrgmGistOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/pgtrgm.sgml b/doc/src/sgml/pgtrgm.sgml
index 83b0033..a4151aa 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -368,6 +368,15 @@ CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
   </para>
 
   <para>
+   GiST opclass <literal>gist_trgm_ops</literal> has one optional parameter
+   <literal>siglen</literal> for specifying signature length in bytes
+   (default signature length is 12 bytes, maximal length is 488 bytes):
+<programlisting>  
+CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));
+</programlisting>
+  </para>
+
+  <para>
    At this point, you will have an index on the <structfield>t</structfield> column that
    you can use for similarity searching.  A typical query is
 <programlisting>
-- 
2.7.4

