[PATCH] Opclass parameters

Started by Nikita Glukhovalmost 8 years ago34 messages
#1Nikita Glukhov
n.gluhov@postgrespro.ru
8 attachment(s)

Hi hackers.

I would like to present patch set implementing opclass parameters.

This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdf

A analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.

Opclass parameters can give user ability to:
* Define the values of the constants that are hardcoded now in the opclasses
depending on the indexed data.
* Specify what to index for non-atomic data types (arrays, json[b], tsvector).
Partial index can only filter whole rows.
* Specify what indexing algorithm to use depending on the indexed data.

Description of patches:

1. Infrastructure for opclass parameters.

SQL grammar is changed only for CREATE INDEX statement: parenthesized parameters
in reloptions format are added after column's opclass name. Default opclass can
be specified with DEFAULT keyword:

CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);

Example for contrib/intarray:

CREATE INDEX ON arrays USING gist (
arr gist__intbig_ops (siglen = 32),
arr DEFAULT (numranges = 100)
);

\d arrays
Table "public.arrays"
Column | Type | Collation | Nullable | Default
--------+-----------+-----------+----------+---------
arr | integer[] | | |
Indexes:
"arrays_arr_arr1_idx" gist (arr gist__intbig_ops (siglen='32'), arr gist__int_ops (numranges='100'))

I decided to store parameters in text[] column pg_index.indoptions near to
existing columns like indkey, indcollation, indclass, indoption. I-th element of
indoptions[] is a text array of parameters of i-th index column serialized into
a string. Each parameter is stored as 'name=value' text string like ordinal
reloptions. There is another way to store opclass parameters: store them in
the existing column pg_attribute.attoptions (as it was done by Nikolay Shaplov)
and there will be no need to serialize reloptions to a text array element.

Example query showing how parameters are stored:

SELECT ARRAY(
SELECT (pg_identify_object('pg_opclass'::regclass, opcid, 0)).name
FROM unnest(indclass::int[]) opcid
) indclass, indoptions
FROM pg_index
WHERE indoptions IS NOT NULL;

indclass | indoptions
----------------------------------+------------------------------------
{gist__intbig_ops,gist__int_ops} | {"{siglen=32}","{numranges=100}"}
{jsonb_path_ops} | {"{projection=$.tags[*].term}"}
(2 rows)

Each access method supporting opclass parameters specifies amopclassoptions
routine for transformation of text[] parameters datum into a binary bytea
structure which will be cached in RelationData and IndexOptInfo structures:

typedef bytea *(*amopclassoptions_function) (
Relation index, AttrNumber colnum, Datum indoptions, bool validate
);

If access method wants simply to delegate parameters processing to one of
column opclass's support functions, then it can use
index_opclass_options_generic() subroutine in its amopclassoptions
implementation:

bytea *index_opclass_options_generic(
Relation relation, AttrNumber attnum, uint16 procnum,
Datum indoptions, bool validate
);

This support functions must have the following signature:
internal (options internal, validate bool).
Opclass parameters are passed as a text[] reloptions datum, returned pointer to
a bytea structure with parsed parameter values.

Opclass can use new functions parseLocalRelOptions(),
parseAndFillLocalRelOptions() for reloptions parsing. This functions differ
from the standard parseRelOptions() in that a local array of reloptions
descriptions is passed here, not a global relopt_kind. But it seems that
reloptions processing still needs deeper refactoring like the one already done
by Nikolay Shaplov (/messages/by-id/2146419.veIEZdk4E4@x200m

2. Opclass parameters support in GiST indices.

Parametrized GiST opclass specifies optional 10th (GIST_OPCLASSOPT_PROC)
support function with the following signature:
internal (options internal, validate bool)

Returned parsed bytea pointer with parameters will be passed to all support
functions in the last argument.

3. Opclass parameters support in GIN indices.

Everything is the same as for GiST, except for the optional support
function number which is 7 (GIN_OPCLASSOPTIONS_PROC) here.

4. Opclass parameters for GiST tsvector_ops
5. Opclass parameters for contrib/intarray
6. Opclass parameters for contrib/ltree
7. Opclass parameters for contrib/pg_trgm
8. Opclass parameters for contrib/hstore

This 5 patches for GiST opclasses are very similar: added optional 'siglen'
parameter for specifying signature length. Default signature length is left
equal to the hardcoded value that was here before. Also added 'numranges'
parameter for gist__int_ops.

We also have two more complex unfinished patches for GIN opclasses which
should be posted in separate threads:

* tsvector_ops: added parameter 'weights' for specification of indexed
lexeme's weight groups. This parameter can reduce index size and its
build/update time and can also eliminate recheck. By default, all weights
are indexed within the same group.

* jsonb_ops: added jsonpath parameter 'projection' for specification of
indexed paths in jsonb (this patch depends on SQL/JSON jsonpath patch).
Analogically to tsvector_ops, this parameter can reduce index size and its
build/update time, but can not eliminate recheck.

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0005-opclass-parameters-contrib_intarray-v01.patchtext/x-patch; name=0005-opclass-parameters-contrib_intarray-v01.patchDownload
diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile
index 2505294..d99d977 100644
--- a/contrib/intarray/Makefile
+++ b/contrib/intarray/Makefile
@@ -5,8 +5,8 @@ OBJS = _int_bool.o _int_gist.o _int_op.o _int_tool.o \
 	_intbig_gist.o _int_gin.o _int_selfuncs.o $(WIN32RES)
 
 EXTENSION = intarray
-DATA = intarray--1.2.sql intarray--1.1--1.2.sql intarray--1.0--1.1.sql \
-	intarray--unpackaged--1.0.sql
+DATA = intarray--1.3.sql intarray--1.2-1.3.sql intarray--1.1--1.2.sql \
+	intarray--1.0--1.1.sql intarray--unpackaged--1.0.sql
 PGFILEDESC = "intarray - functions and operators for arrays of integers"
 
 REGRESS = _int
diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index b689eb7..87d21e9 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -8,7 +8,15 @@
 #include "utils/memutils.h"
 
 /* number ranges for compression */
-#define MAXNUMRANGE 100
+#define G_INT_NUMRANGES_DEFAULT		100
+#define G_INT_NUMRANGES_MAX			1000
+
+/* gist_int_ops opclass options */
+typedef struct IntArrayOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			num_ranges;		/* number of ranges */
+}	IntArrayOptions;
 
 /* useful macros for accessing int4 arrays */
 #define ARRPTR(x)  ( (int32 *) ARR_DATA_PTR(x) )
@@ -47,15 +55,14 @@
 
 
 /* bigint defines */
-#define SIGLENINT  63			/* >122 => key will toast, so very slow!!! */
-#define SIGLEN	( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
+#define SIGLEN_DEFAULT		(63 * 4)
+#define SIGLEN_MAX			(122 * 4)		/* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen)	((siglen) * BITS_PER_BYTE)
 
-typedef char BITVEC[SIGLEN];
 typedef char *BITVECP;
 
-#define LOOPBYTE \
-			for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+			for (i = 0; i < siglen; i++)
 
 /* beware of multiple evaluation of arguments to these macros! */
 #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
@@ -63,8 +70,15 @@ typedef char *BITVECP;
 #define CLRBIT(x,i)   GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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))
+
+/* gist_intbig_ops opclass options */
+typedef struct IntArrayBigOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+}	IntArrayBigOptions;
 
 /*
  * type of index key
@@ -81,7 +95,7 @@ typedef struct
 #define ISALLTRUE(x)	( ((GISTTYPE*)x)->flag & ALLISTRUE )
 
 #define GTHDRSIZE		(VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
 
 #define GETSIGN(x)		( (BITVECP)( (char*)x+GTHDRSIZE ) )
 
@@ -109,7 +123,7 @@ bool		inner_int_contains(ArrayType *a, ArrayType *b);
 ArrayType  *inner_int_union(ArrayType *a, ArrayType *b);
 ArrayType  *inner_int_inter(ArrayType *a, ArrayType *b);
 void		rt__int_size(ArrayType *a, float *size);
-void		gensign(BITVEC sign, int *a, int len);
+void		gensign(BITVECP sign, int *a, int len, int siglen);
 
 
 /*****************************************************************************
@@ -155,7 +169,7 @@ typedef struct QUERYTYPE
 #define PG_GETARG_QUERYTYPE_P(n)	  DatumGetQueryTypeP(PG_GETARG_DATUM(n))
 #define PG_GETARG_QUERYTYPE_P_COPY(n) DatumGetQueryTypePCopy(PG_GETARG_DATUM(n))
 
-bool		signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot);
+bool		signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot);
 bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index 91e2a80..4e7d55a 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -233,7 +233,7 @@ typedef struct
  * is there value 'val' in (sorted) array or not ?
  */
 static bool
-checkcondition_arr(void *checkval, ITEM *item)
+checkcondition_arr(void *checkval, ITEM *item, void *options)
 {
 	int32	   *StopLow = ((CHKVAL *) checkval)->arrb;
 	int32	   *StopHigh = ((CHKVAL *) checkval)->arre;
@@ -255,42 +255,42 @@ checkcondition_arr(void *checkval, ITEM *item)
 }
 
 static bool
-checkcondition_bit(void *checkval, ITEM *item)
+checkcondition_bit(void *checkval, ITEM *item, void *siglen)
 {
-	return GETBIT(checkval, HASHVAL(item->val));
+	return GETBIT(checkval, HASHVAL(item->val, (int)(intptr_t) siglen));
 }
 
 /*
  * evaluate boolean expression, using chkcond() to test the primitive cases
  */
 static bool
-execute(ITEM *curitem, void *checkval, bool calcnot,
-		bool (*chkcond) (void *checkval, ITEM *item))
+execute(ITEM *curitem, void *checkval, void *options, bool calcnot,
+		bool (*chkcond) (void *checkval, ITEM *item, void *options))
 {
 	/* since this function recurses, it could be driven to stack overflow */
 	check_stack_depth();
 
 	if (curitem->type == VAL)
-		return (*chkcond) (checkval, curitem);
+		return (*chkcond) (checkval, curitem, options);
 	else if (curitem->val == (int32) '!')
 	{
 		return calcnot ?
-			((execute(curitem - 1, checkval, calcnot, chkcond)) ? false : true)
+			((execute(curitem - 1, checkval, options, calcnot, chkcond)) ? false : true)
 			: true;
 	}
 	else if (curitem->val == (int32) '&')
 	{
-		if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
-			return execute(curitem - 1, checkval, calcnot, chkcond);
+		if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
+			return execute(curitem - 1, checkval, options, calcnot, chkcond);
 		else
 			return false;
 	}
 	else
 	{							/* |-operator */
-		if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
+		if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
 			return true;
 		else
-			return execute(curitem - 1, checkval, calcnot, chkcond);
+			return execute(curitem - 1, checkval, options, calcnot, chkcond);
 	}
 }
 
@@ -298,10 +298,10 @@ execute(ITEM *curitem, void *checkval, bool calcnot,
  * signconsistent & execconsistent called by *_consistent
  */
 bool
-signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot)
+signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot)
 {
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) sign, calcnot,
+				   (void *) sign, (void *)(intptr_t) siglen, calcnot,
 				   checkcondition_bit);
 }
 
@@ -315,7 +315,7 @@ execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot)
 	chkval.arrb = ARRPTR(array);
 	chkval.arre = chkval.arrb + ARRNELEMS(array);
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) &chkval, calcnot,
+				   (void *) &chkval, NULL, calcnot,
 				   checkcondition_arr);
 }
 
@@ -326,7 +326,7 @@ typedef struct
 } GinChkVal;
 
 static bool
-checkcondition_gin(void *checkval, ITEM *item)
+checkcondition_gin(void *checkval, ITEM *item, void *options)
 {
 	GinChkVal  *gcv = (GinChkVal *) checkval;
 
@@ -357,7 +357,7 @@ gin_bool_consistent(QUERYTYPE *query, bool *check)
 	}
 
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) &gcv, true,
+				   (void *) &gcv, NULL, true,
 				   checkcondition_gin);
 }
 
@@ -429,7 +429,7 @@ boolop(PG_FUNCTION_ARGS)
 	chkval.arrb = ARRPTR(val);
 	chkval.arre = chkval.arrb + ARRNELEMS(val);
 	result = execute(GETQUERY(query) + query->size - 1,
-					 &chkval, true,
+					 &chkval, NULL, true,
 					 checkcondition_arr);
 	pfree(val);
 
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index 911d180..9de7f55 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -6,6 +6,7 @@
 #include <limits.h>
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 
 #include "_int.h"
@@ -22,6 +23,7 @@ PG_FUNCTION_INFO_V1(g_int_penalty);
 PG_FUNCTION_INFO_V1(g_int_picksplit);
 PG_FUNCTION_INFO_V1(g_int_union);
 PG_FUNCTION_INFO_V1(g_int_same);
+PG_FUNCTION_INFO_V1(g_int_options);
 
 
 /*
@@ -139,6 +141,7 @@ Datum
 g_int_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
 	GISTENTRY  *retval;
 	ArrayType  *r;
 	int			len;
@@ -153,9 +156,9 @@ g_int_compress(PG_FUNCTION_ARGS)
 		CHECKARRVALID(r);
 		PREPAREARR(r);
 
-		if (ARRNELEMS(r) >= 2 * MAXNUMRANGE)
+		if (ARRNELEMS(r) >= 2 * opts->num_ranges)
 			elog(NOTICE, "input array is too big (%d maximum allowed, %d current), use gist__intbig_ops opclass instead",
-				 2 * MAXNUMRANGE - 1, ARRNELEMS(r));
+				 2 * opts->num_ranges - 1, ARRNELEMS(r));
 
 		retval = palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(r),
@@ -178,7 +181,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 		PG_RETURN_POINTER(entry);
 	}
 
-	if ((len = ARRNELEMS(r)) >= 2 * MAXNUMRANGE)
+	if ((len = ARRNELEMS(r)) >= 2 * opts->num_ranges)
 	{							/* compress */
 		if (r == (ArrayType *) DatumGetPointer(entry->key))
 			r = DatumGetArrayTypePCopy(entry->key);
@@ -191,7 +194,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 
 		len *= 2;
 		cand = 1;
-		while (len > MAXNUMRANGE * 2)
+		while (len > opts->num_ranges * 2)
 		{
 			min = INT_MAX;
 			for (i = 2; i < len; i += 2)
@@ -217,6 +220,7 @@ Datum
 g_int_decompress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
 	GISTENTRY  *retval;
 	ArrayType  *r;
 	int		   *dr,
@@ -245,7 +249,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
 
 	lenin = ARRNELEMS(in);
 
-	if (lenin < 2 * MAXNUMRANGE)
+	if (lenin < 2 * opts->num_ranges)
 	{							/* not compressed value */
 		if (in != (ArrayType *) DatumGetPointer(entry->key))
 		{
@@ -542,3 +546,20 @@ g_int_picksplit(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(v);
 }
+
+Datum
+g_int_options(PG_FUNCTION_ARGS)
+{
+	Datum		raw_options = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+	relopt_int	siglen =
+		{ {"numranges", "number of ranges for compression", 0, 0, 9, RELOPT_TYPE_INT },
+			G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(IntArrayOptions, num_ranges) };
+	IntArrayOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(IntArrayOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index ee8fb64..d7c5317 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -313,14 +313,14 @@ _int_unique(ArrayType *r)
 }
 
 void
-gensign(BITVEC sign, int *a, int len)
+gensign(BITVECP sign, int *a, int len, int siglen)
 {
 	int			i;
 
 	/* we assume that the sign vector is previously zeroed */
 	for (i = 0; i < len; i++)
 	{
-		HASH(sign, *a);
+		HASH(sign, *a, siglen);
 		a++;
 	}
 }
diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c
index de7bc82..4d2b3a2 100644
--- a/contrib/intarray/_intbig_gist.c
+++ b/contrib/intarray/_intbig_gist.c
@@ -4,6 +4,7 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 
 #include "_int.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(g_intbig_penalty);
 PG_FUNCTION_INFO_V1(g_intbig_picksplit);
 PG_FUNCTION_INFO_V1(g_intbig_union);
 PG_FUNCTION_INFO_V1(g_intbig_same);
+PG_FUNCTION_INFO_V1(g_intbig_options);
 
 /* Number of one-bits in an unsigned byte */
 static const uint8 number_of_ones[256] = {
@@ -61,12 +63,33 @@ _intbig_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+static GISTTYPE *
+_intbig_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+	int			flag = allistrue ? ALLISTRUE : 0;
+	int			size = CALCGTSIZE(flag, siglen);
+	GISTTYPE   *res = (GISTTYPE *) palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if (!allistrue)
+	{
+		if (sign)
+			memcpy(GETSIGN(res), sign, siglen);
+		else
+			memset(GETSIGN(res), 0, siglen);
+	}
+
+	return res;
+}
+
 
 /*********************************************************************
 ** intbig functions
 *********************************************************************/
 static bool
-_intbig_overlap(GISTTYPE *a, ArrayType *b)
+_intbig_overlap(GISTTYPE *a, ArrayType *b, int siglen)
 {
 	int			num = ARRNELEMS(b);
 	int32	   *ptr = ARRPTR(b);
@@ -75,7 +98,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
 
 	while (num--)
 	{
-		if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+		if (GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
 			return true;
 		ptr++;
 	}
@@ -84,7 +107,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
 }
 
 static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglen)
 {
 	int			num = ARRNELEMS(b);
 	int32	   *ptr = ARRPTR(b);
@@ -93,7 +116,7 @@ _intbig_contains(GISTTYPE *a, ArrayType *b)
 
 	while (num--)
 	{
-		if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+		if (!GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
 			return false;
 		ptr++;
 	}
@@ -107,6 +130,8 @@ g_intbig_same(PG_FUNCTION_ARGS)
 	GISTTYPE   *a = (GISTTYPE *) PG_GETARG_POINTER(0);
 	GISTTYPE   *b = (GISTTYPE *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+	int			siglen = opts->siglen;
 
 	if (ISALLTRUE(a) && ISALLTRUE(b))
 		*result = true;
@@ -121,7 +146,7 @@ g_intbig_same(PG_FUNCTION_ARGS)
 					sb = GETSIGN(b);
 
 		*result = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -137,6 +162,8 @@ Datum
 g_intbig_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(1);
+	int			siglen = opts->siglen;
 
 	if (entry->leafkey)
 	{
@@ -144,7 +171,7 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 		ArrayType  *in = DatumGetArrayTypeP(entry->key);
 		int32	   *ptr;
 		int			num;
-		GISTTYPE   *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		GISTTYPE   *res = _intbig_alloc(false, siglen, NULL);
 
 		CHECKARRVALID(in);
 		if (ARRISEMPTY(in))
@@ -157,11 +184,10 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 			ptr = ARRPTR(in);
 			num = ARRNELEMS(in);
 		}
-		SET_VARSIZE(res, CALCGTSIZE(0));
 
 		while (num--)
 		{
-			HASH(GETSIGN(res), *ptr);
+			HASH(GETSIGN(res), *ptr, siglen);
 			ptr++;
 		}
 
@@ -182,16 +208,13 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 		BITVECP		sign = GETSIGN(DatumGetPointer(entry->key));
 		GISTTYPE   *res;
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(entry);
 		}
 
-		res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
-		SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
-		res->flag = ALLISTRUE;
-
+		res = _intbig_alloc(true, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -205,24 +228,24 @@ g_intbig_compress(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];
@@ -231,19 +254,19 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *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
@@ -253,14 +276,14 @@ g_intbig_decompress(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = GETSIGN(add);
 
 	if (ISALLTRUE(add))
 		return 1;
-	LOOPBYTE
+	LOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -270,29 +293,23 @@ g_intbig_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	GISTTYPE   *result;
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+	int			siglen = opts->siglen;
+	int32		i;
+	GISTTYPE   *result = _intbig_alloc(false, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; 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;
 		}
 	}
 
-	len = CALCGTSIZE(flag);
-	result = (GISTTYPE *) 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);
 }
@@ -305,8 +322,10 @@ g_intbig_penalty(PG_FUNCTION_ARGS)
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
 	GISTTYPE   *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
 	GISTTYPE   *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+	int			siglen = opts->siglen;
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -329,6 +348,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+	int			siglen = opts->siglen;
 	OffsetNumber k,
 				j;
 	GISTTYPE   *datum_l,
@@ -361,7 +382,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -383,32 +404,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_l, GTHDRSIZE);
-		datum_l->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC));
-	}
-	if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_r, GTHDRSIZE);
-		datum_r->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
-	}
+	datum_l = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_1)));
+	datum_r = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_2)));
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -417,8 +416,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = Abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -442,20 +441,20 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
 		{
 			if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -466,12 +465,12 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -497,6 +496,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(5);
+	int			siglen = opts->siglen;
 	bool		retval;
 
 	/* All cases served by this function are inexact */
@@ -509,6 +510,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	{
 		retval = signconsistent((QUERYTYPE *) query,
 								GETSIGN(DatumGetPointer(entry->key)),
+								siglen,
 								false);
 		PG_FREE_IF_COPY(query, 1);
 		PG_RETURN_BOOL(retval);
@@ -519,7 +521,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	switch (strategy)
 	{
 		case RTOverlapStrategyNumber:
-			retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+			retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+									 query, siglen);
 			break;
 		case RTSameStrategyNumber:
 			if (GIST_LEAF(entry))
@@ -527,22 +530,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				int			i,
 							num = ARRNELEMS(query);
 				int32	   *ptr = ARRPTR(query);
-				BITVEC		qp;
-				BITVECP		dq,
+				BITVECP		dq = palloc0(siglen),
 							de;
 
-				memset(qp, 0, sizeof(BITVEC));
-
 				while (num--)
 				{
-					HASH(qp, *ptr);
+					HASH(dq, *ptr, siglen);
 					ptr++;
 				}
 
 				de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
-				dq = qp;
 				retval = true;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 				{
 					if (de[i] != dq[i])
 					{
@@ -551,13 +550,16 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 					}
 				}
 
+				pfree(dq);
 			}
 			else
-				retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+				retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+										  query, siglen);
 			break;
 		case RTContainsStrategyNumber:
 		case RTOldContainsStrategyNumber:
-			retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+			retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+									  query, siglen);
 			break;
 		case RTContainedByStrategyNumber:
 		case RTOldContainedByStrategyNumber:
@@ -566,22 +568,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				int			i,
 							num = ARRNELEMS(query);
 				int32	   *ptr = ARRPTR(query);
-				BITVEC		qp;
-				BITVECP		dq,
+				BITVECP		dq = palloc0(siglen),
 							de;
 
-				memset(qp, 0, sizeof(BITVEC));
-
 				while (num--)
 				{
-					HASH(qp, *ptr);
+					HASH(dq, *ptr, siglen);
 					ptr++;
 				}
 
 				de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
-				dq = qp;
 				retval = true;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 				{
 					if (de[i] & ~dq[i])
 					{
@@ -591,7 +589,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				}
 			}
 			else
-				retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+				retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+										 query, siglen);
 			break;
 		default:
 			retval = false;
@@ -599,3 +598,20 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(retval);
 }
+
+Datum
+g_intbig_options(PG_FUNCTION_ARGS)
+{
+	Datum		raw_options = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+	relopt_int	siglen =
+		{ {"siglen", "signature length", 0, NoLock, 6, RELOPT_TYPE_INT },
+			SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(IntArrayBigOptions, siglen) };
+	IntArrayBigOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(IntArrayBigOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/intarray--1.2-1.3.sql b/contrib/intarray/intarray--1.2-1.3.sql
new file mode 100644
index 0000000..c9b83ef
--- /dev/null
+++ b/contrib/intarray/intarray--1.2-1.3.sql
@@ -0,0 +1,34 @@
+/* contrib/intarray/intarray--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.3'" 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
+('g_int_compress(internal)', '{internal,internal}'),
+('g_int_decompress(internal)', '{internal,internal}'),
+('g_intbig_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('g_intbig_compress(internal)', '{internal,internal}'),
+('g_intbig_decompress(internal)', '{internal,internal}'),
+('g_intbig_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('g_intbig_picksplit(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_union(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_same(intbig_gkey,intbig_gkey,internal)', '{intbig_gkey,intbig_gkey,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist__intbig_ops USING gist
+ADD FUNCTION 10 (_int4) g_intbig_options (internal, boolean);
diff --git a/contrib/intarray/intarray--1.2.sql b/contrib/intarray/intarray--1.2.sql
deleted file mode 100644
index f10b53d..0000000
--- a/contrib/intarray/intarray--1.2.sql
+++ /dev/null
@@ -1,520 +0,0 @@
-/* contrib/intarray/intarray--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION intarray" to load this file. \quit
-
---
--- Create the user-defined type for the 1-D integer arrays (_int4)
---
-
--- Query type
-CREATE FUNCTION bqarr_in(cstring)
-RETURNS query_int
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION bqarr_out(query_int)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE query_int (
-	INTERNALLENGTH = -1,
-	INPUT = bqarr_in,
-	OUTPUT = bqarr_out
-);
-
---only for debug
-CREATE FUNCTION querytree(query_int)
-RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-
-CREATE FUNCTION boolop(_int4, query_int)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
-
-CREATE FUNCTION rboolop(query_int, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
-
-CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE OPERATOR @@ (
-	LEFTARG = _int4,
-	RIGHTARG = query_int,
-	PROCEDURE = boolop,
-	COMMUTATOR = '~~',
-	RESTRICT = _int_matchsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~~ (
-	LEFTARG = query_int,
-	RIGHTARG = _int4,
-	PROCEDURE = rboolop,
-	COMMUTATOR = '@@',
-	RESTRICT = _int_matchsel,
-	JOIN = contjoinsel
-);
-
-
---
--- External C-functions for R-tree methods
---
-
--- Comparison methods
-
-CREATE FUNCTION _int_contains(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
-
-CREATE FUNCTION _int_contained(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
-
-CREATE FUNCTION _int_overlap(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
-
-CREATE FUNCTION _int_same(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
-
-CREATE FUNCTION _int_different(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
-
--- support routines for indexing
-
-CREATE FUNCTION _int_union(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_inter(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
---
--- OPERATORS
---
-
-CREATE OPERATOR && (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_overlap,
-	COMMUTATOR = '&&',
-	RESTRICT = _int_overlap_sel,
-	JOIN = _int_overlap_joinsel
-);
-
---CREATE OPERATOR = (
---	LEFTARG = _int4,
---	RIGHTARG = _int4,
---	PROCEDURE = _int_same,
---	COMMUTATOR = '=',
---	NEGATOR = '<>',
---	RESTRICT = eqsel,
---	JOIN = eqjoinsel,
---	SORT1 = '<',
---	SORT2 = '<'
---);
-
---CREATE OPERATOR <> (
---	LEFTARG = _int4,
---	RIGHTARG = _int4,
---	PROCEDURE = _int_different,
---	COMMUTATOR = '<>',
---	NEGATOR = '=',
---	RESTRICT = neqsel,
---	JOIN = neqjoinsel
---);
-
-CREATE OPERATOR @> (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contains,
-	COMMUTATOR = '<@',
-	RESTRICT = _int_contains_sel,
-	JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR <@ (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contained,
-	COMMUTATOR = '@>',
-	RESTRICT = _int_contained_sel,
-	JOIN = _int_contained_joinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contains,
-	COMMUTATOR = '~',
-	RESTRICT = _int_contains_sel,
-	JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR ~ (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contained,
-	COMMUTATOR = '@',
-	RESTRICT = _int_contained_sel,
-	JOIN = _int_contained_joinsel
-);
-
---------------
-CREATE FUNCTION intset(int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION icount(_int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
-	RIGHTARG = _int4,
-	PROCEDURE = icount
-);
-
-CREATE FUNCTION sort(_int4, text)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_asc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_desc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION uniq(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION idx(_int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = idx
-);
-
-CREATE FUNCTION subarray(_int4, int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION subarray(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION intarray_push_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = intarray_push_elem
-);
-
-CREATE FUNCTION intarray_push_array(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	COMMUTATOR = +,
-	PROCEDURE = intarray_push_array
-);
-
-CREATE FUNCTION intarray_del_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = intarray_del_elem
-);
-
-CREATE FUNCTION intset_union_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR | (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = intset_union_elem
-);
-
-CREATE OPERATOR | (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	COMMUTATOR = |,
-	PROCEDURE = _int_union
-);
-
-CREATE FUNCTION intset_subtract(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = intset_subtract
-);
-
-CREATE OPERATOR & (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	COMMUTATOR = &,
-	PROCEDURE = _int_inter
-);
---------------
-
--- define the GiST support methods
-CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_union(internal, internal)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_same(_int4, _int4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-
--- Create the operator class for indexing
-
-CREATE OPERATOR CLASS gist__int_ops
-DEFAULT FOR TYPE _int4 USING gist AS
-	OPERATOR	3	&&,
-	OPERATOR	6	= (anyarray, anyarray),
-	OPERATOR	7	@>,
-	OPERATOR	8	<@,
-	OPERATOR	13	@,
-	OPERATOR	14	~,
-	OPERATOR	20	@@ (_int4, query_int),
-	FUNCTION	1	g_int_consistent (internal, _int4, smallint, oid, internal),
-	FUNCTION	2	g_int_union (internal, internal),
-	FUNCTION	3	g_int_compress (internal),
-	FUNCTION	4	g_int_decompress (internal),
-	FUNCTION	5	g_int_penalty (internal, internal, internal),
-	FUNCTION	6	g_int_picksplit (internal, internal),
-	FUNCTION	7	g_int_same (_int4, _int4, internal);
-
-
----------------------------------------------
--- intbig
----------------------------------------------
--- define the GiST support methods
-
-CREATE FUNCTION _intbig_in(cstring)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _intbig_out(intbig_gkey)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE intbig_gkey (
-        INTERNALLENGTH = -1,
-        INPUT = _intbig_in,
-        OUTPUT = _intbig_out
-);
-
-CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_union(internal, internal)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
--- register the opclass for indexing (not as default)
-
-CREATE OPERATOR CLASS gist__intbig_ops
-FOR TYPE _int4 USING gist
-AS
-	OPERATOR	3	&&,
-	OPERATOR	6	= (anyarray, anyarray),
-	OPERATOR	7	@>,
-	OPERATOR	8	<@,
-	OPERATOR	13	@,
-	OPERATOR	14	~,
-	OPERATOR	20	@@ (_int4, query_int),
-	FUNCTION	1	g_intbig_consistent (internal, _int4, smallint, oid, internal),
-	FUNCTION	2	g_intbig_union (internal, internal),
-	FUNCTION	3	g_intbig_compress (internal),
-	FUNCTION	4	g_intbig_decompress (internal),
-	FUNCTION	5	g_intbig_penalty (internal, internal, internal),
-	FUNCTION	6	g_intbig_picksplit (internal, internal),
-	FUNCTION	7	g_intbig_same (intbig_gkey, intbig_gkey, internal),
-	STORAGE		intbig_gkey;
-
---GIN
-
-CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE OPERATOR CLASS gin__int_ops
-FOR TYPE _int4 USING gin
-AS
-	OPERATOR	3	&&,
-	OPERATOR	6	= (anyarray, anyarray),
-	OPERATOR	7	@>,
-	OPERATOR	8	<@,
-	OPERATOR	13	@,
-	OPERATOR	14	~,
-	OPERATOR	20	@@ (_int4, query_int),
-	FUNCTION	1	btint4cmp (int4, int4),
-	FUNCTION	2	ginarrayextract (anyarray, internal, internal),
-	FUNCTION	3	ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
-	FUNCTION	4	ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
-	STORAGE		int4;
diff --git a/contrib/intarray/intarray--1.3.sql b/contrib/intarray/intarray--1.3.sql
new file mode 100644
index 0000000..6c91c75
--- /dev/null
+++ b/contrib/intarray/intarray--1.3.sql
@@ -0,0 +1,531 @@
+/* contrib/intarray/intarray--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION intarray" to load this file. \quit
+
+--
+-- Create the user-defined type for the 1-D integer arrays (_int4)
+--
+
+-- Query type
+CREATE FUNCTION bqarr_in(cstring)
+RETURNS query_int
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION bqarr_out(query_int)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE query_int (
+	INTERNALLENGTH = -1,
+	INPUT = bqarr_in,
+	OUTPUT = bqarr_out
+);
+
+--only for debug
+CREATE FUNCTION querytree(query_int)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE FUNCTION boolop(_int4, query_int)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
+
+CREATE FUNCTION rboolop(query_int, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
+
+CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE OPERATOR @@ (
+	LEFTARG = _int4,
+	RIGHTARG = query_int,
+	PROCEDURE = boolop,
+	COMMUTATOR = '~~',
+	RESTRICT = _int_matchsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~~ (
+	LEFTARG = query_int,
+	RIGHTARG = _int4,
+	PROCEDURE = rboolop,
+	COMMUTATOR = '@@',
+	RESTRICT = _int_matchsel,
+	JOIN = contjoinsel
+);
+
+
+--
+-- External C-functions for R-tree methods
+--
+
+-- Comparison methods
+
+CREATE FUNCTION _int_contains(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
+
+CREATE FUNCTION _int_contained(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
+
+CREATE FUNCTION _int_overlap(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
+
+CREATE FUNCTION _int_same(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
+
+CREATE FUNCTION _int_different(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
+
+-- support routines for indexing
+
+CREATE FUNCTION _int_union(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_inter(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+--
+-- OPERATORS
+--
+
+CREATE OPERATOR && (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_overlap,
+	COMMUTATOR = '&&',
+	RESTRICT = _int_overlap_sel,
+	JOIN = _int_overlap_joinsel
+);
+
+--CREATE OPERATOR = (
+--	LEFTARG = _int4,
+--	RIGHTARG = _int4,
+--	PROCEDURE = _int_same,
+--	COMMUTATOR = '=',
+--	NEGATOR = '<>',
+--	RESTRICT = eqsel,
+--	JOIN = eqjoinsel,
+--	SORT1 = '<',
+--	SORT2 = '<'
+--);
+
+--CREATE OPERATOR <> (
+--	LEFTARG = _int4,
+--	RIGHTARG = _int4,
+--	PROCEDURE = _int_different,
+--	COMMUTATOR = '<>',
+--	NEGATOR = '=',
+--	RESTRICT = neqsel,
+--	JOIN = neqjoinsel
+--);
+
+CREATE OPERATOR @> (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contains,
+	COMMUTATOR = '<@',
+	RESTRICT = _int_contains_sel,
+	JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR <@ (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contained,
+	COMMUTATOR = '@>',
+	RESTRICT = _int_contained_sel,
+	JOIN = _int_contained_joinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contains,
+	COMMUTATOR = '~',
+	RESTRICT = _int_contains_sel,
+	JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR ~ (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contained,
+	COMMUTATOR = '@',
+	RESTRICT = _int_contained_sel,
+	JOIN = _int_contained_joinsel
+);
+
+--------------
+CREATE FUNCTION intset(int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION icount(_int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+	RIGHTARG = _int4,
+	PROCEDURE = icount
+);
+
+CREATE FUNCTION sort(_int4, text)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_asc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_desc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION uniq(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION idx(_int4, int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = idx
+);
+
+CREATE FUNCTION subarray(_int4, int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subarray(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION intarray_push_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = intarray_push_elem
+);
+
+CREATE FUNCTION intarray_push_array(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	COMMUTATOR = +,
+	PROCEDURE = intarray_push_array
+);
+
+CREATE FUNCTION intarray_del_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = intarray_del_elem
+);
+
+CREATE FUNCTION intset_union_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR | (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = intset_union_elem
+);
+
+CREATE OPERATOR | (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	COMMUTATOR = |,
+	PROCEDURE = _int_union
+);
+
+CREATE FUNCTION intset_subtract(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = intset_subtract
+);
+
+CREATE OPERATOR & (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	COMMUTATOR = &,
+	PROCEDURE = _int_inter
+);
+--------------
+
+-- define the GiST support methods
+CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_compress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_decompress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_union(internal, internal)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_same(_int4, _int4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_int_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- Create the operator class for indexing
+
+CREATE OPERATOR CLASS gist__int_ops
+DEFAULT FOR TYPE _int4 USING gist AS
+	OPERATOR	3	&&,
+	OPERATOR	6	= (anyarray, anyarray),
+	OPERATOR	7	@>,
+	OPERATOR	8	<@,
+	OPERATOR	13	@,
+	OPERATOR	14	~,
+	OPERATOR	20	@@ (_int4, query_int),
+	FUNCTION	1	g_int_consistent (internal, _int4, smallint, oid, internal),
+	FUNCTION	2	g_int_union (internal, internal),
+	FUNCTION	3	g_int_compress (internal, internal),
+	FUNCTION	4	g_int_decompress (internal, internal),
+	FUNCTION	5	g_int_penalty (internal, internal, internal),
+	FUNCTION	6	g_int_picksplit (internal, internal),
+	FUNCTION	7	g_int_same (_int4, _int4, internal),
+	FUNCTION	10	g_int_options (internal, boolean);
+
+
+---------------------------------------------
+-- intbig
+---------------------------------------------
+-- define the GiST support methods
+
+CREATE FUNCTION _intbig_in(cstring)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _intbig_out(intbig_gkey)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE intbig_gkey (
+        INTERNALLENGTH = -1,
+        INPUT = _intbig_in,
+        OUTPUT = _intbig_out
+);
+
+CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_compress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_decompress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_penalty(internal,internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_picksplit(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_union(internal, internal, internal)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- register the opclass for indexing (not as default)
+
+CREATE OPERATOR CLASS gist__intbig_ops
+FOR TYPE _int4 USING gist
+AS
+	OPERATOR	3	&&,
+	OPERATOR	6	= (anyarray, anyarray),
+	OPERATOR	7	@>,
+	OPERATOR	8	<@,
+	OPERATOR	13	@,
+	OPERATOR	14	~,
+	OPERATOR	20	@@ (_int4, query_int),
+	FUNCTION	1	g_intbig_consistent (internal, _int4, smallint, oid, internal, internal),
+	FUNCTION	2	g_intbig_union (internal, internal, internal),
+	FUNCTION	3	g_intbig_compress (internal, internal),
+	FUNCTION	4	g_intbig_decompress (internal, internal),
+	FUNCTION	5	g_intbig_penalty (internal, internal, internal, internal),
+	FUNCTION	6	g_intbig_picksplit (internal, internal, internal),
+	FUNCTION	7	g_intbig_same (intbig_gkey, intbig_gkey, internal, internal),
+	FUNCTION	10	g_intbig_options (internal, boolean),
+	STORAGE		intbig_gkey;
+
+--GIN
+
+CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gin__int_ops
+FOR TYPE _int4 USING gin
+AS
+	OPERATOR	3	&&,
+	OPERATOR	6	= (anyarray, anyarray),
+	OPERATOR	7	@>,
+	OPERATOR	8	<@,
+	OPERATOR	13	@,
+	OPERATOR	14	~,
+	OPERATOR	20	@@ (_int4, query_int),
+	FUNCTION	1	btint4cmp (int4, int4),
+	FUNCTION	2	ginarrayextract (anyarray, internal, internal),
+	FUNCTION	3	ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
+	FUNCTION	4	ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
+	STORAGE		int4;
diff --git a/contrib/intarray/intarray.control b/contrib/intarray/intarray.control
index 7e50cc3..72952f7 100644
--- a/contrib/intarray/intarray.control
+++ b/contrib/intarray/intarray.control
@@ -1,5 +1,5 @@
 # intarray extension
 comment = 'functions, operators, and index support for 1-D arrays of integers'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/_int'
 relocatable = true
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index b633cf3..2540cc6 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -259,14 +259,22 @@
   </para>
 
   <para>
-   Two GiST index operator classes are provided:
-   <literal>gist__int_ops</literal> (used by default) is suitable for
+   Two parametrized GiST index operator classes are provided:
+   <literal>gist__int_ops(numranges)</literal> (used by default) is suitable for
    small- to medium-size data sets, while
-   <literal>gist__intbig_ops</literal> uses a larger signature and is more
+   <literal>gist__intbig_ops(siglen)</literal> uses a larger signature and is more
    suitable for indexing large data sets (i.e., columns containing
    a large number of distinct array values).
    The implementation uses an RD-tree data structure with
    built-in lossy compression.
+   
+   Optional integer parameter <literal>numranges</literal> of 
+   <literal>gist__int_ops</literal> determines 
+   number of ranges in compressed array (100 by default).
+   
+   Optional integer parameter <literal>siglen</literal> of 
+   <literal>gist__intbig_ops</literal> determines
+   signature length in bytes (252 by default, 488 maximum).
   </para>
 
   <para>
0006-opclass-parameters-contrib_ltree-v01.patchtext/x-patch; name=0006-opclass-parameters-contrib_ltree-v01.patchDownload
diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index c101603..b0e90a3 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -6,7 +6,7 @@ OBJS = 	ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
 PG_CPPFLAGS = -DLOWER_NODE
 
 EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.1--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
 PGFILEDESC = "ltree - hierarchical label data type"
 
 REGRESS = ltree
diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c
index 28bf7ad..394f7ad 100644
--- a/contrib/ltree/_ltree_gist.c
+++ b/contrib/ltree/_ltree_gist.c
@@ -8,6 +8,7 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "crc32.h"
 #include "ltree.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(_ltree_union);
 PG_FUNCTION_INFO_V1(_ltree_penalty);
 PG_FUNCTION_INFO_V1(_ltree_picksplit);
 PG_FUNCTION_INFO_V1(_ltree_consistent);
+PG_FUNCTION_INFO_V1(_ltree_gist_options);
 
 #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
 #define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
@@ -47,7 +49,7 @@ static const uint8 number_of_ones[256] = {
 
 
 static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
 {
 	int			tlen = t->numlevel;
 	ltree_level *cur = LTREE_FIRST(t);
@@ -56,7 +58,7 @@ hashing(BITVECP sign, ltree *t)
 	while (tlen > 0)
 	{
 		hash = ltree_crc32_sz(cur->name, cur->len);
-		AHASH(sign, hash);
+		AHASH(sign, hash, siglen);
 		cur = LEVEL_NEXT(cur);
 		tlen--;
 	}
@@ -67,12 +69,13 @@ _ltree_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 	GISTENTRY  *retval = entry;
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(1);
+	int			siglen = options->siglen;
 
 	if (entry->leafkey)
 	{							/* ltree */
 		ltree_gist *key;
 		ArrayType  *val = DatumGetArrayTypeP(entry->key);
-		int32		len = LTG_HDRSIZE + ASIGLEN;
 		int			num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
 		ltree	   *item = (ltree *) ARR_DATA_PTR(val);
 
@@ -85,14 +88,11 @@ _ltree_compress(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 					 errmsg("array must not contain nulls")));
 
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = 0;
+		key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
 
-		MemSet(LTG_SIGN(key), 0, ASIGLEN);
 		while (num > 0)
 		{
-			hashing(LTG_SIGN(key), item);
+			hashing(LTG_SIGN(key), item, siglen);
 			num--;
 			item = NEXTVAL(item);
 		}
@@ -104,22 +104,17 @@ _ltree_compress(PG_FUNCTION_ARGS)
 	}
 	else if (!LTG_ISALLTRUE(entry->key))
 	{
-		int32		i,
-					len;
+		int32		i;
 		ltree_gist *key;
-
 		BITVECP		sign = LTG_SIGN(DatumGetPointer(entry->key));
 
-		ALOOPBYTE
+		ALOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(retval);
 		}
-		len = LTG_HDRSIZE;
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = LTG_ALLTRUE;
 
+		key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(key),
 					  entry->rel, entry->page,
@@ -134,6 +129,8 @@ _ltree_same(PG_FUNCTION_ARGS)
 	ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
 	ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 
 	if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
 		*result = true;
@@ -148,7 +145,7 @@ _ltree_same(PG_FUNCTION_ARGS)
 					sb = LTG_SIGN(b);
 
 		*result = true;
-		ALOOPBYTE
+		ALOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -161,7 +158,7 @@ _ltree_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, ltree_gist *add)
+unionkey(BITVECP sbase, ltree_gist *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = LTG_SIGN(add);
@@ -169,7 +166,7 @@ unionkey(BITVECP sbase, ltree_gist *add)
 	if (LTG_ISALLTRUE(add))
 		return 1;
 
-	ALOOPBYTE
+	ALOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -179,52 +176,46 @@ _ltree_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	ABITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	ltree_gist *result;
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
+	int32		i;
+	ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
+	BITVECP		base = LTG_SIGN(result);
 
-	MemSet((void *) base, 0, sizeof(ABITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = LTG_ALLTRUE;
+			result->flag |= LTG_ALLTRUE;
+			SET_VARSIZE(result, LTG_HDRSIZE);
 			break;
 		}
 	}
 
-	len = LTG_HDRSIZE + ((flag & LTG_ALLTRUE) ? 0 : ASIGLEN);
-	result = (ltree_gist *) palloc0(len);
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!LTG_ISALLTRUE(result))
-		memcpy((void *) LTG_SIGN(result), (void *) base, sizeof(ABITVEC));
-	*size = len;
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
 	int32		size = 0,
 				i;
 
-	ALOOPBYTE
+	ALOOPBYTE(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;
 
-	ALOOPBYTE
+	ALOOPBYTE(siglen)
 	{
 		diff = (unsigned char) (a[i] ^ b[i]);
 		dist += number_of_ones[diff];
@@ -233,19 +224,19 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(ltree_gist *a, ltree_gist *b)
+hemdist(ltree_gist *a, ltree_gist *b, int siglen)
 {
 	if (LTG_ISALLTRUE(a))
 	{
 		if (LTG_ISALLTRUE(b))
 			return 0;
 		else
-			return ASIGLENBIT - sizebitvec(LTG_SIGN(b));
+			return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
 	}
 	else if (LTG_ISALLTRUE(b))
-		return ASIGLENBIT - sizebitvec(LTG_SIGN(a));
+		return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
 
-	return hemdistsign(LTG_SIGN(a), LTG_SIGN(b));
+	return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
 }
 
 
@@ -255,8 +246,10 @@ _ltree_penalty(PG_FUNCTION_ARGS)
 	ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
 	ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -277,6 +270,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	OffsetNumber k,
 				j;
 	ltree_gist *datum_l,
@@ -309,7 +304,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -331,32 +326,13 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE);
-		SET_VARSIZE(datum_l, LTG_HDRSIZE);
-		datum_l->flag = LTG_ALLTRUE;
-	}
-	else
-	{
-		datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
-		SET_VARSIZE(datum_l, LTG_HDRSIZE + ASIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) LTG_SIGN(datum_l), (void *) LTG_SIGN(GETENTRY(entryvec, seed_1)), sizeof(ABITVEC));
-	}
-	if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE);
-		SET_VARSIZE(datum_r, LTG_HDRSIZE);
-		datum_r->flag = LTG_ALLTRUE;
-	}
-	else
-	{
-		datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
-		SET_VARSIZE(datum_r, LTG_HDRSIZE + ASIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) LTG_SIGN(datum_r), (void *) LTG_SIGN(GETENTRY(entryvec, seed_2)), sizeof(ABITVEC));
-	}
+	datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
+							   LTG_SIGN(GETENTRY(entryvec, seed_1)),
+							   siglen, NULL, NULL);
+
+	datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
+							   LTG_SIGN(GETENTRY(entryvec, seed_2)),
+							   siglen, NULL, NULL);
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -365,8 +341,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = Abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -390,20 +366,20 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
 		{
 			if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
 			{
 				if (!LTG_ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(ABITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = LTG_SIGN(_j);
-				ALOOPBYTE
+				ALOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -414,12 +390,12 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 			if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
 			{
 				if (!LTG_ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(ABITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = LTG_SIGN(_j);
-				ALOOPBYTE
+				ALOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -436,7 +412,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 }
 
 static bool
-gist_te(ltree_gist *key, ltree *query)
+gist_te(ltree_gist *key, ltree *query, int siglen)
 {
 	ltree_level *curq = LTREE_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -449,7 +425,7 @@ gist_te(ltree_gist *key, ltree *query)
 	while (qlen > 0)
 	{
 		hv = ltree_crc32_sz(curq->name, curq->len);
-		if (!GETBIT(sign, AHASHVAL(hv)))
+		if (!GETBIT(sign, AHASHVAL(hv, siglen)))
 			return false;
 		curq = LEVEL_NEXT(curq);
 		qlen--;
@@ -458,27 +434,40 @@ gist_te(ltree_gist *key, ltree *query)
 	return true;
 }
 
+typedef struct LtreeSignature
+{
+	BITVECP	sign;
+	int		siglen;
+} LtreeSignature;
+
 static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
 {
-	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, AHASHVAL(val->val)) : true;
+	LtreeSignature *sig = cxt;
+
+	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
 }
 
 static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
 {
+	LtreeSignature sig;
+
 	if (LTG_ISALLTRUE(key))
 		return true;
 
+	sig.sign = LTG_SIGN(key);
+	sig.siglen = siglen;
+
 	return ltree_execute(
 						 GETQUERY(query),
-						 (void *) LTG_SIGN(key), false,
+						 &sig, false,
 						 checkcondition_bit
 		);
 }
 
 static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
 {
 	lquery_level *curq = LQUERY_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -497,7 +486,7 @@ gist_qe(ltree_gist *key, lquery *query)
 
 			while (vlen > 0)
 			{
-				if (GETBIT(sign, AHASHVAL(curv->val)))
+				if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
 				{
 					isexist = true;
 					break;
@@ -517,7 +506,7 @@ gist_qe(ltree_gist *key, lquery *query)
 }
 
 static bool
-_arrq_cons(ltree_gist *key, ArrayType *_query)
+_arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -533,7 +522,7 @@ _arrq_cons(ltree_gist *key, ArrayType *_query)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query))
+		if (gist_qe(key, query, siglen))
 			return true;
 		num--;
 		query = (lquery *) NEXTVAL(query);
@@ -550,6 +539,8 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
 	bool		res = false;
 
@@ -560,19 +551,19 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 	{
 		case 10:
 		case 11:
-			res = gist_te(key, (ltree *) query);
+			res = gist_te(key, (ltree *) query, siglen);
 			break;
 		case 12:
 		case 13:
-			res = gist_qe(key, (lquery *) query);
+			res = gist_qe(key, (lquery *) query, siglen);
 			break;
 		case 14:
 		case 15:
-			res = gist_qtxt(key, (ltxtquery *) query);
+			res = gist_qtxt(key, (ltxtquery *) query, siglen);
 			break;
 		case 16:
 		case 17:
-			res = _arrq_cons(key, (ArrayType *) query);
+			res = _arrq_cons(key, (ArrayType *) query, siglen);
 			break;
 		default:
 			/* internal error */
@@ -581,3 +572,20 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+_ltree_gist_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 },
+			LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(LtreeGistOptions, siglen) };
+	LtreeGistOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(LtreeGistOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/contrib/ltree/ltree--1.1--1.2.sql b/contrib/ltree/ltree--1.1--1.2.sql
new file mode 100644
index 0000000..d002e2b
--- /dev/null
+++ b/contrib/ltree/ltree--1.1--1.2.sql
@@ -0,0 +1,47 @@
+/* contrib/ltree/ltree--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION ltree UPDATE TO '1.2'" 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
+('ltree_compress(internal)', '{internal,internal}'),
+('ltree_decompress(internal)', '{internal,internal}'),
+('ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('ltree_union(internal,internal)', '{internal,internal,internal}'),
+('ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('_ltree_compress(internal)', '{internal,internal}'),
+('_ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('_ltree_union(internal,internal)', '{internal,internal,internal}'),
+('_ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('_ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('_ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', '_ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_ltree_ops USING gist
+ADD FUNCTION 10 (ltree) ltree_gist_options (internal, boolean);
+
+ALTER OPERATOR FAMILY gist__ltree_ops USING gist
+ADD FUNCTION 10 (_ltree) _ltree_gist_options (internal, boolean);
+
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1..61c8cdf 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
 # ltree extension
 comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
 module_pathname = '$libdir/ltree'
 relocatable = true
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index e4b8c84..6247564 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -183,15 +183,16 @@ int			ltree_strncasecmp(const char *a, const char *b, size_t s);
 
 /* GiST support for ltree */
 
+#define SIGLEN_MAX		(122 * sizeof(int32))
+#define SIGLEN_DEFAULT	(2 * sizeof(int32))
 #define BITBYTE 8
-#define SIGLENINT  2
-#define SIGLEN	( sizeof(int32)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
-typedef unsigned char BITVEC[SIGLEN];
+#define SIGLEN	(sizeof(int32) * SIGLENINT)
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
+
 typedef unsigned 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) ( ((unsigned char)(x)) >> i & 0x01 )
@@ -199,8 +200,8 @@ typedef unsigned 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))
 
 /*
  * type of index key for ltree. Tree are combined B-Tree and R-Tree
@@ -230,26 +231,34 @@ typedef struct
 #define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
 #define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
 #define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
-#define LTG_LNODE(x)	( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
-#define LTG_RENODE(x)	( (ltree*)( ((char*)LTG_LNODE(x)) + VARSIZE(LTG_LNODE(x))) )
-#define LTG_RNODE(x)	( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
+#define LTG_LNODE(x, siglen)	( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : (siglen) ) ) )
+#define LTG_RENODE(x, siglen)	( (ltree*)( ((char*)LTG_LNODE(x, siglen)) + VARSIZE(LTG_LNODE(x, siglen))) )
+#define LTG_RNODE(x, siglen)	( LTG_ISNORIGHT(x) ? LTG_LNODE(x, siglen) : LTG_RENODE(x, siglen) )
 
-#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
-#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
+#define LTG_GETLNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x, siglen) )
+#define LTG_GETRNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x, siglen) )
 
+extern ltree_gist *ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+				 ltree *left, ltree *right);
 
 /* GiST support for ltree[] */
 
-#define ASIGLENINT	(7)
-#define ASIGLEN		(sizeof(int32)*ASIGLENINT)
-#define ASIGLENBIT (ASIGLEN*BITBYTE)
-typedef unsigned char ABITVEC[ASIGLEN];
+#define LTREE_ASIGLEN_DEFAULT	(7 * sizeof(int32))
+#define LTREE_ASIGLEN_MAX		(122 * sizeof(int32))
+#define ASIGLENBIT(siglen)		((siglen) * BITBYTE)
+
+#define ALOOPBYTE(siglen) \
+			for (i = 0; i < (siglen); i++)
 
-#define ALOOPBYTE \
-			for(i=0;i<ASIGLEN;i++)
+#define AHASHVAL(val, siglen) (((unsigned int)(val)) % ASIGLENBIT(siglen))
+#define AHASH(sign, val, siglen) SETBIT((sign), AHASHVAL(val, siglen))
 
-#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
-#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
+/* gist_ltree_ops and gist__ltree_ops opclass options */
+typedef struct LtreeGistOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+}	LtreeGistOptions;
 
 /* type of key is the same to ltree_gist */
 
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 12aa8ff..9847b83 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -6,11 +6,13 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "crc32.h"
 #include "ltree.h"
 
 #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
 
 PG_FUNCTION_INFO_V1(ltree_gist_in);
 PG_FUNCTION_INFO_V1(ltree_gist_out);
@@ -33,6 +35,47 @@ ltree_gist_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+ltree_gist *
+ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+				 ltree *left, ltree *right)
+{
+	int32		size = LTG_HDRSIZE + (isalltrue ? 0 : siglen) +
+		(left ? VARSIZE(left) + (right ? VARSIZE(right) : 0) : 0);
+	ltree_gist *result = palloc(size);
+
+	SET_VARSIZE(result, size);
+
+	if (siglen)
+	{
+		result->flag = 0;
+
+		if (isalltrue)
+			result->flag |= LTG_ALLTRUE;
+		else if (sign)
+			memcpy(LTG_SIGN(result), sign, siglen);
+		else
+			memset(LTG_SIGN(result), 0, siglen);
+
+		if (left)
+		{
+			memcpy(LTG_LNODE(result, siglen), left, VARSIZE(left));
+
+			if (!right || left == right || ISEQ(left, right))
+				result->flag |= LTG_NORIGHT;
+			else
+				memcpy(LTG_RNODE(result, siglen), right, VARSIZE(right));
+		}
+	}
+	else
+	{
+		Assert(left);
+		result->flag = LTG_ONENODE;
+		memcpy(LTG_NODE(result), left, VARSIZE(left));
+	}
+
+	return result;
+}
+
 PG_FUNCTION_INFO_V1(ltree_compress);
 PG_FUNCTION_INFO_V1(ltree_decompress);
 PG_FUNCTION_INFO_V1(ltree_same);
@@ -40,8 +83,8 @@ PG_FUNCTION_INFO_V1(ltree_union);
 PG_FUNCTION_INFO_V1(ltree_penalty);
 PG_FUNCTION_INFO_V1(ltree_picksplit);
 PG_FUNCTION_INFO_V1(ltree_consistent);
+PG_FUNCTION_INFO_V1(ltree_gist_options);
 
-#define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
 #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
 
 Datum
@@ -52,14 +95,8 @@ ltree_compress(PG_FUNCTION_ARGS)
 
 	if (entry->leafkey)
 	{							/* ltree */
-		ltree_gist *key;
 		ltree	   *val = DatumGetLtreeP(entry->key);
-		int32		len = LTG_HDRSIZE + VARSIZE(val);
-
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = LTG_ONENODE;
-		memcpy((void *) LTG_NODE(key), (void *) val, VARSIZE(val));
+		ltree_gist *key = ltree_gist_alloc(false, NULL, 0, val, 0);
 
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(key),
@@ -93,6 +130,8 @@ ltree_same(PG_FUNCTION_ARGS)
 	ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
 	ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 
 	*result = false;
 	if (LTG_ISONENODE(a) != LTG_ISONENODE(b))
@@ -109,15 +148,15 @@ ltree_same(PG_FUNCTION_ARGS)
 		if (LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b))
 			PG_RETURN_POINTER(result);
 
-		if (!ISEQ(LTG_LNODE(a), LTG_LNODE(b)))
+		if (!ISEQ(LTG_LNODE(a, siglen), LTG_LNODE(b, siglen)))
 			PG_RETURN_POINTER(result);
-		if (!ISEQ(LTG_RNODE(a), LTG_RNODE(b)))
+		if (!ISEQ(LTG_RNODE(a, siglen), LTG_RNODE(b, siglen)))
 			PG_RETURN_POINTER(result);
 
 		*result = true;
 		if (!LTG_ISALLTRUE(a))
 		{
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -132,7 +171,7 @@ ltree_same(PG_FUNCTION_ARGS)
 }
 
 static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
 {
 	int			tlen = t->numlevel;
 	ltree_level *cur = LTREE_FIRST(t);
@@ -141,7 +180,7 @@ hashing(BITVECP sign, ltree *t)
 	while (tlen > 0)
 	{
 		hash = ltree_crc32_sz(cur->name, cur->len);
-		HASH(sign, hash);
+		HASH(sign, hash, siglen);
 		cur = LEVEL_NEXT(cur);
 		tlen--;
 	}
@@ -152,7 +191,9 @@ ltree_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
+	BITVECP		base = palloc0(siglen);
 	int32		i,
 				j;
 	ltree_gist *result,
@@ -161,16 +202,14 @@ ltree_union(PG_FUNCTION_ARGS)
 			   *right = NULL,
 			   *curtree;
 	bool		isalltrue = false;
-	bool		isleqr;
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (j = 0; j < entryvec->n; j++)
 	{
 		cur = GETENTRY(entryvec, j);
 		if (LTG_ISONENODE(cur))
 		{
 			curtree = LTG_NODE(cur);
-			hashing(base, curtree);
+			hashing(base, curtree, siglen);
 			if (!left || ltree_compare(left, curtree) > 0)
 				left = curtree;
 			if (!right || ltree_compare(right, curtree) < 0)
@@ -184,14 +223,14 @@ ltree_union(PG_FUNCTION_ARGS)
 			{
 				BITVECP		sc = LTG_SIGN(cur);
 
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					((unsigned char *) base)[i] |= sc[i];
 			}
 
-			curtree = LTG_LNODE(cur);
+			curtree = LTG_LNODE(cur, siglen);
 			if (!left || ltree_compare(left, curtree) > 0)
 				left = curtree;
-			curtree = LTG_RNODE(cur);
+			curtree = LTG_RNODE(cur, siglen);
 			if (!right || ltree_compare(right, curtree) < 0)
 				right = curtree;
 		}
@@ -200,7 +239,7 @@ ltree_union(PG_FUNCTION_ARGS)
 	if (isalltrue == false)
 	{
 		isalltrue = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) base)[i] != 0xff)
 			{
@@ -210,23 +249,9 @@ ltree_union(PG_FUNCTION_ARGS)
 		}
 	}
 
-	isleqr = (left == right || ISEQ(left, right)) ? true : false;
-	*size = LTG_HDRSIZE + ((isalltrue) ? 0 : SIGLEN) + VARSIZE(left) + ((isleqr) ? 0 : VARSIZE(right));
-
-	result = (ltree_gist *) palloc0(*size);
-	SET_VARSIZE(result, *size);
-	result->flag = 0;
+	result = ltree_gist_alloc(isalltrue, base, siglen, left, right);
 
-	if (isalltrue)
-		result->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(result), base, SIGLEN);
-
-	memcpy((void *) LTG_LNODE(result), (void *) left, VARSIZE(left));
-	if (isleqr)
-		result->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(result), (void *) right, VARSIZE(right));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -237,11 +262,13 @@ ltree_penalty(PG_FUNCTION_ARGS)
 	ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
 	ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 	int32		cmpr,
 				cmpl;
 
-	cmpl = ltree_compare(LTG_GETLNODE(origval), LTG_GETLNODE(newval));
-	cmpr = ltree_compare(LTG_GETRNODE(newval), LTG_GETRNODE(origval));
+	cmpl = ltree_compare(LTG_GETLNODE(origval, siglen), LTG_GETLNODE(newval, siglen));
+	cmpr = ltree_compare(LTG_GETRNODE(newval, siglen), LTG_GETRNODE(origval, siglen));
 
 	*penalty = Max(cmpl, 0) + Max(cmpr, 0);
 
@@ -270,26 +297,24 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	OffsetNumber j;
 	int32		i;
 	RIX		   *array;
 	OffsetNumber maxoff;
 	int			nbytes;
-	int			size;
 	ltree	   *lu_l,
 			   *lu_r,
 			   *ru_l,
 			   *ru_r;
 	ltree_gist *lu,
 			   *ru;
-	BITVEC		ls,
-				rs;
+	BITVECP		ls = palloc0(siglen),
+				rs = palloc0(siglen);
 	bool		lisat = false,
-				risat = false,
-				isleqr;
+				risat = false;
 
-	memset((void *) ls, 0, sizeof(BITVEC));
-	memset((void *) rs, 0, sizeof(BITVEC));
 	maxoff = entryvec->n - 1;
 	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
 	v->spl_left = (OffsetNumber *) palloc(nbytes);
@@ -303,7 +328,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	{
 		array[j].index = j;
 		lu = GETENTRY(entryvec, j); /* use as tmp val */
-		array[j].r = LTG_GETLNODE(lu);
+		array[j].r = LTG_GETLNODE(lu, siglen);
 	}
 
 	qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
@@ -317,10 +342,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		{
 			v->spl_left[v->spl_nleft] = array[j].index;
 			v->spl_nleft++;
-			if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu), lu_r) > 0)
-				lu_r = LTG_GETRNODE(lu);
+			if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), lu_r) > 0)
+				lu_r = LTG_GETRNODE(lu, siglen);
 			if (LTG_ISONENODE(lu))
-				hashing(ls, LTG_NODE(lu));
+				hashing(ls, LTG_NODE(lu), siglen);
 			else
 			{
 				if (lisat || LTG_ISALLTRUE(lu))
@@ -329,7 +354,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 				{
 					BITVECP		sc = LTG_SIGN(lu);
 
-					LOOPBYTE
+					LOOPBYTE(siglen)
 						((unsigned char *) ls)[i] |= sc[i];
 				}
 			}
@@ -338,10 +363,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		{
 			v->spl_right[v->spl_nright] = array[j].index;
 			v->spl_nright++;
-			if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu), ru_r) > 0)
-				ru_r = LTG_GETRNODE(lu);
+			if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), ru_r) > 0)
+				ru_r = LTG_GETRNODE(lu, siglen);
 			if (LTG_ISONENODE(lu))
-				hashing(rs, LTG_NODE(lu));
+				hashing(rs, LTG_NODE(lu), siglen);
 			else
 			{
 				if (risat || LTG_ISALLTRUE(lu))
@@ -350,7 +375,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 				{
 					BITVECP		sc = LTG_SIGN(lu);
 
-					LOOPBYTE
+					LOOPBYTE(siglen)
 						((unsigned char *) rs)[i] |= sc[i];
 				}
 			}
@@ -360,7 +385,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	if (lisat == false)
 	{
 		lisat = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) ls)[i] != 0xff)
 			{
@@ -373,7 +398,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	if (risat == false)
 	{
 		risat = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) rs)[i] != 0xff)
 			{
@@ -383,38 +408,14 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		}
 	}
 
-	lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index));
-	isleqr = (lu_l == lu_r || ISEQ(lu_l, lu_r)) ? true : false;
-	size = LTG_HDRSIZE + ((lisat) ? 0 : SIGLEN) + VARSIZE(lu_l) + ((isleqr) ? 0 : VARSIZE(lu_r));
-	lu = (ltree_gist *) palloc0(size);
-	SET_VARSIZE(lu, size);
-	lu->flag = 0;
-	if (lisat)
-		lu->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(lu), ls, SIGLEN);
-	memcpy((void *) LTG_LNODE(lu), (void *) lu_l, VARSIZE(lu_l));
-	if (isleqr)
-		lu->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(lu), (void *) lu_r, VARSIZE(lu_r));
+	lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index), siglen);
+	lu = ltree_gist_alloc(lisat, ls, siglen, lu_l, lu_r);
 
+	ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index), siglen);
+	ru = ltree_gist_alloc(risat, rs, siglen, ru_l, ru_r);
 
-	ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index));
-	isleqr = (ru_l == ru_r || ISEQ(ru_l, ru_r)) ? true : false;
-	size = LTG_HDRSIZE + ((risat) ? 0 : SIGLEN) + VARSIZE(ru_l) + ((isleqr) ? 0 : VARSIZE(ru_r));
-	ru = (ltree_gist *) palloc0(size);
-	SET_VARSIZE(ru, size);
-	ru->flag = 0;
-	if (risat)
-		ru->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(ru), rs, SIGLEN);
-	memcpy((void *) LTG_LNODE(ru), (void *) ru_l, VARSIZE(ru_l));
-	if (isleqr)
-		ru->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(ru), (void *) ru_r, VARSIZE(ru_r));
+	pfree(ls);
+	pfree(rs);
 
 	v->spl_ldatum = PointerGetDatum(lu);
 	v->spl_rdatum = PointerGetDatum(ru);
@@ -423,7 +424,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 }
 
 static bool
-gist_isparent(ltree_gist *key, ltree *query)
+gist_isparent(ltree_gist *key, ltree *query, int siglen)
 {
 	int32		numlevel = query->numlevel;
 	int			i;
@@ -431,7 +432,8 @@ gist_isparent(ltree_gist *key, ltree *query)
 	for (i = query->numlevel; i >= 0; i--)
 	{
 		query->numlevel = i;
-		if (ltree_compare(query, LTG_GETLNODE(key)) >= 0 && ltree_compare(query, LTG_GETRNODE(key)) <= 0)
+		if (ltree_compare(query, LTG_GETLNODE(key, siglen)) >= 0 &&
+			ltree_compare(query, LTG_GETRNODE(key, siglen)) <= 0)
 		{
 			query->numlevel = numlevel;
 			return true;
@@ -452,10 +454,10 @@ copy_ltree(ltree *src)
 }
 
 static bool
-gist_ischild(ltree_gist *key, ltree *query)
+gist_ischild(ltree_gist *key, ltree *query, int siglen)
 {
-	ltree	   *left = copy_ltree(LTG_GETLNODE(key));
-	ltree	   *right = copy_ltree(LTG_GETRNODE(key));
+	ltree	   *left = copy_ltree(LTG_GETLNODE(key, siglen));
+	ltree	   *right = copy_ltree(LTG_GETRNODE(key, siglen));
 	bool		res = true;
 
 	if (left->numlevel > query->numlevel)
@@ -477,7 +479,7 @@ gist_ischild(ltree_gist *key, ltree *query)
 }
 
 static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
 {
 	lquery_level *curq = LQUERY_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -496,7 +498,7 @@ gist_qe(ltree_gist *key, lquery *query)
 
 			while (vlen > 0)
 			{
-				if (GETBIT(sign, HASHVAL(curv->val)))
+				if (GETBIT(sign, HASHVAL(curv->val, siglen)))
 				{
 					isexist = true;
 					break;
@@ -545,41 +547,54 @@ gist_tqcmp(ltree *t, lquery *q)
 }
 
 static bool
-gist_between(ltree_gist *key, lquery *query)
+gist_between(ltree_gist *key, lquery *query, int siglen)
 {
 	if (query->firstgood == 0)
 		return true;
 
-	if (gist_tqcmp(LTG_GETLNODE(key), query) > 0)
+	if (gist_tqcmp(LTG_GETLNODE(key, siglen), query) > 0)
 		return false;
 
-	if (gist_tqcmp(LTG_GETRNODE(key), query) < 0)
+	if (gist_tqcmp(LTG_GETRNODE(key, siglen), query) < 0)
 		return false;
 
 	return true;
 }
 
+typedef struct LtreeSignature
+{
+	BITVECP	sign;
+	int		siglen;
+} LtreeSignature;
+
 static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
 {
-	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, HASHVAL(val->val)) : true;
+	LtreeSignature *sig = cxt;
+
+	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, HASHVAL(val->val, sig->siglen)) : true;
 }
 
 static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
 {
+	LtreeSignature sig;
+
 	if (LTG_ISALLTRUE(key))
 		return true;
 
+	sig.sign = LTG_SIGN(key);
+	sig.siglen = siglen;
+
 	return ltree_execute(
 						 GETQUERY(query),
-						 (void *) LTG_SIGN(key), false,
+						 &sig, false,
 						 checkcondition_bit
 		);
 }
 
 static bool
-arrq_cons(ltree_gist *key, ArrayType *_query)
+arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -595,7 +610,7 @@ arrq_cons(ltree_gist *key, ArrayType *_query)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query) && gist_between(key, query))
+		if (gist_qe(key, query, siglen) && gist_between(key, query, siglen))
 			return true;
 		num--;
 		query = NEXTVAL(query);
@@ -611,6 +626,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
 	void	   *query = NULL;
 	bool		res = false;
@@ -625,11 +642,11 @@ ltree_consistent(PG_FUNCTION_ARGS)
 			res = (GIST_LEAF(entry)) ?
 				(ltree_compare((ltree *) query, LTG_NODE(key)) > 0)
 				:
-				(ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+				(ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
 			break;
 		case BTLessEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
-			res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+			res = (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
 			break;
 		case BTEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
@@ -637,35 +654,35 @@ ltree_consistent(PG_FUNCTION_ARGS)
 				res = (ltree_compare((ltree *) query, LTG_NODE(key)) == 0);
 			else
 				res = (
-					   ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0
+					   ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0
 					   &&
-					   ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0
+					   ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0
 					);
 			break;
 		case BTGreaterEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
-			res = (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+			res = (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
 			break;
 		case BTGreaterStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
 			res = (GIST_LEAF(entry)) ?
-				(ltree_compare((ltree *) query, LTG_GETRNODE(key)) < 0)
+				(ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) < 0)
 				:
-				(ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+				(ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
 			break;
 		case 10:
 			query = PG_GETARG_LTREE_P_COPY(1);
 			res = (GIST_LEAF(entry)) ?
 				inner_isparent((ltree *) query, LTG_NODE(key))
 				:
-				gist_isparent(key, (ltree *) query);
+				gist_isparent(key, (ltree *) query, siglen);
 			break;
 		case 11:
 			query = PG_GETARG_LTREE_P(1);
 			res = (GIST_LEAF(entry)) ?
 				inner_isparent(LTG_NODE(key), (ltree *) query)
 				:
-				gist_ischild(key, (ltree *) query);
+				gist_ischild(key, (ltree *) query, siglen);
 			break;
 		case 12:
 		case 13:
@@ -676,7 +693,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((lquery *) query)
 													   ));
 			else
-				res = (gist_qe(key, (lquery *) query) && gist_between(key, (lquery *) query));
+				res = (gist_qe(key, (lquery *) query, siglen) &&
+					   gist_between(key, (lquery *) query, siglen));
 			break;
 		case 14:
 		case 15:
@@ -687,7 +705,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((ltxtquery *) query)
 													   ));
 			else
-				res = gist_qtxt(key, (ltxtquery *) query);
+				res = gist_qtxt(key, (ltxtquery *) query, siglen);
 			break;
 		case 16:
 		case 17:
@@ -698,7 +716,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((ArrayType *) query)
 													   ));
 			else
-				res = arrq_cons(key, (ArrayType *) query);
+				res = arrq_cons(key, (ArrayType *) query, siglen);
 			break;
 		default:
 			/* internal error */
@@ -708,3 +726,20 @@ ltree_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+ltree_gist_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 },
+			LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(LtreeGistOptions, siglen) };
+	LtreeGistOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(LtreeGistOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index ea362f8..dd4e269 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -502,11 +502,17 @@ Europe &amp; Russia*@ &amp; !Transportation
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     Example of creating such an index:
+     Example of creating such an index with a default signature length of 8 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (path);
 </programlisting>
+    <para>
+     Example of creating such an index with a signature length of 100 bytes:
+    </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
+</programlisting>
    </listitem>
    <listitem>
     <para>
@@ -515,12 +521,18 @@ CREATE INDEX path_gist_idx ON test USING GIST (path);
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     Example of creating such an index:
+     Example of creating such an index with a default signature length of 28 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (array_path);
 </programlisting>
     <para>
+     Example of creating such an index with a signature length of 100 bytes:
+    </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
+</programlisting>
+    <para>
      Note: This index type is lossy.
     </para>
    </listitem>
0007-opclass-parameters-contrib_pg_trgm-v01.patchtext/x-patch; name=0007-opclass-parameters-contrib_pg_trgm-v01.patchDownload
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 212a890..eb6b594 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -4,8 +4,8 @@ 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.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
+DATA = 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"
 
 REGRESS = pg_trgm pg_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.3--1.4.sql b/contrib/pg_trgm/pg_trgm--1.3--1.4.sql
new file mode 100644
index 0000000..318671a
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.3--1.4.sql
@@ -0,0 +1,33 @@
+/* contrib/pg_trgm/pg_trgm--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.4'" 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 06f274f..3e325dd 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.3'
+default_version = '1.4'
 module_pathname = '$libdir/pg_trgm'
 relocatable = true
diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h
index 45df918..ba15cc9 100644
--- a/contrib/pg_trgm/trgm.h
+++ b/contrib/pg_trgm/trgm.h
@@ -70,17 +70,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 )
@@ -88,8 +87,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
@@ -99,7 +98,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 e55dc19..b877b96 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;
@@ -307,7 +335,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)
@@ -349,7 +377,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;
@@ -402,7 +430,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);
@@ -432,6 +460,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;
@@ -487,7 +517,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);
@@ -503,7 +533,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, TRGM *add)
+unionkey(BITVECP sbase, TRGM *add, int siglen)
 {
 	int32		i;
 
@@ -514,7 +544,7 @@ unionkey(BITVECP sbase, TRGM *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -525,7 +555,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;
@@ -538,29 +568,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);
 }
@@ -571,6 +595,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 */
@@ -587,7 +613,7 @@ gtrgm_same(PG_FUNCTION_ARGS)
 						sb = GETSIGN(b);
 
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -624,24 +650,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];
@@ -650,19 +676,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
@@ -671,6 +697,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);
@@ -680,7 +708,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;
 
@@ -694,12 +722,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)
@@ -711,31 +739,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) )
@@ -756,19 +785,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
@@ -777,6 +806,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,
@@ -795,19 +826,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;
@@ -832,44 +867,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);
@@ -895,36 +908,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;
@@ -935,12 +948,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;
@@ -954,3 +967,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 338ef30..82e25b9 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -267,6 +267,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>
0008-opclass-parameters-contrib_hstore-v01.patchtext/x-patch; name=0008-opclass-parameters-contrib_hstore-v01.patchDownload
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index ab7fef3..6782f33 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -5,7 +5,7 @@ OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
 	$(WIN32RES)
 
 EXTENSION = hstore
-DATA = hstore--1.4.sql hstore--1.4--1.5.sql \
+DATA = hstore--1.4.sql hstore--1.4--1.5.sql hstore--1.5--1.6.sql \
 	hstore--1.3--1.4.sql hstore--1.2--1.3.sql \
 	hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
 	hstore--unpackaged--1.0.sql
diff --git a/contrib/hstore/hstore--1.5--1.6.sql b/contrib/hstore/hstore--1.5--1.6.sql
new file mode 100644
index 0000000..08c85ee
--- /dev/null
+++ b/contrib/hstore/hstore--1.5--1.6.sql
@@ -0,0 +1,32 @@
+/* contrib/hstore/hstore--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.6'" 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
+('ghstore_compress(internal)', '{internal,internal}'),
+('ghstore_decompress(internal)', '{internal,internal}'),
+('ghstore_consistent(internal,hstore,smallint,oid,internal)', '{internal,hstore,smallint,oid,internal,internal}'),
+('ghstore_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ghstore_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ghstore_union(internal,internal)', '{internal,internal,internal}'),
+('ghstore_same(ghstore,ghstore,internal)', '{ghstore,ghstore,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ghstore_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ghstore_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_hstore_ops USING gist
+ADD FUNCTION 10 (hstore) ghstore_options (internal, boolean);
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 8a71947..93688cd 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.5'
+default_version = '1.6'
 module_pathname = '$libdir/hstore'
 relocatable = true
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 6d24d2f..13be2c0 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -4,29 +4,33 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "catalog/pg_type.h"
 #include "utils/pg_crc.h"
 
 #include "hstore.h"
 
+/* gist_hstore_ops opclass options */
+typedef struct HstoreGistOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+} GistHstoreOptions;
+
 /* bigint defines */
 #define BITBYTE 8
-#define SIGLENINT  4			/* >122 => key will toast, so very slow!!! */
-#define SIGLEN	( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
+#define SIGLEN_DEFAULT	(sizeof(int32) * 4)
+#define SIGLEN_MAX		(sizeof(int32) * 122)	/* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
 
-typedef char BITVEC[SIGLEN];
 typedef char *BITVECP;
 
-#define SIGPTR(x)  ( (BITVECP) ARR_DATA_PTR(x) )
-
+#define LOOPBYTE(siglen) \
+			for (i = 0; i < (siglen); i++)
 
-#define LOOPBYTE \
-			for(i=0;i<SIGLEN;i++)
-
-#define LOOPBIT \
-			for(i=0;i<SIGLENBIT;i++)
+#define LOOPBIT(siglen) \
+			for (i = 0; i < SIGLENBIT(siglen); i++)
 
 /* beware of multiple evaluation of arguments to these macros! */
 #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
@@ -34,8 +38,8 @@ typedef char *BITVECP;
 #define CLRBIT(x,i)   GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) )
 #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))
 
 typedef struct
 {
@@ -49,7 +53,7 @@ typedef struct
 #define ISALLTRUE(x)	( ((GISTTYPE*)x)->flag & ALLISTRUE )
 
 #define GTHDRSIZE		(VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
 
 #define GETSIGN(x)		( (BITVECP)( (char*)x+GTHDRSIZE ) )
 
@@ -100,6 +104,27 @@ ghstore_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+static GISTTYPE *
+ghstore_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+	int			flag = allistrue ? ALLISTRUE : 0;
+	int			size = CALCGTSIZE(flag, siglen);
+	GISTTYPE   *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if (!allistrue)
+	{
+		if (sign)
+			memcpy(GETSIGN(res), sign, siglen);
+		else
+			memset(GETSIGN(res), 0, siglen);
+	}
+
+	return res;
+}
+
 PG_FUNCTION_INFO_V1(ghstore_consistent);
 PG_FUNCTION_INFO_V1(ghstore_compress);
 PG_FUNCTION_INFO_V1(ghstore_decompress);
@@ -107,36 +132,37 @@ PG_FUNCTION_INFO_V1(ghstore_penalty);
 PG_FUNCTION_INFO_V1(ghstore_picksplit);
 PG_FUNCTION_INFO_V1(ghstore_union);
 PG_FUNCTION_INFO_V1(ghstore_same);
+PG_FUNCTION_INFO_V1(ghstore_options);
 
 Datum
 ghstore_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(1);
+	int			siglen = options->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{
-		GISTTYPE   *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		GISTTYPE   *res = ghstore_alloc(false, siglen, NULL);
 		HStore	   *val = DatumGetHStoreP(entry->key);
 		HEntry	   *hsent = ARRPTR(val);
 		char	   *ptr = STRPTR(val);
 		int			count = HS_COUNT(val);
 		int			i;
 
-		SET_VARSIZE(res, CALCGTSIZE(0));
-
 		for (i = 0; i < count; ++i)
 		{
 			int			h;
 
 			h = crc32_sz((char *) HSTORE_KEY(hsent, ptr, i),
 						 HSTORE_KEYLEN(hsent, i));
-			HASH(GETSIGN(res), h);
+			HASH(GETSIGN(res), h, siglen);
 			if (!HSTORE_VALISNULL(hsent, i))
 			{
 				h = crc32_sz((char *) HSTORE_VAL(hsent, ptr, i),
 							 HSTORE_VALLEN(hsent, i));
-				HASH(GETSIGN(res), h);
+				HASH(GETSIGN(res), h, siglen);
 			}
 		}
 
@@ -152,15 +178,13 @@ ghstore_compress(PG_FUNCTION_ARGS)
 		GISTTYPE   *res;
 		BITVECP		sign = GETSIGN(DatumGetPointer(entry->key));
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(retval);
 		}
 
-		res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
-		SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
-		res->flag = ALLISTRUE;
+		res = ghstore_alloc(true, siglen, NULL);
 
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
@@ -188,6 +212,9 @@ ghstore_same(PG_FUNCTION_ARGS)
 	GISTTYPE   *a = (GISTTYPE *) PG_GETARG_POINTER(0);
 	GISTTYPE   *b = (GISTTYPE *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
+
 
 	if (ISALLTRUE(a) && ISALLTRUE(b))
 		*result = true;
@@ -202,7 +229,7 @@ ghstore_same(PG_FUNCTION_ARGS)
 					sb = GETSIGN(b);
 
 		*result = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -215,12 +242,12 @@ ghstore_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
 	int32		size = 0,
 				i;
 
-	LOOPBYTE
+	LOOPBYTE(siglen)
 	{
 		size += SUMBIT(sign);
 		sign = (BITVECP) (((char *) sign) + 1);
@@ -229,12 +256,12 @@ sizebitvec(BITVECP sign)
 }
 
 static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
 {
 	int			i,
 				dist = 0;
 
-	LOOPBIT
+	LOOPBIT(siglen)
 	{
 		if (GETBIT(a, i) != GETBIT(b, i))
 			dist++;
@@ -243,30 +270,30 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *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);
 }
 
 static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = GETSIGN(add);
 
 	if (ISALLTRUE(add))
 		return 1;
-	LOOPBYTE
+	LOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -278,28 +305,23 @@ ghstore_union(PG_FUNCTION_ARGS)
 	int32		len = entryvec->n;
 
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	int32		i;
-	int32		flag = 0;
-	GISTTYPE   *result;
+	GISTTYPE   *result = ghstore_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;
 		}
 	}
 
-	len = CALCGTSIZE(flag);
-	result = (GISTTYPE *) 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);
 }
@@ -310,10 +332,12 @@ ghstore_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);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 	GISTTYPE   *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
 	GISTTYPE   *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -338,6 +362,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	OffsetNumber maxoff = entryvec->n - 2;
 
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	OffsetNumber k,
 				j;
 	GISTTYPE   *datum_l,
@@ -368,7 +394,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -390,33 +416,10 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_l, GTHDRSIZE);
-		datum_l->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
-			;
-	}
-	if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_r, GTHDRSIZE);
-		datum_r->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
-	}
+	datum_l = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_1)));
+	datum_r = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_2)));
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -425,8 +428,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -450,20 +453,20 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.0001))
 		{
 			if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -474,12 +477,12 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -504,6 +507,8 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	bool		res = true;
 	BITVECP		sign;
 
@@ -529,13 +534,13 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			int			crc = crc32_sz((char *) HSTORE_KEY(qe, qv, i),
 									   HSTORE_KEYLEN(qe, i));
 
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (GETBIT(sign, HASHVAL(crc, siglen)))
 			{
 				if (!HSTORE_VALISNULL(qe, i))
 				{
 					crc = crc32_sz((char *) HSTORE_VAL(qe, qv, i),
 								   HSTORE_VALLEN(qe, i));
-					if (!GETBIT(sign, HASHVAL(crc)))
+					if (!GETBIT(sign, HASHVAL(crc, siglen)))
 						res = false;
 				}
 			}
@@ -548,7 +553,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 		text	   *query = PG_GETARG_TEXT_PP(1);
 		int			crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-		res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
+		res = (GETBIT(sign, HASHVAL(crc, siglen))) ? true : false;
 	}
 	else if (strategy == HStoreExistsAllStrategyNumber)
 	{
@@ -569,7 +574,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			if (key_nulls[i])
 				continue;
 			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (!(GETBIT(sign, HASHVAL(crc))))
+			if (!(GETBIT(sign, HASHVAL(crc, siglen))))
 				res = false;
 		}
 	}
@@ -594,7 +599,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			if (key_nulls[i])
 				continue;
 			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (GETBIT(sign, HASHVAL(crc, siglen)))
 				res = true;
 		}
 	}
@@ -603,3 +608,20 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+ghstore_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(GistHstoreOptions, siglen) };
+	GistHstoreOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(GistHstoreOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 94ccd12..45a811f 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -462,6 +462,15 @@ CREATE INDEX hidx ON testhstore USING GIN (h);
 </programlisting>
 
   <para>
+   GiST opclass <literal>gist_hstore_ops</literal> has one optional parameter
+   <literal>siglen</literal> for specifying signature length in bytes
+   (default signature length is 16 bytes, maximal length is 488 bytes):
+<programlisting>
+  CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32));
+</programlisting>
+  </para>
+
+  <para>
    <type>hstore</type> also supports <type>btree</type> or <type>hash</type> indexes for
    the <literal>=</literal> operator. This allows <type>hstore</type> columns to be
    declared <literal>UNIQUE</literal>, or to be used in <literal>GROUP BY</literal>,
0001-opclass-parameters-v01.patchtext/x-patch; name=0001-opclass-parameters-v01.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 71e20f2..4cfd759 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3874,6 +3874,17 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        partial index.
       </entry>
      </row>
+
+     <row>
+      <entry><structfield>indoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       This is an array of <structfield>indnatts</structfield> strings that
+       store per-column opclass options or null.  Each array element is a
+       serialized array of column's opclass options in reloptions format or null.
+      </entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 0818196..e6c967b 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -983,7 +983,7 @@ CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
    An index definition can specify an <firstterm>operator
    class</firstterm> for each column of an index.
 <synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
 </synopsis>
    The operator class identifies the operators to be used by the index
    for that column.  For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 1fd21e1..907b436 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
-    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
     [ WHERE <replaceable class="parameter">predicate</replaceable> ]
@@ -232,6 +232,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
      </varlistentry>
 
      <varlistentry>
+      <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+      <listitem>
+       <para>
+        The name of an operator class parameter. See below for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><literal>ASC</literal></term>
       <listitem>
        <para>
@@ -544,8 +553,9 @@ Indexes:
   </para>
 
   <para>
-   An <firstterm>operator class</firstterm> can be specified for each
-   column of an index. The operator class identifies the operators to be
+   An <firstterm>operator class</firstterm> with its optional parameters 
+   can be specified for each column of an index.
+   The operator class identifies the operators to be
    used by the index for that column. For example, a B-tree index on
    four-byte integers would use the <literal>int4_ops</literal> class;
    this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 46276ce..4c72986 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1007,6 +1007,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 	return options;
 }
 
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+				 relopt_value *reloptions, int numoptions)
+{
+	ArrayType  *array = DatumGetArrayTypeP(options);
+	Datum	   *optiondatums;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(array, TEXTOID, -1, false, 'i',
+					  &optiondatums, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *text_str = VARDATA(optiondatums[i]);
+		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		int			j;
+
+		/* Search for a match in reloptions */
+		for (j = 0; j < numoptions; j++)
+		{
+			int			kw_len = reloptions[j].gen->namelen;
+
+			if (text_len > kw_len && text_str[kw_len] == '=' &&
+				strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+			{
+				parse_one_reloption(&reloptions[j], text_str, text_len,
+									validate);
+				break;
+			}
+		}
+
+		if (j >= numoptions && validate)
+		{
+			char	   *s;
+			char	   *p;
+
+			s = TextDatumGetCString(optiondatums[i]);
+			p = strchr(s, '=');
+			if (p)
+				*p = '\0';
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized parameter \"%s\"", s)));
+		}
+	}
+
+	/* It's worth avoiding memory leaks in this function */
+	pfree(optiondatums);
+
+	if (((void *) array) != DatumGetPointer(options))
+		pfree(array);
+}
+
 /*
  * Interpret reloptions that are given in text-array format.
  *
@@ -1061,57 +1115,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
 
 	/* Done if no options */
 	if (PointerIsValid(DatumGetPointer(options)))
-	{
-		ArrayType  *array = DatumGetArrayTypeP(options);
-		Datum	   *optiondatums;
-		int			noptions;
+		parseRelOptionsInternal(options, validate, reloptions, numoptions);
 
-		deconstruct_array(array, TEXTOID, -1, false, 'i',
-						  &optiondatums, NULL, &noptions);
+	*numrelopts = numoptions;
+	return reloptions;
+}
 
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *text_str = VARDATA(optiondatums[i]);
-			int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
-			int			j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen *optgen[], int nopts)
+{
+	relopt_value *values = palloc(sizeof(*values) * nopts);
+	int			i;
 
-			/* Search for a match in reloptions */
-			for (j = 0; j < numoptions; j++)
-			{
-				int			kw_len = reloptions[j].gen->namelen;
+	for (i = 0; i < nopts; i++)
+	{
+		values[i].gen = optgen[i];
+		values[i].isset = false;
+	}
 
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
-				{
-					parse_one_reloption(&reloptions[j], text_str, text_len,
-										validate);
-					break;
-				}
-			}
+	if (options != (Datum) 0)
+		parseRelOptionsInternal(options, validate, values, nopts);
 
-			if (j >= numoptions && validate)
-			{
-				char	   *s;
-				char	   *p;
+	return values;
+}
 
-				s = TextDatumGetCString(optiondatums[i]);
-				p = strchr(s, '=');
-				if (p)
-					*p = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("unrecognized parameter \"%s\"", s)));
-			}
-		}
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+							int noptions, size_t base_size, bool validate)
+{
+	relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+	relopt_value *vals;
+	void	   *opts;
+	int			i;
 
-		/* It's worth avoiding memory leaks in this function */
-		pfree(optiondatums);
-		if (((void *) array) != DatumGetPointer(options))
-			pfree(array);
+	for (i = 0; i < noptions; i++)
+	{
+		elems[i].optname = optgen[i]->name;
+		elems[i].opttype = optgen[i]->type;
+		elems[i].offset = offsets[i];
 	}
 
-	*numrelopts = numoptions;
-	return reloptions;
+	vals = parseLocalRelOptions(options, validate, optgen, noptions);
+	opts = allocateReloptStruct(base_size, vals, noptions);
+	fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+	if (elems)
+		pfree(elems);
+
+	return opts;
 }
 
 /*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 91247f0..9f8aad2 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -75,11 +75,14 @@
 #include "access/xlog.h"
 #include "catalog/catalog.h"
 #include "catalog/index.h"
+#include "commands/defrem.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 #include "utils/tqual.h"
 
 
@@ -898,3 +901,72 @@ index_getprocinfo(Relation irel,
 
 	return locinfo;
 }
+
+/*
+ * Parse opclass-specific options for index column.
+ *
+ *	amoptions	index relation
+ *	attnum		column number
+ *	indoptions	options as text[] datum
+ *	validate	error flag
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum indoptions,
+					  bool validate)
+{
+	amopclassoptions_function amopclassoptions =
+		relation->rd_amroutine->amopclassoptions;
+
+	if (!amopclassoptions)
+	{
+		if (validate && PointerIsValid(DatumGetPointer(indoptions)))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("access method \"%s\" does not support opclass options ",
+							 get_am_name(relation->rd_rel->relam))));
+
+		return NULL;
+	}
+
+	return amopclassoptions(relation, attnum, indoptions, validate);
+}
+
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+							  uint16 procnum, Datum reloptions, bool validate)
+{
+	Oid			procid = index_getprocid(indrel, attnum, procnum);
+	FmgrInfo   *procinfo;
+
+	if (!OidIsValid(procid))
+	{
+		StringInfoData opclassname;
+		Oid			opclass;
+		Datum		indclassDatum;
+		oidvector  *indclass;
+		bool		isnull;
+
+		if (!DatumGetPointer(reloptions))
+			return NULL;
+
+		indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+										Anum_pg_index_indclass, &isnull);
+		Assert(!isnull);
+		indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+		opclass = indclass->values[attnum - 1];
+
+		initStringInfo(&opclassname);
+		get_opclass_name(opclass, InvalidOid, &opclassname);
+
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("operator class%s has no options ", opclassname.data)));
+	}
+
+	procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+	return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+												   reloptions,
+												   BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 564f206..f74cac1 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -25,6 +25,7 @@
 
 #include "access/amapi.h"
 #include "access/multixact.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
@@ -586,6 +587,7 @@ UpdateIndexRelation(Oid indexoid,
 	int2vector *indoption;
 	Datum		exprsDatum;
 	Datum		predDatum;
+	Datum		indoptionsDatum;
 	Datum		values[Natts_pg_index];
 	bool		nulls[Natts_pg_index];
 	Relation	pg_index;
@@ -632,6 +634,53 @@ UpdateIndexRelation(Oid indexoid,
 	else
 		predDatum = (Datum) 0;
 
+	if (indexInfo->ii_OpclassOptions != NULL)
+	{
+		Datum	   *vals = palloc(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+		bool	   *nulls = palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
+		int			dims[1];
+		int			lbs[1];
+
+		for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+		{
+			Datum		options = indexInfo->ii_OpclassOptions[i];
+
+			nulls[i] = options == (Datum) 0;
+
+			if (!nulls[i])
+			{
+				FunctionCallInfoData fcinfo;
+				Datum		result;
+				FmgrInfo	flinfo = { 0 };
+
+				InitFunctionCallInfoData(fcinfo, &flinfo, 1, InvalidOid, NULL,
+										 NULL);
+
+				fcinfo.arg[0] = options;
+				fcinfo.argnull[0] = false;
+
+				flinfo.fn_mcxt = CurrentMemoryContext;
+
+				result = array_out(&fcinfo);
+
+				/* Check for null result, since caller is clearly not expecting one */
+				if (fcinfo.isnull)
+					elog(ERROR, "function %p returned NULL", (void *) array_out);
+
+				vals[i] = CStringGetTextDatum(DatumGetCString(result));
+			}
+		}
+
+		dims[0] = indexInfo->ii_NumIndexAttrs;
+		lbs[0] = 1;
+
+		indoptionsDatum = PointerGetDatum(construct_md_array(vals, nulls, 1,
+															 dims, lbs, TEXTOID,
+															 -1, false, 'i'));
+	}
+	else
+		indoptionsDatum = (Datum) 0;
+
 	/*
 	 * open the system catalog index relation
 	 */
@@ -665,6 +714,9 @@ UpdateIndexRelation(Oid indexoid,
 	values[Anum_pg_index_indpred - 1] = predDatum;
 	if (predDatum == (Datum) 0)
 		nulls[Anum_pg_index_indpred - 1] = true;
+	values[Anum_pg_index_indoptions - 1] = indoptionsDatum;
+	if (indoptionsDatum == (Datum) 0)
+		nulls[Anum_pg_index_indoptions - 1] = true;
 
 	tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls);
 
@@ -1140,6 +1192,12 @@ index_create(Relation heapRelation,
 	else
 		Assert(indexRelation->rd_indexcxt != NULL);
 
+	/* Validate opclass-specific options */
+	if (indexInfo->ii_OpclassOptions)
+		for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+			(void) index_opclass_options(indexRelation, i + 1,
+										 indexInfo->ii_OpclassOptions[i], true);
+
 	/*
 	 * If this is bootstrap (initdb) time, then we don't actually fill in the
 	 * index yet.  We'll be creating more indexes and classes later, so we
@@ -1777,6 +1835,8 @@ BuildIndexInfo(Relation index)
 		ii->ii_ExclusionStrats = NULL;
 	}
 
+	ii->ii_OpclassOptions = RelationGetRawOpclassOptions(index);
+
 	/* other info */
 	ii->ii_Unique = indexStruct->indisunique;
 	ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 8bf2698..099fb7d 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -311,6 +311,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;
 	indexInfo->ii_Unique = true;
 	indexInfo->ii_ReadyForInserts = true;
 	indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 3e48a58..97cf5d9 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -612,6 +612,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;	/* for now */
 	indexInfo->ii_Unique = stmt->unique;
 	/* In a concurrent build, mark it not-ready-for-inserts */
 	indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1348,12 +1349,11 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 	ListCell   *nextExclOp;
 	ListCell   *lc;
 	int			attn;
+	int			ncols = list_length(attList);
 
 	/* Allocate space for exclusion operator info, if needed */
 	if (exclusionOpNames)
 	{
-		int			ncols = list_length(attList);
-
 		Assert(list_length(exclusionOpNames) == ncols);
 		indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * ncols);
 		indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * ncols);
@@ -1594,6 +1594,17 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 								accessMethodName)));
 		}
 
+		/* Set up the per-column opclass options (indoptions field). */
+		if (attribute->opclassopts)
+		{
+			if (!indexInfo->ii_OpclassOptions)
+				indexInfo->ii_OpclassOptions = palloc0(sizeof(Datum) * ncols);
+
+			indexInfo->ii_OpclassOptions[attn] =
+				transformRelOptions((Datum) 0, attribute->opclassopts,
+									NULL, NULL, false, false);
+		}
+
 		attn++;
 	}
 }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 266a3ef..e089a6a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2807,6 +2807,7 @@ _copyIndexElem(const IndexElem *from)
 	COPY_STRING_FIELD(indexcolname);
 	COPY_NODE_FIELD(collation);
 	COPY_NODE_FIELD(opclass);
+	COPY_NODE_FIELD(opclassopts);
 	COPY_SCALAR_FIELD(ordering);
 	COPY_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index bbffc87..3c484f4 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2545,6 +2545,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
 	COMPARE_STRING_FIELD(indexcolname);
 	COMPARE_NODE_FIELD(collation);
 	COMPARE_NODE_FIELD(opclass);
+	COMPARE_NODE_FIELD(opclassopts);
 	COMPARE_SCALAR_FIELD(ordering);
 	COMPARE_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 011d2a3..c4c2e2e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2872,6 +2872,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
 	WRITE_STRING_FIELD(indexcolname);
 	WRITE_NODE_FIELD(collation);
 	WRITE_NODE_FIELD(opclass);
+	WRITE_NODE_FIELD(opclassopts);
 	WRITE_ENUM_FIELD(ordering, SortByDir);
 	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
 }
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 60f2171..5e8f1dd 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -353,6 +353,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				info->nulls_first = NULL;
 			}
 
+			/* Fetch index opclass options */
+			info->opclassoptions =
+				RelationGetParsedOpclassOptions(indexRelation);
+
 			/*
 			 * Fetch the index expressions and predicate, if any.  We must
 			 * modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d99f2be..eb780ce 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -483,7 +483,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <alias>	alias_clause opt_alias_clause
 %type <list>	func_alias_clause
 %type <sortby>	sortby
-%type <ielem>	index_elem
+%type <ielem>	index_elem index_elem_options
 %type <node>	table_ref
 %type <jexpr>	joined_table
 %type <range>	relation_expr
@@ -7438,43 +7438,65 @@ index_params:	index_elem							{ $$ = list_make1($1); }
 			| index_params ',' index_elem			{ $$ = lappend($1, $3); }
 		;
 
+
+index_elem_options:
+	opt_collate opt_class opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = NIL;
+			$$->ordering = $3;
+			$$->nulls_ordering = $4;
+		}
+	| opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	| opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = NIL;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	;
+
 /*
  * Index attributes can be either simple column references, or arbitrary
  * expressions in parens.  For backwards-compatibility reasons, we allow
  * an expression that's just a function call to be written without parens.
  */
-index_elem:	ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
 				{
-					$$ = makeNode(IndexElem);
+					$$ = $2;
 					$$->name = $1;
-					$$->expr = NULL;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+			| func_expr_windowless index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $2;
 					$$->expr = $1;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+			| '(' a_expr ')' index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $4;
 					$$->expr = $2;
-					$$->indexcolname = NULL;
-					$$->collation = $4;
-					$$->opclass = $5;
-					$$->ordering = $6;
-					$$->nulls_ordering = $7;
 				}
 		;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3697466..7e8bd56 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -450,8 +450,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
 						   deparse_context *context);
 static void get_tablesample_def(TableSampleClause *tablesample,
 					deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
-				 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context);
 static void printSubscripts(ArrayRef *aref, deparse_context *context);
 static char *get_relation_name(Oid relid);
@@ -465,6 +463,7 @@ static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1180,6 +1179,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	oidvector  *indcollation;
 	oidvector  *indclass;
 	int2vector *indoption;
+	Datum	   *indoptions = NULL;
 	StringInfoData buf;
 	char	   *str;
 	char	   *sep;
@@ -1215,6 +1215,16 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	Assert(!isnull);
 	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
 
+	if (!attrsOnly)
+	{
+		Datum indoptionsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+												Anum_pg_index_indoptions,
+												&isnull);
+		if (!isnull)
+			indoptions = ExtractRawOpclassOptions(indoptionsDatum,
+												  idxrec->indnatts);
+	}
+
 	/*
 	 * Fetch the pg_class tuple of the index relation
 	 */
@@ -1338,6 +1348,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 		if (!attrsOnly && (!colno || colno == keyno + 1))
 		{
 			Oid			indcoll;
+			bool		has_options =
+				indoptions && indoptions[keyno] != (Datum) 0;
 
 			/* Add collation, if not default for column */
 			indcoll = indcollation->values[keyno];
@@ -1346,7 +1358,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 								 generate_collation_name((indcoll)));
 
 			/* Add the operator class name, if not default */
-			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+			get_opclass_name(indclass->values[keyno],
+							 has_options ? InvalidOid : keycoltype, &buf);
+
+			if (has_options)
+			{
+				appendStringInfoString(&buf, " (");
+				get_reloptions(&buf, indoptions[keyno]);
+				appendStringInfoChar(&buf, ')');
+			}
 
 			/* Add options if relevant */
 			if (amroutine->amcanorder)
@@ -10297,7 +10317,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
  * actual_datatype.  (If you don't want this behavior, just pass
  * InvalidOid for actual_datatype.)
  */
-static void
+void
 get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf)
 {
@@ -10934,6 +10954,62 @@ string_to_text(char *str)
 }
 
 /*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+	Datum	   *options;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(reloptions),
+					  TEXTOID, -1, false, 'i',
+					  &options, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *option = TextDatumGetCString(options[i]);
+		char	   *name;
+		char	   *separator;
+		char	   *value;
+
+		/*
+		 * Each array element should have the form name=value.  If the "="
+		 * is missing for some reason, treat it like an empty value.
+		 */
+		name = option;
+		separator = strchr(option, '=');
+		if (separator)
+		{
+			*separator = '\0';
+			value = separator + 1;
+		}
+		else
+			value = "";
+
+		if (i > 0)
+			appendStringInfoString(buf, ", ");
+		appendStringInfo(buf, "%s=", quote_identifier(name));
+
+		/*
+		 * In general we need to quote the value; but to avoid unnecessary
+		 * clutter, do not quote if it is an identifier that would not
+		 * need quoting.  (We could also allow numbers, but that is a bit
+		 * trickier than it looks --- for example, are leading zeroes
+		 * significant?  We don't want to assume very much here about what
+		 * custom reloptions might mean.)
+		 */
+		if (quote_identifier(value) == value)
+			appendStringInfoString(buf, value);
+		else
+			simple_quote_literal(buf, value);
+
+		pfree(option);
+	}
+}
+
+/*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
 static char *
@@ -10953,56 +11029,9 @@ flatten_reloptions(Oid relid)
 	if (!isnull)
 	{
 		StringInfoData buf;
-		Datum	   *options;
-		int			noptions;
-		int			i;
 
 		initStringInfo(&buf);
-
-		deconstruct_array(DatumGetArrayTypeP(reloptions),
-						  TEXTOID, -1, false, 'i',
-						  &options, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *option = TextDatumGetCString(options[i]);
-			char	   *name;
-			char	   *separator;
-			char	   *value;
-
-			/*
-			 * Each array element should have the form name=value.  If the "="
-			 * is missing for some reason, treat it like an empty value.
-			 */
-			name = option;
-			separator = strchr(option, '=');
-			if (separator)
-			{
-				*separator = '\0';
-				value = separator + 1;
-			}
-			else
-				value = "";
-
-			if (i > 0)
-				appendStringInfoString(&buf, ", ");
-			appendStringInfo(&buf, "%s=", quote_identifier(name));
-
-			/*
-			 * In general we need to quote the value; but to avoid unnecessary
-			 * clutter, do not quote if it is an identifier that would not
-			 * need quoting.  (We could also allow numbers, but that is a bit
-			 * trickier than it looks --- for example, are leading zeroes
-			 * significant?  We don't want to assume very much here about what
-			 * custom reloptions might mean.)
-			 */
-			if (quote_identifier(value) == value)
-				appendStringInfoString(&buf, value);
-			else
-				simple_quote_literal(&buf, value);
-
-			pfree(option);
-		}
+		get_reloptions(&buf, reloptions);
 
 		result = buf.data;
 	}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1ebf9c4..1556927 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -77,6 +77,7 @@
 #include "storage/smgr.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/datum.h"
 #include "utils/fmgroids.h"
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
@@ -5212,6 +5213,185 @@ GetRelationPublicationActions(Relation relation)
 }
 
 /*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+	MemoryContext oldcxt;
+	bytea	  **opts;
+	Datum		indoptionsDatum;
+	Datum	   *indoptions;
+	bool	   *indoptionsnulls;
+	int			indoptionsnum;
+	int			i;
+	int			ncols = relation->rd_rel->relnatts;
+	bool		isnull;
+
+	if (relation->rd_indoptions)
+	{
+		opts = palloc(sizeof(*opts) * ncols);
+
+		for (i = 0; i < ncols; i++)
+		{
+			bytea	   *opt = relation->rd_indoptions[i];
+
+			opts[i] = !opt ? NULL : (bytea *)
+				DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+		}
+
+		return opts;
+	}
+
+	opts = palloc0(sizeof(*opts) * ncols);
+
+	if (relation->rd_indextuple == NULL ||
+		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions))
+	{
+		indoptions = NULL;
+		indoptionsnulls = NULL;
+		indoptionsnum = 0;
+	}
+	else
+	{
+		indoptionsDatum = heap_getattr(relation->rd_indextuple,
+									   Anum_pg_index_indoptions,
+									   GetPgIndexDescriptor(),
+									   &isnull);
+		Assert(!isnull);
+
+		deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+						  TEXTOID, -1, false, 'i',
+						  &indoptions, &indoptionsnulls, &indoptionsnum);
+
+		Assert(indoptionsnum == ncols);
+	}
+
+	for (i = 0; i < ncols; i++)
+	{
+		Datum		options;
+
+		if (i < indoptionsnum && !indoptionsnulls[i])
+		{
+			FunctionCallInfoData fcinfo;
+			FmgrInfo	flinfo = { 0 };
+			char	   *optionsstr = TextDatumGetCString(indoptions[i]);
+
+			InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL, NULL);
+
+			fcinfo.arg[0] = CStringGetDatum(optionsstr);
+			fcinfo.argnull[0] = false;
+			fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+			fcinfo.argnull[1] = false;
+			fcinfo.arg[2] = Int32GetDatum(-1);
+			fcinfo.argnull[2] = false;
+
+			flinfo.fn_mcxt = CurrentMemoryContext;
+
+			options = array_in(&fcinfo);
+
+			if (fcinfo.isnull)
+				elog(ERROR, "function %p returned NULL", (void *) array_in);
+
+			pfree(optionsstr);
+		}
+		else
+		{
+			options = PointerGetDatum(NULL);
+		}
+
+		opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+		if (options != PointerGetDatum(NULL))
+			pfree(DatumGetPointer(options));
+	}
+
+	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+	relation->rd_indoptions = palloc(sizeof(*opts) * ncols);
+
+	for (i = 0; i < ncols; i++)
+		relation->rd_indoptions[i] = !opts[i] ? NULL : (bytea *)
+			DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return opts;
+}
+
+Datum *
+ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols)
+{
+	Datum	   *indoptions;
+	bool	   *indoptionsnulls;
+	int			indoptionsnum;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+					  TEXTOID, -1, false, 'i',
+					  &indoptions, &indoptionsnulls, &indoptionsnum);
+
+	Assert(indoptionsnum == ncols);
+
+	for (i = 0; i < indoptionsnum; i++)
+	{
+		if (indoptionsnulls[i])
+			indoptions[i] = PointerGetDatum(NULL);
+		else
+		{
+			FunctionCallInfoData fcinfo;
+			FmgrInfo	flinfo = { 0 };
+			char	   *optionsstr = TextDatumGetCString(indoptions[i]);
+
+			InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL,
+									 NULL);
+
+			fcinfo.arg[0] = CStringGetDatum(optionsstr);
+			fcinfo.argnull[0] = false;
+			fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+			fcinfo.argnull[1] = false;
+			fcinfo.arg[2] = Int32GetDatum(-1);
+			fcinfo.argnull[2] = false;
+
+			flinfo.fn_mcxt = CurrentMemoryContext;
+
+			indoptions[i] = array_in(&fcinfo);
+
+			/* Check for null result, since caller is clearly not expecting one */
+			if (fcinfo.isnull)
+				elog(ERROR, "function %p returned NULL", (void *) array_in);
+		}
+	}
+
+	return indoptions;
+}
+
+/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for an index
+ */
+Datum *
+RelationGetRawOpclassOptions(Relation relation)
+{
+	Datum		indoptionsDatum;
+	bool		isnull;
+
+	/* Quick exit if there is nothing to do. */
+	if (relation->rd_indextuple == NULL ||
+		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions))
+		return NULL;
+
+	indoptionsDatum = heap_getattr(relation->rd_indextuple,
+								   Anum_pg_index_indoptions,
+								   GetPgIndexDescriptor(),
+								   &isnull);
+	Assert(!isnull);
+
+	return ExtractRawOpclassOptions(indoptionsDatum,
+									relation->rd_rel->relnatts);
+}
+
+
+/*
  * Routines to support ereport() reports of relation-related errors
  *
  * These could have been put into elog.c, but it seems like a module layering
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 8d7bc24..bde93c3 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -102,6 +102,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
 typedef bytea *(*amoptions_function) (Datum reloptions,
 									  bool validate);
 
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+											 AttrNumber colno,
+											 Datum indoptions,
+											 bool validate);
+
 /* report AM, index, or index column property */
 typedef bool (*amproperty_function) (Oid index_oid, int attno,
 									 IndexAMProperty prop, const char *propname,
@@ -203,6 +209,7 @@ typedef struct IndexAmRoutine
 	amcanreturn_function amcanreturn;	/* can be NULL */
 	amcostestimate_function amcostestimate;
 	amoptions_function amoptions;
+	amopclassoptions_function amopclassoptions;
 	amproperty_function amproperty; /* can be NULL */
 	amvalidate_function amvalidate;
 	ambeginscan_function ambeginscan;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 24c720b..0d490d6 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -174,6 +174,13 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
 				uint16 procnum);
 extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
 				  uint16 procnum);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+					  Datum indoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+							  AttrNumber attnum, uint16 procnum,
+							  Datum indoptions, bool validate);
+
+
 
 /*
  * index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index b32c1e9..9bb82d2 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -263,12 +263,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 				  amoptions_function amoptions);
 extern relopt_value *parseRelOptions(Datum options, bool validate,
 				relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen **gen, int nelems);
 extern void *allocateReloptStruct(Size base, relopt_value *options,
 					 int numoptions);
 extern void fillRelOptions(void *rdopts, Size basesize,
 			   relopt_value *options, int numoptions,
 			   bool validate,
 			   const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+							int offsets[], int noptions, size_t base_size,
+							bool validate);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 057a9f7..c40d009 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -56,6 +56,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
 								 * each zero entry in indkey[] */
 	pg_node_tree indpred;		/* expression tree for predicate, if a partial
 								 * index; else NULL */
+	text		indoptions[1];	/* opclass options */
 #endif
 } FormData_pg_index;
 
@@ -70,7 +71,7 @@ typedef FormData_pg_index *Form_pg_index;
  *		compiler constants for pg_index
  * ----------------
  */
-#define Natts_pg_index					19
+#define Natts_pg_index					20
 #define Anum_pg_index_indexrelid		1
 #define Anum_pg_index_indrelid			2
 #define Anum_pg_index_indnatts			3
@@ -90,6 +91,7 @@ typedef FormData_pg_index *Form_pg_index;
 #define Anum_pg_index_indoption			17
 #define Anum_pg_index_indexprs			18
 #define Anum_pg_index_indpred			19
+#define Anum_pg_index_indoptions		20
 
 /*
  * Index AMs that support ordered scans must support these two indoption
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a953820..9e65097 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -129,6 +129,7 @@ typedef struct ExprState
  *		UniqueProcs
  *		UniqueStrats
  *		Unique				is it a unique index?
+ *		OpclassOptions		opclass-specific options, or NULL if none
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
  *		BrokenHotChain		did we detect any broken HOT chains?
@@ -155,6 +156,7 @@ typedef struct IndexInfo
 	Oid		   *ii_UniqueOps;	/* array with one entry per column */
 	Oid		   *ii_UniqueProcs; /* array with one entry per column */
 	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	Datum	   *ii_OpclassOptions;	/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac292bc..5a359f4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -698,6 +698,7 @@ typedef struct IndexElem
 	char	   *indexcolname;	/* name for index column; NULL = default */
 	List	   *collation;		/* name of collation; NIL = default */
 	List	   *opclass;		/* name of desired opclass; NIL = default */
+	List	   *opclassopts;	/* opclass-specific options, or NIL */
 	SortByDir	ordering;		/* ASC/DESC/default */
 	SortByNulls nulls_ordering; /* FIRST/LAST/default */
 } IndexElem;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index db8de2d..795f7f6 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -740,6 +740,7 @@ typedef struct IndexOptInfo
 	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
 	bool	   *reverse_sort;	/* is sort order descending? */
 	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
+	bytea	 **opclassoptions;	/* opclass-specific options for columns */
 	bool	   *canreturn;		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index aa8add5..739478f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -191,6 +191,7 @@ typedef struct RelationData
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
 	void	   *rd_amcache;		/* available for use by index AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
+	bytea	  **rd_indoptions;	/* parsed opclass-specific options */
 
 	/*
 	 * foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 8a546ab..8ef5795 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
 #ifndef RELCACHE_H
 #define RELCACHE_H
 
+#include "postgres.h"
 #include "access/tupdesc.h"
 #include "nodes/bitmapset.h"
 
@@ -50,6 +51,9 @@ extern Oid	RelationGetPrimaryKeyIndex(Relation relation);
 extern Oid	RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Relation relation);
+extern Datum *ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols);
 
 typedef enum IndexAttrBitmapKind
 {
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9f9b029..86e6953 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,5 +34,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
 								Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
 extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
 
 #endif							/* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 755cd17..5504dd9 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,8 @@ vacuum btree_tall_tbl;
 -- need to insert some rows to cause the fast root page to split.
 insert into btree_tall_tbl (id, t)
   select g, repeat('x', 100) from generate_series(1, 500) g;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  access method "btree" does not support opclass options 
+create index on btree_tall_tbl (id default(foo=1));
+ERROR:  access method "btree" does not support opclass options 
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 65b08c8..b7a4890 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -92,3 +92,7 @@ vacuum btree_tall_tbl;
 -- need to insert some rows to cause the fast root page to split.
 insert into btree_tall_tbl (id, t)
   select g, repeat('x', 100) from generate_series(1, 500) g;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
0002-opclass-parameters-GiST-v01.patchtext/x-patch; name=0002-opclass-parameters-GiST-v01.patchDownload
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9f5c0c3..2f00930 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
        index-only scans (optional)</entry>
        <entry>9</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>parse opclass-specific options (optional)</entry>
+       <entry>10</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 51c32e4..e260e2e 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -94,6 +94,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->amopclassoptions = gistopclassoptions;
 
 	PG_RETURN_POINTER(amroutine);
 }
@@ -1445,6 +1446,7 @@ initGISTstate(Relation index)
 	giststate->scanCxt = scanCxt;
 	giststate->tempCxt = scanCxt;	/* caller must change this if needed */
 	giststate->tupdesc = index->rd_att;
+	giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < index->rd_att->natts; i++)
 	{
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index b30b931..fe8d75e 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -194,6 +194,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		test;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -214,13 +215,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 */
 			recheck = true;
 
-			test = FunctionCall5Coll(&key->sk_func,
+			test = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 
 			if (!DatumGetBool(test))
 				return false;
@@ -255,6 +257,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		dist;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -277,13 +280,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 * about the flag, but are expected to never be lossy.
 			 */
 			recheck = false;
-			dist = FunctionCall5Coll(&key->sk_func,
+			dist = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 			*recheck_distances_p |= recheck;
 			*distance_p = DatumGetFloat8(dist);
 		}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index a7038cc..96a9290 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
 	evec->n = v->spl_nleft;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	evec->n = v->spl_nright;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 }
 
 /*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
 	 * Let the opclass-specific PickSplit method do its thing.  Note that at
 	 * this point we know there are no null keys in the entryvec.
 	 */
-	FunctionCall2Coll(&giststate->picksplitFn[attno],
+	FunctionCall3Coll(&giststate->picksplitFn[attno],
 					  giststate->supportCollation[attno],
 					  PointerGetDatum(entryvec),
-					  PointerGetDatum(sv));
+					  PointerGetDatum(sv),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	if (sv->spl_nleft == 0 || sv->spl_nright == 0)
 	{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 55cccd2..bab02f1 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -23,6 +23,7 @@
 #include "storage/indexfsm.h"
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
+#include "utils/ruleutils.h"
 #include "utils/syscache.h"
 
 
@@ -199,10 +200,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
 			}
 
 			/* Make union and store in attr array */
-			attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+			attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
 										giststate->supportCollation[i],
 										PointerGetDatum(evec),
-										PointerGetDatum(&attrsize));
+										PointerGetDatum(&attrsize),
+										PointerGetDatum(giststate->opclassoptions[i]));
 
 			isnull[i] = false;
 		}
@@ -268,10 +270,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
 		}
 
 		*dstisnull = false;
-		*dst = FunctionCall2Coll(&giststate->unionFn[attno],
+		*dst = FunctionCall3Coll(&giststate->unionFn[attno],
 								 giststate->supportCollation[attno],
 								 PointerGetDatum(evec),
-								 PointerGetDatum(&dstsize));
+								 PointerGetDatum(&dstsize),
+								 PointerGetDatum(giststate->opclassoptions[attno]));
 	}
 }
 
@@ -280,10 +283,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
 {
 	bool		result;
 
-	FunctionCall3Coll(&giststate->equalFn[attno],
+	FunctionCall4Coll(&giststate->equalFn[attno],
 					  giststate->supportCollation[attno],
 					  a, b,
-					  PointerGetDatum(&result));
+					  PointerGetDatum(&result),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 	return result;
 }
 
@@ -556,9 +560,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
 			return;
 
 		dep = (GISTENTRY *)
-			DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+			DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
 											  giststate->supportCollation[nkey],
-											  PointerGetDatum(e)));
+											  PointerGetDatum(e),
+											  PointerGetDatum(giststate->opclassoptions[nkey])));
 		/* decompressFn may just return the given pointer */
 		if (dep != e)
 			gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -593,9 +598,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
 			/* there may not be a compress function in opclass */
 			if (OidIsValid(giststate->compressFn[i].fn_oid))
 				cep = (GISTENTRY *)
-					DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+					DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
 													  giststate->supportCollation[i],
-													  PointerGetDatum(&centry)));
+													  PointerGetDatum(&centry),
+													  PointerGetDatum(giststate->opclassoptions[i])));
 			else
 				cep = &centry;
 			compatt[i] = cep->key;
@@ -624,9 +630,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
 	gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
 
 	fep = (GISTENTRY *)
-		DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+		DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
 										  giststate->supportCollation[nkey],
-										  PointerGetDatum(&fentry)));
+										  PointerGetDatum(&fentry),
+										  PointerGetDatum(giststate->opclassoptions[nkey])));
 
 	/* fetchFn set 'key', return it to the caller */
 	return fep->key;
@@ -694,11 +701,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
 	if (giststate->penaltyFn[attno].fn_strict == false ||
 		(isNullOrig == false && isNullAdd == false))
 	{
-		FunctionCall3Coll(&giststate->penaltyFn[attno],
+		FunctionCall4Coll(&giststate->penaltyFn[attno],
 						  giststate->supportCollation[attno],
 						  PointerGetDatum(orig),
 						  PointerGetDatum(add),
-						  PointerGetDatum(&penalty));
+						  PointerGetDatum(&penalty),
+						  PointerGetDatum(giststate->opclassoptions[attno]));
 		/* disallow negative or NaN penalty */
 		if (isnan(penalty) || penalty < 0.0)
 			penalty = 0.0;
@@ -860,6 +868,14 @@ gistoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum options, bool validate)
+{
+	return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+										 options, validate);
+}
+
+
 /*
  *	gistproperty() -- Check boolean properties of indexes.
  *
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index dd87dad..0fa5646 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
 		{
 			case GIST_CONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_UNION_PROC:
 				ok = check_amproc_signature(procform->amproc, opckeytype, false,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_COMPRESS_PROC:
 			case GIST_DECOMPRESS_PROC:
 			case GIST_FETCH_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											1, 1, INTERNALOID);
+											1, 2, INTERNALOID, INTERNALOID);
 				break;
 			case GIST_PENALTY_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											3, 3, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											3, 4, INTERNALOID,
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_PICKSPLIT_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_EQUAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											3, 3, opckeytype, opckeytype,
-											INTERNALOID);
+											3, 4, opckeytype, opckeytype,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIST_DISTANCE_PROC:
 				ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
+				break;
+			case GIST_OPCLASSOPT_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+											2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
 		if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
-			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+			i == GIST_OPCLASSOPT_PROC)
 			continue;			/* optional methods */
 		ereport(INFO,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 827566d..08e7ada 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
 #define GIST_EQUAL_PROC					7
 #define GIST_DISTANCE_PROC				8
 #define GIST_FETCH_PROC					9
-#define GISTNProcs					9
+#define GIST_OPCLASSOPT_PROC			10
+#define GISTNProcs						10
 
 /*
  * Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 36ed724..eedd896 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -94,6 +94,8 @@ typedef struct GISTSTATE
 
 	/* Collations to pass to the support functions */
 	Oid			supportCollation[INDEX_MAX_KEYS];
+
+	bytea	  **opclassoptions;	/* parsed opclass-specific options */
 } GISTSTATE;
 
 
@@ -436,6 +438,8 @@ extern bool gistvalidate(Oid opclassoid);
 #define GIST_DEFAULT_FILLFACTOR		90
 
 extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+				   Datum options, bool validate);
 extern bool gistproperty(Oid index_oid, int attno,
 			 IndexAMProperty prop, const char *propname,
 			 bool *res, bool *isnull);
0003-opclass-parameters-GIN-v01.patchtext/x-patch; name=0003-opclass-parameters-GIN-v01.patchDownload
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 2f00930..10937bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
        </entry>
        <entry>6</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>
+        parse opclass-specific options (optional)
+       </entry>
+       <entry>7</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 6fe67f3..eb6b778 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -178,13 +178,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
 			 * case cmp < 0 => not match and continue scan
 			 *----------
 			 */
-			cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+			cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
 												  btree->ginstate->supportCollation[attnum - 1],
 												  scanEntry->queryKey,
 												  idatum,
 												  UInt16GetDatum(scanEntry->strategy),
-												  PointerGetDatum(scanEntry->extra_data)));
-
+												  PointerGetDatum(scanEntry->extra_data),
+												  PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
 			if (cmp > 0)
 				return true;
 			else if (cmp < 0)
@@ -1460,12 +1460,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
 		 * case cmp < 0 => not match and continue scan
 		 *----------
 		 */
-		cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+		cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
 											  ginstate->supportCollation[entry->attnum - 1],
 											  entry->queryKey,
 											  datum[off - 1],
 											  UInt16GetDatum(entry->strategy),
-											  PointerGetDatum(entry->extra_data)));
+											  PointerGetDatum(entry->extra_data),
+											  PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
 		if (cmp == 0)
 			return true;
 		else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
 	 */
 	key->recheckCurItem = true;
 
-	return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+	return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
 										  key->collation,
 										  PointerGetDatum(key->entryRes),
 										  UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
 										  PointerGetDatum(key->extra_data),
 										  PointerGetDatum(&key->recheckCurItem),
 										  PointerGetDatum(key->queryValues),
-										  PointerGetDatum(key->queryCategories)));
+										  PointerGetDatum(key->queryCategories),
+										  PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
 static GinTernaryValue
 directTriConsistentFn(GinScanKey key)
 {
-	return DatumGetGinTernaryValue(FunctionCall7Coll(
+	return DatumGetGinTernaryValue(FunctionCall8Coll(
 													 key->triConsistentFmgrInfo,
 													 key->collation,
 													 PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
 													 UInt32GetDatum(key->nuserentries),
 													 PointerGetDatum(key->extra_data),
 													 PointerGetDatum(key->queryValues),
-													 PointerGetDatum(key->queryCategories)));
+													 PointerGetDatum(key->queryCategories),
+													 PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
 {
 	GinTernaryValue result;
 
-	result = DatumGetGinTernaryValue(FunctionCall7Coll(
+	result = DatumGetGinTernaryValue(FunctionCall8Coll(
 													   key->triConsistentFmgrInfo,
 													   key->collation,
 													   PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
 													   UInt32GetDatum(key->nuserentries),
 													   PointerGetDatum(key->extra_data),
 													   PointerGetDatum(key->queryValues),
-													   PointerGetDatum(key->queryCategories)));
+													   PointerGetDatum(key->queryCategories),
+													   PointerGetDatum(key->opclassOptions)));
 	if (result == GIN_MAYBE)
 	{
 		key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
 	key->strategy = strategy;
 	key->searchMode = searchMode;
 	key->attnum = attnum;
+	key->opclassOptions = ginstate->opclassOptions[attnum - 1];
 
 	ItemPointerSetMin(&key->curItem);
 	key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
 
 		/* OK to call the extractQueryFn */
 		queryValues = (Datum *)
-			DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+			DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
 											  so->ginstate.supportCollation[skey->sk_attno - 1],
 											  skey->sk_argument,
 											  PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
 											  PointerGetDatum(&partial_matches),
 											  PointerGetDatum(&extra_data),
 											  PointerGetDatum(&nullFlags),
-											  PointerGetDatum(&searchMode)));
+											  PointerGetDatum(&searchMode),
+											  PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
 
 		/*
 		 * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 7bac7a1..c34b1bc 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -61,6 +61,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = gincostestimate;
 	amroutine->amoptions = ginoptions;
+	amroutine->amopclassoptions = ginopclassoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = ginvalidate;
 	amroutine->ambeginscan = ginbeginscan;
@@ -93,6 +94,7 @@ initGinState(GinState *state, Relation index)
 	state->index = index;
 	state->oneCol = (origTupdesc->natts == 1) ? true : false;
 	state->origTupdesc = origTupdesc;
+	state->opclassOptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < origTupdesc->natts; i++)
 	{
@@ -401,9 +403,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
 		return 0;
 
 	/* both not null, so safe to call the compareFn */
-	return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+	return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
 										   ginstate->supportCollation[attnum - 1],
-										   a, b));
+										   a, b,
+										   PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 }
 
 /*
@@ -439,6 +442,7 @@ typedef struct
 {
 	FmgrInfo   *cmpDatumFunc;
 	Oid			collation;
+	Datum		options;
 	bool		haveDups;
 } cmpEntriesArg;
 
@@ -460,9 +464,10 @@ cmpEntries(const void *a, const void *b, void *arg)
 	else if (bb->isnull)
 		res = -1;				/* not-NULL "<" NULL */
 	else
-		res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+		res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
 											  data->collation,
-											  aa->datum, bb->datum));
+											  aa->datum, bb->datum,
+											  data->options));
 
 	/*
 	 * Detect if we have any duplicates.  If there are equal keys, qsort must
@@ -508,11 +513,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 	/* OK, call the opclass's extractValueFn */
 	nullFlags = NULL;			/* in case extractValue doesn't set it */
 	entries = (Datum *)
-		DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+		DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
 										  ginstate->supportCollation[attnum - 1],
 										  value,
 										  PointerGetDatum(nentries),
-										  PointerGetDatum(&nullFlags)));
+										  PointerGetDatum(&nullFlags),
+										  PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 
 	/*
 	 * Generate a placeholder if the item contained no keys.
@@ -555,6 +561,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 
 		arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
 		arg.collation = ginstate->supportCollation[attnum - 1];
+		arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
 		arg.haveDups = false;
 		qsort_arg(keydata, *nentries, sizeof(keyEntryData),
 				  cmpEntries, (void *) &arg);
@@ -630,6 +637,14 @@ ginoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum indoptions,
+				  bool validate)
+{
+	return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+										 indoptions, validate);
+}
+
 /*
  * Fetch index's statistical data into *stats
  *
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1035be4..3035c2c 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
 		{
 			case GIN_COMPARE_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											2, 2, opckeytype, opckeytype);
+											2, 3, opckeytype, opckeytype,
+											INTERNALOID);
 				break;
 			case GIN_EXTRACTVALUE_PROC:
 				/* Some opclasses omit nullFlags */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											2, 3, opcintype, INTERNALOID,
-											INTERNALOID);
+											2, 4, opcintype, INTERNALOID,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIN_EXTRACTQUERY_PROC:
 				/* Some opclasses omit nullFlags and searchMode */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											5, 7, opcintype, INTERNALOID,
+											5, 8, opcintype, INTERNALOID,
 											INT2OID, INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_CONSISTENT_PROC:
 				/* Some opclasses omit queryKeys and nullFlags */
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											6, 8, INTERNALOID, INT2OID,
+											6, 9, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_COMPARE_PARTIAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											4, 4, opckeytype, opckeytype,
-											INT2OID, INTERNALOID);
+											4, 5, opckeytype, opckeytype,
+											INT2OID, INTERNALOID, INTERNALOID);
 				break;
 			case GIN_TRICONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, CHAROID, false,
-											7, 7, INTERNALOID, INT2OID,
+											7, 8, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID);
+											INTERNALOID, INTERNALOID);
+				break;
+			case GIN_OPCLASSOPTIONS_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID,
+											false, 2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
 		if (opclassgroup &&
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
-		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+			i == GIN_OPCLASSOPTIONS_PROC)
 			continue;			/* optional method */
 		if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
 			continue;			/* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index fcc8323..65ecf28 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -141,6 +141,7 @@
 #include "utils/nabstime.h"
 #include "utils/pg_locale.h"
 #include "utils/rel.h"
+#include "utils/relcache.h"
 #include "utils/selfuncs.h"
 #include "utils/snapmgr.h"
 #include "utils/spccache.h"
@@ -7410,7 +7411,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	else
 		collation = DEFAULT_COLLATION_OID;
 
-	OidFunctionCall7Coll(extractProcOid,
+	OidFunctionCall8Coll(extractProcOid,
 						 collation,
 						 query,
 						 PointerGetDatum(&nentries),
@@ -7418,7 +7419,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 						 PointerGetDatum(&partial_matches),
 						 PointerGetDatum(&extra_data),
 						 PointerGetDatum(&nullFlags),
-						 PointerGetDatum(&searchMode));
+						 PointerGetDatum(&searchMode),
+						 PointerGetDatum(index->opclassoptions[indexcol]));
 
 	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
 	{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 0acdb88..6240aab 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
 #define GIN_CONSISTENT_PROC			   4
 #define GIN_COMPARE_PARTIAL_PROC	   5
 #define GIN_TRICONSISTENT_PROC		   6
-#define GINNProcs					   6
+#define GIN_OPCLASSOPTIONS_PROC		   7
+#define GINNProcs					   7
 
 /*
  * searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index a709596..d371799 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
 	TupleDesc	origTupdesc;
 	TupleDesc	tupdesc[INDEX_MAX_KEYS];
 
+	bytea	  **opclassOptions;	/* per-index-column opclass options */
+
 	/*
 	 * Per-index-column opclass support functions
 	 */
@@ -85,6 +87,8 @@ typedef struct GinState
 
 /* ginutil.c */
 extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+				  Datum indoptions, bool validate);
 extern void initGinState(GinState *state, Relation index);
 extern Buffer GinNewBuffer(Relation index);
 extern void GinInitBuffer(Buffer b, uint32 f);
@@ -297,6 +301,7 @@ typedef struct GinScanKeyData
 	StrategyNumber strategy;
 	int32		searchMode;
 	OffsetNumber attnum;
+	bytea	   *opclassOptions;
 
 	/*
 	 * Match status data.  curItem is the TID most recently tested (could be a
0004-opclass-parameters-GiST-tsvector_ops-v01.patchtext/x-patch; name=0004-opclass-parameters-GiST-tsvector_ops-v01.patchDownload
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 610b7bf..3fead3a 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3555,7 +3555,7 @@ SELECT plainto_tsquery('supernovae stars');
       <tertiary>text search</tertiary>
      </indexterm>
 
-      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
      </term>
 
      <listitem>
@@ -3563,6 +3563,8 @@ SELECT plainto_tsquery('supernovae stars');
        Creates a GiST (Generalized Search Tree)-based index.
        The <replaceable>column</replaceable> can be of <type>tsvector</type> or
        <type>tsquery</type> type.
+       Optional integer parameter <literal>siglen</literal> determines
+       signature length in bytes (see below for details).
       </para>
      </listitem>
     </varlistentry>
@@ -3586,7 +3588,10 @@ SELECT plainto_tsquery('supernovae stars');
    to check the actual table row to eliminate such false matches.
    (<productname>PostgreSQL</productname> does this automatically when needed.)
    GiST indexes are lossy because each document is represented in the
-   index by a fixed-length signature. The signature is generated by hashing
+   index by a fixed-length signature.  Signature length in bytes is determined
+   by the value of the optional integer parameter <literal>siglen</literal>.
+   Default signature length (when <literal>siglen</literal> is not specied) is
+   124 bytes, maximal length is 484 bytes. The signature is generated by hashing
    each word into a single bit in an n-bit string, with all these bits OR-ed
    together to produce an n-bit document signature.  When two words hash to
    the same bit position there will be a false match.  If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 2d9ecc4..462791a 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,23 +15,29 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/tuptoaster.h"
 #include "tsearch/ts_utils.h"
 #include "utils/builtins.h"
 #include "utils/pg_crc.h"
 
 
-#define SIGLENINT  31			/* >121 => key will toast, so it will not work
-								 * !!! */
+#define SIGLEN_DEFAULT	(31 * 4)
+#define SIGLEN_MAX		(121 * 4)	/* key will toast, so it will not work !!! */
 
-#define SIGLEN	( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length */
+}	GistTsVectorOptions;
 
-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) / BITS_PER_BYTE ) ) )
 #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -39,8 +45,8 @@ typedef char *BITVECP;
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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 GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
 
@@ -64,9 +70,10 @@ typedef struct
 #define ISALLTRUE(x)	( ((SignTSVector*)(x))->flag & ALLISTRUE )
 
 #define GTHDRSIZE	( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
 
 #define GETSIGN(x)	( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
 #define GETARR(x)	( (int32*)( (char*)(x)+GTHDRSIZE ) )
 #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
 
@@ -90,7 +97,7 @@ static const uint8 number_of_ones[256] = {
 	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
 
 Datum
 gtsvectorin(PG_FUNCTION_ARGS)
@@ -121,9 +128,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
 		sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
 	else
 	{
-		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+		int			siglen = GETSIGLEN(key);
+		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
 
-		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
 	}
 
 	PG_FREE_IF_COPY(key, 0);
@@ -167,36 +175,49 @@ uniqueint(int32 *a, int32 l)
 }
 
 static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
 {
 	int32		k,
 				len = ARRNELEM(a);
 	int32	   *ptr = GETARR(a);
 
-	MemSet((void *) sign, 0, sizeof(BITVEC));
+	MemSet((void *) sign, 0, siglen);
 	for (k = 0; k < len; k++)
-		HASH(sign, ptr[k]);
+		HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+	int			size = CALCGTSIZE(flag, len);
+	SignTSVector *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+		memcpy(GETSIGN(res), sign, len);
+
+	return res;
 }
 
+
 Datum
 gtsvector_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{							/* tsvector */
-		SignTSVector *res;
 		TSVector	val = DatumGetTSVector(entry->key);
+		SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
 		int32		len;
 		int32	   *arr;
 		WordEntry  *ptr = ARRPTR(val);
 		char	   *words = STRPTR(val);
 
-		len = CALCGTSIZE(ARRKEY, val->size);
-		res = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = ARRKEY;
 		arr = GETARR(res);
 		len = val->size;
 		while (len--)
@@ -227,13 +248,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 		/* make signature, if array is too long */
 		if (VARSIZE(res) > TOAST_INDEX_TARGET)
 		{
-			SignTSVector *ressign;
+			SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
 
-			len = CALCGTSIZE(SIGNKEY, 0);
-			ressign = (SignTSVector *) palloc(len);
-			SET_VARSIZE(ressign, len);
-			ressign->flag = SIGNKEY;
-			makesign(GETSIGN(ressign), res);
+			makesign(GETSIGN(ressign), res, siglen);
 			res = ressign;
 		}
 
@@ -245,22 +262,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 	else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
 			 !ISALLTRUE(DatumGetPointer(entry->key)))
 	{
-		int32		i,
-					len;
+		int32		i;
 		SignTSVector *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 = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = SIGNKEY | ALLISTRUE;
-
+		res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -334,12 +346,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
 static bool
 checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
 {
+	void *key = (SignTSVector *) checkval;
+
 	/*
 	 * we are not able to find a prefix in signature tree
 	 */
 	if (val->prefix)
 		return true;
-	return GETBIT(checkval, HASHVAL(val->valcrc));
+	return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
 }
 
 Datum
@@ -366,7 +380,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 
 		/* since signature is lossy, cannot specify CALC_NOT here */
 		PG_RETURN_BOOL(TS_execute(GETQUERY(query),
-								  (void *) GETSIGN(key),
+								  key,
 								  TS_EXEC_PHRASE_NO_POS,
 								  checkcondition_bit));
 	}
@@ -384,7 +398,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
 {
 	int32		i;
 
@@ -395,7 +409,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		Assert(GETSIGLEN(add) == siglen);
+
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -403,7 +419,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		int32	   *ptr = GETARR(add);
 
 		for (i = 0; i < ARRNELEM(add); i++)
-			HASH(sbase, ptr[i]);
+			HASH(sbase, ptr[i], siglen);
 	}
 	return 0;
 }
@@ -414,30 +430,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	SignTSVector *result;
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+	SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
+	int32		i;
+
+	memset(base, 0, siglen);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = ALLISTRUE;
+			result->flag |= ALLISTRUE;
+			SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
 			break;
 		}
 	}
 
-	flag |= SIGNKEY;
-	len = CALCGTSIZE(flag, 0);
-	result = (SignTSVector *) palloc(len);
-	*size = len;
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!ISALLTRUE(result))
-		memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -448,6 +458,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
 	SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
 	SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -463,8 +474,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
 			BITVECP		sa = GETSIGN(a),
 						sb = GETSIGN(b);
 
+			Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -501,24 +514,24 @@ gtsvector_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];
@@ -529,17 +542,22 @@ hemdistsign(BITVECP a, BITVECP b)
 static int
 hemdist(SignTSVector *a, SignTSVector *b)
 {
+	int siglena = GETSIGLEN(a);
+	int siglenb = GETSIGLEN(b);
+
 	if (ISALLTRUE(a))
 	{
 		if (ISALLTRUE(b))
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(GETSIGN(b));
+			return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
 	}
 	else if (ISALLTRUE(b))
-		return SIGLENBIT - sizebitvec(GETSIGN(a));
+		return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
 
-	return hemdistsign(GETSIGN(a), GETSIGN(b));
+	Assert(siglena == siglenb);
+
+	return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
 }
 
 Datum
@@ -548,6 +566,7 @@ gtsvector_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);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 	SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
 	SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -556,14 +575,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 
 	if (ISARRKEY(newval))
 	{
-		BITVEC		sign;
+		BITVECP		sign = palloc(siglen);
 
-		makesign(sign, newval);
+		makesign(sign, newval, siglen);
 
 		if (ISALLTRUE(origval))
-			*penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+		{
+			int			siglenbit = SIGLENBIT(siglen);
+
+			*penalty =
+				(float) (siglenbit - sizebitvec(sign, siglen)) /
+				(float) (siglenbit + 1);
+		}
 		else
-			*penalty = hemdistsign(sign, orig);
+			*penalty = hemdistsign(sign, orig, siglen);
+
+		pfree(sign);
 	}
 	else
 		*penalty = hemdist(origval, newval);
@@ -573,19 +600,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 typedef struct
 {
 	bool		allistrue;
-	BITVEC		sign;
+	BITVECP		sign;
 } CACHESIGN;
 
 static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
 {
 	item->allistrue = false;
 	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) )
@@ -609,19 +636,19 @@ comparecost(const void *va, const void *vb)
 
 
 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
@@ -629,6 +656,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
 	OffsetNumber k,
 				j;
 	SignTSVector *datum_l,
@@ -648,6 +676,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	BITVECP		ptr;
 	int			i;
 	CACHESIGN  *cache;
+	char	   *cache_sign;
 	SPLITCOST  *costvector;
 
 	maxoff = entryvec->n - 2;
@@ -656,16 +685,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	v->spl_right = (OffsetNumber *) palloc(nbytes);
 
 	cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
-	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+	cache_sign = palloc(siglen * (maxoff + 2));
+
+	for (j = 0; j < maxoff + 2; j++)
+		cache[j].sign = &cache_sign[siglen * j];
+
+	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+			  siglen);
 
 	for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
 	{
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
 			if (k == FirstOffsetNumber)
-				fillcache(&cache[j], GETENTRY(entryvec, j));
+				fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
 
-			size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+			size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -687,44 +722,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (cache[seed_1].allistrue)
-	{
-		datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_l->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (SignTSVector *) 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 = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_r->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (SignTSVector *) 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 = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+							  siglen, cache[seed_1].sign);
+	datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+							  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), 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);
@@ -750,36 +762,34 @@ gtsvector_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;
@@ -790,12 +800,12 @@ gtsvector_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;
@@ -822,3 +832,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
 {
 	return gtsvector_consistent(fcinfo);
 }
+
+Datum
+gtsvector_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(GistTsVectorOptions, siglen) };
+	GistTsVectorOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(GistTsVectorOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index eb595e8..b49a332 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -277,6 +277,7 @@ DATA(insert (	3655   3614 3614 4 3649 ));
 DATA(insert (	3655   3614 3614 5 3653 ));
 DATA(insert (	3655   3614 3614 6 3650 ));
 DATA(insert (	3655   3614 3614 7 3652 ));
+DATA(insert (	3655   3614 3614 10 3696 ));
 DATA(insert (	3702   3615 3615 1 3701 ));
 DATA(insert (	3702   3615 3615 2 3698 ));
 DATA(insert (	3702   3615 3615 3 3695 ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c00d055..91a5c48 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4756,22 +4756,24 @@ DATA(insert OID = 3635 (  ts_match_qv			PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2
 DATA(insert OID = 3760 (  ts_match_tt			PGNSP PGUID 12 100 0 0 0 f f f f t f s s 2 0 16 "25 25" _null_ _null_ _null_ _null_ _null_ ts_match_tt _null_ _null_ _null_ ));
 DATA(insert OID = 3761 (  ts_match_tq			PGNSP PGUID 12 100 0 0 0 f f f f t f s s 2 0 16 "25 3615" _null_ _null_ _null_ _null_ _null_ ts_match_tq _null_ _null_ _null_ ));
 
-DATA(insert OID = 3648 (  gtsvector_compress	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ gtsvector_compress _null_ _null_ _null_ ));
+DATA(insert OID = 3648 (  gtsvector_compress	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_compress _null_ _null_ _null_ ));
 DESCR("GiST tsvector support");
-DATA(insert OID = 3649 (  gtsvector_decompress	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ gtsvector_decompress _null_ _null_ _null_ ));
+DATA(insert OID = 3649 (  gtsvector_decompress	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_decompress _null_ _null_ _null_ ));
 DESCR("GiST tsvector support");
-DATA(insert OID = 3650 (  gtsvector_picksplit	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_picksplit _null_ _null_ _null_ ));
+DATA(insert OID = 3650 (  gtsvector_picksplit	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_picksplit _null_ _null_ _null_ ));
 DESCR("GiST tsvector support");
-DATA(insert OID = 3651 (  gtsvector_union		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 3642 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_union _null_ _null_ _null_ ));
+DATA(insert OID = 3651 (  gtsvector_union		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 3642 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_union _null_ _null_ _null_ ));
 DESCR("GiST tsvector support");
-DATA(insert OID = 3652 (  gtsvector_same		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "3642 3642 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_same _null_ _null_ _null_ ));
+DATA(insert OID = 3652 (  gtsvector_same		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 2281 "3642 3642 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_same _null_ _null_ _null_ ));
 DESCR("GiST tsvector support");
-DATA(insert OID = 3653 (  gtsvector_penalty		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_penalty _null_ _null_ _null_ ));
+DATA(insert OID = 3653 (  gtsvector_penalty		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_penalty _null_ _null_ _null_ ));
 DESCR("GiST tsvector support");
-DATA(insert OID = 3654 (  gtsvector_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 5 0 16 "2281 3614 21 26 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_consistent _null_ _null_ _null_ ));
+DATA(insert OID = 3654 (  gtsvector_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 6 0 16 "2281 3614 21 26 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_consistent _null_ _null_ _null_ ));
 DESCR("GiST tsvector support");
 DATA(insert OID = 3790 (  gtsvector_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 5 0 16 "2281 3642 23 26 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_consistent_oldsig _null_ _null_ _null_ ));
 DESCR("GiST tsvector support (obsolete)");
+DATA(insert OID = 3696 (  gtsvector_options		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ _null_ gtsvector_options _null_ _null_ _null_ ));
+DESCR("GiST tsvector support");
 
 DATA(insert OID = 3656 (  gin_extract_tsvector	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "3614 2281 2281" _null_ _null_ _null_ _null_ _null_ gin_extract_tsvector _null_ _null_ _null_ ));
 DESCR("GIN tsvector support");
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index d63fb12..c8353ab 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
    508
 (1 row)
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR:  value 485 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR:  parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a)
+    "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx2
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a tsvector_ops (siglen='484'))
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 1c8520b..396958d 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
 SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
 SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
#2Nikolay Shaplov
dhyan@nataraj.su
In reply to: Nikita Glukhov (#1)
Re: [PATCH] Opclass parameters

В письме от 28 февраля 2018 00:46:36 пользователь Nikita Glukhov написал:

I would like to present patch set implementing opclass parameters.

This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdf

A analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.

Hi!

You know, I am still working on this issue.

When I started to work on it, I found out that option code is not flexible at
all, and you can' reuse it for options that are not relOptions.

I gave your patch just a short glance for now, but as far as I can you start
deviding options into global and local one. I am afraid it will create grater
mess than it is now.

What I am doing right now, I am creating a new reloption internal API, that
will allow to create any option in any place using the very same code.

I think it should be done first, and then use it for opclass parameters and
any kind of options you like.

The big patch is here https://commitfest.postgresql.org/14/992/ (I am afraid
it will not apply to current master as it is, It is quite old. But you can get
the idea)

The smaller parts of the patch that in current commitfest are

https://commitfest.postgresql.org/17/1536/
https://commitfest.postgresql.org/17/1489/

You can help reviewing them. Then the whole thing will go faster. The second
patch is quite trivial. The first will also need attention of someone who is
really good in understanding postgres internals.

-----------

Concerning the patch that you've provided. I've just have a short look. But I
already have some question.

1. I've seen you've added a new attribute into pg_index. Why??!!
As far as I can get, if have index built on several columns (A1, A2, A3) you
can set, own opclass for each column. And set individual options for each
opclass if we are speaking about options. So I would expect to have these
options not in pg_index, but in pg_attribute. And we already have one there:
attoptions.I just do not get how you have come to per-index options. May be I
should read code more attentively...

2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for intarray.
May be you should do the same. (Use my example it will be more easy then do it
from scratch). It will give more understanding of how this improvement can be
used.

3.

--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,8 @@ vacuum btree_tall_tbl;
 -- need to insert some rows to cause the fast root page to split.
 insert into btree_tall_tbl (id, t)
   select g, repeat('x', 100) from generate_series(1, 500) g;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  access method "btree" does not support opclass options 
+create index on btree_tall_tbl (id default(foo=1));
+ERROR:  access method "btree" does not support opclass options 

Are you sure we really need these in postgres???

---------------------------------

So my proposal is the following:
let's commit

https://commitfest.postgresql.org/17/1536/
https://commitfest.postgresql.org/17/1489/

I will provide a final patch for option engine refactoring in commit fest, and
you will write you implementation of opclass options on top of it. We can to
it simultaneously. Or I will write opclass options myself... I do not care who
will do oplcass options as long it is done using refactored options engine. If
you do it, I can review it.

Opclass parameters can give user ability to:
* Define the values of the constants that are hardcoded now in the
opclasses depending on the indexed data.
* Specify what to index for non-atomic data types (arrays, json[b],
tsvector). Partial index can only filter whole rows.
* Specify what indexing algorithm to use depending on the indexed data.

Description of patches:

1. Infrastructure for opclass parameters.

SQL grammar is changed only for CREATE INDEX statement: parenthesized
parameters in reloptions format are added after column's opclass name.
Default opclass can be specified with DEFAULT keyword:

CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);

Example for contrib/intarray:

CREATE INDEX ON arrays USING gist (
arr gist__intbig_ops (siglen = 32),
arr DEFAULT (numranges = 100)
);

\d arrays
Table "public.arrays"
Column | Type | Collation | Nullable | Default
--------+-----------+-----------+----------+---------
arr | integer[] | | |
Indexes:
"arrays_arr_arr1_idx" gist (arr gist__intbig_ops (siglen='32'), arr
gist__int_ops (numranges='100'))

I decided to store parameters in text[] column pg_index.indoptions near to
existing columns like indkey, indcollation, indclass, indoption. I-th
element of indoptions[] is a text array of parameters of i-th index column
serialized into a string. Each parameter is stored as 'name=value' text
string like ordinal reloptions. There is another way to store opclass
parameters: store them in the existing column pg_attribute.attoptions (as
it was done by Nikolay Shaplov) and there will be no need to serialize
reloptions to a text array element.

Example query showing how parameters are stored:

SELECT ARRAY(
SELECT (pg_identify_object('pg_opclass'::regclass, opcid, 0)).name
FROM unnest(indclass::int[]) opcid
) indclass, indoptions
FROM pg_index
WHERE indoptions IS NOT NULL;

indclass | indoptions
----------------------------------+------------------------------------
{gist__intbig_ops,gist__int_ops} | {"{siglen=32}","{numranges=100}"}
{jsonb_path_ops} | {"{projection=$.tags[*].term}"}
(2 rows)

Each access method supporting opclass parameters specifies amopclassoptions
routine for transformation of text[] parameters datum into a binary bytea
structure which will be cached in RelationData and IndexOptInfo structures:

typedef bytea *(*amopclassoptions_function) (
Relation index, AttrNumber colnum, Datum indoptions, bool validate
);

If access method wants simply to delegate parameters processing to one of
column opclass's support functions, then it can use
index_opclass_options_generic() subroutine in its amopclassoptions
implementation:

bytea *index_opclass_options_generic(
Relation relation, AttrNumber attnum, uint16 procnum,
Datum indoptions, bool validate
);

This support functions must have the following signature:
internal (options internal, validate bool).
Opclass parameters are passed as a text[] reloptions datum, returned pointer
to a bytea structure with parsed parameter values.

Opclass can use new functions parseLocalRelOptions(),
parseAndFillLocalRelOptions() for reloptions parsing. This functions differ
from the standard parseRelOptions() in that a local array of reloptions
descriptions is passed here, not a global relopt_kind. But it seems that
reloptions processing still needs deeper refactoring like the one already
done by Nikolay Shaplov
(/messages/by-id/2146419.veIEZdk4E4@x200m
419.veIEZdk4E4@x200m).

2. Opclass parameters support in GiST indices.

Parametrized GiST opclass specifies optional 10th (GIST_OPCLASSOPT_PROC)
support function with the following signature:
internal (options internal, validate bool)

Returned parsed bytea pointer with parameters will be passed to all support
functions in the last argument.

3. Opclass parameters support in GIN indices.

Everything is the same as for GiST, except for the optional support
function number which is 7 (GIN_OPCLASSOPTIONS_PROC) here.

4. Opclass parameters for GiST tsvector_ops
5. Opclass parameters for contrib/intarray
6. Opclass parameters for contrib/ltree
7. Opclass parameters for contrib/pg_trgm
8. Opclass parameters for contrib/hstore

This 5 patches for GiST opclasses are very similar: added optional 'siglen'
parameter for specifying signature length. Default signature length is left
equal to the hardcoded value that was here before. Also added 'numranges'
parameter for gist__int_ops.

We also have two more complex unfinished patches for GIN opclasses which
should be posted in separate threads:

* tsvector_ops: added parameter 'weights' for specification of indexed
lexeme's weight groups. This parameter can reduce index size and its
build/update time and can also eliminate recheck. By default, all
weights are indexed within the same group.

* jsonb_ops: added jsonpath parameter 'projection' for specification of
indexed paths in jsonb (this patch depends on SQL/JSON jsonpath patch).
Analogically to tsvector_ops, this parameter can reduce index size and
its build/update time, but can not eliminate recheck.

--
Do code for fun.

#3David Steele
david@pgmasters.net
In reply to: Nikolay Shaplov (#2)
Re: Re: [PATCH] Opclass parameters

Hi Nikita,

On 2/28/18 9:46 AM, Nikolay Shaplov wrote:

В письме от 28 февраля 2018 00:46:36 пользователь Nikita Glukhov написал:

I would like to present patch set implementing opclass parameters.

This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdf

A analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.

You know, I am still working on this issue.

This patch was submitted to the 2018-03 CF at the last moment with no
prior discussion or review as far as I can tell. It appears to be
non-trivial and therefore not a good fit for the last CF for PG11.

In addition, based on Nikolay's response, I think the patch should be
marked Returned with Feedback until it is reconciled with the existing
patches.

Any objects to marking this Returned with Feedback? Or, I can move it
to the next CF as is.

Regards,
--
-David
david@pgmasters.net

#4Oleg Bartunov
obartunov@gmail.com
In reply to: Nikolay Shaplov (#2)
Re: [PATCH] Opclass parameters

On Wed, Feb 28, 2018 at 5:46 PM, Nikolay Shaplov <dhyan@nataraj.su> wrote:

Concerning the patch that you've provided. I've just have a short look. But I
already have some question.

1. I've seen you've added a new attribute into pg_index. Why??!!
As far as I can get, if have index built on several columns (A1, A2, A3) you
can set, own opclass for each column. And set individual options for each
opclass if we are speaking about options. So I would expect to have these
options not in pg_index, but in pg_attribute. And we already have one there:
attoptions.I just do not get how you have come to per-index options. May be I
should read code more attentively...

this is what we want to discuss.

2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for intarray.
May be you should do the same. (Use my example it will be more easy then do it
from scratch). It will give more understanding of how this improvement can be
used.

Hey, look on patches, there are many examples !

3.

--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,8 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  access method "btree" does not support opclass options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR:  access method "btree" does not support opclass options

Are you sure we really need these in postgres???

Hey, this is a just a test to check unsupported opclass options !

#5Oleg Bartunov
obartunov@gmail.com
In reply to: David Steele (#3)
Re: Re: [PATCH] Opclass parameters

On Thu, Mar 1, 2018 at 7:02 PM, David Steele <david@pgmasters.net> wrote:

Hi Nikita,

On 2/28/18 9:46 AM, Nikolay Shaplov wrote:

В письме от 28 февраля 2018 00:46:36 пользователь Nikita Glukhov написал:

I would like to present patch set implementing opclass parameters.

This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdf

A analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.

You know, I am still working on this issue.

This patch was submitted to the 2018-03 CF at the last moment with no
prior discussion or review as far as I can tell. It appears to be
non-trivial and therefore not a good fit for the last CF for PG11.

the idea is simple, the main problem is where to store parameters.
We hoped that we get a bright idea from developers.

In addition, based on Nikolay's response, I think the patch should be
marked Returned with Feedback until it is reconciled with the existing
patches.

We proposed something that works and could be useful for people,
especially for people, who use complex json documents. It would
require minimal changes if Nikolay's patch, which is quite invasive,
will be committed in future.

What we need to discuss is the user-visible features - the syntax changes.

Any objects to marking this Returned with Feedback? Or, I can move it
to the next CF as is.

I think that Returned with Feedback would be good. We will continue
discussion in -hackers.

Show quoted text

Regards,
--
-David
david@pgmasters.net

#6David Steele
david@pgmasters.net
In reply to: Oleg Bartunov (#5)
Re: [PATCH] Opclass parameters

On 3/1/18 3:50 PM, Oleg Bartunov wrote:

On Thu, Mar 1, 2018 at 7:02 PM, David Steele <david@pgmasters.net> wrote:

Any objections to marking this Returned with Feedback? Or, I can move it
to the next CF as is.

I think that Returned with Feedback would be good. We will continue
discussion in -hackers.

Marked as Returned with Feedback. Hopefully we'll see this patch in the
2018-09 CF.

--
-David
david@pgmasters.net

#7Nikolay Shaplov
dhyan@nataraj.su
In reply to: Oleg Bartunov (#4)
Re: [PATCH] Opclass parameters

В письме от 1 марта 2018 23:02:20 пользователь Oleg Bartunov написал:

2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for
intarray. May be you should do the same. (Use my example it will be more
easy then do it from scratch). It will give more understanding of how
this improvement can be used.

Hey, look on patches, there are many examples !

Oups...Sorry. I've looked at the patch from commitfest
https://commitfest.postgresql.org/17/1559/ and it have shown only the first
file. And When I read the letter I did not pay attention to attachments at
all. So I was sure there is only one there.

Yes. Now I see seven examples. But I think seven it is too many.
For each case a reviewer should make consideration if this parameter worth
moving to opclass options, or fixed definition in the C code is quite ok.
Doing it for whole bunch, may make it messy. I think, it would be good to
commit an implementation of opclass options, with a good example of usage. And
then commit patches for all cases where these options can be used.

But since it is now "Rejected with feedback", let's wait till autumn.

--
Do code for fun.

#8Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Nikolay Shaplov (#7)
Re: [PATCH] Opclass parameters

On 02.03.2018 19:12, Nikolay Shaplov wrote:

В письме от 1 марта 2018 23:02:20 пользователь Oleg Bartunov написал:

2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for
intarray. May be you should do the same. (Use my example it will be more
easy then do it from scratch). It will give more understanding of how
this improvement can be used.

Hey, look on patches, there are many examples !

Oups...Sorry. I've looked at the patch from commitfest
https://commitfest.postgresql.org/17/1559/ and it have shown only the first
file. And When I read the letter I did not pay attention to attachments at
all. So I was sure there is only one there.

Yes. Now I see seven examples. But I think seven it is too many.
For each case a reviewer should make consideration if this parameter worth
moving to opclass options, or fixed definition in the C code is quite ok.
Doing it for whole bunch, may make it messy. I think, it would be good to
commit an implementation of opclass options, with a good example of usage. And
then commit patches for all cases where these options can be used.

There are 5 examples for GiST opclasses, not 7, and they are almost
identical -- in all of them added 'siglen' parameter for signature length
specification.

But since it is now "Rejected with feedback", let's wait till autumn.

We don't want to wait that long. But now we only need to сome to an agreement
about CREATE INDEX syntax and where to store the opclass parameters.

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#9Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Nikita Glukhov (#8)
8 attachment(s)
Re: [PATCH] Opclass parameters

On 03.03.2018 1:32, Nikita Glukhov wrote:

On 02.03.2018 19:12, Nikolay Shaplov wrote:

But since it is now "Rejected with feedback", let's wait till autumn.

We don't want to wait that long.  But now we only need to сome to an
agreement
about CREATE INDEX syntax and where to store the opclass parameters.

Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.

CREATE INDEX syntax and parameters storage method still need discussion.

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0001-Add-opclass-parameters-v02.patchtext/x-patch; name=0001-Add-opclass-parameters-v02.patchDownload
From 76513025cf34b2a72c8396a3d868db286fe54dbb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Wed, 10 Jan 2018 00:46:28 +0300
Subject: [PATCH 1/9] Add opclass parameters

---
 doc/src/sgml/catalogs.sgml                |  11 ++
 doc/src/sgml/indices.sgml                 |   2 +-
 doc/src/sgml/ref/create_index.sgml        |  16 ++-
 src/backend/access/common/reloptions.c    | 142 +++++++++++++++++-------
 src/backend/access/index/indexam.c        |  72 ++++++++++++
 src/backend/catalog/index.c               |  60 ++++++++++
 src/backend/catalog/toasting.c            |   1 +
 src/backend/commands/indexcmds.c          |  12 ++
 src/backend/nodes/copyfuncs.c             |   1 +
 src/backend/nodes/equalfuncs.c            |   1 +
 src/backend/nodes/outfuncs.c              |   1 +
 src/backend/optimizer/util/plancat.c      |   4 +
 src/backend/parser/gram.y                 |  72 +++++++-----
 src/backend/utils/adt/ruleutils.c         | 133 +++++++++++++---------
 src/backend/utils/cache/relcache.c        | 179 ++++++++++++++++++++++++++++++
 src/include/access/amapi.h                |   7 ++
 src/include/access/genam.h                |   6 +
 src/include/access/reloptions.h           |   5 +
 src/include/catalog/pg_index.h            |   1 +
 src/include/nodes/execnodes.h             |   2 +
 src/include/nodes/parsenodes.h            |   1 +
 src/include/nodes/relation.h              |   1 +
 src/include/utils/rel.h                   |   1 +
 src/include/utils/relcache.h              |   4 +
 src/include/utils/ruleutils.h             |   2 +
 src/test/regress/expected/btree_index.out |   5 +
 src/test/regress/expected/misc_sanity.out |   3 +-
 src/test/regress/sql/btree_index.sql      |   4 +
 28 files changed, 625 insertions(+), 124 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 8b7f169..bdba62b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3901,6 +3901,17 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        partial index.
       </entry>
      </row>
+
+     <row>
+      <entry><structfield>indoptions</structfield></entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>
+       This is an array of <structfield>indnatts</structfield> strings that
+       store per-column opclass options or null.  Each array element is a
+       serialized array of column's opclass options in reloptions format or null.
+      </entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index df7d16f..8afc93e 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -993,7 +993,7 @@ CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
    An index definition can specify an <firstterm>operator
    class</firstterm> for each column of an index.
 <synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
 </synopsis>
    The operator class identifies the operators to be used by the index
    for that column.  For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index ad619cd..1946d3b 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
-    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
      </varlistentry>
 
      <varlistentry>
+      <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+      <listitem>
+       <para>
+        The name of an operator class parameter. See below for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><literal>ASC</literal></term>
       <listitem>
        <para>
@@ -607,8 +616,9 @@ Indexes:
   </para>
 
   <para>
-   An <firstterm>operator class</firstterm> can be specified for each
-   column of an index. The operator class identifies the operators to be
+   An <firstterm>operator class</firstterm> with its optional parameters 
+   can be specified for each column of an index.
+   The operator class identifies the operators to be
    used by the index for that column. For example, a B-tree index on
    four-byte integers would use the <literal>int4_ops</literal> class;
    this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index db84da0..4820a14 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1025,6 +1025,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 	return options;
 }
 
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+				 relopt_value *reloptions, int numoptions)
+{
+	ArrayType  *array = DatumGetArrayTypeP(options);
+	Datum	   *optiondatums;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(array, TEXTOID, -1, false, 'i',
+					  &optiondatums, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *text_str = VARDATA(optiondatums[i]);
+		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		int			j;
+
+		/* Search for a match in reloptions */
+		for (j = 0; j < numoptions; j++)
+		{
+			int			kw_len = reloptions[j].gen->namelen;
+
+			if (text_len > kw_len && text_str[kw_len] == '=' &&
+				strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+			{
+				parse_one_reloption(&reloptions[j], text_str, text_len,
+									validate);
+				break;
+			}
+		}
+
+		if (j >= numoptions && validate)
+		{
+			char	   *s;
+			char	   *p;
+
+			s = TextDatumGetCString(optiondatums[i]);
+			p = strchr(s, '=');
+			if (p)
+				*p = '\0';
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized parameter \"%s\"", s)));
+		}
+	}
+
+	/* It's worth avoiding memory leaks in this function */
+	pfree(optiondatums);
+
+	if (((void *) array) != DatumGetPointer(options))
+		pfree(array);
+}
+
 /*
  * Interpret reloptions that are given in text-array format.
  *
@@ -1079,57 +1133,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
 
 	/* Done if no options */
 	if (PointerIsValid(DatumGetPointer(options)))
-	{
-		ArrayType  *array = DatumGetArrayTypeP(options);
-		Datum	   *optiondatums;
-		int			noptions;
+		parseRelOptionsInternal(options, validate, reloptions, numoptions);
 
-		deconstruct_array(array, TEXTOID, -1, false, 'i',
-						  &optiondatums, NULL, &noptions);
+	*numrelopts = numoptions;
+	return reloptions;
+}
 
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *text_str = VARDATA(optiondatums[i]);
-			int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
-			int			j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen *optgen[], int nopts)
+{
+	relopt_value *values = palloc(sizeof(*values) * nopts);
+	int			i;
 
-			/* Search for a match in reloptions */
-			for (j = 0; j < numoptions; j++)
-			{
-				int			kw_len = reloptions[j].gen->namelen;
+	for (i = 0; i < nopts; i++)
+	{
+		values[i].gen = optgen[i];
+		values[i].isset = false;
+	}
 
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
-				{
-					parse_one_reloption(&reloptions[j], text_str, text_len,
-										validate);
-					break;
-				}
-			}
+	if (options != (Datum) 0)
+		parseRelOptionsInternal(options, validate, values, nopts);
 
-			if (j >= numoptions && validate)
-			{
-				char	   *s;
-				char	   *p;
+	return values;
+}
 
-				s = TextDatumGetCString(optiondatums[i]);
-				p = strchr(s, '=');
-				if (p)
-					*p = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("unrecognized parameter \"%s\"", s)));
-			}
-		}
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+							int noptions, size_t base_size, bool validate)
+{
+	relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+	relopt_value *vals;
+	void	   *opts;
+	int			i;
 
-		/* It's worth avoiding memory leaks in this function */
-		pfree(optiondatums);
-		if (((void *) array) != DatumGetPointer(options))
-			pfree(array);
+	for (i = 0; i < noptions; i++)
+	{
+		elems[i].optname = optgen[i]->name;
+		elems[i].opttype = optgen[i]->type;
+		elems[i].offset = offsets[i];
 	}
 
-	*numrelopts = numoptions;
-	return reloptions;
+	vals = parseLocalRelOptions(options, validate, optgen, noptions);
+	opts = allocateReloptStruct(base_size, vals, noptions);
+	fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+	if (elems)
+		pfree(elems);
+
+	return opts;
 }
 
 /*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index eade540..c03ad0c 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -75,11 +75,14 @@
 #include "access/xlog.h"
 #include "catalog/index.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 #include "utils/tqual.h"
 
 
@@ -967,3 +970,72 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
 		}
 	}
 }
+
+/*
+ * Parse opclass-specific options for index column.
+ *
+ *	amoptions	index relation
+ *	attnum		column number
+ *	indoptions	options as text[] datum
+ *	validate	error flag
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum indoptions,
+					  bool validate)
+{
+	amopclassoptions_function amopclassoptions =
+		relation->rd_amroutine->amopclassoptions;
+
+	if (!amopclassoptions)
+	{
+		if (validate && PointerIsValid(DatumGetPointer(indoptions)))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("access method \"%s\" does not support opclass options ",
+							 get_am_name(relation->rd_rel->relam))));
+
+		return NULL;
+	}
+
+	return amopclassoptions(relation, attnum, indoptions, validate);
+}
+
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+							  uint16 procnum, Datum reloptions, bool validate)
+{
+	Oid			procid = index_getprocid(indrel, attnum, procnum);
+	FmgrInfo   *procinfo;
+
+	if (!OidIsValid(procid))
+	{
+		StringInfoData opclassname;
+		Oid			opclass;
+		Datum		indclassDatum;
+		oidvector  *indclass;
+		bool		isnull;
+
+		if (!DatumGetPointer(reloptions))
+			return NULL;
+
+		indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+										Anum_pg_index_indclass, &isnull);
+		Assert(!isnull);
+		indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+		opclass = indclass->values[attnum - 1];
+
+		initStringInfo(&opclassname);
+		get_opclass_name(opclass, InvalidOid, &opclassname);
+
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("operator class%s has no options ", opclassname.data)));
+	}
+
+	procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+	return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+												   reloptions,
+												   BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 4088286..e186dc6 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -25,6 +25,7 @@
 
 #include "access/amapi.h"
 #include "access/multixact.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/reloptions.h"
 #include "access/sysattr.h"
@@ -588,6 +589,7 @@ UpdateIndexRelation(Oid indexoid,
 	int2vector *indoption;
 	Datum		exprsDatum;
 	Datum		predDatum;
+	Datum		indoptionsDatum;
 	Datum		values[Natts_pg_index];
 	bool		nulls[Natts_pg_index];
 	Relation	pg_index;
@@ -634,6 +636,53 @@ UpdateIndexRelation(Oid indexoid,
 	else
 		predDatum = (Datum) 0;
 
+	if (indexInfo->ii_OpclassOptions != NULL)
+	{
+		Datum	   *vals = palloc(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+		bool	   *nulls = palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
+		int			dims[1];
+		int			lbs[1];
+
+		for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+		{
+			Datum		options = indexInfo->ii_OpclassOptions[i];
+
+			nulls[i] = options == (Datum) 0;
+
+			if (!nulls[i])
+			{
+				FunctionCallInfoData fcinfo;
+				Datum		result;
+				FmgrInfo	flinfo = { 0 };
+
+				InitFunctionCallInfoData(fcinfo, &flinfo, 1, InvalidOid, NULL,
+										 NULL);
+
+				fcinfo.arg[0] = options;
+				fcinfo.argnull[0] = false;
+
+				flinfo.fn_mcxt = CurrentMemoryContext;
+
+				result = array_out(&fcinfo);
+
+				/* Check for null result, since caller is clearly not expecting one */
+				if (fcinfo.isnull)
+					elog(ERROR, "function %p returned NULL", (void *) array_out);
+
+				vals[i] = CStringGetTextDatum(DatumGetCString(result));
+			}
+		}
+
+		dims[0] = indexInfo->ii_NumIndexAttrs;
+		lbs[0] = 1;
+
+		indoptionsDatum = PointerGetDatum(construct_md_array(vals, nulls, 1,
+															 dims, lbs, TEXTOID,
+															 -1, false, 'i'));
+	}
+	else
+		indoptionsDatum = (Datum) 0;
+
 	/*
 	 * open the system catalog index relation
 	 */
@@ -668,6 +717,9 @@ UpdateIndexRelation(Oid indexoid,
 	values[Anum_pg_index_indpred - 1] = predDatum;
 	if (predDatum == (Datum) 0)
 		nulls[Anum_pg_index_indpred - 1] = true;
+	values[Anum_pg_index_indoptions - 1] = indoptionsDatum;
+	if (indoptionsDatum == (Datum) 0)
+		nulls[Anum_pg_index_indoptions - 1] = true;
 
 	tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls);
 
@@ -1175,6 +1227,12 @@ index_create(Relation heapRelation,
 
 	indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
 
+	/* Validate opclass-specific options */
+	if (indexInfo->ii_OpclassOptions)
+		for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+			(void) index_opclass_options(indexRelation, i + 1,
+										 indexInfo->ii_OpclassOptions[i], true);
+
 	/*
 	 * If this is bootstrap (initdb) time, then we don't actually fill in the
 	 * index yet.  We'll be creating more indexes and classes later, so we
@@ -1803,6 +1861,8 @@ BuildIndexInfo(Relation index)
 		ii->ii_ExclusionStrats = NULL;
 	}
 
+	ii->ii_OpclassOptions = RelationGetRawOpclassOptions(index);
+
 	/* other info */
 	ii->ii_Unique = indexStruct->indisunique;
 	ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3baaa08..8d08b6a 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -313,6 +313,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;
 	indexInfo->ii_Unique = true;
 	indexInfo->ii_ReadyForInserts = true;
 	indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 906d711..75c6013 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -642,6 +642,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;	/* for now */
 	indexInfo->ii_Unique = stmt->unique;
 	/* In a concurrent build, mark it not-ready-for-inserts */
 	indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1695,6 +1696,17 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 								accessMethodName)));
 		}
 
+		/* Set up the per-column opclass options (indoptions field). */
+		if (attribute->opclassopts)
+		{
+			if (!indexInfo->ii_OpclassOptions)
+				indexInfo->ii_OpclassOptions = palloc0(sizeof(Datum) * nkeycols);
+
+			indexInfo->ii_OpclassOptions[attn] =
+				transformRelOptions((Datum) 0, attribute->opclassopts,
+									NULL, NULL, false, false);
+		}
+
 		attn++;
 	}
 }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index db49968..ad3ff3c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2856,6 +2856,7 @@ _copyIndexElem(const IndexElem *from)
 	COPY_STRING_FIELD(indexcolname);
 	COPY_NODE_FIELD(collation);
 	COPY_NODE_FIELD(opclass);
+	COPY_NODE_FIELD(opclassopts);
 	COPY_SCALAR_FIELD(ordering);
 	COPY_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a084b4..e3dfe6e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2538,6 +2538,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
 	COMPARE_STRING_FIELD(indexcolname);
 	COMPARE_NODE_FIELD(collation);
 	COMPARE_NODE_FIELD(opclass);
+	COMPARE_NODE_FIELD(opclassopts);
 	COMPARE_SCALAR_FIELD(ordering);
 	COMPARE_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f0c3965..a23c508 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2931,6 +2931,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
 	WRITE_STRING_FIELD(indexcolname);
 	WRITE_NODE_FIELD(collation);
 	WRITE_NODE_FIELD(opclass);
+	WRITE_NODE_FIELD(opclassopts);
 	WRITE_ENUM_FIELD(ordering, SortByDir);
 	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
 }
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 0c88c90..2af7c41 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -361,6 +361,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				info->nulls_first = NULL;
 			}
 
+			/* Fetch index opclass options */
+			info->opclassoptions =
+				RelationGetParsedOpclassOptions(indexRelation);
+
 			/*
 			 * Fetch the index expressions and predicate, if any.  We must
 			 * modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2effd51..9584262 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -485,7 +485,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <alias>	alias_clause opt_alias_clause
 %type <list>	func_alias_clause
 %type <sortby>	sortby
-%type <ielem>	index_elem
+%type <ielem>	index_elem index_elem_options
 %type <node>	table_ref
 %type <jexpr>	joined_table
 %type <range>	relation_expr
@@ -7433,43 +7433,65 @@ index_params:	index_elem							{ $$ = list_make1($1); }
 			| index_params ',' index_elem			{ $$ = lappend($1, $3); }
 		;
 
+
+index_elem_options:
+	opt_collate opt_class opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = NIL;
+			$$->ordering = $3;
+			$$->nulls_ordering = $4;
+		}
+	| opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	| opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = NIL;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	;
+
 /*
  * Index attributes can be either simple column references, or arbitrary
  * expressions in parens.  For backwards-compatibility reasons, we allow
  * an expression that's just a function call to be written without parens.
  */
-index_elem:	ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
 				{
-					$$ = makeNode(IndexElem);
+					$$ = $2;
 					$$->name = $1;
-					$$->expr = NULL;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+			| func_expr_windowless index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $2;
 					$$->expr = $1;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+			| '(' a_expr ')' index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $4;
 					$$->expr = $2;
-					$$->indexcolname = NULL;
-					$$->collation = $4;
-					$$->opclass = $5;
-					$$->ordering = $6;
-					$$->nulls_ordering = $7;
 				}
 		;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 29884f1..64a4990 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -452,8 +452,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
 						   deparse_context *context);
 static void get_tablesample_def(TableSampleClause *tablesample,
 					deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
-				 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context);
 static void printSubscripts(ArrayRef *aref, deparse_context *context);
 static char *get_relation_name(Oid relid);
@@ -468,6 +466,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1197,6 +1196,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	oidvector  *indcollation;
 	oidvector  *indclass;
 	int2vector *indoption;
+	Datum	   *indoptions = NULL;
 	StringInfoData buf;
 	char	   *str;
 	char	   *sep;
@@ -1232,6 +1232,16 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	Assert(!isnull);
 	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
 
+	if (!attrsOnly)
+	{
+		Datum indoptionsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+												Anum_pg_index_indoptions,
+												&isnull);
+		if (!isnull)
+			indoptions = ExtractRawOpclassOptions(indoptionsDatum,
+												  idxrec->indnatts);
+	}
+
 	/*
 	 * Fetch the pg_class tuple of the index relation
 	 */
@@ -1370,6 +1380,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 			(!colno || colno == keyno + 1))
 		{
 			Oid			indcoll;
+			bool		has_options =
+				indoptions && indoptions[keyno] != (Datum) 0;
 
 			/* Add collation, if not default for column */
 			indcoll = indcollation->values[keyno];
@@ -1378,7 +1390,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 								 generate_collation_name((indcoll)));
 
 			/* Add the operator class name, if not default */
-			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+			get_opclass_name(indclass->values[keyno],
+							 has_options ? InvalidOid : keycoltype, &buf);
+
+			if (has_options)
+			{
+				appendStringInfoString(&buf, " (");
+				get_reloptions(&buf, indoptions[keyno]);
+				appendStringInfoChar(&buf, ')');
+			}
 
 			/* Add options if relevant */
 			if (amroutine->amcanorder)
@@ -10401,7 +10421,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
  * actual_datatype.  (If you don't want this behavior, just pass
  * InvalidOid for actual_datatype.)
  */
-static void
+void
 get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf)
 {
@@ -11112,6 +11132,62 @@ string_to_text(char *str)
 }
 
 /*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+	Datum	   *options;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(reloptions),
+					  TEXTOID, -1, false, 'i',
+					  &options, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *option = TextDatumGetCString(options[i]);
+		char	   *name;
+		char	   *separator;
+		char	   *value;
+
+		/*
+		 * Each array element should have the form name=value.  If the "="
+		 * is missing for some reason, treat it like an empty value.
+		 */
+		name = option;
+		separator = strchr(option, '=');
+		if (separator)
+		{
+			*separator = '\0';
+			value = separator + 1;
+		}
+		else
+			value = "";
+
+		if (i > 0)
+			appendStringInfoString(buf, ", ");
+		appendStringInfo(buf, "%s=", quote_identifier(name));
+
+		/*
+		 * In general we need to quote the value; but to avoid unnecessary
+		 * clutter, do not quote if it is an identifier that would not
+		 * need quoting.  (We could also allow numbers, but that is a bit
+		 * trickier than it looks --- for example, are leading zeroes
+		 * significant?  We don't want to assume very much here about what
+		 * custom reloptions might mean.)
+		 */
+		if (quote_identifier(value) == value)
+			appendStringInfoString(buf, value);
+		else
+			simple_quote_literal(buf, value);
+
+		pfree(option);
+	}
+}
+
+/*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
 static char *
@@ -11131,56 +11207,9 @@ flatten_reloptions(Oid relid)
 	if (!isnull)
 	{
 		StringInfoData buf;
-		Datum	   *options;
-		int			noptions;
-		int			i;
 
 		initStringInfo(&buf);
-
-		deconstruct_array(DatumGetArrayTypeP(reloptions),
-						  TEXTOID, -1, false, 'i',
-						  &options, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *option = TextDatumGetCString(options[i]);
-			char	   *name;
-			char	   *separator;
-			char	   *value;
-
-			/*
-			 * Each array element should have the form name=value.  If the "="
-			 * is missing for some reason, treat it like an empty value.
-			 */
-			name = option;
-			separator = strchr(option, '=');
-			if (separator)
-			{
-				*separator = '\0';
-				value = separator + 1;
-			}
-			else
-				value = "";
-
-			if (i > 0)
-				appendStringInfoString(&buf, ", ");
-			appendStringInfo(&buf, "%s=", quote_identifier(name));
-
-			/*
-			 * In general we need to quote the value; but to avoid unnecessary
-			 * clutter, do not quote if it is an identifier that would not
-			 * need quoting.  (We could also allow numbers, but that is a bit
-			 * trickier than it looks --- for example, are leading zeroes
-			 * significant?  We don't want to assume very much here about what
-			 * custom reloptions might mean.)
-			 */
-			if (quote_identifier(value) == value)
-				appendStringInfoString(&buf, value);
-			else
-				simple_quote_literal(&buf, value);
-
-			pfree(option);
-		}
+		get_reloptions(&buf, reloptions);
 
 		result = buf.data;
 	}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index aecbd4a..0fd7dbe 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5257,6 +5257,185 @@ GetRelationPublicationActions(Relation relation)
 }
 
 /*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+	MemoryContext oldcxt;
+	bytea	  **opts;
+	Datum		indoptionsDatum;
+	Datum	   *indoptions;
+	bool	   *indoptionsnulls;
+	int			indoptionsnum;
+	int			i;
+	int			ncols = relation->rd_rel->relnatts;
+	bool		isnull;
+
+	if (relation->rd_indoptions)
+	{
+		opts = palloc(sizeof(*opts) * ncols);
+
+		for (i = 0; i < ncols; i++)
+		{
+			bytea	   *opt = relation->rd_indoptions[i];
+
+			opts[i] = !opt ? NULL : (bytea *)
+				DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+		}
+
+		return opts;
+	}
+
+	opts = palloc0(sizeof(*opts) * ncols);
+
+	if (relation->rd_indextuple == NULL ||
+		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions, NULL))
+	{
+		indoptions = NULL;
+		indoptionsnulls = NULL;
+		indoptionsnum = 0;
+	}
+	else
+	{
+		indoptionsDatum = heap_getattr(relation->rd_indextuple,
+									   Anum_pg_index_indoptions,
+									   GetPgIndexDescriptor(),
+									   &isnull);
+		Assert(!isnull);
+
+		deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+						  TEXTOID, -1, false, 'i',
+						  &indoptions, &indoptionsnulls, &indoptionsnum);
+
+		Assert(indoptionsnum == ncols);
+	}
+
+	for (i = 0; i < ncols; i++)
+	{
+		Datum		options;
+
+		if (i < indoptionsnum && !indoptionsnulls[i])
+		{
+			FunctionCallInfoData fcinfo;
+			FmgrInfo	flinfo = { 0 };
+			char	   *optionsstr = TextDatumGetCString(indoptions[i]);
+
+			InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL, NULL);
+
+			fcinfo.arg[0] = CStringGetDatum(optionsstr);
+			fcinfo.argnull[0] = false;
+			fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+			fcinfo.argnull[1] = false;
+			fcinfo.arg[2] = Int32GetDatum(-1);
+			fcinfo.argnull[2] = false;
+
+			flinfo.fn_mcxt = CurrentMemoryContext;
+
+			options = array_in(&fcinfo);
+
+			if (fcinfo.isnull)
+				elog(ERROR, "function %p returned NULL", (void *) array_in);
+
+			pfree(optionsstr);
+		}
+		else
+		{
+			options = PointerGetDatum(NULL);
+		}
+
+		opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+		if (options != PointerGetDatum(NULL))
+			pfree(DatumGetPointer(options));
+	}
+
+	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+	relation->rd_indoptions = palloc(sizeof(*opts) * ncols);
+
+	for (i = 0; i < ncols; i++)
+		relation->rd_indoptions[i] = !opts[i] ? NULL : (bytea *)
+			DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return opts;
+}
+
+Datum *
+ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols)
+{
+	Datum	   *indoptions;
+	bool	   *indoptionsnulls;
+	int			indoptionsnum;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+					  TEXTOID, -1, false, 'i',
+					  &indoptions, &indoptionsnulls, &indoptionsnum);
+
+	Assert(indoptionsnum == ncols);
+
+	for (i = 0; i < indoptionsnum; i++)
+	{
+		if (indoptionsnulls[i])
+			indoptions[i] = PointerGetDatum(NULL);
+		else
+		{
+			FunctionCallInfoData fcinfo;
+			FmgrInfo	flinfo = { 0 };
+			char	   *optionsstr = TextDatumGetCString(indoptions[i]);
+
+			InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL,
+									 NULL);
+
+			fcinfo.arg[0] = CStringGetDatum(optionsstr);
+			fcinfo.argnull[0] = false;
+			fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+			fcinfo.argnull[1] = false;
+			fcinfo.arg[2] = Int32GetDatum(-1);
+			fcinfo.argnull[2] = false;
+
+			flinfo.fn_mcxt = CurrentMemoryContext;
+
+			indoptions[i] = array_in(&fcinfo);
+
+			/* Check for null result, since caller is clearly not expecting one */
+			if (fcinfo.isnull)
+				elog(ERROR, "function %p returned NULL", (void *) array_in);
+		}
+	}
+
+	return indoptions;
+}
+
+/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for an index
+ */
+Datum *
+RelationGetRawOpclassOptions(Relation relation)
+{
+	Datum		indoptionsDatum;
+	bool		isnull;
+
+	/* Quick exit if there is nothing to do. */
+	if (relation->rd_indextuple == NULL ||
+		heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions, NULL))
+		return NULL;
+
+	indoptionsDatum = heap_getattr(relation->rd_indextuple,
+								   Anum_pg_index_indoptions,
+								   GetPgIndexDescriptor(),
+								   &isnull);
+	Assert(!isnull);
+
+	return ExtractRawOpclassOptions(indoptionsDatum,
+									relation->rd_rel->relnatts);
+}
+
+
+/*
  * Routines to support ereport() reports of relation-related errors
  *
  * These could have been put into elog.c, but it seems like a module layering
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 14526a6..c1da8e9 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -103,6 +103,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
 typedef bytea *(*amoptions_function) (Datum reloptions,
 									  bool validate);
 
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+											 AttrNumber colno,
+											 Datum indoptions,
+											 bool validate);
+
 /* report AM, index, or index column property */
 typedef bool (*amproperty_function) (Oid index_oid, int attno,
 									 IndexAMProperty prop, const char *propname,
@@ -212,6 +218,7 @@ typedef struct IndexAmRoutine
 	amcanreturn_function amcanreturn;	/* can be NULL */
 	amcostestimate_function amcostestimate;
 	amoptions_function amoptions;
+	amopclassoptions_function amopclassoptions;
 	amproperty_function amproperty; /* can be NULL */
 	amvalidate_function amvalidate;
 	ambeginscan_function ambeginscan;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 534fac7..e558ba3 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -177,6 +177,12 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
 extern void index_store_float8_orderby_distances(IndexScanDesc scan,
 									 Oid *orderByTypes, double *distances,
 									 bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+					  Datum indoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+							  AttrNumber attnum, uint16 procnum,
+							  Datum indoptions, bool validate);
+
 
 /*
  * index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 4022c14..f6d6b43 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -264,12 +264,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 				  amoptions_function amoptions);
 extern relopt_value *parseRelOptions(Datum options, bool validate,
 				relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen **gen, int nelems);
 extern void *allocateReloptStruct(Size base, relopt_value *options,
 					 int numoptions);
 extern void fillRelOptions(void *rdopts, Size basesize,
 			   relopt_value *options, int numoptions,
 			   bool validate,
 			   const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+							int offsets[], int noptions, size_t base_size,
+							bool validate);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 5f72a55..04a5c4c 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -55,6 +55,7 @@ CATALOG(pg_index,2610,IndexRelationId) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
 								 * each zero entry in indkey[] */
 	pg_node_tree indpred;		/* expression tree for predicate, if a partial
 								 * index; else NULL */
+	text		indoptions[1];	/* opclass options */
 #endif
 } FormData_pg_index;
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1854456..fde9153 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -136,6 +136,7 @@ typedef struct ExprState
  *		UniqueProcs
  *		UniqueStrats
  *		Unique				is it a unique index?
+ *		OpclassOptions		opclass-specific options, or NULL if none
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
  *		BrokenHotChain		did we detect any broken HOT chains?
@@ -164,6 +165,7 @@ typedef struct IndexInfo
 	Oid		   *ii_UniqueOps;	/* array with one entry per column */
 	Oid		   *ii_UniqueProcs; /* array with one entry per column */
 	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	Datum	   *ii_OpclassOptions;	/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9da8bf2..c715a02 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -699,6 +699,7 @@ typedef struct IndexElem
 	char	   *indexcolname;	/* name for index column; NULL = default */
 	List	   *collation;		/* name of collation; NIL = default */
 	List	   *opclass;		/* name of desired opclass; NIL = default */
+	List	   *opclassopts;	/* opclass-specific options, or NIL */
 	SortByDir	ordering;		/* ASC/DESC/default */
 	SortByNulls nulls_ordering; /* FIRST/LAST/default */
 } IndexElem;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6fd2420..fba493e 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -783,6 +783,7 @@ typedef struct IndexOptInfo
 	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
 	bool	   *reverse_sort;	/* is sort order descending? */
 	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
+	bytea	 **opclassoptions;	/* opclass-specific options for columns */
 	bool	   *canreturn;		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 84469f5..2826e5d 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -163,6 +163,7 @@ typedef struct RelationData
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
 	void	   *rd_amcache;		/* available for use by index AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
+	bytea	  **rd_indoptions;	/* parsed opclass-specific options */
 
 	/*
 	 * foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index dbbf41b..7af7bb6 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
 #ifndef RELCACHE_H
 #define RELCACHE_H
 
+#include "postgres.h"
 #include "access/tupdesc.h"
 #include "nodes/bitmapset.h"
 
@@ -50,6 +51,9 @@ extern Oid	RelationGetPrimaryKeyIndex(Relation relation);
 extern Oid	RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Relation relation);
+extern Datum *ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols);
 
 typedef enum IndexAttrBitmapKind
 {
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9f9b029..86e6953 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,5 +34,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
 								Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
 extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
 
 #endif							/* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..e420f10 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,8 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
  {vacuum_cleanup_index_scale_factor=70.0}
 (1 row)
 
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  access method "btree" does not support opclass options 
+create index on btree_tall_tbl (id default(foo=1));
+ERROR:  access method "btree" does not support opclass options 
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b..43a9962 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -102,8 +102,9 @@ ORDER BY 1, 2;
  pg_class                | reloptions    | text[]
  pg_class                | relpartbound  | pg_node_tree
  pg_index                | indexprs      | pg_node_tree
+ pg_index                | indoptions    | text[]
  pg_index                | indpred       | pg_node_tree
  pg_largeobject          | data          | bytea
  pg_largeobject_metadata | lomacl        | aclitem[]
-(11 rows)
+(12 rows)
 
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..a3f7d9b 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,7 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
 -- Simple ALTER INDEX
 alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
 select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
-- 
2.7.4

0002-Add-opclass-parameters-to-GiST-v02.patchtext/x-patch; name=0002-Add-opclass-parameters-to-GiST-v02.patchDownload
From 8618654d93554f0684b0cf3c89de049d6d29435c Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 18:58:39 +0300
Subject: [PATCH 2/9] Add opclass parameters to GiST

---
 doc/src/sgml/xindex.sgml               |  5 ++++
 src/backend/access/gist/gist.c         |  2 ++
 src/backend/access/gist/gistget.c      | 12 ++++++----
 src/backend/access/gist/gistsplit.c    | 15 +++++++-----
 src/backend/access/gist/gistutil.c     | 43 +++++++++++++++++++++++-----------
 src/backend/access/gist/gistvalidate.c | 34 +++++++++++++++++----------
 src/include/access/gist.h              |  3 ++-
 src/include/access/gist_private.h      |  4 ++++
 8 files changed, 81 insertions(+), 37 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..8c5b528 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
        index-only scans (optional)</entry>
        <entry>9</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>parse opclass-specific options (optional)</entry>
+       <entry>10</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..905cbbf 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -97,6 +97,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->amopclassoptions = gistopclassoptions;
 
 	PG_RETURN_POINTER(amroutine);
 }
@@ -1457,6 +1458,7 @@ initGISTstate(Relation index)
 	giststate->scanCxt = scanCxt;
 	giststate->tempCxt = scanCxt;	/* caller must change this if needed */
 	giststate->tupdesc = index->rd_att;
+	giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < index->rd_att->natts; i++)
 	{
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index e4a3786..9302e68 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		test;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 */
 			recheck = true;
 
-			test = FunctionCall5Coll(&key->sk_func,
+			test = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 
 			if (!DatumGetBool(test))
 				return false;
@@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		dist;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 * about the flag, but are expected to never be lossy.
 			 */
 			recheck = false;
-			dist = FunctionCall5Coll(&key->sk_func,
+			dist = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 			*recheck_distances_p |= recheck;
 			*distance_p = DatumGetFloat8(dist);
 		}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index a7038cc..96a9290 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
 	evec->n = v->spl_nleft;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	evec->n = v->spl_nright;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 }
 
 /*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
 	 * Let the opclass-specific PickSplit method do its thing.  Note that at
 	 * this point we know there are no null keys in the entryvec.
 	 */
-	FunctionCall2Coll(&giststate->picksplitFn[attno],
+	FunctionCall3Coll(&giststate->picksplitFn[attno],
 					  giststate->supportCollation[attno],
 					  PointerGetDatum(entryvec),
-					  PointerGetDatum(sv));
+					  PointerGetDatum(sv),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	if (sv->spl_nleft == 0 || sv->spl_nright == 0)
 	{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 70627e5..f9367f9 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -199,10 +199,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
 			}
 
 			/* Make union and store in attr array */
-			attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+			attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
 										giststate->supportCollation[i],
 										PointerGetDatum(evec),
-										PointerGetDatum(&attrsize));
+										PointerGetDatum(&attrsize),
+										PointerGetDatum(giststate->opclassoptions[i]));
 
 			isnull[i] = false;
 		}
@@ -268,10 +269,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
 		}
 
 		*dstisnull = false;
-		*dst = FunctionCall2Coll(&giststate->unionFn[attno],
+		*dst = FunctionCall3Coll(&giststate->unionFn[attno],
 								 giststate->supportCollation[attno],
 								 PointerGetDatum(evec),
-								 PointerGetDatum(&dstsize));
+								 PointerGetDatum(&dstsize),
+								 PointerGetDatum(giststate->opclassoptions[attno]));
 	}
 }
 
@@ -280,10 +282,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
 {
 	bool		result;
 
-	FunctionCall3Coll(&giststate->equalFn[attno],
+	FunctionCall4Coll(&giststate->equalFn[attno],
 					  giststate->supportCollation[attno],
 					  a, b,
-					  PointerGetDatum(&result));
+					  PointerGetDatum(&result),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 	return result;
 }
 
@@ -556,9 +559,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
 			return;
 
 		dep = (GISTENTRY *)
-			DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+			DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
 											  giststate->supportCollation[nkey],
-											  PointerGetDatum(e)));
+											  PointerGetDatum(e),
+											  PointerGetDatum(giststate->opclassoptions[nkey])));
 		/* decompressFn may just return the given pointer */
 		if (dep != e)
 			gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -593,9 +597,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
 			/* there may not be a compress function in opclass */
 			if (OidIsValid(giststate->compressFn[i].fn_oid))
 				cep = (GISTENTRY *)
-					DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+					DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
 													  giststate->supportCollation[i],
-													  PointerGetDatum(&centry)));
+													  PointerGetDatum(&centry),
+													  PointerGetDatum(giststate->opclassoptions[i])));
 			else
 				cep = &centry;
 			compatt[i] = cep->key;
@@ -624,9 +629,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
 	gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
 
 	fep = (GISTENTRY *)
-		DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+		DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
 										  giststate->supportCollation[nkey],
-										  PointerGetDatum(&fentry)));
+										  PointerGetDatum(&fentry),
+										  PointerGetDatum(giststate->opclassoptions[nkey])));
 
 	/* fetchFn set 'key', return it to the caller */
 	return fep->key;
@@ -694,11 +700,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
 	if (giststate->penaltyFn[attno].fn_strict == false ||
 		(isNullOrig == false && isNullAdd == false))
 	{
-		FunctionCall3Coll(&giststate->penaltyFn[attno],
+		FunctionCall4Coll(&giststate->penaltyFn[attno],
 						  giststate->supportCollation[attno],
 						  PointerGetDatum(orig),
 						  PointerGetDatum(add),
-						  PointerGetDatum(&penalty));
+						  PointerGetDatum(&penalty),
+						  PointerGetDatum(giststate->opclassoptions[attno]));
 		/* disallow negative or NaN penalty */
 		if (isnan(penalty) || penalty < 0.0)
 			penalty = 0.0;
@@ -860,6 +867,14 @@ gistoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum options, bool validate)
+{
+	return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+										 options, validate);
+}
+
+
 /*
  *	gistproperty() -- Check boolean properties of indexes.
  *
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index c300e52..c5e1b23 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
 		{
 			case GIST_CONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_UNION_PROC:
 				ok = check_amproc_signature(procform->amproc, opckeytype, false,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_COMPRESS_PROC:
 			case GIST_DECOMPRESS_PROC:
 			case GIST_FETCH_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											1, 1, INTERNALOID);
+											1, 2, INTERNALOID, INTERNALOID);
 				break;
 			case GIST_PENALTY_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											3, 3, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											3, 4, INTERNALOID,
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_PICKSPLIT_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_EQUAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											3, 3, opckeytype, opckeytype,
-											INTERNALOID);
+											3, 4, opckeytype, opckeytype,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIST_DISTANCE_PROC:
 				ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
+				break;
+			case GIST_OPCLASSOPT_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+											2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
 		if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
-			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+			i == GIST_OPCLASSOPT_PROC)
 			continue;			/* optional methods */
 		ereport(INFO,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 827566d..08e7ada 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
 #define GIST_EQUAL_PROC					7
 #define GIST_DISTANCE_PROC				8
 #define GIST_FETCH_PROC					9
-#define GISTNProcs					9
+#define GIST_OPCLASSOPT_PROC			10
+#define GISTNProcs						10
 
 /*
  * Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 36ed724..eedd896 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -94,6 +94,8 @@ typedef struct GISTSTATE
 
 	/* Collations to pass to the support functions */
 	Oid			supportCollation[INDEX_MAX_KEYS];
+
+	bytea	  **opclassoptions;	/* parsed opclass-specific options */
 } GISTSTATE;
 
 
@@ -436,6 +438,8 @@ extern bool gistvalidate(Oid opclassoid);
 #define GIST_DEFAULT_FILLFACTOR		90
 
 extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+				   Datum options, bool validate);
 extern bool gistproperty(Oid index_oid, int attno,
 			 IndexAMProperty prop, const char *propname,
 			 bool *res, bool *isnull);
-- 
2.7.4

0003-Add-opclass-parameters-to-GIN-v02.patchtext/x-patch; name=0003-Add-opclass-parameters-to-GIN-v02.patchDownload
From 8a015b2803852a03da2d1bde42951473cca1d3c7 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 01:50:09 +0300
Subject: [PATCH 3/9] Add opclass parameters to GIN

---
 doc/src/sgml/xindex.sgml             |  7 +++++++
 src/backend/access/gin/ginget.c      | 11 ++++++-----
 src/backend/access/gin/ginlogic.c    | 15 +++++++++------
 src/backend/access/gin/ginscan.c     |  6 ++++--
 src/backend/access/gin/ginutil.c     | 27 +++++++++++++++++++++------
 src/backend/access/gin/ginvalidate.c | 32 ++++++++++++++++++++------------
 src/backend/utils/adt/selfuncs.c     |  6 ++++--
 src/include/access/gin.h             |  3 ++-
 src/include/access/gin_private.h     |  5 +++++
 9 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b528..658ec9b 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
        </entry>
        <entry>6</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>
+        parse opclass-specific options (optional)
+       </entry>
+       <entry>7</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 8466d94..5eb47e0 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
 			 * case cmp < 0 => not match and continue scan
 			 *----------
 			 */
-			cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+			cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
 												  btree->ginstate->supportCollation[attnum - 1],
 												  scanEntry->queryKey,
 												  idatum,
 												  UInt16GetDatum(scanEntry->strategy),
-												  PointerGetDatum(scanEntry->extra_data)));
-
+												  PointerGetDatum(scanEntry->extra_data),
+												  PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
 			if (cmp > 0)
 				return true;
 			else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
 		 * case cmp < 0 => not match and continue scan
 		 *----------
 		 */
-		cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+		cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
 											  ginstate->supportCollation[entry->attnum - 1],
 											  entry->queryKey,
 											  datum[off - 1],
 											  UInt16GetDatum(entry->strategy),
-											  PointerGetDatum(entry->extra_data)));
+											  PointerGetDatum(entry->extra_data),
+											  PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
 		if (cmp == 0)
 			return true;
 		else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
 	 */
 	key->recheckCurItem = true;
 
-	return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+	return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
 										  key->collation,
 										  PointerGetDatum(key->entryRes),
 										  UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
 										  PointerGetDatum(key->extra_data),
 										  PointerGetDatum(&key->recheckCurItem),
 										  PointerGetDatum(key->queryValues),
-										  PointerGetDatum(key->queryCategories)));
+										  PointerGetDatum(key->queryCategories),
+										  PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
 static GinTernaryValue
 directTriConsistentFn(GinScanKey key)
 {
-	return DatumGetGinTernaryValue(FunctionCall7Coll(
+	return DatumGetGinTernaryValue(FunctionCall8Coll(
 													 key->triConsistentFmgrInfo,
 													 key->collation,
 													 PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
 													 UInt32GetDatum(key->nuserentries),
 													 PointerGetDatum(key->extra_data),
 													 PointerGetDatum(key->queryValues),
-													 PointerGetDatum(key->queryCategories)));
+													 PointerGetDatum(key->queryCategories),
+													 PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
 {
 	GinTernaryValue result;
 
-	result = DatumGetGinTernaryValue(FunctionCall7Coll(
+	result = DatumGetGinTernaryValue(FunctionCall8Coll(
 													   key->triConsistentFmgrInfo,
 													   key->collation,
 													   PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
 													   UInt32GetDatum(key->nuserentries),
 													   PointerGetDatum(key->extra_data),
 													   PointerGetDatum(key->queryValues),
-													   PointerGetDatum(key->queryCategories)));
+													   PointerGetDatum(key->queryCategories),
+													   PointerGetDatum(key->opclassOptions)));
 	if (result == GIN_MAYBE)
 	{
 		key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
 	key->strategy = strategy;
 	key->searchMode = searchMode;
 	key->attnum = attnum;
+	key->opclassOptions = ginstate->opclassOptions[attnum - 1];
 
 	ItemPointerSetMin(&key->curItem);
 	key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
 
 		/* OK to call the extractQueryFn */
 		queryValues = (Datum *)
-			DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+			DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
 											  so->ginstate.supportCollation[skey->sk_attno - 1],
 											  skey->sk_argument,
 											  PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
 											  PointerGetDatum(&partial_matches),
 											  PointerGetDatum(&extra_data),
 											  PointerGetDatum(&nullFlags),
-											  PointerGetDatum(&searchMode)));
+											  PointerGetDatum(&searchMode),
+											  PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
 
 		/*
 		 * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 0a32182..817bbce 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = gincostestimate;
 	amroutine->amoptions = ginoptions;
+	amroutine->amopclassoptions = ginopclassoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = ginvalidate;
 	amroutine->ambeginscan = ginbeginscan;
@@ -95,6 +96,7 @@ initGinState(GinState *state, Relation index)
 	state->index = index;
 	state->oneCol = (origTupdesc->natts == 1) ? true : false;
 	state->origTupdesc = origTupdesc;
+	state->opclassOptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < origTupdesc->natts; i++)
 	{
@@ -403,9 +405,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
 		return 0;
 
 	/* both not null, so safe to call the compareFn */
-	return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+	return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
 										   ginstate->supportCollation[attnum - 1],
-										   a, b));
+										   a, b,
+										   PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 }
 
 /*
@@ -441,6 +444,7 @@ typedef struct
 {
 	FmgrInfo   *cmpDatumFunc;
 	Oid			collation;
+	Datum		options;
 	bool		haveDups;
 } cmpEntriesArg;
 
@@ -462,9 +466,10 @@ cmpEntries(const void *a, const void *b, void *arg)
 	else if (bb->isnull)
 		res = -1;				/* not-NULL "<" NULL */
 	else
-		res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+		res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
 											  data->collation,
-											  aa->datum, bb->datum));
+											  aa->datum, bb->datum,
+											  data->options));
 
 	/*
 	 * Detect if we have any duplicates.  If there are equal keys, qsort must
@@ -510,11 +515,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 	/* OK, call the opclass's extractValueFn */
 	nullFlags = NULL;			/* in case extractValue doesn't set it */
 	entries = (Datum *)
-		DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+		DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
 										  ginstate->supportCollation[attnum - 1],
 										  value,
 										  PointerGetDatum(nentries),
-										  PointerGetDatum(&nullFlags)));
+										  PointerGetDatum(&nullFlags),
+										  PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 
 	/*
 	 * Generate a placeholder if the item contained no keys.
@@ -557,6 +563,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 
 		arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
 		arg.collation = ginstate->supportCollation[attnum - 1];
+		arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
 		arg.haveDups = false;
 		qsort_arg(keydata, *nentries, sizeof(keyEntryData),
 				  cmpEntries, (void *) &arg);
@@ -632,6 +639,14 @@ ginoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum indoptions,
+				  bool validate)
+{
+	return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+										 indoptions, validate);
+}
+
 /*
  * Fetch index's statistical data into *stats
  *
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1922260..4f87d12 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
 		{
 			case GIN_COMPARE_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											2, 2, opckeytype, opckeytype);
+											2, 3, opckeytype, opckeytype,
+											INTERNALOID);
 				break;
 			case GIN_EXTRACTVALUE_PROC:
 				/* Some opclasses omit nullFlags */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											2, 3, opcintype, INTERNALOID,
-											INTERNALOID);
+											2, 4, opcintype, INTERNALOID,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIN_EXTRACTQUERY_PROC:
 				/* Some opclasses omit nullFlags and searchMode */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											5, 7, opcintype, INTERNALOID,
+											5, 8, opcintype, INTERNALOID,
 											INT2OID, INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_CONSISTENT_PROC:
 				/* Some opclasses omit queryKeys and nullFlags */
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											6, 8, INTERNALOID, INT2OID,
+											6, 9, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_COMPARE_PARTIAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											4, 4, opckeytype, opckeytype,
-											INT2OID, INTERNALOID);
+											4, 5, opckeytype, opckeytype,
+											INT2OID, INTERNALOID, INTERNALOID);
 				break;
 			case GIN_TRICONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, CHAROID, false,
-											7, 7, INTERNALOID, INT2OID,
+											7, 8, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID);
+											INTERNALOID, INTERNALOID);
+				break;
+			case GIN_OPCLASSOPTIONS_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID,
+											false, 2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
 		if (opclassgroup &&
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
-		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+			i == GIN_OPCLASSOPTIONS_PROC)
 			continue;			/* optional method */
 		if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
 			continue;			/* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index e0ece74..a53f416 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -139,6 +139,7 @@
 #include "utils/lsyscache.h"
 #include "utils/pg_locale.h"
 #include "utils/rel.h"
+#include "utils/relcache.h"
 #include "utils/selfuncs.h"
 #include "utils/snapmgr.h"
 #include "utils/spccache.h"
@@ -7456,7 +7457,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	else
 		collation = DEFAULT_COLLATION_OID;
 
-	OidFunctionCall7Coll(extractProcOid,
+	OidFunctionCall8Coll(extractProcOid,
 						 collation,
 						 query,
 						 PointerGetDatum(&nentries),
@@ -7464,7 +7465,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 						 PointerGetDatum(&partial_matches),
 						 PointerGetDatum(&extra_data),
 						 PointerGetDatum(&nullFlags),
-						 PointerGetDatum(&searchMode));
+						 PointerGetDatum(&searchMode),
+						 PointerGetDatum(index->opclassoptions[indexcol]));
 
 	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
 	{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 3d8a130..20ce792 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
 #define GIN_CONSISTENT_PROC			   4
 #define GIN_COMPARE_PARTIAL_PROC	   5
 #define GIN_TRICONSISTENT_PROC		   6
-#define GINNProcs					   6
+#define GIN_OPCLASSOPTIONS_PROC		   7
+#define GINNProcs					   7
 
 /*
  * searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 81bf873..6820899 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
 	TupleDesc	origTupdesc;
 	TupleDesc	tupdesc[INDEX_MAX_KEYS];
 
+	bytea	  **opclassOptions;	/* per-index-column opclass options */
+
 	/*
 	 * Per-index-column opclass support functions
 	 */
@@ -85,6 +87,8 @@ typedef struct GinState
 
 /* ginutil.c */
 extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+				  Datum indoptions, bool validate);
 extern void initGinState(GinState *state, Relation index);
 extern Buffer GinNewBuffer(Relation index);
 extern void GinInitBuffer(Buffer b, uint32 f);
@@ -296,6 +300,7 @@ typedef struct GinScanKeyData
 	StrategyNumber strategy;
 	int32		searchMode;
 	OffsetNumber attnum;
+	bytea	   *opclassOptions;
 
 	/*
 	 * Match status data.  curItem is the TID most recently tested (could be a
-- 
2.7.4

0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patchtext/x-patch; name=0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patchDownload
From af502308192d3f316e493682a36f63733881043b Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 18:31:23 +0300
Subject: [PATCH 4/9] Add opclass parameters to GiST tsvector_ops

---
 doc/src/sgml/textsearch.sgml          |   9 +-
 src/backend/utils/adt/tsgistidx.c     | 269 +++++++++++++++++++---------------
 src/include/catalog/pg_amproc.dat     |   5 +-
 src/include/catalog/pg_proc.dat       |  19 ++-
 src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
 src/test/regress/sql/tsearch.sql      |  45 ++++++
 6 files changed, 392 insertions(+), 131 deletions(-)

diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index ecebade..92abf6e 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3635,7 +3635,7 @@ SELECT plainto_tsquery('supernovae stars');
       <tertiary>text search</tertiary>
      </indexterm>
 
-      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
      </term>
 
      <listitem>
@@ -3643,6 +3643,8 @@ SELECT plainto_tsquery('supernovae stars');
        Creates a GiST (Generalized Search Tree)-based index.
        The <replaceable>column</replaceable> can be of <type>tsvector</type> or
        <type>tsquery</type> type.
+       Optional integer parameter <literal>siglen</literal> determines
+       signature length in bytes (see below for details).
       </para>
      </listitem>
     </varlistentry>
@@ -3666,7 +3668,10 @@ SELECT plainto_tsquery('supernovae stars');
    to check the actual table row to eliminate such false matches.
    (<productname>PostgreSQL</productname> does this automatically when needed.)
    GiST indexes are lossy because each document is represented in the
-   index by a fixed-length signature. The signature is generated by hashing
+   index by a fixed-length signature.  Signature length in bytes is determined
+   by the value of the optional integer parameter <literal>siglen</literal>.
+   Default signature length (when <literal>siglen</literal> is not specied) is
+   124 bytes, maximal length is 484 bytes. The signature is generated by hashing
    each word into a single bit in an n-bit string, with all these bits OR-ed
    together to produce an n-bit document signature.  When two words hash to
    the same bit position there will be a false match.  If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 2d9ecc4..462791a 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,23 +15,29 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/tuptoaster.h"
 #include "tsearch/ts_utils.h"
 #include "utils/builtins.h"
 #include "utils/pg_crc.h"
 
 
-#define SIGLENINT  31			/* >121 => key will toast, so it will not work
-								 * !!! */
+#define SIGLEN_DEFAULT	(31 * 4)
+#define SIGLEN_MAX		(121 * 4)	/* key will toast, so it will not work !!! */
 
-#define SIGLEN	( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length */
+}	GistTsVectorOptions;
 
-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) / BITS_PER_BYTE ) ) )
 #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -39,8 +45,8 @@ typedef char *BITVECP;
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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 GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
 
@@ -64,9 +70,10 @@ typedef struct
 #define ISALLTRUE(x)	( ((SignTSVector*)(x))->flag & ALLISTRUE )
 
 #define GTHDRSIZE	( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
 
 #define GETSIGN(x)	( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
 #define GETARR(x)	( (int32*)( (char*)(x)+GTHDRSIZE ) )
 #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
 
@@ -90,7 +97,7 @@ static const uint8 number_of_ones[256] = {
 	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
 
 Datum
 gtsvectorin(PG_FUNCTION_ARGS)
@@ -121,9 +128,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
 		sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
 	else
 	{
-		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+		int			siglen = GETSIGLEN(key);
+		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
 
-		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
 	}
 
 	PG_FREE_IF_COPY(key, 0);
@@ -167,36 +175,49 @@ uniqueint(int32 *a, int32 l)
 }
 
 static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
 {
 	int32		k,
 				len = ARRNELEM(a);
 	int32	   *ptr = GETARR(a);
 
-	MemSet((void *) sign, 0, sizeof(BITVEC));
+	MemSet((void *) sign, 0, siglen);
 	for (k = 0; k < len; k++)
-		HASH(sign, ptr[k]);
+		HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+	int			size = CALCGTSIZE(flag, len);
+	SignTSVector *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+		memcpy(GETSIGN(res), sign, len);
+
+	return res;
 }
 
+
 Datum
 gtsvector_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{							/* tsvector */
-		SignTSVector *res;
 		TSVector	val = DatumGetTSVector(entry->key);
+		SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
 		int32		len;
 		int32	   *arr;
 		WordEntry  *ptr = ARRPTR(val);
 		char	   *words = STRPTR(val);
 
-		len = CALCGTSIZE(ARRKEY, val->size);
-		res = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = ARRKEY;
 		arr = GETARR(res);
 		len = val->size;
 		while (len--)
@@ -227,13 +248,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 		/* make signature, if array is too long */
 		if (VARSIZE(res) > TOAST_INDEX_TARGET)
 		{
-			SignTSVector *ressign;
+			SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
 
-			len = CALCGTSIZE(SIGNKEY, 0);
-			ressign = (SignTSVector *) palloc(len);
-			SET_VARSIZE(ressign, len);
-			ressign->flag = SIGNKEY;
-			makesign(GETSIGN(ressign), res);
+			makesign(GETSIGN(ressign), res, siglen);
 			res = ressign;
 		}
 
@@ -245,22 +262,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 	else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
 			 !ISALLTRUE(DatumGetPointer(entry->key)))
 	{
-		int32		i,
-					len;
+		int32		i;
 		SignTSVector *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 = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = SIGNKEY | ALLISTRUE;
-
+		res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -334,12 +346,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
 static bool
 checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
 {
+	void *key = (SignTSVector *) checkval;
+
 	/*
 	 * we are not able to find a prefix in signature tree
 	 */
 	if (val->prefix)
 		return true;
-	return GETBIT(checkval, HASHVAL(val->valcrc));
+	return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
 }
 
 Datum
@@ -366,7 +380,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 
 		/* since signature is lossy, cannot specify CALC_NOT here */
 		PG_RETURN_BOOL(TS_execute(GETQUERY(query),
-								  (void *) GETSIGN(key),
+								  key,
 								  TS_EXEC_PHRASE_NO_POS,
 								  checkcondition_bit));
 	}
@@ -384,7 +398,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
 {
 	int32		i;
 
@@ -395,7 +409,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		Assert(GETSIGLEN(add) == siglen);
+
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -403,7 +419,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		int32	   *ptr = GETARR(add);
 
 		for (i = 0; i < ARRNELEM(add); i++)
-			HASH(sbase, ptr[i]);
+			HASH(sbase, ptr[i], siglen);
 	}
 	return 0;
 }
@@ -414,30 +430,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	SignTSVector *result;
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+	SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
+	int32		i;
+
+	memset(base, 0, siglen);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = ALLISTRUE;
+			result->flag |= ALLISTRUE;
+			SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
 			break;
 		}
 	}
 
-	flag |= SIGNKEY;
-	len = CALCGTSIZE(flag, 0);
-	result = (SignTSVector *) palloc(len);
-	*size = len;
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!ISALLTRUE(result))
-		memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -448,6 +458,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
 	SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
 	SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -463,8 +474,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
 			BITVECP		sa = GETSIGN(a),
 						sb = GETSIGN(b);
 
+			Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -501,24 +514,24 @@ gtsvector_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];
@@ -529,17 +542,22 @@ hemdistsign(BITVECP a, BITVECP b)
 static int
 hemdist(SignTSVector *a, SignTSVector *b)
 {
+	int siglena = GETSIGLEN(a);
+	int siglenb = GETSIGLEN(b);
+
 	if (ISALLTRUE(a))
 	{
 		if (ISALLTRUE(b))
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(GETSIGN(b));
+			return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
 	}
 	else if (ISALLTRUE(b))
-		return SIGLENBIT - sizebitvec(GETSIGN(a));
+		return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
 
-	return hemdistsign(GETSIGN(a), GETSIGN(b));
+	Assert(siglena == siglenb);
+
+	return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
 }
 
 Datum
@@ -548,6 +566,7 @@ gtsvector_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);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 	SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
 	SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -556,14 +575,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 
 	if (ISARRKEY(newval))
 	{
-		BITVEC		sign;
+		BITVECP		sign = palloc(siglen);
 
-		makesign(sign, newval);
+		makesign(sign, newval, siglen);
 
 		if (ISALLTRUE(origval))
-			*penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+		{
+			int			siglenbit = SIGLENBIT(siglen);
+
+			*penalty =
+				(float) (siglenbit - sizebitvec(sign, siglen)) /
+				(float) (siglenbit + 1);
+		}
 		else
-			*penalty = hemdistsign(sign, orig);
+			*penalty = hemdistsign(sign, orig, siglen);
+
+		pfree(sign);
 	}
 	else
 		*penalty = hemdist(origval, newval);
@@ -573,19 +600,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 typedef struct
 {
 	bool		allistrue;
-	BITVEC		sign;
+	BITVECP		sign;
 } CACHESIGN;
 
 static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
 {
 	item->allistrue = false;
 	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) )
@@ -609,19 +636,19 @@ comparecost(const void *va, const void *vb)
 
 
 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
@@ -629,6 +656,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
 	OffsetNumber k,
 				j;
 	SignTSVector *datum_l,
@@ -648,6 +676,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	BITVECP		ptr;
 	int			i;
 	CACHESIGN  *cache;
+	char	   *cache_sign;
 	SPLITCOST  *costvector;
 
 	maxoff = entryvec->n - 2;
@@ -656,16 +685,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	v->spl_right = (OffsetNumber *) palloc(nbytes);
 
 	cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
-	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+	cache_sign = palloc(siglen * (maxoff + 2));
+
+	for (j = 0; j < maxoff + 2; j++)
+		cache[j].sign = &cache_sign[siglen * j];
+
+	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+			  siglen);
 
 	for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
 	{
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
 			if (k == FirstOffsetNumber)
-				fillcache(&cache[j], GETENTRY(entryvec, j));
+				fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
 
-			size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+			size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -687,44 +722,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (cache[seed_1].allistrue)
-	{
-		datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_l->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (SignTSVector *) 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 = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_r->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (SignTSVector *) 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 = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+							  siglen, cache[seed_1].sign);
+	datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+							  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), 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);
@@ -750,36 +762,34 @@ gtsvector_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;
@@ -790,12 +800,12 @@ gtsvector_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;
@@ -822,3 +832,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
 {
 	return gtsvector_consistent(fcinfo);
 }
+
+Datum
+gtsvector_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(GistTsVectorOptions, siglen) };
+	GistTsVectorOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(GistTsVectorOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 0ef2c08..c1ee3db 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -450,7 +450,7 @@
   amproc => 'gist_circle_distance' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '1',
-  amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
+  amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '2',
   amproc => 'gtsvector_union' },
@@ -468,6 +468,9 @@
   amproc => 'gtsvector_picksplit' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+  amprocrighttype => 'tsvector', amprocnum => '10',
+  amproc => 'gtsvector_options' },
 { amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
   amprocrighttype => 'tsquery', amprocnum => '1',
   amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 9264a2e..c22d639 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8430,30 +8430,35 @@
 
 { oid => '3648', descr => 'GiST tsvector support',
   proname => 'gtsvector_compress', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'gtsvector_compress' },
+  proargtypes => 'internal internal', prosrc => 'gtsvector_compress' },
 { oid => '3649', descr => 'GiST tsvector support',
   proname => 'gtsvector_decompress', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'gtsvector_decompress' },
+  proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' },
 { oid => '3650', descr => 'GiST tsvector support',
   proname => 'gtsvector_picksplit', prorettype => 'internal',
-  proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' },
+  proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' },
 { oid => '3651', descr => 'GiST tsvector support',
   proname => 'gtsvector_union', prorettype => 'gtsvector',
-  proargtypes => 'internal internal', prosrc => 'gtsvector_union' },
+  proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' },
 { oid => '3652', descr => 'GiST tsvector support',
   proname => 'gtsvector_same', prorettype => 'internal',
-  proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' },
+  proargtypes => 'gtsvector gtsvector internal internal',
+  prosrc => 'gtsvector_same' },
 { oid => '3653', descr => 'GiST tsvector support',
   proname => 'gtsvector_penalty', prorettype => 'internal',
-  proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' },
+  proargtypes => 'internal internal internal internal',
+  prosrc => 'gtsvector_penalty' },
 { oid => '3654', descr => 'GiST tsvector support',
   proname => 'gtsvector_consistent', prorettype => 'bool',
-  proargtypes => 'internal tsvector int2 oid internal',
+  proargtypes => 'internal tsvector int2 oid internal internal',
   prosrc => 'gtsvector_consistent' },
 { oid => '3790', descr => 'GiST tsvector support (obsolete)',
   proname => 'gtsvector_consistent', prorettype => 'bool',
   proargtypes => 'internal gtsvector int4 oid internal',
   prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3996', descr => 'GiST tsvector support',
+  proname => 'gtsvector_options', prorettype => 'internal',
+  proargtypes => 'internal bool', prosrc => 'gtsvector_options' },
 
 { oid => '3656', descr => 'GIN tsvector support',
   proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index b088ff0..9d81489 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
    508
 (1 row)
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR:  value 485 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR:  parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a)
+    "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx2
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a tsvector_ops (siglen='484'))
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 637bfb3..740d5e0 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
 SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
 SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
-- 
2.7.4

0005-Add-opclass-parameters-to-contrib-intarray-v02.patchtext/x-patch; name=0005-Add-opclass-parameters-to-contrib-intarray-v02.patchDownload
From c246a65e9a613f57e8812d32c395705bb940cfef Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 17:09:22 +0300
Subject: [PATCH 5/9] Add opclass parameters to contrib/intarray

---
 contrib/intarray/Makefile              |   4 +-
 contrib/intarray/_int.h                |  38 ++-
 contrib/intarray/_int_bool.c           |  34 +--
 contrib/intarray/_int_gist.c           |  31 +-
 contrib/intarray/_int_tool.c           |   4 +-
 contrib/intarray/_intbig_gist.c        | 200 +++++++------
 contrib/intarray/intarray--1.2-1.3.sql |  34 +++
 contrib/intarray/intarray--1.2.sql     | 520 --------------------------------
 contrib/intarray/intarray--1.3.sql     | 531 +++++++++++++++++++++++++++++++++
 contrib/intarray/intarray.control      |   2 +-
 doc/src/sgml/intarray.sgml             |  14 +-
 11 files changed, 758 insertions(+), 654 deletions(-)
 create mode 100644 contrib/intarray/intarray--1.2-1.3.sql
 delete mode 100644 contrib/intarray/intarray--1.2.sql
 create mode 100644 contrib/intarray/intarray--1.3.sql

diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile
index 2505294..d99d977 100644
--- a/contrib/intarray/Makefile
+++ b/contrib/intarray/Makefile
@@ -5,8 +5,8 @@ OBJS = _int_bool.o _int_gist.o _int_op.o _int_tool.o \
 	_intbig_gist.o _int_gin.o _int_selfuncs.o $(WIN32RES)
 
 EXTENSION = intarray
-DATA = intarray--1.2.sql intarray--1.1--1.2.sql intarray--1.0--1.1.sql \
-	intarray--unpackaged--1.0.sql
+DATA = intarray--1.3.sql intarray--1.2-1.3.sql intarray--1.1--1.2.sql \
+	intarray--1.0--1.1.sql intarray--unpackaged--1.0.sql
 PGFILEDESC = "intarray - functions and operators for arrays of integers"
 
 REGRESS = _int
diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index b689eb7..87d21e9 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -8,7 +8,15 @@
 #include "utils/memutils.h"
 
 /* number ranges for compression */
-#define MAXNUMRANGE 100
+#define G_INT_NUMRANGES_DEFAULT		100
+#define G_INT_NUMRANGES_MAX			1000
+
+/* gist_int_ops opclass options */
+typedef struct IntArrayOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			num_ranges;		/* number of ranges */
+}	IntArrayOptions;
 
 /* useful macros for accessing int4 arrays */
 #define ARRPTR(x)  ( (int32 *) ARR_DATA_PTR(x) )
@@ -47,15 +55,14 @@
 
 
 /* bigint defines */
-#define SIGLENINT  63			/* >122 => key will toast, so very slow!!! */
-#define SIGLEN	( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
+#define SIGLEN_DEFAULT		(63 * 4)
+#define SIGLEN_MAX			(122 * 4)		/* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen)	((siglen) * BITS_PER_BYTE)
 
-typedef char BITVEC[SIGLEN];
 typedef char *BITVECP;
 
-#define LOOPBYTE \
-			for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+			for (i = 0; i < siglen; i++)
 
 /* beware of multiple evaluation of arguments to these macros! */
 #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
@@ -63,8 +70,15 @@ typedef char *BITVECP;
 #define CLRBIT(x,i)   GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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))
+
+/* gist_intbig_ops opclass options */
+typedef struct IntArrayBigOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+}	IntArrayBigOptions;
 
 /*
  * type of index key
@@ -81,7 +95,7 @@ typedef struct
 #define ISALLTRUE(x)	( ((GISTTYPE*)x)->flag & ALLISTRUE )
 
 #define GTHDRSIZE		(VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
 
 #define GETSIGN(x)		( (BITVECP)( (char*)x+GTHDRSIZE ) )
 
@@ -109,7 +123,7 @@ bool		inner_int_contains(ArrayType *a, ArrayType *b);
 ArrayType  *inner_int_union(ArrayType *a, ArrayType *b);
 ArrayType  *inner_int_inter(ArrayType *a, ArrayType *b);
 void		rt__int_size(ArrayType *a, float *size);
-void		gensign(BITVEC sign, int *a, int len);
+void		gensign(BITVECP sign, int *a, int len, int siglen);
 
 
 /*****************************************************************************
@@ -155,7 +169,7 @@ typedef struct QUERYTYPE
 #define PG_GETARG_QUERYTYPE_P(n)	  DatumGetQueryTypeP(PG_GETARG_DATUM(n))
 #define PG_GETARG_QUERYTYPE_P_COPY(n) DatumGetQueryTypePCopy(PG_GETARG_DATUM(n))
 
-bool		signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot);
+bool		signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot);
 bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index 91e2a80..4e7d55a 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -233,7 +233,7 @@ typedef struct
  * is there value 'val' in (sorted) array or not ?
  */
 static bool
-checkcondition_arr(void *checkval, ITEM *item)
+checkcondition_arr(void *checkval, ITEM *item, void *options)
 {
 	int32	   *StopLow = ((CHKVAL *) checkval)->arrb;
 	int32	   *StopHigh = ((CHKVAL *) checkval)->arre;
@@ -255,42 +255,42 @@ checkcondition_arr(void *checkval, ITEM *item)
 }
 
 static bool
-checkcondition_bit(void *checkval, ITEM *item)
+checkcondition_bit(void *checkval, ITEM *item, void *siglen)
 {
-	return GETBIT(checkval, HASHVAL(item->val));
+	return GETBIT(checkval, HASHVAL(item->val, (int)(intptr_t) siglen));
 }
 
 /*
  * evaluate boolean expression, using chkcond() to test the primitive cases
  */
 static bool
-execute(ITEM *curitem, void *checkval, bool calcnot,
-		bool (*chkcond) (void *checkval, ITEM *item))
+execute(ITEM *curitem, void *checkval, void *options, bool calcnot,
+		bool (*chkcond) (void *checkval, ITEM *item, void *options))
 {
 	/* since this function recurses, it could be driven to stack overflow */
 	check_stack_depth();
 
 	if (curitem->type == VAL)
-		return (*chkcond) (checkval, curitem);
+		return (*chkcond) (checkval, curitem, options);
 	else if (curitem->val == (int32) '!')
 	{
 		return calcnot ?
-			((execute(curitem - 1, checkval, calcnot, chkcond)) ? false : true)
+			((execute(curitem - 1, checkval, options, calcnot, chkcond)) ? false : true)
 			: true;
 	}
 	else if (curitem->val == (int32) '&')
 	{
-		if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
-			return execute(curitem - 1, checkval, calcnot, chkcond);
+		if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
+			return execute(curitem - 1, checkval, options, calcnot, chkcond);
 		else
 			return false;
 	}
 	else
 	{							/* |-operator */
-		if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
+		if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
 			return true;
 		else
-			return execute(curitem - 1, checkval, calcnot, chkcond);
+			return execute(curitem - 1, checkval, options, calcnot, chkcond);
 	}
 }
 
@@ -298,10 +298,10 @@ execute(ITEM *curitem, void *checkval, bool calcnot,
  * signconsistent & execconsistent called by *_consistent
  */
 bool
-signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot)
+signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot)
 {
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) sign, calcnot,
+				   (void *) sign, (void *)(intptr_t) siglen, calcnot,
 				   checkcondition_bit);
 }
 
@@ -315,7 +315,7 @@ execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot)
 	chkval.arrb = ARRPTR(array);
 	chkval.arre = chkval.arrb + ARRNELEMS(array);
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) &chkval, calcnot,
+				   (void *) &chkval, NULL, calcnot,
 				   checkcondition_arr);
 }
 
@@ -326,7 +326,7 @@ typedef struct
 } GinChkVal;
 
 static bool
-checkcondition_gin(void *checkval, ITEM *item)
+checkcondition_gin(void *checkval, ITEM *item, void *options)
 {
 	GinChkVal  *gcv = (GinChkVal *) checkval;
 
@@ -357,7 +357,7 @@ gin_bool_consistent(QUERYTYPE *query, bool *check)
 	}
 
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) &gcv, true,
+				   (void *) &gcv, NULL, true,
 				   checkcondition_gin);
 }
 
@@ -429,7 +429,7 @@ boolop(PG_FUNCTION_ARGS)
 	chkval.arrb = ARRPTR(val);
 	chkval.arre = chkval.arrb + ARRNELEMS(val);
 	result = execute(GETQUERY(query) + query->size - 1,
-					 &chkval, true,
+					 &chkval, NULL, true,
 					 checkcondition_arr);
 	pfree(val);
 
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index 911d180..9de7f55 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -6,6 +6,7 @@
 #include <limits.h>
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 
 #include "_int.h"
@@ -22,6 +23,7 @@ PG_FUNCTION_INFO_V1(g_int_penalty);
 PG_FUNCTION_INFO_V1(g_int_picksplit);
 PG_FUNCTION_INFO_V1(g_int_union);
 PG_FUNCTION_INFO_V1(g_int_same);
+PG_FUNCTION_INFO_V1(g_int_options);
 
 
 /*
@@ -139,6 +141,7 @@ Datum
 g_int_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
 	GISTENTRY  *retval;
 	ArrayType  *r;
 	int			len;
@@ -153,9 +156,9 @@ g_int_compress(PG_FUNCTION_ARGS)
 		CHECKARRVALID(r);
 		PREPAREARR(r);
 
-		if (ARRNELEMS(r) >= 2 * MAXNUMRANGE)
+		if (ARRNELEMS(r) >= 2 * opts->num_ranges)
 			elog(NOTICE, "input array is too big (%d maximum allowed, %d current), use gist__intbig_ops opclass instead",
-				 2 * MAXNUMRANGE - 1, ARRNELEMS(r));
+				 2 * opts->num_ranges - 1, ARRNELEMS(r));
 
 		retval = palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(r),
@@ -178,7 +181,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 		PG_RETURN_POINTER(entry);
 	}
 
-	if ((len = ARRNELEMS(r)) >= 2 * MAXNUMRANGE)
+	if ((len = ARRNELEMS(r)) >= 2 * opts->num_ranges)
 	{							/* compress */
 		if (r == (ArrayType *) DatumGetPointer(entry->key))
 			r = DatumGetArrayTypePCopy(entry->key);
@@ -191,7 +194,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 
 		len *= 2;
 		cand = 1;
-		while (len > MAXNUMRANGE * 2)
+		while (len > opts->num_ranges * 2)
 		{
 			min = INT_MAX;
 			for (i = 2; i < len; i += 2)
@@ -217,6 +220,7 @@ Datum
 g_int_decompress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
 	GISTENTRY  *retval;
 	ArrayType  *r;
 	int		   *dr,
@@ -245,7 +249,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
 
 	lenin = ARRNELEMS(in);
 
-	if (lenin < 2 * MAXNUMRANGE)
+	if (lenin < 2 * opts->num_ranges)
 	{							/* not compressed value */
 		if (in != (ArrayType *) DatumGetPointer(entry->key))
 		{
@@ -542,3 +546,20 @@ g_int_picksplit(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(v);
 }
+
+Datum
+g_int_options(PG_FUNCTION_ARGS)
+{
+	Datum		raw_options = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+	relopt_int	siglen =
+		{ {"numranges", "number of ranges for compression", 0, 0, 9, RELOPT_TYPE_INT },
+			G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(IntArrayOptions, num_ranges) };
+	IntArrayOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(IntArrayOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index d86485d..9bec6b9 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -324,14 +324,14 @@ _int_unique(ArrayType *r)
 }
 
 void
-gensign(BITVEC sign, int *a, int len)
+gensign(BITVECP sign, int *a, int len, int siglen)
 {
 	int			i;
 
 	/* we assume that the sign vector is previously zeroed */
 	for (i = 0; i < len; i++)
 	{
-		HASH(sign, *a);
+		HASH(sign, *a, siglen);
 		a++;
 	}
 }
diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c
index de7bc82..4d2b3a2 100644
--- a/contrib/intarray/_intbig_gist.c
+++ b/contrib/intarray/_intbig_gist.c
@@ -4,6 +4,7 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 
 #include "_int.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(g_intbig_penalty);
 PG_FUNCTION_INFO_V1(g_intbig_picksplit);
 PG_FUNCTION_INFO_V1(g_intbig_union);
 PG_FUNCTION_INFO_V1(g_intbig_same);
+PG_FUNCTION_INFO_V1(g_intbig_options);
 
 /* Number of one-bits in an unsigned byte */
 static const uint8 number_of_ones[256] = {
@@ -61,12 +63,33 @@ _intbig_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+static GISTTYPE *
+_intbig_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+	int			flag = allistrue ? ALLISTRUE : 0;
+	int			size = CALCGTSIZE(flag, siglen);
+	GISTTYPE   *res = (GISTTYPE *) palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if (!allistrue)
+	{
+		if (sign)
+			memcpy(GETSIGN(res), sign, siglen);
+		else
+			memset(GETSIGN(res), 0, siglen);
+	}
+
+	return res;
+}
+
 
 /*********************************************************************
 ** intbig functions
 *********************************************************************/
 static bool
-_intbig_overlap(GISTTYPE *a, ArrayType *b)
+_intbig_overlap(GISTTYPE *a, ArrayType *b, int siglen)
 {
 	int			num = ARRNELEMS(b);
 	int32	   *ptr = ARRPTR(b);
@@ -75,7 +98,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
 
 	while (num--)
 	{
-		if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+		if (GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
 			return true;
 		ptr++;
 	}
@@ -84,7 +107,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
 }
 
 static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglen)
 {
 	int			num = ARRNELEMS(b);
 	int32	   *ptr = ARRPTR(b);
@@ -93,7 +116,7 @@ _intbig_contains(GISTTYPE *a, ArrayType *b)
 
 	while (num--)
 	{
-		if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+		if (!GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
 			return false;
 		ptr++;
 	}
@@ -107,6 +130,8 @@ g_intbig_same(PG_FUNCTION_ARGS)
 	GISTTYPE   *a = (GISTTYPE *) PG_GETARG_POINTER(0);
 	GISTTYPE   *b = (GISTTYPE *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+	int			siglen = opts->siglen;
 
 	if (ISALLTRUE(a) && ISALLTRUE(b))
 		*result = true;
@@ -121,7 +146,7 @@ g_intbig_same(PG_FUNCTION_ARGS)
 					sb = GETSIGN(b);
 
 		*result = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -137,6 +162,8 @@ Datum
 g_intbig_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(1);
+	int			siglen = opts->siglen;
 
 	if (entry->leafkey)
 	{
@@ -144,7 +171,7 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 		ArrayType  *in = DatumGetArrayTypeP(entry->key);
 		int32	   *ptr;
 		int			num;
-		GISTTYPE   *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		GISTTYPE   *res = _intbig_alloc(false, siglen, NULL);
 
 		CHECKARRVALID(in);
 		if (ARRISEMPTY(in))
@@ -157,11 +184,10 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 			ptr = ARRPTR(in);
 			num = ARRNELEMS(in);
 		}
-		SET_VARSIZE(res, CALCGTSIZE(0));
 
 		while (num--)
 		{
-			HASH(GETSIGN(res), *ptr);
+			HASH(GETSIGN(res), *ptr, siglen);
 			ptr++;
 		}
 
@@ -182,16 +208,13 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 		BITVECP		sign = GETSIGN(DatumGetPointer(entry->key));
 		GISTTYPE   *res;
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(entry);
 		}
 
-		res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
-		SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
-		res->flag = ALLISTRUE;
-
+		res = _intbig_alloc(true, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -205,24 +228,24 @@ g_intbig_compress(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];
@@ -231,19 +254,19 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *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
@@ -253,14 +276,14 @@ g_intbig_decompress(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = GETSIGN(add);
 
 	if (ISALLTRUE(add))
 		return 1;
-	LOOPBYTE
+	LOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -270,29 +293,23 @@ g_intbig_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	GISTTYPE   *result;
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+	int			siglen = opts->siglen;
+	int32		i;
+	GISTTYPE   *result = _intbig_alloc(false, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; 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;
 		}
 	}
 
-	len = CALCGTSIZE(flag);
-	result = (GISTTYPE *) 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);
 }
@@ -305,8 +322,10 @@ g_intbig_penalty(PG_FUNCTION_ARGS)
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
 	GISTTYPE   *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
 	GISTTYPE   *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+	int			siglen = opts->siglen;
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -329,6 +348,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+	int			siglen = opts->siglen;
 	OffsetNumber k,
 				j;
 	GISTTYPE   *datum_l,
@@ -361,7 +382,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -383,32 +404,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_l, GTHDRSIZE);
-		datum_l->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC));
-	}
-	if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_r, GTHDRSIZE);
-		datum_r->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
-	}
+	datum_l = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_1)));
+	datum_r = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_2)));
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -417,8 +416,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = Abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -442,20 +441,20 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
 		{
 			if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -466,12 +465,12 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -497,6 +496,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(5);
+	int			siglen = opts->siglen;
 	bool		retval;
 
 	/* All cases served by this function are inexact */
@@ -509,6 +510,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	{
 		retval = signconsistent((QUERYTYPE *) query,
 								GETSIGN(DatumGetPointer(entry->key)),
+								siglen,
 								false);
 		PG_FREE_IF_COPY(query, 1);
 		PG_RETURN_BOOL(retval);
@@ -519,7 +521,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	switch (strategy)
 	{
 		case RTOverlapStrategyNumber:
-			retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+			retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+									 query, siglen);
 			break;
 		case RTSameStrategyNumber:
 			if (GIST_LEAF(entry))
@@ -527,22 +530,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				int			i,
 							num = ARRNELEMS(query);
 				int32	   *ptr = ARRPTR(query);
-				BITVEC		qp;
-				BITVECP		dq,
+				BITVECP		dq = palloc0(siglen),
 							de;
 
-				memset(qp, 0, sizeof(BITVEC));
-
 				while (num--)
 				{
-					HASH(qp, *ptr);
+					HASH(dq, *ptr, siglen);
 					ptr++;
 				}
 
 				de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
-				dq = qp;
 				retval = true;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 				{
 					if (de[i] != dq[i])
 					{
@@ -551,13 +550,16 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 					}
 				}
 
+				pfree(dq);
 			}
 			else
-				retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+				retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+										  query, siglen);
 			break;
 		case RTContainsStrategyNumber:
 		case RTOldContainsStrategyNumber:
-			retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+			retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+									  query, siglen);
 			break;
 		case RTContainedByStrategyNumber:
 		case RTOldContainedByStrategyNumber:
@@ -566,22 +568,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				int			i,
 							num = ARRNELEMS(query);
 				int32	   *ptr = ARRPTR(query);
-				BITVEC		qp;
-				BITVECP		dq,
+				BITVECP		dq = palloc0(siglen),
 							de;
 
-				memset(qp, 0, sizeof(BITVEC));
-
 				while (num--)
 				{
-					HASH(qp, *ptr);
+					HASH(dq, *ptr, siglen);
 					ptr++;
 				}
 
 				de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
-				dq = qp;
 				retval = true;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 				{
 					if (de[i] & ~dq[i])
 					{
@@ -591,7 +589,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				}
 			}
 			else
-				retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+				retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+										 query, siglen);
 			break;
 		default:
 			retval = false;
@@ -599,3 +598,20 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(retval);
 }
+
+Datum
+g_intbig_options(PG_FUNCTION_ARGS)
+{
+	Datum		raw_options = PG_GETARG_DATUM(0);
+	bool		validate = PG_GETARG_BOOL(1);
+	relopt_int	siglen =
+		{ {"siglen", "signature length", 0, NoLock, 6, RELOPT_TYPE_INT },
+			SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(IntArrayBigOptions, siglen) };
+	IntArrayBigOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(IntArrayBigOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/intarray--1.2-1.3.sql b/contrib/intarray/intarray--1.2-1.3.sql
new file mode 100644
index 0000000..c9b83ef
--- /dev/null
+++ b/contrib/intarray/intarray--1.2-1.3.sql
@@ -0,0 +1,34 @@
+/* contrib/intarray/intarray--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.3'" 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
+('g_int_compress(internal)', '{internal,internal}'),
+('g_int_decompress(internal)', '{internal,internal}'),
+('g_intbig_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('g_intbig_compress(internal)', '{internal,internal}'),
+('g_intbig_decompress(internal)', '{internal,internal}'),
+('g_intbig_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('g_intbig_picksplit(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_union(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_same(intbig_gkey,intbig_gkey,internal)', '{intbig_gkey,intbig_gkey,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist__intbig_ops USING gist
+ADD FUNCTION 10 (_int4) g_intbig_options (internal, boolean);
diff --git a/contrib/intarray/intarray--1.2.sql b/contrib/intarray/intarray--1.2.sql
deleted file mode 100644
index f10b53d..0000000
--- a/contrib/intarray/intarray--1.2.sql
+++ /dev/null
@@ -1,520 +0,0 @@
-/* contrib/intarray/intarray--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION intarray" to load this file. \quit
-
---
--- Create the user-defined type for the 1-D integer arrays (_int4)
---
-
--- Query type
-CREATE FUNCTION bqarr_in(cstring)
-RETURNS query_int
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION bqarr_out(query_int)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE query_int (
-	INTERNALLENGTH = -1,
-	INPUT = bqarr_in,
-	OUTPUT = bqarr_out
-);
-
---only for debug
-CREATE FUNCTION querytree(query_int)
-RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-
-CREATE FUNCTION boolop(_int4, query_int)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
-
-CREATE FUNCTION rboolop(query_int, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
-
-CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE OPERATOR @@ (
-	LEFTARG = _int4,
-	RIGHTARG = query_int,
-	PROCEDURE = boolop,
-	COMMUTATOR = '~~',
-	RESTRICT = _int_matchsel,
-	JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~~ (
-	LEFTARG = query_int,
-	RIGHTARG = _int4,
-	PROCEDURE = rboolop,
-	COMMUTATOR = '@@',
-	RESTRICT = _int_matchsel,
-	JOIN = contjoinsel
-);
-
-
---
--- External C-functions for R-tree methods
---
-
--- Comparison methods
-
-CREATE FUNCTION _int_contains(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
-
-CREATE FUNCTION _int_contained(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
-
-CREATE FUNCTION _int_overlap(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
-
-CREATE FUNCTION _int_same(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
-
-CREATE FUNCTION _int_different(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
-
--- support routines for indexing
-
-CREATE FUNCTION _int_union(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_inter(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
---
--- OPERATORS
---
-
-CREATE OPERATOR && (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_overlap,
-	COMMUTATOR = '&&',
-	RESTRICT = _int_overlap_sel,
-	JOIN = _int_overlap_joinsel
-);
-
---CREATE OPERATOR = (
---	LEFTARG = _int4,
---	RIGHTARG = _int4,
---	PROCEDURE = _int_same,
---	COMMUTATOR = '=',
---	NEGATOR = '<>',
---	RESTRICT = eqsel,
---	JOIN = eqjoinsel,
---	SORT1 = '<',
---	SORT2 = '<'
---);
-
---CREATE OPERATOR <> (
---	LEFTARG = _int4,
---	RIGHTARG = _int4,
---	PROCEDURE = _int_different,
---	COMMUTATOR = '<>',
---	NEGATOR = '=',
---	RESTRICT = neqsel,
---	JOIN = neqjoinsel
---);
-
-CREATE OPERATOR @> (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contains,
-	COMMUTATOR = '<@',
-	RESTRICT = _int_contains_sel,
-	JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR <@ (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contained,
-	COMMUTATOR = '@>',
-	RESTRICT = _int_contained_sel,
-	JOIN = _int_contained_joinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contains,
-	COMMUTATOR = '~',
-	RESTRICT = _int_contains_sel,
-	JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR ~ (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = _int_contained,
-	COMMUTATOR = '@',
-	RESTRICT = _int_contained_sel,
-	JOIN = _int_contained_joinsel
-);
-
---------------
-CREATE FUNCTION intset(int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION icount(_int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
-	RIGHTARG = _int4,
-	PROCEDURE = icount
-);
-
-CREATE FUNCTION sort(_int4, text)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_asc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_desc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION uniq(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION idx(_int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = idx
-);
-
-CREATE FUNCTION subarray(_int4, int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION subarray(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION intarray_push_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = intarray_push_elem
-);
-
-CREATE FUNCTION intarray_push_array(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	COMMUTATOR = +,
-	PROCEDURE = intarray_push_array
-);
-
-CREATE FUNCTION intarray_del_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = intarray_del_elem
-);
-
-CREATE FUNCTION intset_union_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR | (
-	LEFTARG = _int4,
-	RIGHTARG = int4,
-	PROCEDURE = intset_union_elem
-);
-
-CREATE OPERATOR | (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	COMMUTATOR = |,
-	PROCEDURE = _int_union
-);
-
-CREATE FUNCTION intset_subtract(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	PROCEDURE = intset_subtract
-);
-
-CREATE OPERATOR & (
-	LEFTARG = _int4,
-	RIGHTARG = _int4,
-	COMMUTATOR = &,
-	PROCEDURE = _int_inter
-);
---------------
-
--- define the GiST support methods
-CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_union(internal, internal)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_same(_int4, _int4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-
--- Create the operator class for indexing
-
-CREATE OPERATOR CLASS gist__int_ops
-DEFAULT FOR TYPE _int4 USING gist AS
-	OPERATOR	3	&&,
-	OPERATOR	6	= (anyarray, anyarray),
-	OPERATOR	7	@>,
-	OPERATOR	8	<@,
-	OPERATOR	13	@,
-	OPERATOR	14	~,
-	OPERATOR	20	@@ (_int4, query_int),
-	FUNCTION	1	g_int_consistent (internal, _int4, smallint, oid, internal),
-	FUNCTION	2	g_int_union (internal, internal),
-	FUNCTION	3	g_int_compress (internal),
-	FUNCTION	4	g_int_decompress (internal),
-	FUNCTION	5	g_int_penalty (internal, internal, internal),
-	FUNCTION	6	g_int_picksplit (internal, internal),
-	FUNCTION	7	g_int_same (_int4, _int4, internal);
-
-
----------------------------------------------
--- intbig
----------------------------------------------
--- define the GiST support methods
-
-CREATE FUNCTION _intbig_in(cstring)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _intbig_out(intbig_gkey)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE intbig_gkey (
-        INTERNALLENGTH = -1,
-        INPUT = _intbig_in,
-        OUTPUT = _intbig_out
-);
-
-CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_union(internal, internal)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
--- register the opclass for indexing (not as default)
-
-CREATE OPERATOR CLASS gist__intbig_ops
-FOR TYPE _int4 USING gist
-AS
-	OPERATOR	3	&&,
-	OPERATOR	6	= (anyarray, anyarray),
-	OPERATOR	7	@>,
-	OPERATOR	8	<@,
-	OPERATOR	13	@,
-	OPERATOR	14	~,
-	OPERATOR	20	@@ (_int4, query_int),
-	FUNCTION	1	g_intbig_consistent (internal, _int4, smallint, oid, internal),
-	FUNCTION	2	g_intbig_union (internal, internal),
-	FUNCTION	3	g_intbig_compress (internal),
-	FUNCTION	4	g_intbig_decompress (internal),
-	FUNCTION	5	g_intbig_penalty (internal, internal, internal),
-	FUNCTION	6	g_intbig_picksplit (internal, internal),
-	FUNCTION	7	g_intbig_same (intbig_gkey, intbig_gkey, internal),
-	STORAGE		intbig_gkey;
-
---GIN
-
-CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE OPERATOR CLASS gin__int_ops
-FOR TYPE _int4 USING gin
-AS
-	OPERATOR	3	&&,
-	OPERATOR	6	= (anyarray, anyarray),
-	OPERATOR	7	@>,
-	OPERATOR	8	<@,
-	OPERATOR	13	@,
-	OPERATOR	14	~,
-	OPERATOR	20	@@ (_int4, query_int),
-	FUNCTION	1	btint4cmp (int4, int4),
-	FUNCTION	2	ginarrayextract (anyarray, internal, internal),
-	FUNCTION	3	ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
-	FUNCTION	4	ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
-	STORAGE		int4;
diff --git a/contrib/intarray/intarray--1.3.sql b/contrib/intarray/intarray--1.3.sql
new file mode 100644
index 0000000..6c91c75
--- /dev/null
+++ b/contrib/intarray/intarray--1.3.sql
@@ -0,0 +1,531 @@
+/* contrib/intarray/intarray--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION intarray" to load this file. \quit
+
+--
+-- Create the user-defined type for the 1-D integer arrays (_int4)
+--
+
+-- Query type
+CREATE FUNCTION bqarr_in(cstring)
+RETURNS query_int
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION bqarr_out(query_int)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE query_int (
+	INTERNALLENGTH = -1,
+	INPUT = bqarr_in,
+	OUTPUT = bqarr_out
+);
+
+--only for debug
+CREATE FUNCTION querytree(query_int)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE FUNCTION boolop(_int4, query_int)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
+
+CREATE FUNCTION rboolop(query_int, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
+
+CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE OPERATOR @@ (
+	LEFTARG = _int4,
+	RIGHTARG = query_int,
+	PROCEDURE = boolop,
+	COMMUTATOR = '~~',
+	RESTRICT = _int_matchsel,
+	JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~~ (
+	LEFTARG = query_int,
+	RIGHTARG = _int4,
+	PROCEDURE = rboolop,
+	COMMUTATOR = '@@',
+	RESTRICT = _int_matchsel,
+	JOIN = contjoinsel
+);
+
+
+--
+-- External C-functions for R-tree methods
+--
+
+-- Comparison methods
+
+CREATE FUNCTION _int_contains(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
+
+CREATE FUNCTION _int_contained(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
+
+CREATE FUNCTION _int_overlap(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
+
+CREATE FUNCTION _int_same(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
+
+CREATE FUNCTION _int_different(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
+
+-- support routines for indexing
+
+CREATE FUNCTION _int_union(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_inter(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+--
+-- OPERATORS
+--
+
+CREATE OPERATOR && (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_overlap,
+	COMMUTATOR = '&&',
+	RESTRICT = _int_overlap_sel,
+	JOIN = _int_overlap_joinsel
+);
+
+--CREATE OPERATOR = (
+--	LEFTARG = _int4,
+--	RIGHTARG = _int4,
+--	PROCEDURE = _int_same,
+--	COMMUTATOR = '=',
+--	NEGATOR = '<>',
+--	RESTRICT = eqsel,
+--	JOIN = eqjoinsel,
+--	SORT1 = '<',
+--	SORT2 = '<'
+--);
+
+--CREATE OPERATOR <> (
+--	LEFTARG = _int4,
+--	RIGHTARG = _int4,
+--	PROCEDURE = _int_different,
+--	COMMUTATOR = '<>',
+--	NEGATOR = '=',
+--	RESTRICT = neqsel,
+--	JOIN = neqjoinsel
+--);
+
+CREATE OPERATOR @> (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contains,
+	COMMUTATOR = '<@',
+	RESTRICT = _int_contains_sel,
+	JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR <@ (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contained,
+	COMMUTATOR = '@>',
+	RESTRICT = _int_contained_sel,
+	JOIN = _int_contained_joinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contains,
+	COMMUTATOR = '~',
+	RESTRICT = _int_contains_sel,
+	JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR ~ (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = _int_contained,
+	COMMUTATOR = '@',
+	RESTRICT = _int_contained_sel,
+	JOIN = _int_contained_joinsel
+);
+
+--------------
+CREATE FUNCTION intset(int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION icount(_int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+	RIGHTARG = _int4,
+	PROCEDURE = icount
+);
+
+CREATE FUNCTION sort(_int4, text)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_asc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_desc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION uniq(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION idx(_int4, int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = idx
+);
+
+CREATE FUNCTION subarray(_int4, int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subarray(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION intarray_push_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = intarray_push_elem
+);
+
+CREATE FUNCTION intarray_push_array(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	COMMUTATOR = +,
+	PROCEDURE = intarray_push_array
+);
+
+CREATE FUNCTION intarray_del_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = intarray_del_elem
+);
+
+CREATE FUNCTION intset_union_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR | (
+	LEFTARG = _int4,
+	RIGHTARG = int4,
+	PROCEDURE = intset_union_elem
+);
+
+CREATE OPERATOR | (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	COMMUTATOR = |,
+	PROCEDURE = _int_union
+);
+
+CREATE FUNCTION intset_subtract(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	PROCEDURE = intset_subtract
+);
+
+CREATE OPERATOR & (
+	LEFTARG = _int4,
+	RIGHTARG = _int4,
+	COMMUTATOR = &,
+	PROCEDURE = _int_inter
+);
+--------------
+
+-- define the GiST support methods
+CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_compress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_decompress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_union(internal, internal)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_same(_int4, _int4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_int_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- Create the operator class for indexing
+
+CREATE OPERATOR CLASS gist__int_ops
+DEFAULT FOR TYPE _int4 USING gist AS
+	OPERATOR	3	&&,
+	OPERATOR	6	= (anyarray, anyarray),
+	OPERATOR	7	@>,
+	OPERATOR	8	<@,
+	OPERATOR	13	@,
+	OPERATOR	14	~,
+	OPERATOR	20	@@ (_int4, query_int),
+	FUNCTION	1	g_int_consistent (internal, _int4, smallint, oid, internal),
+	FUNCTION	2	g_int_union (internal, internal),
+	FUNCTION	3	g_int_compress (internal, internal),
+	FUNCTION	4	g_int_decompress (internal, internal),
+	FUNCTION	5	g_int_penalty (internal, internal, internal),
+	FUNCTION	6	g_int_picksplit (internal, internal),
+	FUNCTION	7	g_int_same (_int4, _int4, internal),
+	FUNCTION	10	g_int_options (internal, boolean);
+
+
+---------------------------------------------
+-- intbig
+---------------------------------------------
+-- define the GiST support methods
+
+CREATE FUNCTION _intbig_in(cstring)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _intbig_out(intbig_gkey)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE intbig_gkey (
+        INTERNALLENGTH = -1,
+        INPUT = _intbig_in,
+        OUTPUT = _intbig_out
+);
+
+CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_compress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_decompress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_penalty(internal,internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_picksplit(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_union(internal, internal, internal)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- register the opclass for indexing (not as default)
+
+CREATE OPERATOR CLASS gist__intbig_ops
+FOR TYPE _int4 USING gist
+AS
+	OPERATOR	3	&&,
+	OPERATOR	6	= (anyarray, anyarray),
+	OPERATOR	7	@>,
+	OPERATOR	8	<@,
+	OPERATOR	13	@,
+	OPERATOR	14	~,
+	OPERATOR	20	@@ (_int4, query_int),
+	FUNCTION	1	g_intbig_consistent (internal, _int4, smallint, oid, internal, internal),
+	FUNCTION	2	g_intbig_union (internal, internal, internal),
+	FUNCTION	3	g_intbig_compress (internal, internal),
+	FUNCTION	4	g_intbig_decompress (internal, internal),
+	FUNCTION	5	g_intbig_penalty (internal, internal, internal, internal),
+	FUNCTION	6	g_intbig_picksplit (internal, internal, internal),
+	FUNCTION	7	g_intbig_same (intbig_gkey, intbig_gkey, internal, internal),
+	FUNCTION	10	g_intbig_options (internal, boolean),
+	STORAGE		intbig_gkey;
+
+--GIN
+
+CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gin__int_ops
+FOR TYPE _int4 USING gin
+AS
+	OPERATOR	3	&&,
+	OPERATOR	6	= (anyarray, anyarray),
+	OPERATOR	7	@>,
+	OPERATOR	8	<@,
+	OPERATOR	13	@,
+	OPERATOR	14	~,
+	OPERATOR	20	@@ (_int4, query_int),
+	FUNCTION	1	btint4cmp (int4, int4),
+	FUNCTION	2	ginarrayextract (anyarray, internal, internal),
+	FUNCTION	3	ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
+	FUNCTION	4	ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
+	STORAGE		int4;
diff --git a/contrib/intarray/intarray.control b/contrib/intarray/intarray.control
index 7e50cc3..72952f7 100644
--- a/contrib/intarray/intarray.control
+++ b/contrib/intarray/intarray.control
@@ -1,5 +1,5 @@
 # intarray extension
 comment = 'functions, operators, and index support for 1-D arrays of integers'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/_int'
 relocatable = true
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index b633cf3..2540cc6 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -259,14 +259,22 @@
   </para>
 
   <para>
-   Two GiST index operator classes are provided:
-   <literal>gist__int_ops</literal> (used by default) is suitable for
+   Two parametrized GiST index operator classes are provided:
+   <literal>gist__int_ops(numranges)</literal> (used by default) is suitable for
    small- to medium-size data sets, while
-   <literal>gist__intbig_ops</literal> uses a larger signature and is more
+   <literal>gist__intbig_ops(siglen)</literal> uses a larger signature and is more
    suitable for indexing large data sets (i.e., columns containing
    a large number of distinct array values).
    The implementation uses an RD-tree data structure with
    built-in lossy compression.
+   
+   Optional integer parameter <literal>numranges</literal> of 
+   <literal>gist__int_ops</literal> determines 
+   number of ranges in compressed array (100 by default).
+   
+   Optional integer parameter <literal>siglen</literal> of 
+   <literal>gist__intbig_ops</literal> determines
+   signature length in bytes (252 by default, 488 maximum).
   </para>
 
   <para>
-- 
2.7.4

0006-Add-opclass-parameters-to-contrib-ltree-v02.patchtext/x-patch; name=0006-Add-opclass-parameters-to-contrib-ltree-v02.patchDownload
From 5a69a859c8d066ae73394e67a13b9f535534c1dd Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 2 Feb 2018 03:36:13 +0300
Subject: [PATCH 6/9] Add opclass parameters to contrib/ltree

---
 contrib/ltree/Makefile            |   2 +-
 contrib/ltree/_ltree_gist.c       | 192 ++++++++++++++-------------
 contrib/ltree/ltree--1.1--1.2.sql |  47 +++++++
 contrib/ltree/ltree.control       |   2 +-
 contrib/ltree/ltree.h             |  51 +++++---
 contrib/ltree/ltree_gist.c        | 265 +++++++++++++++++++++-----------------
 doc/src/sgml/ltree.sgml           |  16 ++-
 7 files changed, 343 insertions(+), 232 deletions(-)
 create mode 100644 contrib/ltree/ltree--1.1--1.2.sql

diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index 416c8da..af031b3 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -6,7 +6,7 @@ OBJS = 	ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
 PG_CPPFLAGS = -DLOWER_NODE
 
 EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.1--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
 PGFILEDESC = "ltree - hierarchical label data type"
 
 HEADERS = ltree.h
diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c
index 28bf7ad..394f7ad 100644
--- a/contrib/ltree/_ltree_gist.c
+++ b/contrib/ltree/_ltree_gist.c
@@ -8,6 +8,7 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "crc32.h"
 #include "ltree.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(_ltree_union);
 PG_FUNCTION_INFO_V1(_ltree_penalty);
 PG_FUNCTION_INFO_V1(_ltree_picksplit);
 PG_FUNCTION_INFO_V1(_ltree_consistent);
+PG_FUNCTION_INFO_V1(_ltree_gist_options);
 
 #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
 #define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
@@ -47,7 +49,7 @@ static const uint8 number_of_ones[256] = {
 
 
 static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
 {
 	int			tlen = t->numlevel;
 	ltree_level *cur = LTREE_FIRST(t);
@@ -56,7 +58,7 @@ hashing(BITVECP sign, ltree *t)
 	while (tlen > 0)
 	{
 		hash = ltree_crc32_sz(cur->name, cur->len);
-		AHASH(sign, hash);
+		AHASH(sign, hash, siglen);
 		cur = LEVEL_NEXT(cur);
 		tlen--;
 	}
@@ -67,12 +69,13 @@ _ltree_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 	GISTENTRY  *retval = entry;
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(1);
+	int			siglen = options->siglen;
 
 	if (entry->leafkey)
 	{							/* ltree */
 		ltree_gist *key;
 		ArrayType  *val = DatumGetArrayTypeP(entry->key);
-		int32		len = LTG_HDRSIZE + ASIGLEN;
 		int			num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
 		ltree	   *item = (ltree *) ARR_DATA_PTR(val);
 
@@ -85,14 +88,11 @@ _ltree_compress(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 					 errmsg("array must not contain nulls")));
 
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = 0;
+		key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
 
-		MemSet(LTG_SIGN(key), 0, ASIGLEN);
 		while (num > 0)
 		{
-			hashing(LTG_SIGN(key), item);
+			hashing(LTG_SIGN(key), item, siglen);
 			num--;
 			item = NEXTVAL(item);
 		}
@@ -104,22 +104,17 @@ _ltree_compress(PG_FUNCTION_ARGS)
 	}
 	else if (!LTG_ISALLTRUE(entry->key))
 	{
-		int32		i,
-					len;
+		int32		i;
 		ltree_gist *key;
-
 		BITVECP		sign = LTG_SIGN(DatumGetPointer(entry->key));
 
-		ALOOPBYTE
+		ALOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(retval);
 		}
-		len = LTG_HDRSIZE;
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = LTG_ALLTRUE;
 
+		key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(key),
 					  entry->rel, entry->page,
@@ -134,6 +129,8 @@ _ltree_same(PG_FUNCTION_ARGS)
 	ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
 	ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 
 	if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
 		*result = true;
@@ -148,7 +145,7 @@ _ltree_same(PG_FUNCTION_ARGS)
 					sb = LTG_SIGN(b);
 
 		*result = true;
-		ALOOPBYTE
+		ALOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -161,7 +158,7 @@ _ltree_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, ltree_gist *add)
+unionkey(BITVECP sbase, ltree_gist *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = LTG_SIGN(add);
@@ -169,7 +166,7 @@ unionkey(BITVECP sbase, ltree_gist *add)
 	if (LTG_ISALLTRUE(add))
 		return 1;
 
-	ALOOPBYTE
+	ALOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -179,52 +176,46 @@ _ltree_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	ABITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	ltree_gist *result;
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
+	int32		i;
+	ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
+	BITVECP		base = LTG_SIGN(result);
 
-	MemSet((void *) base, 0, sizeof(ABITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = LTG_ALLTRUE;
+			result->flag |= LTG_ALLTRUE;
+			SET_VARSIZE(result, LTG_HDRSIZE);
 			break;
 		}
 	}
 
-	len = LTG_HDRSIZE + ((flag & LTG_ALLTRUE) ? 0 : ASIGLEN);
-	result = (ltree_gist *) palloc0(len);
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!LTG_ISALLTRUE(result))
-		memcpy((void *) LTG_SIGN(result), (void *) base, sizeof(ABITVEC));
-	*size = len;
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
 	int32		size = 0,
 				i;
 
-	ALOOPBYTE
+	ALOOPBYTE(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;
 
-	ALOOPBYTE
+	ALOOPBYTE(siglen)
 	{
 		diff = (unsigned char) (a[i] ^ b[i]);
 		dist += number_of_ones[diff];
@@ -233,19 +224,19 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(ltree_gist *a, ltree_gist *b)
+hemdist(ltree_gist *a, ltree_gist *b, int siglen)
 {
 	if (LTG_ISALLTRUE(a))
 	{
 		if (LTG_ISALLTRUE(b))
 			return 0;
 		else
-			return ASIGLENBIT - sizebitvec(LTG_SIGN(b));
+			return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
 	}
 	else if (LTG_ISALLTRUE(b))
-		return ASIGLENBIT - sizebitvec(LTG_SIGN(a));
+		return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
 
-	return hemdistsign(LTG_SIGN(a), LTG_SIGN(b));
+	return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
 }
 
 
@@ -255,8 +246,10 @@ _ltree_penalty(PG_FUNCTION_ARGS)
 	ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
 	ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -277,6 +270,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	OffsetNumber k,
 				j;
 	ltree_gist *datum_l,
@@ -309,7 +304,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -331,32 +326,13 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE);
-		SET_VARSIZE(datum_l, LTG_HDRSIZE);
-		datum_l->flag = LTG_ALLTRUE;
-	}
-	else
-	{
-		datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
-		SET_VARSIZE(datum_l, LTG_HDRSIZE + ASIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) LTG_SIGN(datum_l), (void *) LTG_SIGN(GETENTRY(entryvec, seed_1)), sizeof(ABITVEC));
-	}
-	if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE);
-		SET_VARSIZE(datum_r, LTG_HDRSIZE);
-		datum_r->flag = LTG_ALLTRUE;
-	}
-	else
-	{
-		datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
-		SET_VARSIZE(datum_r, LTG_HDRSIZE + ASIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) LTG_SIGN(datum_r), (void *) LTG_SIGN(GETENTRY(entryvec, seed_2)), sizeof(ABITVEC));
-	}
+	datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
+							   LTG_SIGN(GETENTRY(entryvec, seed_1)),
+							   siglen, NULL, NULL);
+
+	datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
+							   LTG_SIGN(GETENTRY(entryvec, seed_2)),
+							   siglen, NULL, NULL);
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -365,8 +341,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = Abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -390,20 +366,20 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
 		{
 			if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
 			{
 				if (!LTG_ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(ABITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = LTG_SIGN(_j);
-				ALOOPBYTE
+				ALOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -414,12 +390,12 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 			if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
 			{
 				if (!LTG_ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(ABITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = LTG_SIGN(_j);
-				ALOOPBYTE
+				ALOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -436,7 +412,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 }
 
 static bool
-gist_te(ltree_gist *key, ltree *query)
+gist_te(ltree_gist *key, ltree *query, int siglen)
 {
 	ltree_level *curq = LTREE_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -449,7 +425,7 @@ gist_te(ltree_gist *key, ltree *query)
 	while (qlen > 0)
 	{
 		hv = ltree_crc32_sz(curq->name, curq->len);
-		if (!GETBIT(sign, AHASHVAL(hv)))
+		if (!GETBIT(sign, AHASHVAL(hv, siglen)))
 			return false;
 		curq = LEVEL_NEXT(curq);
 		qlen--;
@@ -458,27 +434,40 @@ gist_te(ltree_gist *key, ltree *query)
 	return true;
 }
 
+typedef struct LtreeSignature
+{
+	BITVECP	sign;
+	int		siglen;
+} LtreeSignature;
+
 static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
 {
-	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, AHASHVAL(val->val)) : true;
+	LtreeSignature *sig = cxt;
+
+	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
 }
 
 static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
 {
+	LtreeSignature sig;
+
 	if (LTG_ISALLTRUE(key))
 		return true;
 
+	sig.sign = LTG_SIGN(key);
+	sig.siglen = siglen;
+
 	return ltree_execute(
 						 GETQUERY(query),
-						 (void *) LTG_SIGN(key), false,
+						 &sig, false,
 						 checkcondition_bit
 		);
 }
 
 static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
 {
 	lquery_level *curq = LQUERY_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -497,7 +486,7 @@ gist_qe(ltree_gist *key, lquery *query)
 
 			while (vlen > 0)
 			{
-				if (GETBIT(sign, AHASHVAL(curv->val)))
+				if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
 				{
 					isexist = true;
 					break;
@@ -517,7 +506,7 @@ gist_qe(ltree_gist *key, lquery *query)
 }
 
 static bool
-_arrq_cons(ltree_gist *key, ArrayType *_query)
+_arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -533,7 +522,7 @@ _arrq_cons(ltree_gist *key, ArrayType *_query)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query))
+		if (gist_qe(key, query, siglen))
 			return true;
 		num--;
 		query = (lquery *) NEXTVAL(query);
@@ -550,6 +539,8 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
 	bool		res = false;
 
@@ -560,19 +551,19 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 	{
 		case 10:
 		case 11:
-			res = gist_te(key, (ltree *) query);
+			res = gist_te(key, (ltree *) query, siglen);
 			break;
 		case 12:
 		case 13:
-			res = gist_qe(key, (lquery *) query);
+			res = gist_qe(key, (lquery *) query, siglen);
 			break;
 		case 14:
 		case 15:
-			res = gist_qtxt(key, (ltxtquery *) query);
+			res = gist_qtxt(key, (ltxtquery *) query, siglen);
 			break;
 		case 16:
 		case 17:
-			res = _arrq_cons(key, (ArrayType *) query);
+			res = _arrq_cons(key, (ArrayType *) query, siglen);
 			break;
 		default:
 			/* internal error */
@@ -581,3 +572,20 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+_ltree_gist_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 },
+			LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(LtreeGistOptions, siglen) };
+	LtreeGistOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(LtreeGistOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/contrib/ltree/ltree--1.1--1.2.sql b/contrib/ltree/ltree--1.1--1.2.sql
new file mode 100644
index 0000000..d002e2b
--- /dev/null
+++ b/contrib/ltree/ltree--1.1--1.2.sql
@@ -0,0 +1,47 @@
+/* contrib/ltree/ltree--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION ltree UPDATE TO '1.2'" 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
+('ltree_compress(internal)', '{internal,internal}'),
+('ltree_decompress(internal)', '{internal,internal}'),
+('ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('ltree_union(internal,internal)', '{internal,internal,internal}'),
+('ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('_ltree_compress(internal)', '{internal,internal}'),
+('_ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('_ltree_union(internal,internal)', '{internal,internal,internal}'),
+('_ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('_ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('_ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', '_ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_ltree_ops USING gist
+ADD FUNCTION 10 (ltree) ltree_gist_options (internal, boolean);
+
+ALTER OPERATOR FAMILY gist__ltree_ops USING gist
+ADD FUNCTION 10 (_ltree) _ltree_gist_options (internal, boolean);
+
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1..61c8cdf 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
 # ltree extension
 comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
 module_pathname = '$libdir/ltree'
 relocatable = true
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index e4b8c84..6247564 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -183,15 +183,16 @@ int			ltree_strncasecmp(const char *a, const char *b, size_t s);
 
 /* GiST support for ltree */
 
+#define SIGLEN_MAX		(122 * sizeof(int32))
+#define SIGLEN_DEFAULT	(2 * sizeof(int32))
 #define BITBYTE 8
-#define SIGLENINT  2
-#define SIGLEN	( sizeof(int32)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
-typedef unsigned char BITVEC[SIGLEN];
+#define SIGLEN	(sizeof(int32) * SIGLENINT)
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
+
 typedef unsigned 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) ( ((unsigned char)(x)) >> i & 0x01 )
@@ -199,8 +200,8 @@ typedef unsigned 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))
 
 /*
  * type of index key for ltree. Tree are combined B-Tree and R-Tree
@@ -230,26 +231,34 @@ typedef struct
 #define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
 #define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
 #define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
-#define LTG_LNODE(x)	( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
-#define LTG_RENODE(x)	( (ltree*)( ((char*)LTG_LNODE(x)) + VARSIZE(LTG_LNODE(x))) )
-#define LTG_RNODE(x)	( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
+#define LTG_LNODE(x, siglen)	( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : (siglen) ) ) )
+#define LTG_RENODE(x, siglen)	( (ltree*)( ((char*)LTG_LNODE(x, siglen)) + VARSIZE(LTG_LNODE(x, siglen))) )
+#define LTG_RNODE(x, siglen)	( LTG_ISNORIGHT(x) ? LTG_LNODE(x, siglen) : LTG_RENODE(x, siglen) )
 
-#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
-#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
+#define LTG_GETLNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x, siglen) )
+#define LTG_GETRNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x, siglen) )
 
+extern ltree_gist *ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+				 ltree *left, ltree *right);
 
 /* GiST support for ltree[] */
 
-#define ASIGLENINT	(7)
-#define ASIGLEN		(sizeof(int32)*ASIGLENINT)
-#define ASIGLENBIT (ASIGLEN*BITBYTE)
-typedef unsigned char ABITVEC[ASIGLEN];
+#define LTREE_ASIGLEN_DEFAULT	(7 * sizeof(int32))
+#define LTREE_ASIGLEN_MAX		(122 * sizeof(int32))
+#define ASIGLENBIT(siglen)		((siglen) * BITBYTE)
+
+#define ALOOPBYTE(siglen) \
+			for (i = 0; i < (siglen); i++)
 
-#define ALOOPBYTE \
-			for(i=0;i<ASIGLEN;i++)
+#define AHASHVAL(val, siglen) (((unsigned int)(val)) % ASIGLENBIT(siglen))
+#define AHASH(sign, val, siglen) SETBIT((sign), AHASHVAL(val, siglen))
 
-#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
-#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
+/* gist_ltree_ops and gist__ltree_ops opclass options */
+typedef struct LtreeGistOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+}	LtreeGistOptions;
 
 /* type of key is the same to ltree_gist */
 
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 12aa8ff..9847b83 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -6,11 +6,13 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "crc32.h"
 #include "ltree.h"
 
 #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
 
 PG_FUNCTION_INFO_V1(ltree_gist_in);
 PG_FUNCTION_INFO_V1(ltree_gist_out);
@@ -33,6 +35,47 @@ ltree_gist_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+ltree_gist *
+ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+				 ltree *left, ltree *right)
+{
+	int32		size = LTG_HDRSIZE + (isalltrue ? 0 : siglen) +
+		(left ? VARSIZE(left) + (right ? VARSIZE(right) : 0) : 0);
+	ltree_gist *result = palloc(size);
+
+	SET_VARSIZE(result, size);
+
+	if (siglen)
+	{
+		result->flag = 0;
+
+		if (isalltrue)
+			result->flag |= LTG_ALLTRUE;
+		else if (sign)
+			memcpy(LTG_SIGN(result), sign, siglen);
+		else
+			memset(LTG_SIGN(result), 0, siglen);
+
+		if (left)
+		{
+			memcpy(LTG_LNODE(result, siglen), left, VARSIZE(left));
+
+			if (!right || left == right || ISEQ(left, right))
+				result->flag |= LTG_NORIGHT;
+			else
+				memcpy(LTG_RNODE(result, siglen), right, VARSIZE(right));
+		}
+	}
+	else
+	{
+		Assert(left);
+		result->flag = LTG_ONENODE;
+		memcpy(LTG_NODE(result), left, VARSIZE(left));
+	}
+
+	return result;
+}
+
 PG_FUNCTION_INFO_V1(ltree_compress);
 PG_FUNCTION_INFO_V1(ltree_decompress);
 PG_FUNCTION_INFO_V1(ltree_same);
@@ -40,8 +83,8 @@ PG_FUNCTION_INFO_V1(ltree_union);
 PG_FUNCTION_INFO_V1(ltree_penalty);
 PG_FUNCTION_INFO_V1(ltree_picksplit);
 PG_FUNCTION_INFO_V1(ltree_consistent);
+PG_FUNCTION_INFO_V1(ltree_gist_options);
 
-#define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
 #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
 
 Datum
@@ -52,14 +95,8 @@ ltree_compress(PG_FUNCTION_ARGS)
 
 	if (entry->leafkey)
 	{							/* ltree */
-		ltree_gist *key;
 		ltree	   *val = DatumGetLtreeP(entry->key);
-		int32		len = LTG_HDRSIZE + VARSIZE(val);
-
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = LTG_ONENODE;
-		memcpy((void *) LTG_NODE(key), (void *) val, VARSIZE(val));
+		ltree_gist *key = ltree_gist_alloc(false, NULL, 0, val, 0);
 
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(key),
@@ -93,6 +130,8 @@ ltree_same(PG_FUNCTION_ARGS)
 	ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
 	ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 
 	*result = false;
 	if (LTG_ISONENODE(a) != LTG_ISONENODE(b))
@@ -109,15 +148,15 @@ ltree_same(PG_FUNCTION_ARGS)
 		if (LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b))
 			PG_RETURN_POINTER(result);
 
-		if (!ISEQ(LTG_LNODE(a), LTG_LNODE(b)))
+		if (!ISEQ(LTG_LNODE(a, siglen), LTG_LNODE(b, siglen)))
 			PG_RETURN_POINTER(result);
-		if (!ISEQ(LTG_RNODE(a), LTG_RNODE(b)))
+		if (!ISEQ(LTG_RNODE(a, siglen), LTG_RNODE(b, siglen)))
 			PG_RETURN_POINTER(result);
 
 		*result = true;
 		if (!LTG_ISALLTRUE(a))
 		{
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -132,7 +171,7 @@ ltree_same(PG_FUNCTION_ARGS)
 }
 
 static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
 {
 	int			tlen = t->numlevel;
 	ltree_level *cur = LTREE_FIRST(t);
@@ -141,7 +180,7 @@ hashing(BITVECP sign, ltree *t)
 	while (tlen > 0)
 	{
 		hash = ltree_crc32_sz(cur->name, cur->len);
-		HASH(sign, hash);
+		HASH(sign, hash, siglen);
 		cur = LEVEL_NEXT(cur);
 		tlen--;
 	}
@@ -152,7 +191,9 @@ ltree_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
+	BITVECP		base = palloc0(siglen);
 	int32		i,
 				j;
 	ltree_gist *result,
@@ -161,16 +202,14 @@ ltree_union(PG_FUNCTION_ARGS)
 			   *right = NULL,
 			   *curtree;
 	bool		isalltrue = false;
-	bool		isleqr;
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (j = 0; j < entryvec->n; j++)
 	{
 		cur = GETENTRY(entryvec, j);
 		if (LTG_ISONENODE(cur))
 		{
 			curtree = LTG_NODE(cur);
-			hashing(base, curtree);
+			hashing(base, curtree, siglen);
 			if (!left || ltree_compare(left, curtree) > 0)
 				left = curtree;
 			if (!right || ltree_compare(right, curtree) < 0)
@@ -184,14 +223,14 @@ ltree_union(PG_FUNCTION_ARGS)
 			{
 				BITVECP		sc = LTG_SIGN(cur);
 
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					((unsigned char *) base)[i] |= sc[i];
 			}
 
-			curtree = LTG_LNODE(cur);
+			curtree = LTG_LNODE(cur, siglen);
 			if (!left || ltree_compare(left, curtree) > 0)
 				left = curtree;
-			curtree = LTG_RNODE(cur);
+			curtree = LTG_RNODE(cur, siglen);
 			if (!right || ltree_compare(right, curtree) < 0)
 				right = curtree;
 		}
@@ -200,7 +239,7 @@ ltree_union(PG_FUNCTION_ARGS)
 	if (isalltrue == false)
 	{
 		isalltrue = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) base)[i] != 0xff)
 			{
@@ -210,23 +249,9 @@ ltree_union(PG_FUNCTION_ARGS)
 		}
 	}
 
-	isleqr = (left == right || ISEQ(left, right)) ? true : false;
-	*size = LTG_HDRSIZE + ((isalltrue) ? 0 : SIGLEN) + VARSIZE(left) + ((isleqr) ? 0 : VARSIZE(right));
-
-	result = (ltree_gist *) palloc0(*size);
-	SET_VARSIZE(result, *size);
-	result->flag = 0;
+	result = ltree_gist_alloc(isalltrue, base, siglen, left, right);
 
-	if (isalltrue)
-		result->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(result), base, SIGLEN);
-
-	memcpy((void *) LTG_LNODE(result), (void *) left, VARSIZE(left));
-	if (isleqr)
-		result->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(result), (void *) right, VARSIZE(right));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -237,11 +262,13 @@ ltree_penalty(PG_FUNCTION_ARGS)
 	ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
 	ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 	int32		cmpr,
 				cmpl;
 
-	cmpl = ltree_compare(LTG_GETLNODE(origval), LTG_GETLNODE(newval));
-	cmpr = ltree_compare(LTG_GETRNODE(newval), LTG_GETRNODE(origval));
+	cmpl = ltree_compare(LTG_GETLNODE(origval, siglen), LTG_GETLNODE(newval, siglen));
+	cmpr = ltree_compare(LTG_GETRNODE(newval, siglen), LTG_GETRNODE(origval, siglen));
 
 	*penalty = Max(cmpl, 0) + Max(cmpr, 0);
 
@@ -270,26 +297,24 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	OffsetNumber j;
 	int32		i;
 	RIX		   *array;
 	OffsetNumber maxoff;
 	int			nbytes;
-	int			size;
 	ltree	   *lu_l,
 			   *lu_r,
 			   *ru_l,
 			   *ru_r;
 	ltree_gist *lu,
 			   *ru;
-	BITVEC		ls,
-				rs;
+	BITVECP		ls = palloc0(siglen),
+				rs = palloc0(siglen);
 	bool		lisat = false,
-				risat = false,
-				isleqr;
+				risat = false;
 
-	memset((void *) ls, 0, sizeof(BITVEC));
-	memset((void *) rs, 0, sizeof(BITVEC));
 	maxoff = entryvec->n - 1;
 	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
 	v->spl_left = (OffsetNumber *) palloc(nbytes);
@@ -303,7 +328,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	{
 		array[j].index = j;
 		lu = GETENTRY(entryvec, j); /* use as tmp val */
-		array[j].r = LTG_GETLNODE(lu);
+		array[j].r = LTG_GETLNODE(lu, siglen);
 	}
 
 	qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
@@ -317,10 +342,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		{
 			v->spl_left[v->spl_nleft] = array[j].index;
 			v->spl_nleft++;
-			if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu), lu_r) > 0)
-				lu_r = LTG_GETRNODE(lu);
+			if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), lu_r) > 0)
+				lu_r = LTG_GETRNODE(lu, siglen);
 			if (LTG_ISONENODE(lu))
-				hashing(ls, LTG_NODE(lu));
+				hashing(ls, LTG_NODE(lu), siglen);
 			else
 			{
 				if (lisat || LTG_ISALLTRUE(lu))
@@ -329,7 +354,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 				{
 					BITVECP		sc = LTG_SIGN(lu);
 
-					LOOPBYTE
+					LOOPBYTE(siglen)
 						((unsigned char *) ls)[i] |= sc[i];
 				}
 			}
@@ -338,10 +363,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		{
 			v->spl_right[v->spl_nright] = array[j].index;
 			v->spl_nright++;
-			if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu), ru_r) > 0)
-				ru_r = LTG_GETRNODE(lu);
+			if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), ru_r) > 0)
+				ru_r = LTG_GETRNODE(lu, siglen);
 			if (LTG_ISONENODE(lu))
-				hashing(rs, LTG_NODE(lu));
+				hashing(rs, LTG_NODE(lu), siglen);
 			else
 			{
 				if (risat || LTG_ISALLTRUE(lu))
@@ -350,7 +375,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 				{
 					BITVECP		sc = LTG_SIGN(lu);
 
-					LOOPBYTE
+					LOOPBYTE(siglen)
 						((unsigned char *) rs)[i] |= sc[i];
 				}
 			}
@@ -360,7 +385,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	if (lisat == false)
 	{
 		lisat = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) ls)[i] != 0xff)
 			{
@@ -373,7 +398,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	if (risat == false)
 	{
 		risat = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) rs)[i] != 0xff)
 			{
@@ -383,38 +408,14 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		}
 	}
 
-	lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index));
-	isleqr = (lu_l == lu_r || ISEQ(lu_l, lu_r)) ? true : false;
-	size = LTG_HDRSIZE + ((lisat) ? 0 : SIGLEN) + VARSIZE(lu_l) + ((isleqr) ? 0 : VARSIZE(lu_r));
-	lu = (ltree_gist *) palloc0(size);
-	SET_VARSIZE(lu, size);
-	lu->flag = 0;
-	if (lisat)
-		lu->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(lu), ls, SIGLEN);
-	memcpy((void *) LTG_LNODE(lu), (void *) lu_l, VARSIZE(lu_l));
-	if (isleqr)
-		lu->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(lu), (void *) lu_r, VARSIZE(lu_r));
+	lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index), siglen);
+	lu = ltree_gist_alloc(lisat, ls, siglen, lu_l, lu_r);
 
+	ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index), siglen);
+	ru = ltree_gist_alloc(risat, rs, siglen, ru_l, ru_r);
 
-	ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index));
-	isleqr = (ru_l == ru_r || ISEQ(ru_l, ru_r)) ? true : false;
-	size = LTG_HDRSIZE + ((risat) ? 0 : SIGLEN) + VARSIZE(ru_l) + ((isleqr) ? 0 : VARSIZE(ru_r));
-	ru = (ltree_gist *) palloc0(size);
-	SET_VARSIZE(ru, size);
-	ru->flag = 0;
-	if (risat)
-		ru->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(ru), rs, SIGLEN);
-	memcpy((void *) LTG_LNODE(ru), (void *) ru_l, VARSIZE(ru_l));
-	if (isleqr)
-		ru->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(ru), (void *) ru_r, VARSIZE(ru_r));
+	pfree(ls);
+	pfree(rs);
 
 	v->spl_ldatum = PointerGetDatum(lu);
 	v->spl_rdatum = PointerGetDatum(ru);
@@ -423,7 +424,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 }
 
 static bool
-gist_isparent(ltree_gist *key, ltree *query)
+gist_isparent(ltree_gist *key, ltree *query, int siglen)
 {
 	int32		numlevel = query->numlevel;
 	int			i;
@@ -431,7 +432,8 @@ gist_isparent(ltree_gist *key, ltree *query)
 	for (i = query->numlevel; i >= 0; i--)
 	{
 		query->numlevel = i;
-		if (ltree_compare(query, LTG_GETLNODE(key)) >= 0 && ltree_compare(query, LTG_GETRNODE(key)) <= 0)
+		if (ltree_compare(query, LTG_GETLNODE(key, siglen)) >= 0 &&
+			ltree_compare(query, LTG_GETRNODE(key, siglen)) <= 0)
 		{
 			query->numlevel = numlevel;
 			return true;
@@ -452,10 +454,10 @@ copy_ltree(ltree *src)
 }
 
 static bool
-gist_ischild(ltree_gist *key, ltree *query)
+gist_ischild(ltree_gist *key, ltree *query, int siglen)
 {
-	ltree	   *left = copy_ltree(LTG_GETLNODE(key));
-	ltree	   *right = copy_ltree(LTG_GETRNODE(key));
+	ltree	   *left = copy_ltree(LTG_GETLNODE(key, siglen));
+	ltree	   *right = copy_ltree(LTG_GETRNODE(key, siglen));
 	bool		res = true;
 
 	if (left->numlevel > query->numlevel)
@@ -477,7 +479,7 @@ gist_ischild(ltree_gist *key, ltree *query)
 }
 
 static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
 {
 	lquery_level *curq = LQUERY_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -496,7 +498,7 @@ gist_qe(ltree_gist *key, lquery *query)
 
 			while (vlen > 0)
 			{
-				if (GETBIT(sign, HASHVAL(curv->val)))
+				if (GETBIT(sign, HASHVAL(curv->val, siglen)))
 				{
 					isexist = true;
 					break;
@@ -545,41 +547,54 @@ gist_tqcmp(ltree *t, lquery *q)
 }
 
 static bool
-gist_between(ltree_gist *key, lquery *query)
+gist_between(ltree_gist *key, lquery *query, int siglen)
 {
 	if (query->firstgood == 0)
 		return true;
 
-	if (gist_tqcmp(LTG_GETLNODE(key), query) > 0)
+	if (gist_tqcmp(LTG_GETLNODE(key, siglen), query) > 0)
 		return false;
 
-	if (gist_tqcmp(LTG_GETRNODE(key), query) < 0)
+	if (gist_tqcmp(LTG_GETRNODE(key, siglen), query) < 0)
 		return false;
 
 	return true;
 }
 
+typedef struct LtreeSignature
+{
+	BITVECP	sign;
+	int		siglen;
+} LtreeSignature;
+
 static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
 {
-	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, HASHVAL(val->val)) : true;
+	LtreeSignature *sig = cxt;
+
+	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, HASHVAL(val->val, sig->siglen)) : true;
 }
 
 static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
 {
+	LtreeSignature sig;
+
 	if (LTG_ISALLTRUE(key))
 		return true;
 
+	sig.sign = LTG_SIGN(key);
+	sig.siglen = siglen;
+
 	return ltree_execute(
 						 GETQUERY(query),
-						 (void *) LTG_SIGN(key), false,
+						 &sig, false,
 						 checkcondition_bit
 		);
 }
 
 static bool
-arrq_cons(ltree_gist *key, ArrayType *_query)
+arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -595,7 +610,7 @@ arrq_cons(ltree_gist *key, ArrayType *_query)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query) && gist_between(key, query))
+		if (gist_qe(key, query, siglen) && gist_between(key, query, siglen))
 			return true;
 		num--;
 		query = NEXTVAL(query);
@@ -611,6 +626,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
 	void	   *query = NULL;
 	bool		res = false;
@@ -625,11 +642,11 @@ ltree_consistent(PG_FUNCTION_ARGS)
 			res = (GIST_LEAF(entry)) ?
 				(ltree_compare((ltree *) query, LTG_NODE(key)) > 0)
 				:
-				(ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+				(ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
 			break;
 		case BTLessEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
-			res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+			res = (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
 			break;
 		case BTEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
@@ -637,35 +654,35 @@ ltree_consistent(PG_FUNCTION_ARGS)
 				res = (ltree_compare((ltree *) query, LTG_NODE(key)) == 0);
 			else
 				res = (
-					   ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0
+					   ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0
 					   &&
-					   ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0
+					   ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0
 					);
 			break;
 		case BTGreaterEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
-			res = (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+			res = (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
 			break;
 		case BTGreaterStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
 			res = (GIST_LEAF(entry)) ?
-				(ltree_compare((ltree *) query, LTG_GETRNODE(key)) < 0)
+				(ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) < 0)
 				:
-				(ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+				(ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
 			break;
 		case 10:
 			query = PG_GETARG_LTREE_P_COPY(1);
 			res = (GIST_LEAF(entry)) ?
 				inner_isparent((ltree *) query, LTG_NODE(key))
 				:
-				gist_isparent(key, (ltree *) query);
+				gist_isparent(key, (ltree *) query, siglen);
 			break;
 		case 11:
 			query = PG_GETARG_LTREE_P(1);
 			res = (GIST_LEAF(entry)) ?
 				inner_isparent(LTG_NODE(key), (ltree *) query)
 				:
-				gist_ischild(key, (ltree *) query);
+				gist_ischild(key, (ltree *) query, siglen);
 			break;
 		case 12:
 		case 13:
@@ -676,7 +693,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((lquery *) query)
 													   ));
 			else
-				res = (gist_qe(key, (lquery *) query) && gist_between(key, (lquery *) query));
+				res = (gist_qe(key, (lquery *) query, siglen) &&
+					   gist_between(key, (lquery *) query, siglen));
 			break;
 		case 14:
 		case 15:
@@ -687,7 +705,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((ltxtquery *) query)
 													   ));
 			else
-				res = gist_qtxt(key, (ltxtquery *) query);
+				res = gist_qtxt(key, (ltxtquery *) query, siglen);
 			break;
 		case 16:
 		case 17:
@@ -698,7 +716,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((ArrayType *) query)
 													   ));
 			else
-				res = arrq_cons(key, (ArrayType *) query);
+				res = arrq_cons(key, (ArrayType *) query, siglen);
 			break;
 		default:
 			/* internal error */
@@ -708,3 +726,20 @@ ltree_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+ltree_gist_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 },
+			LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+	relopt_gen *optgen[] = { &siglen.gen };
+	int			offsets[] = { offsetof(LtreeGistOptions, siglen) };
+	LtreeGistOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(LtreeGistOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index 3ddd335..dd37445 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -500,11 +500,17 @@ Europe &amp; Russia*@ &amp; !Transportation
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     Example of creating such an index:
+     Example of creating such an index with a default signature length of 8 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (path);
 </programlisting>
+    <para>
+     Example of creating such an index with a signature length of 100 bytes:
+    </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
+</programlisting>
    </listitem>
    <listitem>
     <para>
@@ -513,12 +519,18 @@ CREATE INDEX path_gist_idx ON test USING GIST (path);
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     Example of creating such an index:
+     Example of creating such an index with a default signature length of 28 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (array_path);
 </programlisting>
     <para>
+     Example of creating such an index with a signature length of 100 bytes:
+    </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
+</programlisting>
+    <para>
      Note: This index type is lossy.
     </para>
    </listitem>
-- 
2.7.4

0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patchtext/x-patch; name=0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patchDownload
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

0008-Add-opclass-parameters-to-contrib-hstore-v02.patchtext/x-patch; name=0008-Add-opclass-parameters-to-contrib-hstore-v02.patchDownload
From 413cd728ba7db42f23315b08ee00c163d3f1c7eb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 9 Feb 2018 18:34:29 +0300
Subject: [PATCH 8/9] Add opclass parameters to contrib/hstore

---
 contrib/hstore/Makefile             |   2 +-
 contrib/hstore/hstore--1.5--1.6.sql |  32 ++++++
 contrib/hstore/hstore.control       |   2 +-
 contrib/hstore/hstore_gist.c        | 200 ++++++++++++++++++++----------------
 doc/src/sgml/hstore.sgml            |   9 ++
 5 files changed, 154 insertions(+), 91 deletions(-)
 create mode 100644 contrib/hstore/hstore--1.5--1.6.sql

diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 46d26f8..a8a5105 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -5,7 +5,7 @@ OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
 	$(WIN32RES)
 
 EXTENSION = hstore
-DATA = hstore--1.4.sql hstore--1.4--1.5.sql \
+DATA = hstore--1.4.sql hstore--1.4--1.5.sql hstore--1.5--1.6.sql \
 	hstore--1.3--1.4.sql hstore--1.2--1.3.sql \
 	hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
 	hstore--unpackaged--1.0.sql
diff --git a/contrib/hstore/hstore--1.5--1.6.sql b/contrib/hstore/hstore--1.5--1.6.sql
new file mode 100644
index 0000000..08c85ee
--- /dev/null
+++ b/contrib/hstore/hstore--1.5--1.6.sql
@@ -0,0 +1,32 @@
+/* contrib/hstore/hstore--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.6'" 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
+('ghstore_compress(internal)', '{internal,internal}'),
+('ghstore_decompress(internal)', '{internal,internal}'),
+('ghstore_consistent(internal,hstore,smallint,oid,internal)', '{internal,hstore,smallint,oid,internal,internal}'),
+('ghstore_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ghstore_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ghstore_union(internal,internal)', '{internal,internal,internal}'),
+('ghstore_same(ghstore,ghstore,internal)', '{ghstore,ghstore,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ghstore_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ghstore_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_hstore_ops USING gist
+ADD FUNCTION 10 (hstore) ghstore_options (internal, boolean);
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 8a71947..93688cd 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.5'
+default_version = '1.6'
 module_pathname = '$libdir/hstore'
 relocatable = true
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 6d24d2f..13be2c0 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -4,29 +4,33 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "catalog/pg_type.h"
 #include "utils/pg_crc.h"
 
 #include "hstore.h"
 
+/* gist_hstore_ops opclass options */
+typedef struct HstoreGistOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+} GistHstoreOptions;
+
 /* bigint defines */
 #define BITBYTE 8
-#define SIGLENINT  4			/* >122 => key will toast, so very slow!!! */
-#define SIGLEN	( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
+#define SIGLEN_DEFAULT	(sizeof(int32) * 4)
+#define SIGLEN_MAX		(sizeof(int32) * 122)	/* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
 
-typedef char BITVEC[SIGLEN];
 typedef char *BITVECP;
 
-#define SIGPTR(x)  ( (BITVECP) ARR_DATA_PTR(x) )
-
+#define LOOPBYTE(siglen) \
+			for (i = 0; i < (siglen); i++)
 
-#define LOOPBYTE \
-			for(i=0;i<SIGLEN;i++)
-
-#define LOOPBIT \
-			for(i=0;i<SIGLENBIT;i++)
+#define LOOPBIT(siglen) \
+			for (i = 0; i < SIGLENBIT(siglen); i++)
 
 /* beware of multiple evaluation of arguments to these macros! */
 #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
@@ -34,8 +38,8 @@ typedef char *BITVECP;
 #define CLRBIT(x,i)   GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) )
 #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))
 
 typedef struct
 {
@@ -49,7 +53,7 @@ typedef struct
 #define ISALLTRUE(x)	( ((GISTTYPE*)x)->flag & ALLISTRUE )
 
 #define GTHDRSIZE		(VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
 
 #define GETSIGN(x)		( (BITVECP)( (char*)x+GTHDRSIZE ) )
 
@@ -100,6 +104,27 @@ ghstore_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+static GISTTYPE *
+ghstore_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+	int			flag = allistrue ? ALLISTRUE : 0;
+	int			size = CALCGTSIZE(flag, siglen);
+	GISTTYPE   *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if (!allistrue)
+	{
+		if (sign)
+			memcpy(GETSIGN(res), sign, siglen);
+		else
+			memset(GETSIGN(res), 0, siglen);
+	}
+
+	return res;
+}
+
 PG_FUNCTION_INFO_V1(ghstore_consistent);
 PG_FUNCTION_INFO_V1(ghstore_compress);
 PG_FUNCTION_INFO_V1(ghstore_decompress);
@@ -107,36 +132,37 @@ PG_FUNCTION_INFO_V1(ghstore_penalty);
 PG_FUNCTION_INFO_V1(ghstore_picksplit);
 PG_FUNCTION_INFO_V1(ghstore_union);
 PG_FUNCTION_INFO_V1(ghstore_same);
+PG_FUNCTION_INFO_V1(ghstore_options);
 
 Datum
 ghstore_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(1);
+	int			siglen = options->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{
-		GISTTYPE   *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		GISTTYPE   *res = ghstore_alloc(false, siglen, NULL);
 		HStore	   *val = DatumGetHStoreP(entry->key);
 		HEntry	   *hsent = ARRPTR(val);
 		char	   *ptr = STRPTR(val);
 		int			count = HS_COUNT(val);
 		int			i;
 
-		SET_VARSIZE(res, CALCGTSIZE(0));
-
 		for (i = 0; i < count; ++i)
 		{
 			int			h;
 
 			h = crc32_sz((char *) HSTORE_KEY(hsent, ptr, i),
 						 HSTORE_KEYLEN(hsent, i));
-			HASH(GETSIGN(res), h);
+			HASH(GETSIGN(res), h, siglen);
 			if (!HSTORE_VALISNULL(hsent, i))
 			{
 				h = crc32_sz((char *) HSTORE_VAL(hsent, ptr, i),
 							 HSTORE_VALLEN(hsent, i));
-				HASH(GETSIGN(res), h);
+				HASH(GETSIGN(res), h, siglen);
 			}
 		}
 
@@ -152,15 +178,13 @@ ghstore_compress(PG_FUNCTION_ARGS)
 		GISTTYPE   *res;
 		BITVECP		sign = GETSIGN(DatumGetPointer(entry->key));
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(retval);
 		}
 
-		res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
-		SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
-		res->flag = ALLISTRUE;
+		res = ghstore_alloc(true, siglen, NULL);
 
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
@@ -188,6 +212,9 @@ ghstore_same(PG_FUNCTION_ARGS)
 	GISTTYPE   *a = (GISTTYPE *) PG_GETARG_POINTER(0);
 	GISTTYPE   *b = (GISTTYPE *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
+
 
 	if (ISALLTRUE(a) && ISALLTRUE(b))
 		*result = true;
@@ -202,7 +229,7 @@ ghstore_same(PG_FUNCTION_ARGS)
 					sb = GETSIGN(b);
 
 		*result = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -215,12 +242,12 @@ ghstore_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
 	int32		size = 0,
 				i;
 
-	LOOPBYTE
+	LOOPBYTE(siglen)
 	{
 		size += SUMBIT(sign);
 		sign = (BITVECP) (((char *) sign) + 1);
@@ -229,12 +256,12 @@ sizebitvec(BITVECP sign)
 }
 
 static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
 {
 	int			i,
 				dist = 0;
 
-	LOOPBIT
+	LOOPBIT(siglen)
 	{
 		if (GETBIT(a, i) != GETBIT(b, i))
 			dist++;
@@ -243,30 +270,30 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *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);
 }
 
 static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = GETSIGN(add);
 
 	if (ISALLTRUE(add))
 		return 1;
-	LOOPBYTE
+	LOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -278,28 +305,23 @@ ghstore_union(PG_FUNCTION_ARGS)
 	int32		len = entryvec->n;
 
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	int32		i;
-	int32		flag = 0;
-	GISTTYPE   *result;
+	GISTTYPE   *result = ghstore_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;
 		}
 	}
 
-	len = CALCGTSIZE(flag);
-	result = (GISTTYPE *) 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);
 }
@@ -310,10 +332,12 @@ ghstore_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);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+	int			siglen = options->siglen;
 	GISTTYPE   *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
 	GISTTYPE   *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -338,6 +362,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	OffsetNumber maxoff = entryvec->n - 2;
 
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+	int			siglen = options->siglen;
 	OffsetNumber k,
 				j;
 	GISTTYPE   *datum_l,
@@ -368,7 +394,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -390,33 +416,10 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_l, GTHDRSIZE);
-		datum_l->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
-			;
-	}
-	if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_r, GTHDRSIZE);
-		datum_r->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
-	}
+	datum_l = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_1)));
+	datum_r = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_2)));
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -425,8 +428,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -450,20 +453,20 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.0001))
 		{
 			if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -474,12 +477,12 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -504,6 +507,8 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(5);
+	int			siglen = options->siglen;
 	bool		res = true;
 	BITVECP		sign;
 
@@ -529,13 +534,13 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			int			crc = crc32_sz((char *) HSTORE_KEY(qe, qv, i),
 									   HSTORE_KEYLEN(qe, i));
 
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (GETBIT(sign, HASHVAL(crc, siglen)))
 			{
 				if (!HSTORE_VALISNULL(qe, i))
 				{
 					crc = crc32_sz((char *) HSTORE_VAL(qe, qv, i),
 								   HSTORE_VALLEN(qe, i));
-					if (!GETBIT(sign, HASHVAL(crc)))
+					if (!GETBIT(sign, HASHVAL(crc, siglen)))
 						res = false;
 				}
 			}
@@ -548,7 +553,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 		text	   *query = PG_GETARG_TEXT_PP(1);
 		int			crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-		res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
+		res = (GETBIT(sign, HASHVAL(crc, siglen))) ? true : false;
 	}
 	else if (strategy == HStoreExistsAllStrategyNumber)
 	{
@@ -569,7 +574,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			if (key_nulls[i])
 				continue;
 			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (!(GETBIT(sign, HASHVAL(crc))))
+			if (!(GETBIT(sign, HASHVAL(crc, siglen))))
 				res = false;
 		}
 	}
@@ -594,7 +599,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			if (key_nulls[i])
 				continue;
 			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (GETBIT(sign, HASHVAL(crc, siglen)))
 				res = true;
 		}
 	}
@@ -603,3 +608,20 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+ghstore_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(GistHstoreOptions, siglen) };
+	GistHstoreOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(GistHstoreOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 94ccd12..45a811f 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -462,6 +462,15 @@ CREATE INDEX hidx ON testhstore USING GIN (h);
 </programlisting>
 
   <para>
+   GiST opclass <literal>gist_hstore_ops</literal> has one optional parameter
+   <literal>siglen</literal> for specifying signature length in bytes
+   (default signature length is 16 bytes, maximal length is 488 bytes):
+<programlisting>
+  CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32));
+</programlisting>
+  </para>
+
+  <para>
    <type>hstore</type> also supports <type>btree</type> or <type>hash</type> indexes for
    the <literal>=</literal> operator. This allows <type>hstore</type> columns to be
    declared <literal>UNIQUE</literal>, or to be used in <literal>GROUP BY</literal>,
-- 
2.7.4

#10Nikolay Shaplov
dhyan@nataraj.su
In reply to: Nikita Glukhov (#9)
Re: [PATCH] Opclass parameters

В письме от 15 ноября 2018 18:26:43 пользователь Nikita Glukhov написал:

But since it is now "Rejected with feedback", let's wait till autumn.

We don't want to wait that long. But now we only need to сome to an
agreement
about CREATE INDEX syntax and where to store the opclass parameters.

Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.

CREATE INDEX syntax and parameters storage method still need discussion.

Hi! I'd like to review your patch if you do not mind. Since I am familiar
with the subject. I'll add it to the TOP 3 of my IT TODO list :-)

But even without carefully reading the code I have several important
questions:

1. I am sure that this patch should contain code for opclass praram generic
implementation, and _one_ example of how it is used for certain opclass.
From my point of view, we should not add all hardcoded constant as opclass
params just because we can. It should be done only when it is really needed.
And each case needs special consideration. This is not only about code
complexity, this worries me less, but also about documentation complexity.
Once option is added, it should be documented. This will make documentation
more hard to understand (and completely unexperienced users reads
documentation too). If this option is needed, this is price we all pay. But if
nobody really use it, I see no reason to make things more complex for
everyone.

2. The second question, is why you create new column in one of the table of
pg_catalog, when we have attopitions in attribute description. And each
indexed column in index is technically a relation attribute. Options of index
column should be stored there, from my point of view (yes I know it is a
hassle to pass it there, but I did in in my preview, it can be done)
2.1. I did not read the code, I'll do it some time late, but the main question
I have is, how you will manage case, when you set different values for same
options of different columns. like:

CREATE INDEX ON arrays USING gist (
arr1 gist__intbig_ops (siglen = 32),
arr2 gist__intbig_ops (siglen = 64)
);

It is easitly solved when you store them in attoptions. But my question how do
you manage it... (I'll get the answer from the code soon, I hope)

--
Do code for fun.

#11Nikolay Shaplov
dhyan@nataraj.su
In reply to: Nikita Glukhov (#9)
Re: [PATCH] Opclass parameters

В письме от 15 ноября 2018 18:26:43 пользователь Nikita Glukhov написал:

Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.

CREATE INDEX syntax and parameters storage method still need discussion.

I've played around a bit with you patch and come to some conclusions, I'd like
to share. They are almost same as those before, but now there are more
details.

Again some issues about storing opclass options in pg_inedx:

1. Having both indoption and indoptions column in pg_index will make someone's
brain explode for sure. If not, it will bring troubles when people start
confusing them.

2. Now I found out how do you store option values for each opclass: text[] of
indoptions in pg_index is not the same as text[] in
reloptions in pg_catalog (and it brings more confusion). In reloption each
member of the array is a single option.

reloptions | {fillfactor=90,autovacuum_enabled=false}

In indoptions, is a whole string of options for one of the indexed attributes,
each array item has all options for one indexed attribute. And this string
needs furthermore parsing, that differs from reloption parsing.

indoptions | {"{numranges=150}","{numranges=160}"}

This brings us to the following issues:

2a. pg_index stores properties of index in general. Properties of each indexed
attributes is stored in pg_attribute table. If we follow this philosophy
it is wrong to add any kind of per-attribute array values into pg_index. These
values should be added to pg_attribute one per each pg_attribute entry.

2b. Since you've chosen method of storage that differs from one that is used
in reloptions, that will lead to two verstions of code that processes the
attributes. And from now on, if we accept this, we should support both of them
and keep them in sync. (I see that you tried to reuse as much code as
possible, but still you added some more that duplicate current reloptions
functionality.)

I know that relotions code is not really suitable for reusing. This was the
reason why I started solving oplcass option task with rewriting reloptions
code,to make it 100% reusable for any kind of options. So I would offer you
again to join me as a reviewer of that code. This will make opclass code more
simple and more sensible, if my option code is used...

3. Speaking of sensible code

Datum
g_int_options(PG_FUNCTION_ARGS)
{
Datum raw_options = PG_GETARG_DATUM(0);
bool validate = PG_GETARG_BOOL(1);
relopt_int siglen =
{ {"numranges", "number of ranges for compression", 0, 0, 9,
RELOPT_TYPE_INT },
G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
relopt_gen *optgen[] = { &siglen.gen };
int offsets[] = { offsetof(IntArrayOptions, num_ranges) };
IntArrayOptions *options =
parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
sizeof(IntArrayOptions), validate);

PG_RETURN_POINTER(options);
}

It seems to be not a very nice hack.
What would you do if you would like to have both int, real and boolean options
for one opclass? I do not know how to implement it using this code.
We have only int opclass options for now, but more will come and we should be
ready for it.

4. Now getting back to not adding opclass options wherever we can, just
because we can:

4a. For inrarray there were no opclass options tests added. I am sure there
should be one, at least just to make sure it still does not segfault when you
try to set one. And in some cases more tests can be needed. To add and review
them one should be familiar with this opclass internals. So it is good when
different people do it for different opclasses

4b. When you add opclass options instead of hardcoded values, it comes to
setting minimum and maximum value. Why do you choose 1000 as maximum
for num_ranges in gist_int_ops in intarray? Why 1 as minimum? All these
decisions needs careful considerations and can't be done for bunch of
opclasses just in one review.

4c. Patch usually take a long path from prototype to final commit. Do you
really want to update all these opclasses code each time when some changes
in the main opclass option code is made? ;-)

So I would suggest to work only with intarray and add other opclasses later.

5. You've been asking about SQL grammar

CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);

As for me I do not really care about it. For me all the solutions is
acceptable. But looking at is i came to one notion:

I've never seen before DEFAULT keyword to be used in this way. There is logic
in such usage, but I did not remember any practical usage case.
If there are such usages (I can easily missed it) or if it is somehow
recommended in SQL standard -- let it be. But if none above, I would suggest
to use WITH keyword instead. As it is already used for reloptions. As far as I
remember in my prototype I used "WITH OPTIONS" but did if just because did not
find my way through yac with single "WITH". So ideal way for me would be

create index ON test USING GIST (i WITH (sig_len_int=22));

But as I said it is not thing of importance for me. Just an observation.

--
Do code for fun.

#12Robert Haas
robertmhaas@gmail.com
In reply to: Nikolay Shaplov (#2)
Re: [PATCH] Opclass parameters

On Wed, Feb 28, 2018 at 9:46 AM Nikolay Shaplov <dhyan@nataraj.su> wrote:

1. I've seen you've added a new attribute into pg_index. Why??!!
As far as I can get, if have index built on several columns (A1, A2, A3) you
can set, own opclass for each column. And set individual options for each
opclass if we are speaking about options. So I would expect to have these
options not in pg_index, but in pg_attribute. And we already have one there:
attoptions.I just do not get how you have come to per-index options. May be I
should read code more attentively...

It seems sensible to have both per-column options and per-index
options. For example, we've got the fastupdate option for GIN, which
is a property of the index as a whole, not any individual column. But
you could also want to specify some column-specific options, which
seems to be what this patch is about, since an opclass is associated
with an individual column. And since an index can have more than one
column, I agree that it seems more appropriate to store this
information in pg_attribute than pg_index.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#13Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Nikolay Shaplov (#11)
4 attachment(s)
Re: [PATCH] Opclass parameters

Attached 3rd version of the patches.

On 20.11.2018 14:15, Nikolay Shaplov wrote:

В письме от 15 ноября 2018 18:26:43 пользователь Nikita Glukhov написал:

Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.

CREATE INDEX syntax and parameters storage method still need discussion.

I've played around a bit with you patch and come to some conclusions, I'd like
to share. They are almost same as those before, but now there are more
details.

Again some issues about storing opclass options in pg_inedx:

1. Having both indoption and indoptions column in pg_index will make someone's
brain explode for sure. If not, it will bring troubles when people start
confusing them.

2. Now I found out how do you store option values for each opclass: text[] of
indoptions in pg_index is not the same as text[] in
reloptions in pg_catalog (and it brings more confusion). In reloption each
member of the array is a single option.

reloptions | {fillfactor=90,autovacuum_enabled=false}

In indoptions, is a whole string of options for one of the indexed attributes,
each array item has all options for one indexed attribute. And this string
needs furthermore parsing, that differs from reloption parsing.

indoptions | {"{numranges=150}","{numranges=160}"}

This brings us to the following issues:

2a. pg_index stores properties of index in general. Properties of each indexed
attributes is stored in pg_attribute table. If we follow this philosophy
it is wrong to add any kind of per-attribute array values into pg_index. These
values should be added to pg_attribute one per each pg_attribute entry.

2b. Since you've chosen method of storage that differs from one that is used
in reloptions, that will lead to two verstions of code that processes the
attributes. And from now on, if we accept this, we should support both of them
and keep them in sync. (I see that you tried to reuse as much code as
possible, but still you added some more that duplicate current reloptions
functionality.)

On 21.11.2018 18:04, Robert Haas wrote:

It seems sensible to have both per-column options and per-index
options. For example, we've got the fastupdate option for GIN, which
is a property of the index as a whole, not any individual column. But
you could also want to specify some column-specific options, which
seems to be what this patch is about, since an opclass is associated
with an individual column. And since an index can have more than one
column, I agree that it seems more appropriate to store this
information in pg_attribute than pg_index.

Ok, I have switched from pg_index.indoptions to pg_attribute.attoptions.

I agree that we should distinguish per-index and per-column options, but they
can also be AM-specific and opclass-specific.

'fastupdate' option for GIN is an example of AM-specific per-index option.

ASC/DESC and NULLS LAST/FIRST are examples of AM-class-specific per-column
options having special SQL syntax. "AM-class-specific" here means "specific
only for the class of AMs that support ordering". Now they are stored as flags
in pg_index.indoption[] but later can be moved to pg_attribute.attoptions.

Don't know should per-column AM-specific and opclass-specific options be stored
in the single column attoptions or have separate columns (like attamoptions and
attopclassoptions). If we will store them together, we can run into name
collisions, but this collisions can be easily resolved using autogenerated
prefixes in option names ("am.foo=bar", "opclass.foo=baz").

And another problem is the options with default values. They may be not
explicitly specified by the user, and in this case in current implementation
nothing will be stored in the catalog because default values can be obtained
from the code. But this will not allow changing of default values without
compatibility issues. So I think it would be better to store both default and
explicitly specified values of all opclass options, but this will require a
major refactoring of current API.

Also I have idea to define list of opclass parameters declaratively when opclass
is created using syntax like the following:

CREATE OPERATOR CLASS name [ (param_name param_type [= default_value] [,...]) ]
FOR TYPE ... AS (
{ OPTIONS function_name ( arg_type [,...] ) /* reloptions => binary */
| OPERATOR ...
} [,...]
)

But if we remember about the min/max values etc. the syntax will definitely
become more complicated, and it will require much more changes in the catalog
(up to creation of new table pg_opclassparams).

In any case, I think it would be nice to give somehow the user the ability to
obtain a list of opclass parameters not only from the documentation.

On 20.11.2018 14:15, Nikolay Shaplov wrote:

I know that relotions code is not really suitable for reusing. This was the
reason why I started solving oplcass option task with rewriting reloptions
code,to make it 100% reusable for any kind of options. So I would offer you
again to join me as a reviewer of that code. This will make opclass code more
simple and more sensible, if my option code is used...

I absolutely agree that reloptions code needs such refactoring, and I am
planning to continue reviewing your patches.

3. Speaking of sensible code

Datum
g_int_options(PG_FUNCTION_ARGS)
{
Datum raw_options = PG_GETARG_DATUM(0);
bool validate = PG_GETARG_BOOL(1);
relopt_int siglen =
{ {"numranges", "number of ranges for compression", 0, 0, 9,
RELOPT_TYPE_INT },
G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
relopt_gen *optgen[] = { &siglen.gen };
int offsets[] = { offsetof(IntArrayOptions, num_ranges) };
IntArrayOptions *options =
parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
sizeof(IntArrayOptions), validate);

PG_RETURN_POINTER(options);
}

It seems to be not a very nice hack.
What would you do if you would like to have both int, real and boolean options
for one opclass? I do not know how to implement it using this code.
We have only int opclass options for now, but more will come and we should be
ready for it.

Don't understand where is hack.

It's possible to parse options of different types, see the following example:

typedef struct FooOptions
{
int int_opt;
int str_opt_offset;
} FooOptions;

relopt_int int_option = { {"int_opt", "desc", 0, 0, 7, RELOPT_TYPE_INT },
INT_OPT_DEFAULT, INT_OPT_MIN, INT_OPT_MAX };

relopt_string str_option = { {"str_opt", "desc", 0, 0, 7, RELOPT_TYPE_STRING },
0, true, validate_str_option, NULL };

relopt_gen *gen_options[] = { &int_option.gen, &str_option.gen };

int offsets[] = { offsetof(FooOptions, int_opt),
offsetof(FooOptions, str_opt_offset) };

FooOptions *opts =
parseAndFillLocalRelOptions(raw_options, gen_options, offsets, 2,
sizeof(FooOptions), validate);

int int_option_value = opts->int_opt;
char *str_option_value = (char *) opts + opts->str_opt_offset;

Also I can propose to combine this two arrays into the one:

relopt_gen_offset options[] = {
&int_option.gen, offsetof(FooOptions, int_opt),
&str_option.gen, offsetof(FooOptions, str_opt_offset)
};

4. Now getting back to not adding opclass options wherever we can, just
because we can:

4a. For inrarray there were no opclass options tests added. I am sure there
should be one, at least just to make sure it still does not segfault when you
try to set one. And in some cases more tests can be needed. To add and review
them one should be familiar with this opclass internals. So it is good when
different people do it for different opclasses

4b. When you add opclass options instead of hardcoded values, it comes to
setting minimum and maximum value. Why do you choose 1000 as maximum
for num_ranges in gist_int_ops in intarray? Why 1 as minimum? All these
decisions needs careful considerations and can't be done for bunch of
opclasses just in one review.

4c. Patch usually take a long path from prototype to final commit. Do you
really want to update all these opclasses code each time when some changes
in the main opclass option code is made? ;-)

So I would suggest to work only with intarray and add other opclasses later.

I decided to leave only GiST tsvector_ops as an example, because it is an
in-core opclass, not an extension like intarray.

5. You've been asking about SQL grammar

CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);

As for me I do not really care about it. For me all the solutions is
acceptable. But looking at is i came to one notion:

I've never seen before DEFAULT keyword to be used in this way. There is logic
in such usage, but I did not remember any practical usage case.
If there are such usages (I can easily missed it) or if it is somehow
recommended in SQL standard -- let it be. But if none above, I would suggest
to use WITH keyword instead. As it is already used for reloptions. As far as I
remember in my prototype I used "WITH OPTIONS" but did if just because did not
find my way through yac with single "WITH". So ideal way for me would be

create index ON test USING GIST (i WITH (sig_len_int=22));

But as I said it is not thing of importance for me. Just an observation.

"[opclass] WITH OPTIONS (options)" looks too verbose, of course.

"[opclass] WITH (options)" looks acceptable, but it seems to conflict with
exclusion constraints syntax ("index_elem WITH operator").

"opclass (options)" looks the most natural to me. But we still need some
keyword before the parentheses when the opclass is not specified since we
can't distinguish "func_name (func_params)" and "col_name (opclass_options)"
in grammar.

--
Nikita Glukhov
Postgres Professional:http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0001-Add-opclass-parameters-v03.patchtext/x-patch; name=0001-Add-opclass-parameters-v03.patchDownload
From e28cea53ec3d088a3d87093bedc137aa599bd01d Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Wed, 10 Jan 2018 00:46:28 +0300
Subject: [PATCH 1/9] Add opclass parameters

---
 doc/src/sgml/indices.sgml                 |   2 +-
 doc/src/sgml/ref/create_index.sgml        |  16 +++-
 src/backend/access/common/reloptions.c    | 142 +++++++++++++++++++++---------
 src/backend/access/index/indexam.c        |  81 +++++++++++++++++
 src/backend/catalog/heap.c                |   8 +-
 src/backend/catalog/index.c               |  23 ++++-
 src/backend/catalog/toasting.c            |   1 +
 src/backend/commands/indexcmds.c          |  17 +++-
 src/backend/commands/tablecmds.c          |   2 +-
 src/backend/nodes/copyfuncs.c             |   1 +
 src/backend/nodes/equalfuncs.c            |   1 +
 src/backend/nodes/outfuncs.c              |   1 +
 src/backend/optimizer/util/plancat.c      |   4 +
 src/backend/parser/gram.y                 |  72 +++++++++------
 src/backend/utils/adt/ruleutils.c         | 126 +++++++++++++++-----------
 src/backend/utils/cache/relcache.c        |  99 +++++++++++++++++++++
 src/include/access/amapi.h                |   7 ++
 src/include/access/genam.h                |   6 ++
 src/include/access/reloptions.h           |   5 ++
 src/include/catalog/heap.h                |   1 +
 src/include/nodes/execnodes.h             |   2 +
 src/include/nodes/parsenodes.h            |   1 +
 src/include/nodes/relation.h              |   1 +
 src/include/utils/rel.h                   |   1 +
 src/include/utils/relcache.h              |   3 +
 src/include/utils/ruleutils.h             |   2 +
 src/test/regress/expected/btree_index.out |   5 ++
 src/test/regress/sql/btree_index.sql      |   4 +
 28 files changed, 502 insertions(+), 132 deletions(-)

diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 46f427b..82a3e41 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1239,7 +1239,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
    An index definition can specify an <firstterm>operator
    class</firstterm> for each column of an index.
 <synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
 </synopsis>
    The operator class identifies the operators to be used by the index
    for that column.  For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index ad619cd..1946d3b 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
-    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
      </varlistentry>
 
      <varlistentry>
+      <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+      <listitem>
+       <para>
+        The name of an operator class parameter. See below for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><literal>ASC</literal></term>
       <listitem>
        <para>
@@ -607,8 +616,9 @@ Indexes:
   </para>
 
   <para>
-   An <firstterm>operator class</firstterm> can be specified for each
-   column of an index. The operator class identifies the operators to be
+   An <firstterm>operator class</firstterm> with its optional parameters 
+   can be specified for each column of an index.
+   The operator class identifies the operators to be
    used by the index for that column. For example, a B-tree index on
    four-byte integers would use the <literal>int4_ops</literal> class;
    this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index eece89a..87c8e5c 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1040,6 +1040,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 	return options;
 }
 
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+				 relopt_value *reloptions, int numoptions)
+{
+	ArrayType  *array = DatumGetArrayTypeP(options);
+	Datum	   *optiondatums;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(array, TEXTOID, -1, false, 'i',
+					  &optiondatums, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *text_str = VARDATA(optiondatums[i]);
+		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		int			j;
+
+		/* Search for a match in reloptions */
+		for (j = 0; j < numoptions; j++)
+		{
+			int			kw_len = reloptions[j].gen->namelen;
+
+			if (text_len > kw_len && text_str[kw_len] == '=' &&
+				strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+			{
+				parse_one_reloption(&reloptions[j], text_str, text_len,
+									validate);
+				break;
+			}
+		}
+
+		if (j >= numoptions && validate)
+		{
+			char	   *s;
+			char	   *p;
+
+			s = TextDatumGetCString(optiondatums[i]);
+			p = strchr(s, '=');
+			if (p)
+				*p = '\0';
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized parameter \"%s\"", s)));
+		}
+	}
+
+	/* It's worth avoiding memory leaks in this function */
+	pfree(optiondatums);
+
+	if (((void *) array) != DatumGetPointer(options))
+		pfree(array);
+}
+
 /*
  * Interpret reloptions that are given in text-array format.
  *
@@ -1094,57 +1148,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
 
 	/* Done if no options */
 	if (PointerIsValid(DatumGetPointer(options)))
-	{
-		ArrayType  *array = DatumGetArrayTypeP(options);
-		Datum	   *optiondatums;
-		int			noptions;
+		parseRelOptionsInternal(options, validate, reloptions, numoptions);
 
-		deconstruct_array(array, TEXTOID, -1, false, 'i',
-						  &optiondatums, NULL, &noptions);
+	*numrelopts = numoptions;
+	return reloptions;
+}
 
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *text_str = VARDATA(optiondatums[i]);
-			int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
-			int			j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen *optgen[], int nopts)
+{
+	relopt_value *values = palloc(sizeof(*values) * nopts);
+	int			i;
 
-			/* Search for a match in reloptions */
-			for (j = 0; j < numoptions; j++)
-			{
-				int			kw_len = reloptions[j].gen->namelen;
+	for (i = 0; i < nopts; i++)
+	{
+		values[i].gen = optgen[i];
+		values[i].isset = false;
+	}
 
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
-				{
-					parse_one_reloption(&reloptions[j], text_str, text_len,
-										validate);
-					break;
-				}
-			}
+	if (options != (Datum) 0)
+		parseRelOptionsInternal(options, validate, values, nopts);
 
-			if (j >= numoptions && validate)
-			{
-				char	   *s;
-				char	   *p;
+	return values;
+}
 
-				s = TextDatumGetCString(optiondatums[i]);
-				p = strchr(s, '=');
-				if (p)
-					*p = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("unrecognized parameter \"%s\"", s)));
-			}
-		}
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+							int noptions, size_t base_size, bool validate)
+{
+	relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+	relopt_value *vals;
+	void	   *opts;
+	int			i;
 
-		/* It's worth avoiding memory leaks in this function */
-		pfree(optiondatums);
-		if (((void *) array) != DatumGetPointer(options))
-			pfree(array);
+	for (i = 0; i < noptions; i++)
+	{
+		elems[i].optname = optgen[i]->name;
+		elems[i].opttype = optgen[i]->type;
+		elems[i].offset = offsets[i];
 	}
 
-	*numrelopts = numoptions;
-	return reloptions;
+	vals = parseLocalRelOptions(options, validate, optgen, noptions);
+	opts = allocateReloptStruct(base_size, vals, noptions);
+	fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+	if (elems)
+		pfree(elems);
+
+	return opts;
 }
 
 /*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index eade540..2b4a7d5 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -75,11 +75,14 @@
 #include "access/xlog.h"
 #include "catalog/index.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 #include "utils/tqual.h"
 
 
@@ -967,3 +970,81 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
 		}
 	}
 }
+
+/* ----------------
+ *      index_opclass_options
+ *
+ *      Parse opclass-specific options for index column.
+ * ----------------
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum attoptions,
+					  bool validate)
+{
+	amopclassoptions_function amopclassoptions =
+		relation->rd_amroutine->amopclassoptions;
+
+	if (!amopclassoptions)
+	{
+		if (validate && PointerIsValid(DatumGetPointer(attoptions)))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("access method \"%s\" does not support opclass options ",
+							get_am_name(relation->rd_rel->relam))));
+
+		return NULL;
+	}
+
+	return amopclassoptions(relation, attnum, attoptions, validate);
+}
+
+/* ----------------
+ *      index_opclass_options_generic
+ *
+ *      Parse opclass options for index column using the specified support
+ *      function 'procnum' of column's opclass.
+ * ----------------
+ */
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+							  uint16 procnum, Datum attoptions, bool validate)
+{
+	Oid			procid = index_getprocid(indrel, attnum, procnum);
+	FmgrInfo   *procinfo;
+
+	if (!OidIsValid(procid))
+	{
+		StringInfoData opclassname;
+		Oid			opclass;
+		Datum		indclassDatum;
+		oidvector  *indclass;
+		bool		isnull;
+
+		if (!DatumGetPointer(attoptions))
+			return NULL;	/* ok, no options, no procedure */
+
+		/*
+		 * Report an error if the opclass's options-parsing procedure does not
+		 * exist but the opclass options are specified.
+		 */
+		indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+										Anum_pg_index_indclass, &isnull);
+		Assert(!isnull);
+		indclass = (oidvector *) DatumGetPointer(indclassDatum);
+		opclass = indclass->values[attnum - 1];
+
+		initStringInfo(&opclassname);
+		get_opclass_name(opclass, InvalidOid, &opclassname);
+
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("operator class \"%s\" has no options",
+						opclassname.data)));
+	}
+
+	procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+	return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+												   attoptions,
+												   BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 11debaa..dc42338 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -647,6 +647,7 @@ CheckAttributeType(const char *attname,
 void
 InsertPgAttributeTuple(Relation pg_attribute_rel,
 					   Form_pg_attribute new_attribute,
+					   Datum attoptions,
 					   CatalogIndexState indstate)
 {
 	Datum		values[Natts_pg_attribute];
@@ -677,10 +678,11 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
 	values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
 	values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
 	values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+	values[Anum_pg_attribute_attoptions - 1] = attoptions;
 
 	/* start out with empty permissions and empty options */
 	nulls[Anum_pg_attribute_attacl - 1] = true;
-	nulls[Anum_pg_attribute_attoptions - 1] = true;
+	nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0;
 	nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
 	nulls[Anum_pg_attribute_attmissingval - 1] = true;
 
@@ -734,7 +736,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		/* Make sure this is OK, too */
 		attr->attstattarget = -1;
 
-		InsertPgAttributeTuple(rel, attr, indstate);
+		InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate);
 
 		/* Add dependency info */
 		myself.classId = RelationRelationId;
@@ -772,7 +774,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 			/* Fill in the correct relation OID in the copied tuple */
 			attStruct.attrelid = new_rel_oid;
 
-			InsertPgAttributeTuple(rel, &attStruct, indstate);
+			InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate);
 		}
 	}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 8709e8c..f676796 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -25,6 +25,7 @@
 
 #include "access/amapi.h"
 #include "access/multixact.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/reloptions.h"
 #include "access/sysattr.h"
@@ -113,7 +114,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
 						 Oid *classObjectId);
 static void InitializeAttributeOids(Relation indexRelation,
 						int numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, int numatts);
+static void AppendAttributeTuples(Relation indexRelation, int numatts,
+					  Datum *attopts);
 static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 					Oid parentIndexId,
 					IndexInfo *indexInfo,
@@ -517,7 +519,7 @@ InitializeAttributeOids(Relation indexRelation,
  * ----------------------------------------------------------------
  */
 static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
 {
 	Relation	pg_attribute;
 	CatalogIndexState indstate;
@@ -539,10 +541,11 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
 	for (i = 0; i < numatts; i++)
 	{
 		Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
+		Datum		attoptions = attopts ? attopts[i] : (Datum) 0;
 
 		Assert(attr->attnum == i + 1);
 
-		InsertPgAttributeTuple(pg_attribute, attr, indstate);
+		InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate);
 	}
 
 	CatalogCloseIndexes(indstate);
@@ -622,6 +625,7 @@ UpdateIndexRelation(Oid indexoid,
 	else
 		predDatum = (Datum) 0;
 
+
 	/*
 	 * open the system catalog index relation
 	 */
@@ -957,7 +961,8 @@ index_create(Relation heapRelation,
 	/*
 	 * append ATTRIBUTE tuples for the index
 	 */
-	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs,
+						  indexInfo->ii_OpclassOptions);
 
 	/* ----------------
 	 *	  update pg_index
@@ -1162,6 +1167,12 @@ index_create(Relation heapRelation,
 
 	indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
 
+	/* Validate opclass-specific options */
+	if (indexInfo->ii_OpclassOptions)
+		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+			(void) index_opclass_options(indexRelation, i + 1,
+										 indexInfo->ii_OpclassOptions[i], true);
+
 	/*
 	 * If this is bootstrap (initdb) time, then we don't actually fill in the
 	 * index yet.  We'll be creating more indexes and classes later, so we
@@ -1790,6 +1801,10 @@ BuildIndexInfo(Relation index)
 		ii->ii_ExclusionStrats = NULL;
 	}
 
+	ii->ii_OpclassOptions =
+		RelationGetRawOpclassOptions(RelationGetRelid(index),
+									 RelationGetNumberOfAttributes(index));
+
 	/* other info */
 	ii->ii_Unique = indexStruct->indisunique;
 	ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 462969a..19d52da 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -311,6 +311,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;
 	indexInfo->ii_Unique = true;
 	indexInfo->ii_ReadyForInserts = true;
 	indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 6c06167..1770081 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -642,6 +642,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;	/* for now */
 	indexInfo->ii_Unique = stmt->unique;
 	/* In a concurrent build, mark it not-ready-for-inserts */
 	indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1403,7 +1404,7 @@ CheckPredicate(Expr *predicate)
 
 /*
  * Compute per-index-column information, including indexed column numbers
- * or index expressions, opclasses, and indoptions. Note, all output vectors
+ * or index expressions, opclasses and their options. Note, all output vectors
  * should be allocated for all columns, including "including" ones.
  */
 static void
@@ -1704,6 +1705,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 								accessMethodName)));
 		}
 
+		/* Set up the per-column opclass options (attoptions field). */
+		if (attribute->opclassopts)
+		{
+			Assert(attn < nkeycols);
+
+			if (!indexInfo->ii_OpclassOptions)
+				indexInfo->ii_OpclassOptions =
+					palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+
+			indexInfo->ii_OpclassOptions[attn] =
+				transformRelOptions((Datum) 0, attribute->opclassopts,
+									NULL, NULL, false, false);
+		}
+
 		attn++;
 	}
 }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 843ed48..63df4f6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5464,7 +5464,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	ReleaseSysCache(typeTuple);
 
-	InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+	InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
 
 	heap_close(attrdesc, RowExclusiveLock);
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index db49968..ad3ff3c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2856,6 +2856,7 @@ _copyIndexElem(const IndexElem *from)
 	COPY_STRING_FIELD(indexcolname);
 	COPY_NODE_FIELD(collation);
 	COPY_NODE_FIELD(opclass);
+	COPY_NODE_FIELD(opclassopts);
 	COPY_SCALAR_FIELD(ordering);
 	COPY_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a084b4..e3dfe6e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2538,6 +2538,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
 	COMPARE_STRING_FIELD(indexcolname);
 	COMPARE_NODE_FIELD(collation);
 	COMPARE_NODE_FIELD(opclass);
+	COMPARE_NODE_FIELD(opclassopts);
 	COMPARE_SCALAR_FIELD(ordering);
 	COMPARE_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f0c3965..a23c508 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2931,6 +2931,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
 	WRITE_STRING_FIELD(indexcolname);
 	WRITE_NODE_FIELD(collation);
 	WRITE_NODE_FIELD(opclass);
+	WRITE_NODE_FIELD(opclassopts);
 	WRITE_ENUM_FIELD(ordering, SortByDir);
 	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
 }
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a570ac0..0dad458 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -361,6 +361,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				info->nulls_first = NULL;
 			}
 
+			/* Fetch index opclass options */
+			info->opclassoptions =
+				RelationGetParsedOpclassOptions(indexRelation);
+
 			/*
 			 * Fetch the index expressions and predicate, if any.  We must
 			 * modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2c2208f..e5d3a74 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -485,7 +485,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <alias>	alias_clause opt_alias_clause
 %type <list>	func_alias_clause
 %type <sortby>	sortby
-%type <ielem>	index_elem
+%type <ielem>	index_elem index_elem_options
 %type <node>	table_ref
 %type <jexpr>	joined_table
 %type <range>	relation_expr
@@ -7411,43 +7411,65 @@ index_params:	index_elem							{ $$ = list_make1($1); }
 			| index_params ',' index_elem			{ $$ = lappend($1, $3); }
 		;
 
+
+index_elem_options:
+	opt_collate opt_class opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = NIL;
+			$$->ordering = $3;
+			$$->nulls_ordering = $4;
+		}
+	| opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	| opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = NIL;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	;
+
 /*
  * Index attributes can be either simple column references, or arbitrary
  * expressions in parens.  For backwards-compatibility reasons, we allow
  * an expression that's just a function call to be written without parens.
  */
-index_elem:	ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
 				{
-					$$ = makeNode(IndexElem);
+					$$ = $2;
 					$$->name = $1;
-					$$->expr = NULL;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+			| func_expr_windowless index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $2;
 					$$->expr = $1;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+			| '(' a_expr ')' index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $4;
 					$$->expr = $2;
-					$$->indexcolname = NULL;
-					$$->collation = $4;
-					$$->opclass = $5;
-					$$->ordering = $6;
-					$$->nulls_ordering = $7;
 				}
 		;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 4857cae..0ba0bf5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -452,8 +452,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
 						   deparse_context *context);
 static void get_tablesample_def(TableSampleClause *tablesample,
 					deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
-				 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context);
 static void printSubscripts(ArrayRef *aref, deparse_context *context);
 static char *get_relation_name(Oid relid);
@@ -468,6 +466,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1197,6 +1196,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	oidvector  *indcollation;
 	oidvector  *indclass;
 	int2vector *indoption;
+	Datum	   *opcoptions = NULL;
 	StringInfoData buf;
 	char	   *str;
 	char	   *sep;
@@ -1232,6 +1232,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	Assert(!isnull);
 	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
 
+	if (!attrsOnly)
+		opcoptions = RelationGetRawOpclassOptions(indexrelid, idxrec->indnatts);
+
 	/*
 	 * Fetch the pg_class tuple of the index relation
 	 */
@@ -1370,6 +1373,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 			(!colno || colno == keyno + 1))
 		{
 			Oid			indcoll;
+			bool		has_options =
+				opcoptions && opcoptions[keyno] != (Datum) 0;
 
 			/* Add collation, if not default for column */
 			indcoll = indcollation->values[keyno];
@@ -1378,7 +1383,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 								 generate_collation_name((indcoll)));
 
 			/* Add the operator class name, if not default */
-			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+			get_opclass_name(indclass->values[keyno],
+							 has_options ? InvalidOid : keycoltype, &buf);
+
+			if (has_options)
+			{
+				appendStringInfoString(&buf, " (");
+				get_reloptions(&buf, opcoptions[keyno]);
+				appendStringInfoChar(&buf, ')');
+			}
 
 			/* Add options if relevant */
 			if (amroutine->amcanorder)
@@ -10400,7 +10413,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
  * actual_datatype.  (If you don't want this behavior, just pass
  * InvalidOid for actual_datatype.)
  */
-static void
+void
 get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf)
 {
@@ -11111,6 +11124,62 @@ string_to_text(char *str)
 }
 
 /*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+	Datum	   *options;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(reloptions),
+					  TEXTOID, -1, false, 'i',
+					  &options, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *option = TextDatumGetCString(options[i]);
+		char	   *name;
+		char	   *separator;
+		char	   *value;
+
+		/*
+		 * Each array element should have the form name=value.  If the "="
+		 * is missing for some reason, treat it like an empty value.
+		 */
+		name = option;
+		separator = strchr(option, '=');
+		if (separator)
+		{
+			*separator = '\0';
+			value = separator + 1;
+		}
+		else
+			value = "";
+
+		if (i > 0)
+			appendStringInfoString(buf, ", ");
+		appendStringInfo(buf, "%s=", quote_identifier(name));
+
+		/*
+		 * In general we need to quote the value; but to avoid unnecessary
+		 * clutter, do not quote if it is an identifier that would not
+		 * need quoting.  (We could also allow numbers, but that is a bit
+		 * trickier than it looks --- for example, are leading zeroes
+		 * significant?  We don't want to assume very much here about what
+		 * custom reloptions might mean.)
+		 */
+		if (quote_identifier(value) == value)
+			appendStringInfoString(buf, value);
+		else
+			simple_quote_literal(buf, value);
+
+		pfree(option);
+	}
+}
+
+/*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
 static char *
@@ -11130,56 +11199,9 @@ flatten_reloptions(Oid relid)
 	if (!isnull)
 	{
 		StringInfoData buf;
-		Datum	   *options;
-		int			noptions;
-		int			i;
 
 		initStringInfo(&buf);
-
-		deconstruct_array(DatumGetArrayTypeP(reloptions),
-						  TEXTOID, -1, false, 'i',
-						  &options, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *option = TextDatumGetCString(options[i]);
-			char	   *name;
-			char	   *separator;
-			char	   *value;
-
-			/*
-			 * Each array element should have the form name=value.  If the "="
-			 * is missing for some reason, treat it like an empty value.
-			 */
-			name = option;
-			separator = strchr(option, '=');
-			if (separator)
-			{
-				*separator = '\0';
-				value = separator + 1;
-			}
-			else
-				value = "";
-
-			if (i > 0)
-				appendStringInfoString(&buf, ", ");
-			appendStringInfo(&buf, "%s=", quote_identifier(name));
-
-			/*
-			 * In general we need to quote the value; but to avoid unnecessary
-			 * clutter, do not quote if it is an identifier that would not
-			 * need quoting.  (We could also allow numbers, but that is a bit
-			 * trickier than it looks --- for example, are leading zeroes
-			 * significant?  We don't want to assume very much here about what
-			 * custom reloptions might mean.)
-			 */
-			if (quote_identifier(value) == value)
-				appendStringInfoString(&buf, value);
-			else
-				simple_quote_literal(&buf, value);
-
-			pfree(option);
-		}
+		get_reloptions(&buf, reloptions);
 
 		result = buf.data;
 	}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index c3071db..fa87ff5 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5192,6 +5192,105 @@ GetRelationPublicationActions(Relation relation)
 }
 
 /*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for the index
+ */
+Datum *
+RelationGetRawOpclassOptions(Oid indexrelid, int16 natts)
+{
+	Datum	   *options = NULL;
+	int16		attnum;
+
+	for (attnum = 1; attnum <= natts; attnum++)
+	{
+		HeapTuple	tuple;
+		Datum		attopts;
+		bool		isnull;
+
+		tuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(indexrelid),
+								Int16GetDatum(attnum));
+
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+				 attnum, indexrelid);
+
+		attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+								  &isnull);
+
+		if (!isnull)
+		{
+			if (!options)
+				options = palloc0(sizeof(Datum) * natts);
+
+			options[attnum - 1] = datumCopy(attopts, false, -1);	/* text */
+		}
+
+		ReleaseSysCache(tuple);
+	}
+
+	return options;
+}
+
+/*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+	MemoryContext oldcxt;
+	bytea	  **opts;
+	Datum	   *rawopts;
+	int			natts = RelationGetNumberOfAttributes(relation);
+	int			i;
+
+	/* Try to copy cached options. */
+	if (relation->rd_opcoptions)
+	{
+		opts = palloc(sizeof(*opts) * natts);
+
+		for (i = 0; i < natts; i++)
+		{
+			bytea	   *opt = relation->rd_opcoptions[i];
+
+			opts[i] = !opt ? NULL : (bytea *)
+				DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+		}
+
+		return opts;
+	}
+
+	/* Get and parse opclass options. */
+	opts = palloc0(sizeof(*opts) * natts);
+
+	rawopts = RelationGetRawOpclassOptions(RelationGetRelid(relation), natts);
+
+	for (i = 0; i < natts; i++)
+	{
+		Datum		options = rawopts ? rawopts[i] : (Datum) 0;
+
+		opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+		if (options != (Datum) 0)
+			pfree(DatumGetPointer(options));
+	}
+
+	if (rawopts)
+		pfree(rawopts);
+
+	/* Copy parsed options to the cache. */
+	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+	relation->rd_opcoptions = palloc(sizeof(*opts) * natts);
+
+	for (i = 0; i < natts; i++)
+		relation->rd_opcoptions[i] = !opts[i] ? NULL : (bytea *)
+			DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return opts;
+}
+
+/*
  * Routines to support ereport() reports of relation-related errors
  *
  * These could have been put into elog.c, but it seems like a module layering
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 14526a6..de55802 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -103,6 +103,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
 typedef bytea *(*amoptions_function) (Datum reloptions,
 									  bool validate);
 
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+											 AttrNumber colno,
+											 Datum attoptions,
+											 bool validate);
+
 /* report AM, index, or index column property */
 typedef bool (*amproperty_function) (Oid index_oid, int attno,
 									 IndexAMProperty prop, const char *propname,
@@ -212,6 +218,7 @@ typedef struct IndexAmRoutine
 	amcanreturn_function amcanreturn;	/* can be NULL */
 	amcostestimate_function amcostestimate;
 	amoptions_function amoptions;
+	amopclassoptions_function amopclassoptions;
 	amproperty_function amproperty; /* can be NULL */
 	amvalidate_function amvalidate;
 	ambeginscan_function ambeginscan;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 534fac7..ba2fa92 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -177,6 +177,12 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
 extern void index_store_float8_orderby_distances(IndexScanDesc scan,
 									 Oid *orderByTypes, double *distances,
 									 bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+					  Datum attoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+							  AttrNumber attnum, uint16 procnum,
+							  Datum attoptions, bool validate);
+
 
 /*
  * index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 50690b9..7eadb0e 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -264,12 +264,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 				  amoptions_function amoptions);
 extern relopt_value *parseRelOptions(Datum options, bool validate,
 				relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen **gen, int nelems);
 extern void *allocateReloptStruct(Size base, relopt_value *options,
 					 int numoptions);
 extern void fillRelOptions(void *rdopts, Size basesize,
 			   relopt_value *options, int numoptions,
 			   bool validate,
 			   const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+							int offsets[], int noptions, size_t base_size,
+							bool validate);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
 				   relopt_kind kind);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 56a341a..a92517c 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -87,6 +87,7 @@ extern List *heap_truncate_find_FKs(List *relationIds);
 
 extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 					   Form_pg_attribute new_attribute,
+					   Datum attoptions,
 					   CatalogIndexState indstate);
 
 extern void InsertPgClassTuple(Relation pg_class_desc,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5ed0f40..eddacd4 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -137,6 +137,7 @@ typedef struct ExprState
  *		UniqueProcs
  *		UniqueStrats
  *		Unique				is it a unique index?
+ *		OpclassOptions		opclass-specific options, or NULL if none
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
  *		BrokenHotChain		did we detect any broken HOT chains?
@@ -165,6 +166,7 @@ typedef struct IndexInfo
 	Oid		   *ii_UniqueOps;	/* array with one entry per column */
 	Oid		   *ii_UniqueProcs; /* array with one entry per column */
 	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	Datum	   *ii_OpclassOptions;	/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e5bdc1c..4a304c0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -699,6 +699,7 @@ typedef struct IndexElem
 	char	   *indexcolname;	/* name for index column; NULL = default */
 	List	   *collation;		/* name of collation; NIL = default */
 	List	   *opclass;		/* name of desired opclass; NIL = default */
+	List	   *opclassopts;	/* opclass-specific options, or NIL */
 	SortByDir	ordering;		/* ASC/DESC/default */
 	SortByNulls nulls_ordering; /* FIRST/LAST/default */
 } IndexElem;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6fd2420..fba493e 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -783,6 +783,7 @@ typedef struct IndexOptInfo
 	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
 	bool	   *reverse_sort;	/* is sort order descending? */
 	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
+	bytea	 **opclassoptions;	/* opclass-specific options for columns */
 	bool	   *canreturn;		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 2217081..0f649c1 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -162,6 +162,7 @@ typedef struct RelationData
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
 	void	   *rd_amcache;		/* available for use by index AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
+	bytea	  **rd_opcoptions;	/* parsed opclass-specific options */
 
 	/*
 	 * foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index a99d6b6..79719e2 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
 #ifndef RELCACHE_H
 #define RELCACHE_H
 
+#include "postgres.h"
 #include "access/tupdesc.h"
 #include "nodes/bitmapset.h"
 
@@ -49,6 +50,8 @@ extern Oid	RelationGetPrimaryKeyIndex(Relation relation);
 extern Oid	RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Oid indexrelid, int16 natts);
 
 typedef enum IndexAttrBitmapKind
 {
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9f9b029..86e6953 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,5 +34,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
 								Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
 extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
 
 #endif							/* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..e420f10 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,8 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
  {vacuum_cleanup_index_scale_factor=70.0}
 (1 row)
 
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  access method "btree" does not support opclass options 
+create index on btree_tall_tbl (id default(foo=1));
+ERROR:  access method "btree" does not support opclass options 
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..a3f7d9b 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,7 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
 -- Simple ALTER INDEX
 alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
 select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
-- 
2.7.4

0002-Add-opclass-parameters-to-GiST-v03.patchtext/x-patch; name=0002-Add-opclass-parameters-to-GiST-v03.patchDownload
From 4f8072a2bdb9ca83db9da45df597404a1544faa1 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 18:58:39 +0300
Subject: [PATCH 2/9] Add opclass parameters to GiST

---
 doc/src/sgml/xindex.sgml               |  5 ++++
 src/backend/access/gist/gist.c         |  2 ++
 src/backend/access/gist/gistget.c      | 12 ++++++----
 src/backend/access/gist/gistsplit.c    | 15 +++++++-----
 src/backend/access/gist/gistutil.c     | 44 +++++++++++++++++++++++-----------
 src/backend/access/gist/gistvalidate.c | 34 ++++++++++++++++----------
 src/include/access/gist.h              |  3 ++-
 src/include/access/gist_private.h      |  4 ++++
 8 files changed, 82 insertions(+), 37 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..8c5b528 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
        index-only scans (optional)</entry>
        <entry>9</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>parse opclass-specific options (optional)</entry>
+       <entry>10</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..905cbbf 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -97,6 +97,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->amopclassoptions = gistopclassoptions;
 
 	PG_RETURN_POINTER(amroutine);
 }
@@ -1457,6 +1458,7 @@ initGISTstate(Relation index)
 	giststate->scanCxt = scanCxt;
 	giststate->tempCxt = scanCxt;	/* caller must change this if needed */
 	giststate->tupdesc = index->rd_att;
+	giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < index->rd_att->natts; i++)
 	{
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index e4a3786..9302e68 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		test;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 */
 			recheck = true;
 
-			test = FunctionCall5Coll(&key->sk_func,
+			test = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 
 			if (!DatumGetBool(test))
 				return false;
@@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		dist;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 * about the flag, but are expected to never be lossy.
 			 */
 			recheck = false;
-			dist = FunctionCall5Coll(&key->sk_func,
+			dist = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 			*recheck_distances_p |= recheck;
 			*distance_p = DatumGetFloat8(dist);
 		}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index a7038cc..96a9290 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
 	evec->n = v->spl_nleft;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	evec->n = v->spl_nright;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 }
 
 /*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
 	 * Let the opclass-specific PickSplit method do its thing.  Note that at
 	 * this point we know there are no null keys in the entryvec.
 	 */
-	FunctionCall2Coll(&giststate->picksplitFn[attno],
+	FunctionCall3Coll(&giststate->picksplitFn[attno],
 					  giststate->supportCollation[attno],
 					  PointerGetDatum(entryvec),
-					  PointerGetDatum(sv));
+					  PointerGetDatum(sv),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	if (sv->spl_nleft == 0 || sv->spl_nright == 0)
 	{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 70627e5..567cdf1 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -199,10 +199,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
 			}
 
 			/* Make union and store in attr array */
-			attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+			attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
 										giststate->supportCollation[i],
 										PointerGetDatum(evec),
-										PointerGetDatum(&attrsize));
+										PointerGetDatum(&attrsize),
+										PointerGetDatum(giststate->opclassoptions[i]));
 
 			isnull[i] = false;
 		}
@@ -268,10 +269,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
 		}
 
 		*dstisnull = false;
-		*dst = FunctionCall2Coll(&giststate->unionFn[attno],
+		*dst = FunctionCall3Coll(&giststate->unionFn[attno],
 								 giststate->supportCollation[attno],
 								 PointerGetDatum(evec),
-								 PointerGetDatum(&dstsize));
+								 PointerGetDatum(&dstsize),
+								 PointerGetDatum(giststate->opclassoptions[attno]));
 	}
 }
 
@@ -280,10 +282,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
 {
 	bool		result;
 
-	FunctionCall3Coll(&giststate->equalFn[attno],
+	FunctionCall4Coll(&giststate->equalFn[attno],
 					  giststate->supportCollation[attno],
 					  a, b,
-					  PointerGetDatum(&result));
+					  PointerGetDatum(&result),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 	return result;
 }
 
@@ -556,9 +559,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
 			return;
 
 		dep = (GISTENTRY *)
-			DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+			DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
 											  giststate->supportCollation[nkey],
-											  PointerGetDatum(e)));
+											  PointerGetDatum(e),
+											  PointerGetDatum(giststate->opclassoptions[nkey])));
 		/* decompressFn may just return the given pointer */
 		if (dep != e)
 			gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -593,9 +597,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
 			/* there may not be a compress function in opclass */
 			if (OidIsValid(giststate->compressFn[i].fn_oid))
 				cep = (GISTENTRY *)
-					DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+					DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
 													  giststate->supportCollation[i],
-													  PointerGetDatum(&centry)));
+													  PointerGetDatum(&centry),
+													  PointerGetDatum(giststate->opclassoptions[i])));
 			else
 				cep = &centry;
 			compatt[i] = cep->key;
@@ -624,9 +629,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
 	gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
 
 	fep = (GISTENTRY *)
-		DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+		DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
 										  giststate->supportCollation[nkey],
-										  PointerGetDatum(&fentry)));
+										  PointerGetDatum(&fentry),
+										  PointerGetDatum(giststate->opclassoptions[nkey])));
 
 	/* fetchFn set 'key', return it to the caller */
 	return fep->key;
@@ -694,11 +700,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
 	if (giststate->penaltyFn[attno].fn_strict == false ||
 		(isNullOrig == false && isNullAdd == false))
 	{
-		FunctionCall3Coll(&giststate->penaltyFn[attno],
+		FunctionCall4Coll(&giststate->penaltyFn[attno],
 						  giststate->supportCollation[attno],
 						  PointerGetDatum(orig),
 						  PointerGetDatum(add),
-						  PointerGetDatum(&penalty));
+						  PointerGetDatum(&penalty),
+						  PointerGetDatum(giststate->opclassoptions[attno]));
 		/* disallow negative or NaN penalty */
 		if (isnan(penalty) || penalty < 0.0)
 			penalty = 0.0;
@@ -860,6 +867,15 @@ gistoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum attoptions,
+				   bool validate)
+{
+	return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+										 attoptions, validate);
+}
+
+
 /*
  *	gistproperty() -- Check boolean properties of indexes.
  *
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index c300e52..c5e1b23 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
 		{
 			case GIST_CONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_UNION_PROC:
 				ok = check_amproc_signature(procform->amproc, opckeytype, false,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_COMPRESS_PROC:
 			case GIST_DECOMPRESS_PROC:
 			case GIST_FETCH_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											1, 1, INTERNALOID);
+											1, 2, INTERNALOID, INTERNALOID);
 				break;
 			case GIST_PENALTY_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											3, 3, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											3, 4, INTERNALOID,
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_PICKSPLIT_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_EQUAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											3, 3, opckeytype, opckeytype,
-											INTERNALOID);
+											3, 4, opckeytype, opckeytype,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIST_DISTANCE_PROC:
 				ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
+				break;
+			case GIST_OPCLASSOPT_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+											2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
 		if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
-			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+			i == GIST_OPCLASSOPT_PROC)
 			continue;			/* optional methods */
 		ereport(INFO,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 827566d..08e7ada 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
 #define GIST_EQUAL_PROC					7
 #define GIST_DISTANCE_PROC				8
 #define GIST_FETCH_PROC					9
-#define GISTNProcs					9
+#define GIST_OPCLASSOPT_PROC			10
+#define GISTNProcs						10
 
 /*
  * Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 36ed724..eedd896 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -94,6 +94,8 @@ typedef struct GISTSTATE
 
 	/* Collations to pass to the support functions */
 	Oid			supportCollation[INDEX_MAX_KEYS];
+
+	bytea	  **opclassoptions;	/* parsed opclass-specific options */
 } GISTSTATE;
 
 
@@ -436,6 +438,8 @@ extern bool gistvalidate(Oid opclassoid);
 #define GIST_DEFAULT_FILLFACTOR		90
 
 extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+				   Datum options, bool validate);
 extern bool gistproperty(Oid index_oid, int attno,
 			 IndexAMProperty prop, const char *propname,
 			 bool *res, bool *isnull);
-- 
2.7.4

0003-Add-opclass-parameters-to-GIN-v03.patchtext/x-patch; name=0003-Add-opclass-parameters-to-GIN-v03.patchDownload
From 332930913ea6d90e6c61cf7093168ebd4d75cd05 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 01:50:09 +0300
Subject: [PATCH 3/9] Add opclass parameters to GIN

---
 doc/src/sgml/xindex.sgml             |  7 +++++++
 src/backend/access/gin/ginget.c      | 11 ++++++-----
 src/backend/access/gin/ginlogic.c    | 15 +++++++++------
 src/backend/access/gin/ginscan.c     |  6 ++++--
 src/backend/access/gin/ginutil.c     | 27 +++++++++++++++++++++------
 src/backend/access/gin/ginvalidate.c | 32 ++++++++++++++++++++------------
 src/backend/utils/adt/selfuncs.c     |  6 ++++--
 src/include/access/gin.h             |  3 ++-
 src/include/access/gin_private.h     |  5 +++++
 9 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b528..658ec9b 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
        </entry>
        <entry>6</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>
+        parse opclass-specific options (optional)
+       </entry>
+       <entry>7</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 8466d94..5eb47e0 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
 			 * case cmp < 0 => not match and continue scan
 			 *----------
 			 */
-			cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+			cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
 												  btree->ginstate->supportCollation[attnum - 1],
 												  scanEntry->queryKey,
 												  idatum,
 												  UInt16GetDatum(scanEntry->strategy),
-												  PointerGetDatum(scanEntry->extra_data)));
-
+												  PointerGetDatum(scanEntry->extra_data),
+												  PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
 			if (cmp > 0)
 				return true;
 			else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
 		 * case cmp < 0 => not match and continue scan
 		 *----------
 		 */
-		cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+		cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
 											  ginstate->supportCollation[entry->attnum - 1],
 											  entry->queryKey,
 											  datum[off - 1],
 											  UInt16GetDatum(entry->strategy),
-											  PointerGetDatum(entry->extra_data)));
+											  PointerGetDatum(entry->extra_data),
+											  PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
 		if (cmp == 0)
 			return true;
 		else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
 	 */
 	key->recheckCurItem = true;
 
-	return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+	return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
 										  key->collation,
 										  PointerGetDatum(key->entryRes),
 										  UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
 										  PointerGetDatum(key->extra_data),
 										  PointerGetDatum(&key->recheckCurItem),
 										  PointerGetDatum(key->queryValues),
-										  PointerGetDatum(key->queryCategories)));
+										  PointerGetDatum(key->queryCategories),
+										  PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
 static GinTernaryValue
 directTriConsistentFn(GinScanKey key)
 {
-	return DatumGetGinTernaryValue(FunctionCall7Coll(
+	return DatumGetGinTernaryValue(FunctionCall8Coll(
 													 key->triConsistentFmgrInfo,
 													 key->collation,
 													 PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
 													 UInt32GetDatum(key->nuserentries),
 													 PointerGetDatum(key->extra_data),
 													 PointerGetDatum(key->queryValues),
-													 PointerGetDatum(key->queryCategories)));
+													 PointerGetDatum(key->queryCategories),
+													 PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
 {
 	GinTernaryValue result;
 
-	result = DatumGetGinTernaryValue(FunctionCall7Coll(
+	result = DatumGetGinTernaryValue(FunctionCall8Coll(
 													   key->triConsistentFmgrInfo,
 													   key->collation,
 													   PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
 													   UInt32GetDatum(key->nuserentries),
 													   PointerGetDatum(key->extra_data),
 													   PointerGetDatum(key->queryValues),
-													   PointerGetDatum(key->queryCategories)));
+													   PointerGetDatum(key->queryCategories),
+													   PointerGetDatum(key->opclassOptions)));
 	if (result == GIN_MAYBE)
 	{
 		key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
 	key->strategy = strategy;
 	key->searchMode = searchMode;
 	key->attnum = attnum;
+	key->opclassOptions = ginstate->opclassOptions[attnum - 1];
 
 	ItemPointerSetMin(&key->curItem);
 	key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
 
 		/* OK to call the extractQueryFn */
 		queryValues = (Datum *)
-			DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+			DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
 											  so->ginstate.supportCollation[skey->sk_attno - 1],
 											  skey->sk_argument,
 											  PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
 											  PointerGetDatum(&partial_matches),
 											  PointerGetDatum(&extra_data),
 											  PointerGetDatum(&nullFlags),
-											  PointerGetDatum(&searchMode)));
+											  PointerGetDatum(&searchMode),
+											  PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
 
 		/*
 		 * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index d7696a1..d4f5b4c 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = gincostestimate;
 	amroutine->amoptions = ginoptions;
+	amroutine->amopclassoptions = ginopclassoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = ginvalidate;
 	amroutine->ambeginscan = ginbeginscan;
@@ -95,6 +96,7 @@ initGinState(GinState *state, Relation index)
 	state->index = index;
 	state->oneCol = (origTupdesc->natts == 1) ? true : false;
 	state->origTupdesc = origTupdesc;
+	state->opclassOptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < origTupdesc->natts; i++)
 	{
@@ -403,9 +405,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
 		return 0;
 
 	/* both not null, so safe to call the compareFn */
-	return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+	return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
 										   ginstate->supportCollation[attnum - 1],
-										   a, b));
+										   a, b,
+										   PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 }
 
 /*
@@ -441,6 +444,7 @@ typedef struct
 {
 	FmgrInfo   *cmpDatumFunc;
 	Oid			collation;
+	Datum		options;
 	bool		haveDups;
 } cmpEntriesArg;
 
@@ -462,9 +466,10 @@ cmpEntries(const void *a, const void *b, void *arg)
 	else if (bb->isnull)
 		res = -1;				/* not-NULL "<" NULL */
 	else
-		res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+		res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
 											  data->collation,
-											  aa->datum, bb->datum));
+											  aa->datum, bb->datum,
+											  data->options));
 
 	/*
 	 * Detect if we have any duplicates.  If there are equal keys, qsort must
@@ -510,11 +515,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 	/* OK, call the opclass's extractValueFn */
 	nullFlags = NULL;			/* in case extractValue doesn't set it */
 	entries = (Datum *)
-		DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+		DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
 										  ginstate->supportCollation[attnum - 1],
 										  value,
 										  PointerGetDatum(nentries),
-										  PointerGetDatum(&nullFlags)));
+										  PointerGetDatum(&nullFlags),
+										  PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 
 	/*
 	 * Generate a placeholder if the item contained no keys.
@@ -557,6 +563,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 
 		arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
 		arg.collation = ginstate->supportCollation[attnum - 1];
+		arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
 		arg.haveDups = false;
 		qsort_arg(keydata, *nentries, sizeof(keyEntryData),
 				  cmpEntries, (void *) &arg);
@@ -632,6 +639,14 @@ ginoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum attoptions,
+				  bool validate)
+{
+	return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+										 attoptions, validate);
+}
+
 /*
  * Fetch index's statistical data into *stats
  *
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1922260..4f87d12 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
 		{
 			case GIN_COMPARE_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											2, 2, opckeytype, opckeytype);
+											2, 3, opckeytype, opckeytype,
+											INTERNALOID);
 				break;
 			case GIN_EXTRACTVALUE_PROC:
 				/* Some opclasses omit nullFlags */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											2, 3, opcintype, INTERNALOID,
-											INTERNALOID);
+											2, 4, opcintype, INTERNALOID,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIN_EXTRACTQUERY_PROC:
 				/* Some opclasses omit nullFlags and searchMode */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											5, 7, opcintype, INTERNALOID,
+											5, 8, opcintype, INTERNALOID,
 											INT2OID, INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_CONSISTENT_PROC:
 				/* Some opclasses omit queryKeys and nullFlags */
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											6, 8, INTERNALOID, INT2OID,
+											6, 9, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_COMPARE_PARTIAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											4, 4, opckeytype, opckeytype,
-											INT2OID, INTERNALOID);
+											4, 5, opckeytype, opckeytype,
+											INT2OID, INTERNALOID, INTERNALOID);
 				break;
 			case GIN_TRICONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, CHAROID, false,
-											7, 7, INTERNALOID, INT2OID,
+											7, 8, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID);
+											INTERNALOID, INTERNALOID);
+				break;
+			case GIN_OPCLASSOPTIONS_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID,
+											false, 2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
 		if (opclassgroup &&
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
-		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+			i == GIN_OPCLASSOPTIONS_PROC)
 			continue;			/* optional method */
 		if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
 			continue;			/* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index ffca0fe..67431c7 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -139,6 +139,7 @@
 #include "utils/lsyscache.h"
 #include "utils/pg_locale.h"
 #include "utils/rel.h"
+#include "utils/relcache.h"
 #include "utils/selfuncs.h"
 #include "utils/snapmgr.h"
 #include "utils/spccache.h"
@@ -7474,7 +7475,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	else
 		collation = DEFAULT_COLLATION_OID;
 
-	OidFunctionCall7Coll(extractProcOid,
+	OidFunctionCall8Coll(extractProcOid,
 						 collation,
 						 query,
 						 PointerGetDatum(&nentries),
@@ -7482,7 +7483,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 						 PointerGetDatum(&partial_matches),
 						 PointerGetDatum(&extra_data),
 						 PointerGetDatum(&nullFlags),
-						 PointerGetDatum(&searchMode));
+						 PointerGetDatum(&searchMode),
+						 PointerGetDatum(index->opclassoptions[indexcol]));
 
 	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
 	{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 3d8a130..20ce792 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
 #define GIN_CONSISTENT_PROC			   4
 #define GIN_COMPARE_PARTIAL_PROC	   5
 #define GIN_TRICONSISTENT_PROC		   6
-#define GINNProcs					   6
+#define GIN_OPCLASSOPTIONS_PROC		   7
+#define GINNProcs					   7
 
 /*
  * searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 81bf873..6fecfe0 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
 	TupleDesc	origTupdesc;
 	TupleDesc	tupdesc[INDEX_MAX_KEYS];
 
+	bytea	  **opclassOptions;	/* per-index-column opclass options */
+
 	/*
 	 * Per-index-column opclass support functions
 	 */
@@ -85,6 +87,8 @@ typedef struct GinState
 
 /* ginutil.c */
 extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+				  Datum attoptions, bool validate);
 extern void initGinState(GinState *state, Relation index);
 extern Buffer GinNewBuffer(Relation index);
 extern void GinInitBuffer(Buffer b, uint32 f);
@@ -296,6 +300,7 @@ typedef struct GinScanKeyData
 	StrategyNumber strategy;
 	int32		searchMode;
 	OffsetNumber attnum;
+	bytea	   *opclassOptions;
 
 	/*
 	 * Match status data.  curItem is the TID most recently tested (could be a
-- 
2.7.4

0004-Add-opclass-parameters-to-GiST-tsvector_ops-v03.patchtext/x-patch; name=0004-Add-opclass-parameters-to-GiST-tsvector_ops-v03.patchDownload
From 3ecdef58eb4e3413f1a700179d6d38b3d7637d54 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 18:31:23 +0300
Subject: [PATCH 4/9] Add opclass parameters to GiST tsvector_ops

---
 doc/src/sgml/textsearch.sgml          |   9 +-
 src/backend/utils/adt/tsgistidx.c     | 269 +++++++++++++++++++---------------
 src/include/catalog/pg_amproc.dat     |   5 +-
 src/include/catalog/pg_proc.dat       |  19 ++-
 src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
 src/test/regress/sql/tsearch.sql      |  45 ++++++
 6 files changed, 392 insertions(+), 131 deletions(-)

diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index ecebade..92abf6e 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3635,7 +3635,7 @@ SELECT plainto_tsquery('supernovae stars');
       <tertiary>text search</tertiary>
      </indexterm>
 
-      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
      </term>
 
      <listitem>
@@ -3643,6 +3643,8 @@ SELECT plainto_tsquery('supernovae stars');
        Creates a GiST (Generalized Search Tree)-based index.
        The <replaceable>column</replaceable> can be of <type>tsvector</type> or
        <type>tsquery</type> type.
+       Optional integer parameter <literal>siglen</literal> determines
+       signature length in bytes (see below for details).
       </para>
      </listitem>
     </varlistentry>
@@ -3666,7 +3668,10 @@ SELECT plainto_tsquery('supernovae stars');
    to check the actual table row to eliminate such false matches.
    (<productname>PostgreSQL</productname> does this automatically when needed.)
    GiST indexes are lossy because each document is represented in the
-   index by a fixed-length signature. The signature is generated by hashing
+   index by a fixed-length signature.  Signature length in bytes is determined
+   by the value of the optional integer parameter <literal>siglen</literal>.
+   Default signature length (when <literal>siglen</literal> is not specied) is
+   124 bytes, maximal length is 484 bytes. The signature is generated by hashing
    each word into a single bit in an n-bit string, with all these bits OR-ed
    together to produce an n-bit document signature.  When two words hash to
    the same bit position there will be a false match.  If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 2d9ecc4..462791a 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,23 +15,29 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/tuptoaster.h"
 #include "tsearch/ts_utils.h"
 #include "utils/builtins.h"
 #include "utils/pg_crc.h"
 
 
-#define SIGLENINT  31			/* >121 => key will toast, so it will not work
-								 * !!! */
+#define SIGLEN_DEFAULT	(31 * 4)
+#define SIGLEN_MAX		(121 * 4)	/* key will toast, so it will not work !!! */
 
-#define SIGLEN	( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length */
+}	GistTsVectorOptions;
 
-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) / BITS_PER_BYTE ) ) )
 #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -39,8 +45,8 @@ typedef char *BITVECP;
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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 GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
 
@@ -64,9 +70,10 @@ typedef struct
 #define ISALLTRUE(x)	( ((SignTSVector*)(x))->flag & ALLISTRUE )
 
 #define GTHDRSIZE	( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
 
 #define GETSIGN(x)	( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
 #define GETARR(x)	( (int32*)( (char*)(x)+GTHDRSIZE ) )
 #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
 
@@ -90,7 +97,7 @@ static const uint8 number_of_ones[256] = {
 	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
 
 Datum
 gtsvectorin(PG_FUNCTION_ARGS)
@@ -121,9 +128,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
 		sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
 	else
 	{
-		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+		int			siglen = GETSIGLEN(key);
+		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
 
-		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
 	}
 
 	PG_FREE_IF_COPY(key, 0);
@@ -167,36 +175,49 @@ uniqueint(int32 *a, int32 l)
 }
 
 static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
 {
 	int32		k,
 				len = ARRNELEM(a);
 	int32	   *ptr = GETARR(a);
 
-	MemSet((void *) sign, 0, sizeof(BITVEC));
+	MemSet((void *) sign, 0, siglen);
 	for (k = 0; k < len; k++)
-		HASH(sign, ptr[k]);
+		HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+	int			size = CALCGTSIZE(flag, len);
+	SignTSVector *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+		memcpy(GETSIGN(res), sign, len);
+
+	return res;
 }
 
+
 Datum
 gtsvector_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{							/* tsvector */
-		SignTSVector *res;
 		TSVector	val = DatumGetTSVector(entry->key);
+		SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
 		int32		len;
 		int32	   *arr;
 		WordEntry  *ptr = ARRPTR(val);
 		char	   *words = STRPTR(val);
 
-		len = CALCGTSIZE(ARRKEY, val->size);
-		res = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = ARRKEY;
 		arr = GETARR(res);
 		len = val->size;
 		while (len--)
@@ -227,13 +248,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 		/* make signature, if array is too long */
 		if (VARSIZE(res) > TOAST_INDEX_TARGET)
 		{
-			SignTSVector *ressign;
+			SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
 
-			len = CALCGTSIZE(SIGNKEY, 0);
-			ressign = (SignTSVector *) palloc(len);
-			SET_VARSIZE(ressign, len);
-			ressign->flag = SIGNKEY;
-			makesign(GETSIGN(ressign), res);
+			makesign(GETSIGN(ressign), res, siglen);
 			res = ressign;
 		}
 
@@ -245,22 +262,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 	else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
 			 !ISALLTRUE(DatumGetPointer(entry->key)))
 	{
-		int32		i,
-					len;
+		int32		i;
 		SignTSVector *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 = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = SIGNKEY | ALLISTRUE;
-
+		res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -334,12 +346,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
 static bool
 checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
 {
+	void *key = (SignTSVector *) checkval;
+
 	/*
 	 * we are not able to find a prefix in signature tree
 	 */
 	if (val->prefix)
 		return true;
-	return GETBIT(checkval, HASHVAL(val->valcrc));
+	return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
 }
 
 Datum
@@ -366,7 +380,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 
 		/* since signature is lossy, cannot specify CALC_NOT here */
 		PG_RETURN_BOOL(TS_execute(GETQUERY(query),
-								  (void *) GETSIGN(key),
+								  key,
 								  TS_EXEC_PHRASE_NO_POS,
 								  checkcondition_bit));
 	}
@@ -384,7 +398,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
 {
 	int32		i;
 
@@ -395,7 +409,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		Assert(GETSIGLEN(add) == siglen);
+
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -403,7 +419,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		int32	   *ptr = GETARR(add);
 
 		for (i = 0; i < ARRNELEM(add); i++)
-			HASH(sbase, ptr[i]);
+			HASH(sbase, ptr[i], siglen);
 	}
 	return 0;
 }
@@ -414,30 +430,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	SignTSVector *result;
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+	SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
+	int32		i;
+
+	memset(base, 0, siglen);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = ALLISTRUE;
+			result->flag |= ALLISTRUE;
+			SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
 			break;
 		}
 	}
 
-	flag |= SIGNKEY;
-	len = CALCGTSIZE(flag, 0);
-	result = (SignTSVector *) palloc(len);
-	*size = len;
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!ISALLTRUE(result))
-		memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -448,6 +458,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
 	SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
 	SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -463,8 +474,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
 			BITVECP		sa = GETSIGN(a),
 						sb = GETSIGN(b);
 
+			Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -501,24 +514,24 @@ gtsvector_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];
@@ -529,17 +542,22 @@ hemdistsign(BITVECP a, BITVECP b)
 static int
 hemdist(SignTSVector *a, SignTSVector *b)
 {
+	int siglena = GETSIGLEN(a);
+	int siglenb = GETSIGLEN(b);
+
 	if (ISALLTRUE(a))
 	{
 		if (ISALLTRUE(b))
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(GETSIGN(b));
+			return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
 	}
 	else if (ISALLTRUE(b))
-		return SIGLENBIT - sizebitvec(GETSIGN(a));
+		return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
 
-	return hemdistsign(GETSIGN(a), GETSIGN(b));
+	Assert(siglena == siglenb);
+
+	return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
 }
 
 Datum
@@ -548,6 +566,7 @@ gtsvector_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);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 	SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
 	SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -556,14 +575,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 
 	if (ISARRKEY(newval))
 	{
-		BITVEC		sign;
+		BITVECP		sign = palloc(siglen);
 
-		makesign(sign, newval);
+		makesign(sign, newval, siglen);
 
 		if (ISALLTRUE(origval))
-			*penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+		{
+			int			siglenbit = SIGLENBIT(siglen);
+
+			*penalty =
+				(float) (siglenbit - sizebitvec(sign, siglen)) /
+				(float) (siglenbit + 1);
+		}
 		else
-			*penalty = hemdistsign(sign, orig);
+			*penalty = hemdistsign(sign, orig, siglen);
+
+		pfree(sign);
 	}
 	else
 		*penalty = hemdist(origval, newval);
@@ -573,19 +600,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 typedef struct
 {
 	bool		allistrue;
-	BITVEC		sign;
+	BITVECP		sign;
 } CACHESIGN;
 
 static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
 {
 	item->allistrue = false;
 	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) )
@@ -609,19 +636,19 @@ comparecost(const void *va, const void *vb)
 
 
 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
@@ -629,6 +656,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
 	OffsetNumber k,
 				j;
 	SignTSVector *datum_l,
@@ -648,6 +676,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	BITVECP		ptr;
 	int			i;
 	CACHESIGN  *cache;
+	char	   *cache_sign;
 	SPLITCOST  *costvector;
 
 	maxoff = entryvec->n - 2;
@@ -656,16 +685,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	v->spl_right = (OffsetNumber *) palloc(nbytes);
 
 	cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
-	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+	cache_sign = palloc(siglen * (maxoff + 2));
+
+	for (j = 0; j < maxoff + 2; j++)
+		cache[j].sign = &cache_sign[siglen * j];
+
+	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+			  siglen);
 
 	for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
 	{
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
 			if (k == FirstOffsetNumber)
-				fillcache(&cache[j], GETENTRY(entryvec, j));
+				fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
 
-			size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+			size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -687,44 +722,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (cache[seed_1].allistrue)
-	{
-		datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_l->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (SignTSVector *) 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 = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_r->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (SignTSVector *) 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 = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+							  siglen, cache[seed_1].sign);
+	datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+							  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), 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);
@@ -750,36 +762,34 @@ gtsvector_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;
@@ -790,12 +800,12 @@ gtsvector_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;
@@ -822,3 +832,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
 {
 	return gtsvector_consistent(fcinfo);
 }
+
+Datum
+gtsvector_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(GistTsVectorOptions, siglen) };
+	GistTsVectorOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(GistTsVectorOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 0ef2c08..c1ee3db 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -450,7 +450,7 @@
   amproc => 'gist_circle_distance' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '1',
-  amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
+  amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '2',
   amproc => 'gtsvector_union' },
@@ -468,6 +468,9 @@
   amproc => 'gtsvector_picksplit' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+  amprocrighttype => 'tsvector', amprocnum => '10',
+  amproc => 'gtsvector_options' },
 { amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
   amprocrighttype => 'tsquery', amprocnum => '1',
   amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 034a41e..110d4a5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8435,30 +8435,35 @@
 
 { oid => '3648', descr => 'GiST tsvector support',
   proname => 'gtsvector_compress', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'gtsvector_compress' },
+  proargtypes => 'internal internal', prosrc => 'gtsvector_compress' },
 { oid => '3649', descr => 'GiST tsvector support',
   proname => 'gtsvector_decompress', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'gtsvector_decompress' },
+  proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' },
 { oid => '3650', descr => 'GiST tsvector support',
   proname => 'gtsvector_picksplit', prorettype => 'internal',
-  proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' },
+  proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' },
 { oid => '3651', descr => 'GiST tsvector support',
   proname => 'gtsvector_union', prorettype => 'gtsvector',
-  proargtypes => 'internal internal', prosrc => 'gtsvector_union' },
+  proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' },
 { oid => '3652', descr => 'GiST tsvector support',
   proname => 'gtsvector_same', prorettype => 'internal',
-  proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' },
+  proargtypes => 'gtsvector gtsvector internal internal',
+  prosrc => 'gtsvector_same' },
 { oid => '3653', descr => 'GiST tsvector support',
   proname => 'gtsvector_penalty', prorettype => 'internal',
-  proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' },
+  proargtypes => 'internal internal internal internal',
+  prosrc => 'gtsvector_penalty' },
 { oid => '3654', descr => 'GiST tsvector support',
   proname => 'gtsvector_consistent', prorettype => 'bool',
-  proargtypes => 'internal tsvector int2 oid internal',
+  proargtypes => 'internal tsvector int2 oid internal internal',
   prosrc => 'gtsvector_consistent' },
 { oid => '3790', descr => 'GiST tsvector support (obsolete)',
   proname => 'gtsvector_consistent', prorettype => 'bool',
   proargtypes => 'internal gtsvector int4 oid internal',
   prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3996', descr => 'GiST tsvector support',
+  proname => 'gtsvector_options', prorettype => 'internal',
+  proargtypes => 'internal bool', prosrc => 'gtsvector_options' },
 
 { oid => '3656', descr => 'GIN tsvector support',
   proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index b088ff0..bcadcf7 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
    508
 (1 row)
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR:  value 485 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR:  parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a)
+    "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx2
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 637bfb3..9aed780 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
 SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
 SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
-- 
2.7.4

#14Robert Haas
robertmhaas@gmail.com
In reply to: Nikita Glukhov (#13)
Re: [PATCH] Opclass parameters

On Wed, Dec 5, 2018 at 6:58 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:

I agree that we should distinguish per-index and per-column options, but they
can also be AM-specific and opclass-specific.

True, but an index is bound to a single AM, and a column is bound to a
single opclass which is bound to a single AM. So I'm not very worried
about name collisions. Can't we just tell opclass authors to pick
names that are unlikely to collide with names chose by the AM or names
that are globally reserved? It's hard to imagine that we're ever
going to have more than a dozen or so options that could possibly
apply to a column, so splitting things up into different namespaces
seems like an unnecessary burden on the user.

'fastupdate' option for GIN is an example of AM-specific per-index option.

ASC/DESC and NULLS LAST/FIRST are examples of AM-class-specific per-column
options having special SQL syntax. "AM-class-specific" here means "specific
only for the class of AMs that support ordering". Now they are stored as flags
in pg_index.indoption[] but later can be moved to pg_attribute.attoptions.

Or left where they are.

And another problem is the options with default values. They may be not
explicitly specified by the user, and in this case in current implementation
nothing will be stored in the catalog because default values can be obtained
from the code. But this will not allow changing of default values without
compatibility issues. So I think it would be better to store both default and
explicitly specified values of all opclass options, but this will require a
major refactoring of current API.

Hmm. I think if the default ever changes, it will require a new major
release, and we can compensate in pg_dump. That means that a dump
taken with an old version will not preserve the options. However,
using the pg_dump from the newest release is the recommended
procedure, and if you don't do that, you might get outright failures.
Failing to preserve an option value in the rare case that a default
was changed seems less bad than that.

Also I have idea to define list of opclass parameters declaratively when opclass
is created using syntax like the following:

CREATE OPERATOR CLASS name [ (param_name param_type [= default_value] [,...]) ]
FOR TYPE ... AS (
{ OPTIONS function_name ( arg_type [,...] ) /* reloptions => binary */
| OPERATOR ...
} [,...]
)

I'm not sure exposing SQL syntax for this is a very good idea.

"[opclass] WITH OPTIONS (options)" looks too verbose, of course.

It's not that bad.

"[opclass] WITH (options)" looks acceptable, but it seems to conflict with
exclusion constraints syntax ("index_elem WITH operator").

Are you sure? The operator can't be (

But it might be confusing anyhow.

"opclass (options)" looks the most natural to me. But we still need some
keyword before the parentheses when the opclass is not specified since we
can't distinguish "func_name (func_params)" and "col_name (opclass_options)"
in grammar.

Are you sure? What's the SQL syntax where there is actually a problem
here? CREATE INDEX requires parentheses around a non-trivial
expression.

How about just OPTIONS (options) ?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#14)
Re: [PATCH] Opclass parameters

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Dec 5, 2018 at 6:58 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:

"opclass (options)" looks the most natural to me. But we still need some
keyword before the parentheses when the opclass is not specified since we
can't distinguish "func_name (func_params)" and "col_name (opclass_options)"
in grammar.

Are you sure? What's the SQL syntax where there is actually a problem
here? CREATE INDEX requires parentheses around a non-trivial
expression.

Well, the reason we have to require parens around nontrivial expressions
is mostly lack of forethought about making the syntax non-ambiguous :-(

How about just OPTIONS (options) ?

That would require making OPTIONS a fully reserved word, I think,
else it's ambiguous with an opclass name.

How about saying that you must give an opclass name if you want to
specify options, ie the syntax is

[ opclass_name [ ( options... ) ] ]

I'm not necessarily wedded to that, but it seems worth throwing
out the idea.

regards, tom lane

#16Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#15)
Re: [PATCH] Opclass parameters

On Thu, Dec 6, 2018 at 11:55 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:

How about saying that you must give an opclass name if you want to
specify options, ie the syntax is

[ opclass_name [ ( options... ) ] ]

I'm not necessarily wedded to that, but it seems worth throwing
out the idea.

Agreed, that's not bad, certainly better than making OPTIONS more reserved.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

#17Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Robert Haas (#16)
4 attachment(s)
Re: [PATCH] Opclass parameters

Hi,

while rebasing the patch series [1] adding bloom/multi-minmax BRIN
opclasses, I've decided to also rebase it on top of this patch, because it
needs the opclass parameters. So I had to rebase this too - it went mostly
fine, with reasonably limited bitrot. The rebased patch series is attached.

Using this patch series in [1] was mostly smooth, I only have two minor
comments at this point:

1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:

Datum
gtsvector_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(GistTsVectorOptions, siglen) };
GistTsVectorOptions *options =
parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
sizeof(GistTsVectorOptions), validate);

PG_RETURN_POINTER(options);
}

So in other words, it builds all the various pieces (relopts, optgen,
offsets, lengths etc.) manually, which is really error-prone and difficult
to maintain. We need to make it simpler - ideally as simple as defining a
custom GUC, or just an array of relopt_* structs.

2) The 0001 part does this in index_opclass_options_generic:

get_opclass_name(opclass, InvalidOid, &str);

ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));

But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning. So this produces
messages like

ERROR: operator class " int4_bloom_ops" has no options

which is not right. I haven't checked if a better function already exists,
or whether we need to implement it.

regards

/messages/by-id/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-Add-opclass-parameters-20190611.patchtext/plain; charset=us-asciiDownload
From 436eca7a1dd45b78b50c3868fd4be518b814cecd Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 20:59:01 +0200
Subject: [PATCH 01/10] Add opclass parameters

---
 doc/src/sgml/indices.sgml                 |   2 +-
 doc/src/sgml/ref/create_index.sgml        |  16 ++-
 src/backend/access/common/reloptions.c    | 142 +++++++++++++++-------
 src/backend/access/index/indexam.c        |  81 ++++++++++++
 src/backend/catalog/heap.c                |   8 +-
 src/backend/catalog/index.c               |  23 +++-
 src/backend/catalog/toasting.c            |   1 +
 src/backend/commands/indexcmds.c          |  17 ++-
 src/backend/commands/tablecmds.c          |   2 +-
 src/backend/nodes/copyfuncs.c             |   1 +
 src/backend/nodes/equalfuncs.c            |   1 +
 src/backend/nodes/outfuncs.c              |   1 +
 src/backend/optimizer/util/plancat.c      |   4 +
 src/backend/parser/gram.y                 |  72 +++++++----
 src/backend/utils/adt/ruleutils.c         | 128 +++++++++++--------
 src/backend/utils/cache/relcache.c        |  99 +++++++++++++++
 src/include/access/amapi.h                |   7 ++
 src/include/access/genam.h                |   5 +
 src/include/access/reloptions.h           |   5 +
 src/include/catalog/heap.h                |   1 +
 src/include/nodes/execnodes.h             |   2 +
 src/include/nodes/parsenodes.h            |   1 +
 src/include/nodes/pathnodes.h             |   1 +
 src/include/utils/rel.h                   |   1 +
 src/include/utils/relcache.h              |   3 +
 src/include/utils/ruleutils.h             |   2 +
 src/test/regress/expected/btree_index.out |   5 +
 src/test/regress/sql/btree_index.sql      |   4 +
 28 files changed, 503 insertions(+), 132 deletions(-)

diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 95c0a1926c..ea3acea88e 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1253,7 +1253,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
    An index definition can specify an <firstterm>operator
    class</firstterm> for each column of an index.
 <synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
 </synopsis>
    The operator class identifies the operators to be used by the index
    for that column.  For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 629a31ef79..61401f3645 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
-    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -278,6 +278,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+      <listitem>
+       <para>
+        The name of an operator class parameter. See below for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><literal>ASC</literal></term>
       <listitem>
@@ -646,8 +655,9 @@ Indexes:
   </para>
 
   <para>
-   An <firstterm>operator class</firstterm> can be specified for each
-   column of an index. The operator class identifies the operators to be
+   An <firstterm>operator class</firstterm> with its optional parameters 
+   can be specified for each column of an index.
+   The operator class identifies the operators to be
    used by the index for that column. For example, a B-tree index on
    four-byte integers would use the <literal>int4_ops</literal> class;
    this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index de06c92574..abc0082ce7 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1051,6 +1051,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 	return options;
 }
 
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+				 relopt_value *reloptions, int numoptions)
+{
+	ArrayType  *array = DatumGetArrayTypeP(options);
+	Datum	   *optiondatums;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(array, TEXTOID, -1, false, 'i',
+					  &optiondatums, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *text_str = VARDATA(optiondatums[i]);
+		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		int			j;
+
+		/* Search for a match in reloptions */
+		for (j = 0; j < numoptions; j++)
+		{
+			int			kw_len = reloptions[j].gen->namelen;
+
+			if (text_len > kw_len && text_str[kw_len] == '=' &&
+				strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+			{
+				parse_one_reloption(&reloptions[j], text_str, text_len,
+									validate);
+				break;
+			}
+		}
+
+		if (j >= numoptions && validate)
+		{
+			char	   *s;
+			char	   *p;
+
+			s = TextDatumGetCString(optiondatums[i]);
+			p = strchr(s, '=');
+			if (p)
+				*p = '\0';
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized parameter \"%s\"", s)));
+		}
+	}
+
+	/* It's worth avoiding memory leaks in this function */
+	pfree(optiondatums);
+
+	if (((void *) array) != DatumGetPointer(options))
+		pfree(array);
+}
+
 /*
  * Interpret reloptions that are given in text-array format.
  *
@@ -1105,57 +1159,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
 
 	/* Done if no options */
 	if (PointerIsValid(DatumGetPointer(options)))
-	{
-		ArrayType  *array = DatumGetArrayTypeP(options);
-		Datum	   *optiondatums;
-		int			noptions;
+		parseRelOptionsInternal(options, validate, reloptions, numoptions);
 
-		deconstruct_array(array, TEXTOID, -1, false, 'i',
-						  &optiondatums, NULL, &noptions);
+	*numrelopts = numoptions;
+	return reloptions;
+}
 
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *text_str = VARDATA(optiondatums[i]);
-			int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
-			int			j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen *optgen[], int nopts)
+{
+	relopt_value *values = palloc(sizeof(*values) * nopts);
+	int			i;
 
-			/* Search for a match in reloptions */
-			for (j = 0; j < numoptions; j++)
-			{
-				int			kw_len = reloptions[j].gen->namelen;
+	for (i = 0; i < nopts; i++)
+	{
+		values[i].gen = optgen[i];
+		values[i].isset = false;
+	}
 
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
-				{
-					parse_one_reloption(&reloptions[j], text_str, text_len,
-										validate);
-					break;
-				}
-			}
+	if (options != (Datum) 0)
+		parseRelOptionsInternal(options, validate, values, nopts);
 
-			if (j >= numoptions && validate)
-			{
-				char	   *s;
-				char	   *p;
+	return values;
+}
 
-				s = TextDatumGetCString(optiondatums[i]);
-				p = strchr(s, '=');
-				if (p)
-					*p = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("unrecognized parameter \"%s\"", s)));
-			}
-		}
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+							int noptions, size_t base_size, bool validate)
+{
+	relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+	relopt_value *vals;
+	void	   *opts;
+	int			i;
 
-		/* It's worth avoiding memory leaks in this function */
-		pfree(optiondatums);
-		if (((void *) array) != DatumGetPointer(options))
-			pfree(array);
+	for (i = 0; i < noptions; i++)
+	{
+		elems[i].optname = optgen[i]->name;
+		elems[i].opttype = optgen[i]->type;
+		elems[i].offset = offsets[i];
 	}
 
-	*numrelopts = numoptions;
-	return reloptions;
+	vals = parseLocalRelOptions(options, validate, optgen, noptions);
+	opts = allocateReloptStruct(base_size, vals, noptions);
+	fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+	if (elems)
+		pfree(elems);
+
+	return opts;
 }
 
 /*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index aefdd2916d..6cc76914de 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -51,11 +51,14 @@
 #include "access/xlog.h"
 #include "catalog/index.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 
 
 /* ----------------------------------------------------------------
@@ -905,3 +908,81 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
 		}
 	}
 }
+
+/* ----------------
+ *      index_opclass_options
+ *
+ *      Parse opclass-specific options for index column.
+ * ----------------
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum attoptions,
+					  bool validate)
+{
+	amopclassoptions_function amopclassoptions =
+		relation->rd_indam->amopclassoptions;
+
+	if (!amopclassoptions)
+	{
+		if (validate && PointerIsValid(DatumGetPointer(attoptions)))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("access method \"%s\" does not support opclass options ",
+							get_am_name(relation->rd_rel->relam))));
+
+		return NULL;
+	}
+
+	return amopclassoptions(relation, attnum, attoptions, validate);
+}
+
+/* ----------------
+ *      index_opclass_options_generic
+ *
+ *      Parse opclass options for index column using the specified support
+ *      function 'procnum' of column's opclass.
+ * ----------------
+ */
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+							  uint16 procnum, Datum attoptions, bool validate)
+{
+	Oid			procid = index_getprocid(indrel, attnum, procnum);
+	FmgrInfo   *procinfo;
+
+	if (!OidIsValid(procid))
+	{
+		StringInfoData opclassname;
+		Oid			opclass;
+		Datum		indclassDatum;
+		oidvector  *indclass;
+		bool		isnull;
+
+		if (!DatumGetPointer(attoptions))
+			return NULL;	/* ok, no options, no procedure */
+
+		/*
+		 * Report an error if the opclass's options-parsing procedure does not
+		 * exist but the opclass options are specified.
+		 */
+		indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+										Anum_pg_index_indclass, &isnull);
+		Assert(!isnull);
+		indclass = (oidvector *) DatumGetPointer(indclassDatum);
+		opclass = indclass->values[attnum - 1];
+
+		initStringInfo(&opclassname);
+		get_opclass_name(opclass, InvalidOid, &opclassname);
+
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("operator class \"%s\" has no options",
+						opclassname.data)));
+	}
+
+	procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+	return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+												   attoptions,
+												   BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 86820eecfc..4a42cb1523 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -694,6 +694,7 @@ CheckAttributeType(const char *attname,
 void
 InsertPgAttributeTuple(Relation pg_attribute_rel,
 					   Form_pg_attribute new_attribute,
+					   Datum attoptions,
 					   CatalogIndexState indstate)
 {
 	Datum		values[Natts_pg_attribute];
@@ -725,10 +726,11 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
 	values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
 	values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
 	values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+	values[Anum_pg_attribute_attoptions - 1] = attoptions;
 
 	/* start out with empty permissions and empty options */
 	nulls[Anum_pg_attribute_attacl - 1] = true;
-	nulls[Anum_pg_attribute_attoptions - 1] = true;
+	nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0;
 	nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
 	nulls[Anum_pg_attribute_attmissingval - 1] = true;
 
@@ -782,7 +784,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		/* Make sure this is OK, too */
 		attr->attstattarget = -1;
 
-		InsertPgAttributeTuple(rel, attr, indstate);
+		InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate);
 
 		/* Add dependency info */
 		myself.classId = RelationRelationId;
@@ -820,7 +822,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 			/* Fill in the correct relation OID in the copied tuple */
 			attStruct.attrelid = new_rel_oid;
 
-			InsertPgAttributeTuple(rel, &attStruct, indstate);
+			InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate);
 		}
 	}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index d2e4f53a80..125174852b 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
 #include "access/amapi.h"
 #include "access/heapam.h"
 #include "access/multixact.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
@@ -106,7 +107,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
 										  Oid *classObjectId);
 static void InitializeAttributeOids(Relation indexRelation,
 									int numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, int numatts);
+static void AppendAttributeTuples(Relation indexRelation, int numatts,
+					  Datum *attopts);
 static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 								Oid parentIndexId,
 								IndexInfo *indexInfo,
@@ -486,7 +488,7 @@ InitializeAttributeOids(Relation indexRelation,
  * ----------------------------------------------------------------
  */
 static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
 {
 	Relation	pg_attribute;
 	CatalogIndexState indstate;
@@ -508,10 +510,11 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
 	for (i = 0; i < numatts; i++)
 	{
 		Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
+		Datum		attoptions = attopts ? attopts[i] : (Datum) 0;
 
 		Assert(attr->attnum == i + 1);
 
-		InsertPgAttributeTuple(pg_attribute, attr, indstate);
+		InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate);
 	}
 
 	CatalogCloseIndexes(indstate);
@@ -591,6 +594,7 @@ UpdateIndexRelation(Oid indexoid,
 	else
 		predDatum = (Datum) 0;
 
+
 	/*
 	 * open the system catalog index relation
 	 */
@@ -933,7 +937,8 @@ index_create(Relation heapRelation,
 	/*
 	 * append ATTRIBUTE tuples for the index
 	 */
-	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs,
+						  indexInfo->ii_OpclassOptions);
 
 	/* ----------------
 	 *	  update pg_index
@@ -1146,6 +1151,12 @@ index_create(Relation heapRelation,
 
 	indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
 
+	/* Validate opclass-specific options */
+	if (indexInfo->ii_OpclassOptions)
+		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+			(void) index_opclass_options(indexRelation, i + 1,
+										 indexInfo->ii_OpclassOptions[i], true);
+
 	/*
 	 * If this is bootstrap (initdb) time, then we don't actually fill in the
 	 * index yet.  We'll be creating more indexes and classes later, so we
@@ -2208,6 +2219,10 @@ BuildIndexInfo(Relation index)
 		ii->ii_ExclusionStrats = NULL;
 	}
 
+	ii->ii_OpclassOptions =
+		RelationGetRawOpclassOptions(RelationGetRelid(index),
+									 RelationGetNumberOfAttributes(index));
+
 	/* other info */
 	ii->ii_Unique = indexStruct->indisunique;
 	ii->ii_ReadyForInserts = indexStruct->indisready;
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index de6282a667..7290731b3d 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -304,6 +304,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;
 	indexInfo->ii_Unique = true;
 	indexInfo->ii_ReadyForInserts = true;
 	indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d05d2fd3d5..23a57be749 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -792,6 +792,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;	/* for now */
 	indexInfo->ii_Unique = stmt->unique;
 	/* In a concurrent build, mark it not-ready-for-inserts */
 	indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1513,7 +1514,7 @@ CheckPredicate(Expr *predicate)
 
 /*
  * Compute per-index-column information, including indexed column numbers
- * or index expressions, opclasses, and indoptions. Note, all output vectors
+ * or index expressions, opclasses and their options. Note, all output vectors
  * should be allocated for all columns, including "including" ones.
  */
 static void
@@ -1814,6 +1815,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 								accessMethodName)));
 		}
 
+		/* Set up the per-column opclass options (attoptions field). */
+		if (attribute->opclassopts)
+		{
+			Assert(attn < nkeycols);
+
+			if (!indexInfo->ii_OpclassOptions)
+				indexInfo->ii_OpclassOptions =
+					palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+
+			indexInfo->ii_OpclassOptions[attn] =
+				transformRelOptions((Datum) 0, attribute->opclassopts,
+									NULL, NULL, false, false);
+		}
+
 		attn++;
 	}
 }
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 98519ef836..a7c8fa38a0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5688,7 +5688,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	ReleaseSysCache(typeTuple);
 
-	InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+	InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
 
 	table_close(attrdesc, RowExclusiveLock);
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 78deade89b..f5fad43eba 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2864,6 +2864,7 @@ _copyIndexElem(const IndexElem *from)
 	COPY_STRING_FIELD(indexcolname);
 	COPY_NODE_FIELD(collation);
 	COPY_NODE_FIELD(opclass);
+	COPY_NODE_FIELD(opclassopts);
 	COPY_SCALAR_FIELD(ordering);
 	COPY_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4f2ebe5118..6b14095c82 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2546,6 +2546,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
 	COMPARE_STRING_FIELD(indexcolname);
 	COMPARE_NODE_FIELD(collation);
 	COMPARE_NODE_FIELD(opclass);
+	COMPARE_NODE_FIELD(opclassopts);
 	COMPARE_SCALAR_FIELD(ordering);
 	COMPARE_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 237598e110..35806c0681 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2844,6 +2844,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
 	WRITE_STRING_FIELD(indexcolname);
 	WRITE_NODE_FIELD(collation);
 	WRITE_NODE_FIELD(opclass);
+	WRITE_NODE_FIELD(opclassopts);
 	WRITE_ENUM_FIELD(ordering, SortByDir);
 	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
 }
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 2405acbf6f..5c6be745c0 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -362,6 +362,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				info->nulls_first = NULL;
 			}
 
+			/* Fetch index opclass options */
+			info->opclassoptions =
+				RelationGetParsedOpclassOptions(indexRelation);
+
 			/*
 			 * Fetch the index expressions and predicate, if any.  We must
 			 * modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8311b1dd46..2feade5327 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -491,7 +491,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <alias>	alias_clause opt_alias_clause
 %type <list>	func_alias_clause
 %type <sortby>	sortby
-%type <ielem>	index_elem
+%type <ielem>	index_elem index_elem_options
 %type <node>	table_ref
 %type <jexpr>	joined_table
 %type <range>	relation_expr
@@ -7420,43 +7420,65 @@ index_params:	index_elem							{ $$ = list_make1($1); }
 			| index_params ',' index_elem			{ $$ = lappend($1, $3); }
 		;
 
+
+index_elem_options:
+	opt_collate opt_class opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = NIL;
+			$$->ordering = $3;
+			$$->nulls_ordering = $4;
+		}
+	| opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	| opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = NIL;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	;
+
 /*
  * Index attributes can be either simple column references, or arbitrary
  * expressions in parens.  For backwards-compatibility reasons, we allow
  * an expression that's just a function call to be written without parens.
  */
-index_elem:	ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
 				{
-					$$ = makeNode(IndexElem);
+					$$ = $2;
 					$$->name = $1;
-					$$->expr = NULL;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+			| func_expr_windowless index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $2;
 					$$->expr = $1;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+			| '(' a_expr ')' index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $4;
 					$$->expr = $2;
-					$$->indexcolname = NULL;
-					$$->collation = $4;
-					$$->opclass = $5;
-					$$->ordering = $6;
-					$$->nulls_ordering = $7;
 				}
 		;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9dda4820af..ea49bef807 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -453,8 +453,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
 									   deparse_context *context);
 static void get_tablesample_def(TableSampleClause *tablesample,
 								deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
-							 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context);
 static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
 static char *get_relation_name(Oid relid);
@@ -469,6 +467,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1198,6 +1197,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	oidvector  *indcollation;
 	oidvector  *indclass;
 	int2vector *indoption;
+	Datum	   *opcoptions = NULL;
 	StringInfoData buf;
 	char	   *str;
 	char	   *sep;
@@ -1233,6 +1233,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	Assert(!isnull);
 	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
 
+	if (!attrsOnly)
+		opcoptions = RelationGetRawOpclassOptions(indexrelid, idxrec->indnatts);
+
 	/*
 	 * Fetch the pg_class tuple of the index relation
 	 */
@@ -1369,16 +1372,28 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 		if (!attrsOnly && keyno < idxrec->indnkeyatts &&
 			(!colno || colno == keyno + 1))
 		{
+			bool		has_options = false;
 			int16		opt = indoption->values[keyno];
 			Oid			indcoll = indcollation->values[keyno];
 
+			if (opcoptions)
+				has_options = (opcoptions[keyno] != (Datum) 0);
+
 			/* Add collation, if not default for column */
 			if (OidIsValid(indcoll) && indcoll != keycolcollation)
 				appendStringInfo(&buf, " COLLATE %s",
 								 generate_collation_name((indcoll)));
 
 			/* Add the operator class name, if not default */
-			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+			get_opclass_name(indclass->values[keyno],
+							 has_options ? InvalidOid : keycoltype, &buf);
+
+			if (has_options)
+			{
+				appendStringInfoString(&buf, " (");
+				get_reloptions(&buf, opcoptions[keyno]);
+				appendStringInfoChar(&buf, ')');
+			}
 
 			/* Add options if relevant */
 			if (amroutine->amcanorder)
@@ -10459,7 +10474,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
  * actual_datatype.  (If you don't want this behavior, just pass
  * InvalidOid for actual_datatype.)
  */
-static void
+void
 get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf)
 {
@@ -11168,6 +11183,62 @@ string_to_text(char *str)
 	return result;
 }
 
+/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+	Datum	   *options;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(reloptions),
+					  TEXTOID, -1, false, 'i',
+					  &options, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *option = TextDatumGetCString(options[i]);
+		char	   *name;
+		char	   *separator;
+		char	   *value;
+
+		/*
+		 * Each array element should have the form name=value.  If the "="
+		 * is missing for some reason, treat it like an empty value.
+		 */
+		name = option;
+		separator = strchr(option, '=');
+		if (separator)
+		{
+			*separator = '\0';
+			value = separator + 1;
+		}
+		else
+			value = "";
+
+		if (i > 0)
+			appendStringInfoString(buf, ", ");
+		appendStringInfo(buf, "%s=", quote_identifier(name));
+
+		/*
+		 * In general we need to quote the value; but to avoid unnecessary
+		 * clutter, do not quote if it is an identifier that would not
+		 * need quoting.  (We could also allow numbers, but that is a bit
+		 * trickier than it looks --- for example, are leading zeroes
+		 * significant?  We don't want to assume very much here about what
+		 * custom reloptions might mean.)
+		 */
+		if (quote_identifier(value) == value)
+			appendStringInfoString(buf, value);
+		else
+			simple_quote_literal(buf, value);
+
+		pfree(option);
+	}
+}
+
 /*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
@@ -11188,56 +11259,9 @@ flatten_reloptions(Oid relid)
 	if (!isnull)
 	{
 		StringInfoData buf;
-		Datum	   *options;
-		int			noptions;
-		int			i;
 
 		initStringInfo(&buf);
-
-		deconstruct_array(DatumGetArrayTypeP(reloptions),
-						  TEXTOID, -1, false, 'i',
-						  &options, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *option = TextDatumGetCString(options[i]);
-			char	   *name;
-			char	   *separator;
-			char	   *value;
-
-			/*
-			 * Each array element should have the form name=value.  If the "="
-			 * is missing for some reason, treat it like an empty value.
-			 */
-			name = option;
-			separator = strchr(option, '=');
-			if (separator)
-			{
-				*separator = '\0';
-				value = separator + 1;
-			}
-			else
-				value = "";
-
-			if (i > 0)
-				appendStringInfoString(&buf, ", ");
-			appendStringInfo(&buf, "%s=", quote_identifier(name));
-
-			/*
-			 * In general we need to quote the value; but to avoid unnecessary
-			 * clutter, do not quote if it is an identifier that would not
-			 * need quoting.  (We could also allow numbers, but that is a bit
-			 * trickier than it looks --- for example, are leading zeroes
-			 * significant?  We don't want to assume very much here about what
-			 * custom reloptions might mean.)
-			 */
-			if (quote_identifier(value) == value)
-				appendStringInfoString(&buf, value);
-			else
-				simple_quote_literal(&buf, value);
-
-			pfree(option);
-		}
+		get_reloptions(&buf, reloptions);
 
 		result = buf.data;
 	}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2b992d7832..62caa4cfaf 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5173,6 +5173,105 @@ GetRelationPublicationActions(Relation relation)
 	return pubactions;
 }
 
+/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for the index
+ */
+Datum *
+RelationGetRawOpclassOptions(Oid indexrelid, int16 natts)
+{
+	Datum	   *options = NULL;
+	int16		attnum;
+
+	for (attnum = 1; attnum <= natts; attnum++)
+	{
+		HeapTuple	tuple;
+		Datum		attopts;
+		bool		isnull;
+
+		tuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(indexrelid),
+								Int16GetDatum(attnum));
+
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+				 attnum, indexrelid);
+
+		attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+								  &isnull);
+
+		if (!isnull)
+		{
+			if (!options)
+				options = palloc0(sizeof(Datum) * natts);
+
+			options[attnum - 1] = datumCopy(attopts, false, -1);	/* text */
+		}
+
+		ReleaseSysCache(tuple);
+	}
+
+	return options;
+}
+
+/*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+	MemoryContext oldcxt;
+	bytea	  **opts;
+	Datum	   *rawopts;
+	int			natts = RelationGetNumberOfAttributes(relation);
+	int			i;
+
+	/* Try to copy cached options. */
+	if (relation->rd_opcoptions)
+	{
+		opts = palloc(sizeof(*opts) * natts);
+
+		for (i = 0; i < natts; i++)
+		{
+			bytea	   *opt = relation->rd_opcoptions[i];
+
+			opts[i] = !opt ? NULL : (bytea *)
+				DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+		}
+
+		return opts;
+	}
+
+	/* Get and parse opclass options. */
+	opts = palloc0(sizeof(*opts) * natts);
+
+	rawopts = RelationGetRawOpclassOptions(RelationGetRelid(relation), natts);
+
+	for (i = 0; i < natts; i++)
+	{
+		Datum		options = rawopts ? rawopts[i] : (Datum) 0;
+
+		opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+		if (options != (Datum) 0)
+			pfree(DatumGetPointer(options));
+	}
+
+	if (rawopts)
+		pfree(rawopts);
+
+	/* Copy parsed options to the cache. */
+	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+	relation->rd_opcoptions = palloc(sizeof(*opts) * natts);
+
+	for (i = 0; i < natts; i++)
+		relation->rd_opcoptions[i] = !opts[i] ? NULL : (bytea *)
+			DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return opts;
+}
+
 /*
  * Routines to support ereport() reports of relation-related errors
  *
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 6e3db06eed..9c06d1a094 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -103,6 +103,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
 typedef bytea *(*amoptions_function) (Datum reloptions,
 									  bool validate);
 
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+											 AttrNumber colno,
+											 Datum attoptions,
+											 bool validate);
+
 /* report AM, index, or index column property */
 typedef bool (*amproperty_function) (Oid index_oid, int attno,
 									 IndexAMProperty prop, const char *propname,
@@ -215,6 +221,7 @@ typedef struct IndexAmRoutine
 	amcanreturn_function amcanreturn;	/* can be NULL */
 	amcostestimate_function amcostestimate;
 	amoptions_function amoptions;
+	amopclassoptions_function amopclassoptions;
 	amproperty_function amproperty; /* can be NULL */
 	ambuildphasename_function ambuildphasename; /* can be NULL */
 	amvalidate_function amvalidate;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 8c053be2ca..aa170f6de6 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -180,6 +180,11 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
 extern void index_store_float8_orderby_distances(IndexScanDesc scan,
 												 Oid *orderByTypes, double *distances,
 												 bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+					  Datum attoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+							  AttrNumber attnum, uint16 procnum,
+							  Datum attoptions, bool validate);
 
 /*
  * index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a1912f41e6..8dea2d8e69 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -263,12 +263,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 								amoptions_function amoptions);
 extern relopt_value *parseRelOptions(Datum options, bool validate,
 									 relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+					 relopt_gen **gen, int nelems);
 extern void *allocateReloptStruct(Size base, relopt_value *options,
 								  int numoptions);
 extern void fillRelOptions(void *rdopts, Size basesize,
 						   relopt_value *options, int numoptions,
 						   bool validate,
 						   const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+							int offsets[], int noptions, size_t base_size,
+							bool validate);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
 								 relopt_kind kind);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index eec71c29d5..866e94d04f 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -94,6 +94,7 @@ extern List *heap_truncate_find_FKs(List *relationIds);
 
 extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 								   Form_pg_attribute new_attribute,
+								   Datum attoptions,
 								   CatalogIndexState indstate);
 
 extern void InsertPgClassTuple(Relation pg_class_desc,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 99b9fa414f..e0d6c6c375 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -138,6 +138,7 @@ typedef struct ExprState
  *		UniqueProcs
  *		UniqueStrats
  *		Unique				is it a unique index?
+ *		OpclassOptions		opclass-specific options, or NULL if none
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
  *		BrokenHotChain		did we detect any broken HOT chains?
@@ -166,6 +167,7 @@ typedef struct IndexInfo
 	Oid		   *ii_UniqueOps;	/* array with one entry per column */
 	Oid		   *ii_UniqueProcs; /* array with one entry per column */
 	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	Datum	   *ii_OpclassOptions;	/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2a8edf934f..15699971a7 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -701,6 +701,7 @@ typedef struct IndexElem
 	char	   *indexcolname;	/* name for index column; NULL = default */
 	List	   *collation;		/* name of collation; NIL = default */
 	List	   *opclass;		/* name of desired opclass; NIL = default */
+	List	   *opclassopts;	/* opclass-specific options, or NIL */
 	SortByDir	ordering;		/* ASC/DESC/default */
 	SortByNulls nulls_ordering; /* FIRST/LAST/default */
 } IndexElem;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 4b7703d478..f8a935f501 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -802,6 +802,7 @@ struct IndexOptInfo
 	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
 	bool	   *reverse_sort;	/* is sort order descending? */
 	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
+	bytea	 **opclassoptions;	/* opclass-specific options for columns */
 	bool	   *canreturn;		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index d7f33abce3..8440e225ee 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -175,6 +175,7 @@ typedef struct RelationData
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
 	void	   *rd_amcache;		/* available for use by index AM */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
+	bytea	  **rd_opcoptions;	/* parsed opclass-specific options */
 
 	/*
 	 * foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index d9c10ffcba..562929c058 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
 #ifndef RELCACHE_H
 #define RELCACHE_H
 
+#include "postgres.h"
 #include "access/tupdesc.h"
 #include "nodes/bitmapset.h"
 
@@ -49,6 +50,8 @@ extern Oid	RelationGetPrimaryKeyIndex(Relation relation);
 extern Oid	RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Oid indexrelid, int16 natts);
 
 typedef enum IndexAttrBitmapKind
 {
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index d34cad2f4b..68d7ca892e 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -35,5 +35,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
 											 Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
 extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
 
 #endif							/* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index acab8e0b11..33f2bf0e3f 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -244,6 +244,11 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
  {vacuum_cleanup_index_scale_factor=70.0}
 (1 row)
 
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  access method "btree" does not support opclass options 
+create index on btree_tall_tbl (id default(foo=1));
+ERROR:  access method "btree" does not support opclass options 
 --
 -- Test for multilevel page deletion
 --
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 48eaf4fe42..aecd690a01 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -121,6 +121,10 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
 alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
 select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
 
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
+
 --
 -- Test for multilevel page deletion
 --
-- 
2.20.1

0002-Add-opclass-parameters-to-GiST-20190611.patchtext/plain; charset=us-asciiDownload
From ddd99a8fb48774cfaf9ca151ebb8a5238bc1d2b3 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 21:17:42 +0200
Subject: [PATCH 02/10] Add opclass parameters to GiST

---
 doc/src/sgml/xindex.sgml               |  5 +++
 src/backend/access/gist/gist.c         |  2 ++
 src/backend/access/gist/gistget.c      | 12 ++++---
 src/backend/access/gist/gistsplit.c    | 15 +++++----
 src/backend/access/gist/gistutil.c     | 44 ++++++++++++++++++--------
 src/backend/access/gist/gistvalidate.c | 34 +++++++++++++-------
 src/include/access/gist.h              |  3 +-
 src/include/access/gist_private.h      |  4 +++
 8 files changed, 82 insertions(+), 37 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b836..8c5b5289d7 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
        index-only scans (optional)</entry>
        <entry>9</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>parse opclass-specific options (optional)</entry>
+       <entry>10</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 470b121e7d..8e3460f456 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amestimateparallelscan = NULL;
 	amroutine->aminitparallelscan = NULL;
 	amroutine->amparallelrescan = NULL;
+	amroutine->amopclassoptions = gistopclassoptions;
 
 	PG_RETURN_POINTER(amroutine);
 }
@@ -1528,6 +1529,7 @@ initGISTstate(Relation index)
 	giststate->scanCxt = scanCxt;
 	giststate->tempCxt = scanCxt;	/* caller must change this if needed */
 	giststate->leafTupdesc = index->rd_att;
+	giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
 
 	/*
 	 * The truncated tupdesc for non-leaf index tuples, which doesn't contain
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 8108fbb7d8..abfe659ca5 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		test;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 */
 			recheck = true;
 
-			test = FunctionCall5Coll(&key->sk_func,
+			test = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 
 			if (!DatumGetBool(test))
 				return false;
@@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan,
 			Datum		dist;
 			bool		recheck;
 			GISTENTRY	de;
+			bytea	   *options = giststate->opclassoptions[key->sk_attno - 1];
 
 			gistdentryinit(giststate, key->sk_attno - 1, &de,
 						   datum, r, page, offset,
@@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan,
 			 * about the flag, but are expected to never be lossy.
 			 */
 			recheck = false;
-			dist = FunctionCall5Coll(&key->sk_func,
+			dist = FunctionCall6Coll(&key->sk_func,
 									 key->sk_collation,
 									 PointerGetDatum(&de),
 									 key->sk_argument,
 									 Int16GetDatum(key->sk_strategy),
 									 ObjectIdGetDatum(key->sk_subtype),
-									 PointerGetDatum(&recheck));
+									 PointerGetDatum(&recheck),
+									 PointerGetDatum(options));
 			*recheck_distances_p |= recheck;
 			*distance_p = DatumGetFloat8(dist);
 		}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index 6a9c54d86c..2b3cb967e1 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
 	evec->n = v->spl_nleft;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	evec->n = v->spl_nright;
 	memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
 		   sizeof(GISTENTRY) * evec->n);
-	v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+	v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
 									  giststate->supportCollation[attno],
 									  PointerGetDatum(evec),
-									  PointerGetDatum(&nbytes));
+									  PointerGetDatum(&nbytes),
+									  PointerGetDatum(giststate->opclassoptions[attno]));
 }
 
 /*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
 	 * Let the opclass-specific PickSplit method do its thing.  Note that at
 	 * this point we know there are no null keys in the entryvec.
 	 */
-	FunctionCall2Coll(&giststate->picksplitFn[attno],
+	FunctionCall3Coll(&giststate->picksplitFn[attno],
 					  giststate->supportCollation[attno],
 					  PointerGetDatum(entryvec),
-					  PointerGetDatum(sv));
+					  PointerGetDatum(sv),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 
 	if (sv->spl_nleft == 0 || sv->spl_nright == 0)
 	{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 49df05653b..000f10103c 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -201,10 +201,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
 			}
 
 			/* Make union and store in attr array */
-			attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+			attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
 										giststate->supportCollation[i],
 										PointerGetDatum(evec),
-										PointerGetDatum(&attrsize));
+										PointerGetDatum(&attrsize),
+										PointerGetDatum(giststate->opclassoptions[i]));
 
 			isnull[i] = false;
 		}
@@ -270,10 +271,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
 		}
 
 		*dstisnull = false;
-		*dst = FunctionCall2Coll(&giststate->unionFn[attno],
+		*dst = FunctionCall3Coll(&giststate->unionFn[attno],
 								 giststate->supportCollation[attno],
 								 PointerGetDatum(evec),
-								 PointerGetDatum(&dstsize));
+								 PointerGetDatum(&dstsize),
+								 PointerGetDatum(giststate->opclassoptions[attno]));
 	}
 }
 
@@ -282,10 +284,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
 {
 	bool		result;
 
-	FunctionCall3Coll(&giststate->equalFn[attno],
+	FunctionCall4Coll(&giststate->equalFn[attno],
 					  giststate->supportCollation[attno],
 					  a, b,
-					  PointerGetDatum(&result));
+					  PointerGetDatum(&result),
+					  PointerGetDatum(giststate->opclassoptions[attno]));
 	return result;
 }
 
@@ -559,9 +562,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
 			return;
 
 		dep = (GISTENTRY *)
-			DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+			DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
 											  giststate->supportCollation[nkey],
-											  PointerGetDatum(e)));
+											  PointerGetDatum(e),
+											  PointerGetDatum(giststate->opclassoptions[nkey])));
 		/* decompressFn may just return the given pointer */
 		if (dep != e)
 			gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -596,9 +600,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
 			/* there may not be a compress function in opclass */
 			if (OidIsValid(giststate->compressFn[i].fn_oid))
 				cep = (GISTENTRY *)
-					DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+					DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
 													  giststate->supportCollation[i],
-													  PointerGetDatum(&centry)));
+													  PointerGetDatum(&centry),
+													  PointerGetDatum(giststate->opclassoptions[i])));
 			else
 				cep = &centry;
 			compatt[i] = cep->key;
@@ -643,9 +648,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
 	gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
 
 	fep = (GISTENTRY *)
-		DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+		DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
 										  giststate->supportCollation[nkey],
-										  PointerGetDatum(&fentry)));
+										  PointerGetDatum(&fentry),
+										  PointerGetDatum(giststate->opclassoptions[nkey])));
 
 	/* fetchFn set 'key', return it to the caller */
 	return fep->key;
@@ -722,11 +728,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
 	if (giststate->penaltyFn[attno].fn_strict == false ||
 		(isNullOrig == false && isNullAdd == false))
 	{
-		FunctionCall3Coll(&giststate->penaltyFn[attno],
+		FunctionCall4Coll(&giststate->penaltyFn[attno],
 						  giststate->supportCollation[attno],
 						  PointerGetDatum(orig),
 						  PointerGetDatum(add),
-						  PointerGetDatum(&penalty));
+						  PointerGetDatum(&penalty),
+						  PointerGetDatum(giststate->opclassoptions[attno]));
 		/* disallow negative or NaN penalty */
 		if (isnan(penalty) || penalty < 0.0)
 			penalty = 0.0;
@@ -915,6 +922,15 @@ gistoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum attoptions,
+				   bool validate)
+{
+	return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+										 attoptions, validate);
+}
+
+
 /*
  *	gistproperty() -- Check boolean properties of indexes.
  *
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index dfc1a87a75..7ae820070b 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
 		{
 			case GIST_CONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_UNION_PROC:
 				ok = check_amproc_signature(procform->amproc, opckeytype, false,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_COMPRESS_PROC:
 			case GIST_DECOMPRESS_PROC:
 			case GIST_FETCH_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											1, 1, INTERNALOID);
+											1, 2, INTERNALOID, INTERNALOID);
 				break;
 			case GIST_PENALTY_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											3, 3, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											3, 4, INTERNALOID,
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_PICKSPLIT_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
-											2, 2, INTERNALOID, INTERNALOID);
+											2, 3, INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIST_EQUAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											3, 3, opckeytype, opckeytype,
-											INTERNALOID);
+											3, 4, opckeytype, opckeytype,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIST_DISTANCE_PROC:
 				ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
-											5, 5, INTERNALOID, opcintype,
-											INT2OID, OIDOID, INTERNALOID);
+											5, 6, INTERNALOID, opcintype,
+											INT2OID, OIDOID, INTERNALOID,
+											INTERNALOID);
+				break;
+			case GIST_OPCLASSOPT_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+											2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
 		if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
-			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+			i == GIST_OPCLASSOPT_PROC)
 			continue;			/* optional methods */
 		ereport(INFO,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 6902f4115b..0c57a64369 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
 #define GIST_EQUAL_PROC					7
 #define GIST_DISTANCE_PROC				8
 #define GIST_FETCH_PROC					9
-#define GISTNProcs					9
+#define GIST_OPCLASSOPT_PROC			10
+#define GISTNProcs						10
 
 /*
  * Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index f80694bf9a..d46b6b89f9 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -96,6 +96,8 @@ typedef struct GISTSTATE
 
 	/* Collations to pass to the support functions */
 	Oid			supportCollation[INDEX_MAX_KEYS];
+
+	bytea	  **opclassoptions;	/* parsed opclass-specific options */
 } GISTSTATE;
 
 
@@ -462,6 +464,8 @@ extern bool gistvalidate(Oid opclassoid);
 #define GIST_DEFAULT_FILLFACTOR		90
 
 extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+				   Datum options, bool validate);
 extern bool gistproperty(Oid index_oid, int attno,
 						 IndexAMProperty prop, const char *propname,
 						 bool *res, bool *isnull);
-- 
2.20.1

0003-Add-opclass-parameters-to-GIN-20190611.patchtext/plain; charset=us-asciiDownload
From f056cb2deddde2ff0cbef894be2699710720507b Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 21:18:18 +0200
Subject: [PATCH 03/10] Add opclass parameters to GIN

---
 doc/src/sgml/xindex.sgml             |  7 ++++++
 src/backend/access/gin/ginget.c      | 11 +++++-----
 src/backend/access/gin/ginlogic.c    | 15 +++++++------
 src/backend/access/gin/ginscan.c     |  6 ++++--
 src/backend/access/gin/ginutil.c     | 27 +++++++++++++++++------
 src/backend/access/gin/ginvalidate.c | 32 +++++++++++++++++-----------
 src/backend/utils/adt/selfuncs.c     |  6 ++++--
 src/include/access/gin.h             |  3 ++-
 src/include/access/gin_private.h     |  5 +++++
 9 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b5289d7..658ec9bc5a 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
        </entry>
        <entry>6</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>
+        parse opclass-specific options (optional)
+       </entry>
+       <entry>7</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index b18ae2b3ed..547b7b4762 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
 			 * case cmp < 0 => not match and continue scan
 			 *----------
 			 */
-			cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+			cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
 												  btree->ginstate->supportCollation[attnum - 1],
 												  scanEntry->queryKey,
 												  idatum,
 												  UInt16GetDatum(scanEntry->strategy),
-												  PointerGetDatum(scanEntry->extra_data)));
-
+												  PointerGetDatum(scanEntry->extra_data),
+												  PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
 			if (cmp > 0)
 				return true;
 			else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
 		 * case cmp < 0 => not match and continue scan
 		 *----------
 		 */
-		cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+		cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
 											  ginstate->supportCollation[entry->attnum - 1],
 											  entry->queryKey,
 											  datum[off - 1],
 											  UInt16GetDatum(entry->strategy),
-											  PointerGetDatum(entry->extra_data)));
+											  PointerGetDatum(entry->extra_data),
+											  PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
 		if (cmp == 0)
 			return true;
 		else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 8f85978972..028b29a8c0 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
 	 */
 	key->recheckCurItem = true;
 
-	return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+	return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
 										  key->collation,
 										  PointerGetDatum(key->entryRes),
 										  UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
 										  PointerGetDatum(key->extra_data),
 										  PointerGetDatum(&key->recheckCurItem),
 										  PointerGetDatum(key->queryValues),
-										  PointerGetDatum(key->queryCategories)));
+										  PointerGetDatum(key->queryCategories),
+										  PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
 static GinTernaryValue
 directTriConsistentFn(GinScanKey key)
 {
-	return DatumGetGinTernaryValue(FunctionCall7Coll(
+	return DatumGetGinTernaryValue(FunctionCall8Coll(
 													 key->triConsistentFmgrInfo,
 													 key->collation,
 													 PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
 													 UInt32GetDatum(key->nuserentries),
 													 PointerGetDatum(key->extra_data),
 													 PointerGetDatum(key->queryValues),
-													 PointerGetDatum(key->queryCategories)));
+													 PointerGetDatum(key->queryCategories),
+													 PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
 {
 	GinTernaryValue result;
 
-	result = DatumGetGinTernaryValue(FunctionCall7Coll(
+	result = DatumGetGinTernaryValue(FunctionCall8Coll(
 													   key->triConsistentFmgrInfo,
 													   key->collation,
 													   PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
 													   UInt32GetDatum(key->nuserentries),
 													   PointerGetDatum(key->extra_data),
 													   PointerGetDatum(key->queryValues),
-													   PointerGetDatum(key->queryCategories)));
+													   PointerGetDatum(key->queryCategories),
+													   PointerGetDatum(key->opclassOptions)));
 	if (result == GIN_MAYBE)
 	{
 		key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 74d9821ac1..d1384a33d4 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
 	key->strategy = strategy;
 	key->searchMode = searchMode;
 	key->attnum = attnum;
+	key->opclassOptions = ginstate->opclassOptions[attnum - 1];
 
 	ItemPointerSetMin(&key->curItem);
 	key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
 
 		/* OK to call the extractQueryFn */
 		queryValues = (Datum *)
-			DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+			DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
 											  so->ginstate.supportCollation[skey->sk_attno - 1],
 											  skey->sk_argument,
 											  PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
 											  PointerGetDatum(&partial_matches),
 											  PointerGetDatum(&extra_data),
 											  PointerGetDatum(&nullFlags),
-											  PointerGetDatum(&searchMode)));
+											  PointerGetDatum(&searchMode),
+											  PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
 
 		/*
 		 * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index cf9699ad18..03874909b0 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = gincostestimate;
 	amroutine->amoptions = ginoptions;
+	amroutine->amopclassoptions = ginopclassoptions;
 	amroutine->amproperty = NULL;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = ginvalidate;
@@ -96,6 +97,7 @@ initGinState(GinState *state, Relation index)
 	state->index = index;
 	state->oneCol = (origTupdesc->natts == 1) ? true : false;
 	state->origTupdesc = origTupdesc;
+	state->opclassOptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < origTupdesc->natts; i++)
 	{
@@ -399,9 +401,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
 		return 0;
 
 	/* both not null, so safe to call the compareFn */
-	return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+	return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
 										   ginstate->supportCollation[attnum - 1],
-										   a, b));
+										   a, b,
+										   PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 }
 
 /*
@@ -437,6 +440,7 @@ typedef struct
 {
 	FmgrInfo   *cmpDatumFunc;
 	Oid			collation;
+	Datum		options;
 	bool		haveDups;
 } cmpEntriesArg;
 
@@ -458,9 +462,10 @@ cmpEntries(const void *a, const void *b, void *arg)
 	else if (bb->isnull)
 		res = -1;				/* not-NULL "<" NULL */
 	else
-		res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+		res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
 											  data->collation,
-											  aa->datum, bb->datum));
+											  aa->datum, bb->datum,
+											  data->options));
 
 	/*
 	 * Detect if we have any duplicates.  If there are equal keys, qsort must
@@ -506,11 +511,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 	/* OK, call the opclass's extractValueFn */
 	nullFlags = NULL;			/* in case extractValue doesn't set it */
 	entries = (Datum *)
-		DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+		DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
 										  ginstate->supportCollation[attnum - 1],
 										  value,
 										  PointerGetDatum(nentries),
-										  PointerGetDatum(&nullFlags)));
+										  PointerGetDatum(&nullFlags),
+										  PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 
 	/*
 	 * Generate a placeholder if the item contained no keys.
@@ -553,6 +559,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 
 		arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
 		arg.collation = ginstate->supportCollation[attnum - 1];
+		arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
 		arg.haveDups = false;
 		qsort_arg(keydata, *nentries, sizeof(keyEntryData),
 				  cmpEntries, (void *) &arg);
@@ -628,6 +635,14 @@ ginoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum attoptions,
+				  bool validate)
+{
+	return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+										 attoptions, validate);
+}
+
 /*
  * Fetch index's statistical data into *stats
  *
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 63bd7f2adc..a000052f09 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
 		{
 			case GIN_COMPARE_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											2, 2, opckeytype, opckeytype);
+											2, 3, opckeytype, opckeytype,
+											INTERNALOID);
 				break;
 			case GIN_EXTRACTVALUE_PROC:
 				/* Some opclasses omit nullFlags */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											2, 3, opcintype, INTERNALOID,
-											INTERNALOID);
+											2, 4, opcintype, INTERNALOID,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIN_EXTRACTQUERY_PROC:
 				/* Some opclasses omit nullFlags and searchMode */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											5, 7, opcintype, INTERNALOID,
+											5, 8, opcintype, INTERNALOID,
 											INT2OID, INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_CONSISTENT_PROC:
 				/* Some opclasses omit queryKeys and nullFlags */
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											6, 8, INTERNALOID, INT2OID,
+											6, 9, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_COMPARE_PARTIAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											4, 4, opckeytype, opckeytype,
-											INT2OID, INTERNALOID);
+											4, 5, opckeytype, opckeytype,
+											INT2OID, INTERNALOID, INTERNALOID);
 				break;
 			case GIN_TRICONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, CHAROID, false,
-											7, 7, INTERNALOID, INT2OID,
+											7, 8, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID);
+											INTERNALOID, INTERNALOID);
+				break;
+			case GIN_OPCLASSOPTIONS_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID,
+											false, 2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
 		if (opclassgroup &&
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
-		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+			i == GIN_OPCLASSOPTIONS_PROC)
 			continue;			/* optional method */
 		if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
 			continue;			/* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index d7e3f09f1a..d491bdd7ae 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -135,6 +135,7 @@
 #include "utils/lsyscache.h"
 #include "utils/pg_locale.h"
 #include "utils/rel.h"
+#include "utils/relcache.h"
 #include "utils/selfuncs.h"
 #include "utils/snapmgr.h"
 #include "utils/spccache.h"
@@ -6242,7 +6243,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	else
 		collation = DEFAULT_COLLATION_OID;
 
-	OidFunctionCall7Coll(extractProcOid,
+	OidFunctionCall8Coll(extractProcOid,
 						 collation,
 						 query,
 						 PointerGetDatum(&nentries),
@@ -6250,7 +6251,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 						 PointerGetDatum(&partial_matches),
 						 PointerGetDatum(&extra_data),
 						 PointerGetDatum(&nullFlags),
-						 PointerGetDatum(&searchMode));
+						 PointerGetDatum(&searchMode),
+						 PointerGetDatum(index->opclassoptions[indexcol]));
 
 	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
 	{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index a8eef5a379..aabfbcd5ce 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
 #define GIN_CONSISTENT_PROC			   4
 #define GIN_COMPARE_PARTIAL_PROC	   5
 #define GIN_TRICONSISTENT_PROC		   6
-#define GINNProcs					   6
+#define GIN_OPCLASSOPTIONS_PROC		   7
+#define GINNProcs					   7
 
 /*
  * searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index afb3e15721..0f626a7dbb 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
 	TupleDesc	origTupdesc;
 	TupleDesc	tupdesc[INDEX_MAX_KEYS];
 
+	bytea	  **opclassOptions;	/* per-index-column opclass options */
+
 	/*
 	 * Per-index-column opclass support functions
 	 */
@@ -85,6 +87,8 @@ typedef struct GinState
 
 /* ginutil.c */
 extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+				  Datum attoptions, bool validate);
 extern void initGinState(GinState *state, Relation index);
 extern Buffer GinNewBuffer(Relation index);
 extern void GinInitBuffer(Buffer b, uint32 f);
@@ -297,6 +301,7 @@ typedef struct GinScanKeyData
 	StrategyNumber strategy;
 	int32		searchMode;
 	OffsetNumber attnum;
+	bytea	   *opclassOptions;
 
 	/*
 	 * Match status data.  curItem is the TID most recently tested (could be a
-- 
2.20.1

0004-Add-opclass-parameters-to-GiST-tsvector_ops-20190611.patchtext/plain; charset=us-asciiDownload
From ae97174555cbaf059d2df45df3c80d7e67007fdd Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 21:38:42 +0200
Subject: [PATCH 04/10] Add opclass parameters to GiST tsvector_ops

---
 doc/src/sgml/textsearch.sgml          |   9 +-
 src/backend/utils/adt/tsgistidx.c     | 269 ++++++++++++++------------
 src/include/catalog/pg_amproc.dat     |   5 +-
 src/include/catalog/pg_proc.dat       |  19 +-
 src/test/regress/expected/tsearch.out | 176 +++++++++++++++++
 src/test/regress/sql/tsearch.sql      |  45 +++++
 6 files changed, 392 insertions(+), 131 deletions(-)

diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 40888a4d20..54b796ecf1 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3637,7 +3637,7 @@ SELECT plainto_tsquery('supernovae stars');
       <tertiary>text search</tertiary>
      </indexterm>
 
-      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
      </term>
 
      <listitem>
@@ -3645,6 +3645,8 @@ SELECT plainto_tsquery('supernovae stars');
        Creates a GiST (Generalized Search Tree)-based index.
        The <replaceable>column</replaceable> can be of <type>tsvector</type> or
        <type>tsquery</type> type.
+       Optional integer parameter <literal>siglen</literal> determines
+       signature length in bytes (see below for details).
       </para>
      </listitem>
     </varlistentry>
@@ -3668,7 +3670,10 @@ SELECT plainto_tsquery('supernovae stars');
    to check the actual table row to eliminate such false matches.
    (<productname>PostgreSQL</productname> does this automatically when needed.)
    GiST indexes are lossy because each document is represented in the
-   index by a fixed-length signature. The signature is generated by hashing
+   index by a fixed-length signature.  Signature length in bytes is determined
+   by the value of the optional integer parameter <literal>siglen</literal>.
+   Default signature length (when <literal>siglen</literal> is not specied) is
+   124 bytes, maximal length is 484 bytes. The signature is generated by hashing
    each word into a single bit in an n-bit string, with all these bits OR-ed
    together to produce an n-bit document signature.  When two words hash to
    the same bit position there will be a false match.  If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 4f256260fd..91661cf8b8 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/tuptoaster.h"
 #include "port/pg_bitutils.h"
 #include "tsearch/ts_utils.h"
@@ -22,17 +23,22 @@
 #include "utils/pg_crc.h"
 
 
-#define SIGLENINT  31			/* >121 => key will toast, so it will not work
-								 * !!! */
+#define SIGLEN_DEFAULT	(31 * 4)
+#define SIGLEN_MAX		(121 * 4)	/* key will toast, so it will not work !!! */
 
-#define SIGLEN	( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length */
+}	GistTsVectorOptions;
 
-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) / BITS_PER_BYTE ) ) )
 #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -40,8 +46,8 @@ typedef char *BITVECP;
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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 GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
 
@@ -65,13 +71,14 @@ typedef struct
 #define ISALLTRUE(x)	( ((SignTSVector*)(x))->flag & ALLISTRUE )
 
 #define GTHDRSIZE	( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
 
 #define GETSIGN(x)	( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
 #define GETARR(x)	( (int32*)( (char*)(x)+GTHDRSIZE ) )
 #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
 
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
 
 Datum
 gtsvectorin(PG_FUNCTION_ARGS)
@@ -102,9 +109,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
 		sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
 	else
 	{
-		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+		int			siglen = GETSIGLEN(key);
+		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
 
-		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
 	}
 
 	PG_FREE_IF_COPY(key, 0);
@@ -148,36 +156,49 @@ uniqueint(int32 *a, int32 l)
 }
 
 static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
 {
 	int32		k,
 				len = ARRNELEM(a);
 	int32	   *ptr = GETARR(a);
 
-	MemSet((void *) sign, 0, sizeof(BITVEC));
+	MemSet((void *) sign, 0, siglen);
 	for (k = 0; k < len; k++)
-		HASH(sign, ptr[k]);
+		HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+	int			size = CALCGTSIZE(flag, len);
+	SignTSVector *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+		memcpy(GETSIGN(res), sign, len);
+
+	return res;
 }
 
+
 Datum
 gtsvector_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{							/* tsvector */
-		SignTSVector *res;
 		TSVector	val = DatumGetTSVector(entry->key);
+		SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
 		int32		len;
 		int32	   *arr;
 		WordEntry  *ptr = ARRPTR(val);
 		char	   *words = STRPTR(val);
 
-		len = CALCGTSIZE(ARRKEY, val->size);
-		res = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = ARRKEY;
 		arr = GETARR(res);
 		len = val->size;
 		while (len--)
@@ -208,13 +229,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 		/* make signature, if array is too long */
 		if (VARSIZE(res) > TOAST_INDEX_TARGET)
 		{
-			SignTSVector *ressign;
+			SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
 
-			len = CALCGTSIZE(SIGNKEY, 0);
-			ressign = (SignTSVector *) palloc(len);
-			SET_VARSIZE(ressign, len);
-			ressign->flag = SIGNKEY;
-			makesign(GETSIGN(ressign), res);
+			makesign(GETSIGN(ressign), res, siglen);
 			res = ressign;
 		}
 
@@ -226,22 +243,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 	else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
 			 !ISALLTRUE(DatumGetPointer(entry->key)))
 	{
-		int32		i,
-					len;
+		int32		i;
 		SignTSVector *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 = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = SIGNKEY | ALLISTRUE;
-
+		res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -315,12 +327,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
 static bool
 checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
 {
+	void *key = (SignTSVector *) checkval;
+
 	/*
 	 * we are not able to find a prefix in signature tree
 	 */
 	if (val->prefix)
 		return true;
-	return GETBIT(checkval, HASHVAL(val->valcrc));
+	return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
 }
 
 Datum
@@ -347,7 +361,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 
 		/* since signature is lossy, cannot specify CALC_NOT here */
 		PG_RETURN_BOOL(TS_execute(GETQUERY(query),
-								  (void *) GETSIGN(key),
+								  key,
 								  TS_EXEC_PHRASE_NO_POS,
 								  checkcondition_bit));
 	}
@@ -365,7 +379,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
 {
 	int32		i;
 
@@ -376,7 +390,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		Assert(GETSIGLEN(add) == siglen);
+
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -384,7 +400,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		int32	   *ptr = GETARR(add);
 
 		for (i = 0; i < ARRNELEM(add); i++)
-			HASH(sbase, ptr[i]);
+			HASH(sbase, ptr[i], siglen);
 	}
 	return 0;
 }
@@ -395,30 +411,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	SignTSVector *result;
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+	SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
+	int32		i;
+
+	memset(base, 0, siglen);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = ALLISTRUE;
+			result->flag |= ALLISTRUE;
+			SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
 			break;
 		}
 	}
 
-	flag |= SIGNKEY;
-	len = CALCGTSIZE(flag, 0);
-	result = (SignTSVector *) palloc(len);
-	*size = len;
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!ISALLTRUE(result))
-		memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -429,6 +439,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
 	SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
 	SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -444,8 +455,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
 			BITVECP		sa = GETSIGN(a),
 						sb = GETSIGN(b);
 
+			Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -482,19 +495,19 @@ gtsvector_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
-	return pg_popcount(sign, SIGLEN);
+	return pg_popcount(sign, siglen);
 }
 
 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]);
 		/* Using the popcount functions here isn't likely to win */
@@ -506,17 +519,22 @@ hemdistsign(BITVECP a, BITVECP b)
 static int
 hemdist(SignTSVector *a, SignTSVector *b)
 {
+	int siglena = GETSIGLEN(a);
+	int siglenb = GETSIGLEN(b);
+
 	if (ISALLTRUE(a))
 	{
 		if (ISALLTRUE(b))
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(GETSIGN(b));
+			return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
 	}
 	else if (ISALLTRUE(b))
-		return SIGLENBIT - sizebitvec(GETSIGN(a));
+		return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
 
-	return hemdistsign(GETSIGN(a), GETSIGN(b));
+	Assert(siglena == siglenb);
+
+	return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
 }
 
 Datum
@@ -525,6 +543,7 @@ gtsvector_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);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
 	SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
 	SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -533,14 +552,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 
 	if (ISARRKEY(newval))
 	{
-		BITVEC		sign;
+		BITVECP		sign = palloc(siglen);
 
-		makesign(sign, newval);
+		makesign(sign, newval, siglen);
 
 		if (ISALLTRUE(origval))
-			*penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+		{
+			int			siglenbit = SIGLENBIT(siglen);
+
+			*penalty =
+				(float) (siglenbit - sizebitvec(sign, siglen)) /
+				(float) (siglenbit + 1);
+		}
 		else
-			*penalty = hemdistsign(sign, orig);
+			*penalty = hemdistsign(sign, orig, siglen);
+
+		pfree(sign);
 	}
 	else
 		*penalty = hemdist(origval, newval);
@@ -550,19 +577,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 typedef struct
 {
 	bool		allistrue;
-	BITVEC		sign;
+	BITVECP		sign;
 } CACHESIGN;
 
 static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
 {
 	item->allistrue = false;
 	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) )
@@ -586,19 +613,19 @@ comparecost(const void *va, const void *vb)
 
 
 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
@@ -606,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
 	OffsetNumber k,
 				j;
 	SignTSVector *datum_l,
@@ -625,6 +653,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	BITVECP		ptr;
 	int			i;
 	CACHESIGN  *cache;
+	char	   *cache_sign;
 	SPLITCOST  *costvector;
 
 	maxoff = entryvec->n - 2;
@@ -633,16 +662,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	v->spl_right = (OffsetNumber *) palloc(nbytes);
 
 	cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
-	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+	cache_sign = palloc(siglen * (maxoff + 2));
+
+	for (j = 0; j < maxoff + 2; j++)
+		cache[j].sign = &cache_sign[siglen * j];
+
+	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+			  siglen);
 
 	for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
 	{
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
 			if (k == FirstOffsetNumber)
-				fillcache(&cache[j], GETENTRY(entryvec, j));
+				fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
 
-			size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+			size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -664,44 +699,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (cache[seed_1].allistrue)
-	{
-		datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_l->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (SignTSVector *) 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 = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_r->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (SignTSVector *) 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 = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+							  siglen, cache[seed_1].sign);
+	datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+							  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), 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);
@@ -727,36 +739,34 @@ gtsvector_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;
@@ -767,12 +777,12 @@ gtsvector_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;
@@ -799,3 +809,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
 {
 	return gtsvector_consistent(fcinfo);
 }
+
+Datum
+gtsvector_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(GistTsVectorOptions, siglen) };
+	GistTsVectorOptions *options =
+		parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+									sizeof(GistTsVectorOptions), validate);
+
+	PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 020b7413cc..5ceee11ab1 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -458,7 +458,7 @@
   amproc => 'gist_circle_distance' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '1',
-  amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
+  amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '2',
   amproc => 'gtsvector_union' },
@@ -476,6 +476,9 @@
   amproc => 'gtsvector_picksplit' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+  amprocrighttype => 'tsvector', amprocnum => '10',
+  amproc => 'gtsvector_options' },
 { amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
   amprocrighttype => 'tsquery', amprocnum => '1',
   amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87335248a0..82b51fc1bb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8569,30 +8569,35 @@
 
 { oid => '3648', descr => 'GiST tsvector support',
   proname => 'gtsvector_compress', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'gtsvector_compress' },
+  proargtypes => 'internal internal', prosrc => 'gtsvector_compress' },
 { oid => '3649', descr => 'GiST tsvector support',
   proname => 'gtsvector_decompress', prorettype => 'internal',
-  proargtypes => 'internal', prosrc => 'gtsvector_decompress' },
+  proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' },
 { oid => '3650', descr => 'GiST tsvector support',
   proname => 'gtsvector_picksplit', prorettype => 'internal',
-  proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' },
+  proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' },
 { oid => '3651', descr => 'GiST tsvector support',
   proname => 'gtsvector_union', prorettype => 'gtsvector',
-  proargtypes => 'internal internal', prosrc => 'gtsvector_union' },
+  proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' },
 { oid => '3652', descr => 'GiST tsvector support',
   proname => 'gtsvector_same', prorettype => 'internal',
-  proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' },
+  proargtypes => 'gtsvector gtsvector internal internal',
+  prosrc => 'gtsvector_same' },
 { oid => '3653', descr => 'GiST tsvector support',
   proname => 'gtsvector_penalty', prorettype => 'internal',
-  proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' },
+  proargtypes => 'internal internal internal internal',
+  prosrc => 'gtsvector_penalty' },
 { oid => '3654', descr => 'GiST tsvector support',
   proname => 'gtsvector_consistent', prorettype => 'bool',
-  proargtypes => 'internal tsvector int2 oid internal',
+  proargtypes => 'internal tsvector int2 oid internal internal',
   prosrc => 'gtsvector_consistent' },
 { oid => '3790', descr => 'GiST tsvector support (obsolete)',
   proname => 'gtsvector_consistent', prorettype => 'bool',
   proargtypes => 'internal gtsvector int4 oid internal',
   prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3998', descr => 'GiST tsvector support',
+  proname => 'gtsvector_options', prorettype => 'internal',
+  proargtypes => 'internal bool', prosrc => 'gtsvector_options' },
 
 { oid => '3656', descr => 'GIN tsvector support',
   proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index 6f61acc1ed..a1873dc722 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
    508
 (1 row)
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR:  value 485 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR:  parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a)
+    "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx2
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 637bfb3012..9aed780d0c 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
 SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
 SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
-- 
2.20.1

#18Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tomas Vondra (#17)
Re: [PATCH] Opclass parameters

On 2019-Jun-11, Tomas Vondra wrote:

1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:

I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.

2) The 0001 part does this in index_opclass_options_generic:

get_opclass_name(opclass, InvalidOid, &str);

ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));

But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.

Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.

Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.

--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#19Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Alvaro Herrera (#18)
5 attachment(s)
Re: [PATCH] Opclass parameters

On 04.09.2019 1:02, Alvaro Herrera wrote:

On 2019-Jun-11, Tomas Vondra wrote:

1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:

I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.

API was simplified in the new version of the patches (see below).

2) The 0001 part does this in index_opclass_options_generic:

get_opclass_name(opclass, InvalidOid, &str);

ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));

But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.

Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.

I decided to add new function generate_opclass_name() like existing
generate_collation_name(), and to reuse static get_opclass_name().

Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.

New version of rebased patches is attached:

1. Opclass parameters infrastructure.

API was completely refactored since the previous version:

- API was generalized for all AMs. Previously, each AM should implement
opclass options parsing/passing in its own way using its own support
 function numbers.
Now each AMs uses 0th support function (discussable). Binary bytea values
of parsed options are passed to support functions using special expression
node initialized in FmgrInfo.fn_expr (see macro PG_GET_OPCLASS_OPTIONS(),
get_fn_opclass_options(), set_fn_opclass_options).

- Introduced new structure local_relopts (needs a better name, of course)
with a set of functions for opclass/AM options definition. The parsing
was moved into index_opclass_option(). That was necessary for mixing of
 AM- and opclass-specific options. Opclasses now extend the structure with
AM's options adding their own options. See patch #4 for an example.

2. New AM method amattoptions().

amattoptions() is used to specify per-column AM-specific options.
The example is signature length for bloom indexes (patch #3).

3. Use amattoptions() in contrib/bloom.

This is an attempt to replace "col1"-"col32" AM reloptions with
per-column option "bits" using new amattoptions API:

-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);

4. An example of using new API in GiST tsvector_ops.

Opclass options definition looks much simpler now:

typedef struct GistTsVectorOptions
{
/* GistAttOptions am_att_options; (future) */
int siglen;
} GistTsVectorOptions;

Datum
gtsvector_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
GistTsVectorOptions *options = NULL;

extend_local_reloptions(relopts, options, sizeof(*options));
add_local_int_reloption(relopts, "siglen", "signature length",
SIGLEN_DEFAULT, 1, SIGLEN_MAX,
&options->siglen);

PG_RETURN_VOID();
}

5. Remove pg_index.indoption column (experimental).

pg_index.indoption (array of per-column ASC/DESC, NULLS FIRST/LAST
flags) can be removed now, and these flags can be stored as
special per-column AM-specific options "desc" and "nulls_first".

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0003-Use-amattoptions-in-contrib-bloom-20190910.patchtext/x-patch; name=0003-Use-amattoptions-in-contrib-bloom-20190910.patchDownload
From 735e199d6736569eb648e33256b3c5268c2a36eb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 3/5] Use amattoptions in contrib/bloom

---
 contrib/bloom/bloom.h            | 10 +++++++++
 contrib/bloom/blutils.c          | 44 +++++++++++++++++++++-------------------
 contrib/bloom/expected/bloom.out | 23 +++++++++++++--------
 contrib/bloom/sql/bloom.sql      |  9 ++++----
 4 files changed, 53 insertions(+), 33 deletions(-)

diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 010148e..681aaaa 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -16,6 +16,7 @@
 #include "access/amapi.h"
 #include "access/generic_xlog.h"
 #include "access/itup.h"
+#include "access/reloptions.h"
 #include "access/xlog.h"
 #include "nodes/pathnodes.h"
 #include "fmgr.h"
@@ -105,6 +106,13 @@ typedef struct BloomOptions
 											 * index key */
 } BloomOptions;
 
+/* Bloom attribute options */
+typedef struct BloomAttOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			bitSize;		/* # of bits generated for this index key */
+} BloomAttOptions;
+
 /*
  * FreeBlockNumberArray - array of block numbers sized so that metadata fill
  * all space in metapage.
@@ -206,6 +214,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
 extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
 											  IndexBulkDeleteResult *stats);
 extern bytea *bloptions(Datum reloptions, bool validate);
+extern void blattoptions(local_relopts *relopts, int attno);
+
 extern void blcostestimate(PlannerInfo *root, IndexPath *path,
 						   double loop_count, Cost *indexStartupCost,
 						   Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index b62ba7f..b286081 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -39,7 +39,7 @@ PG_FUNCTION_INFO_V1(blhandler);
 static relopt_kind bl_relopt_kind;
 
 /* parse table for fillRelOptions */
-static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];
+static relopt_parse_elt bl_relopt_tab[1];
 
 static int32 myRand(void);
 static void mySrand(uint32 seed);
@@ -52,9 +52,6 @@ static void mySrand(uint32 seed);
 void
 _PG_init(void)
 {
-	int			i;
-	char		buf[16];
-
 	bl_relopt_kind = add_reloption_kind();
 
 	/* Option for length of signature */
@@ -64,19 +61,6 @@ _PG_init(void)
 	bl_relopt_tab[0].optname = "length";
 	bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
 	bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
-
-	/* Number of bits for each possible index column: col1, col2, ... */
-	for (i = 0; i < INDEX_MAX_KEYS; i++)
-	{
-		snprintf(buf, sizeof(buf), "col%d", i + 1);
-		add_int_reloption(bl_relopt_kind, buf,
-						  "Number of bits generated for each index column",
-						  DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
-		bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
-														   buf);
-		bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
-		bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i;
-	}
 }
 
 /*
@@ -86,13 +70,10 @@ static BloomOptions *
 makeDefaultBloomOptions(void)
 {
 	BloomOptions *opts;
-	int			i;
 
 	opts = (BloomOptions *) palloc0(sizeof(BloomOptions));
 	/* Convert DEFAULT_BLOOM_LENGTH from # of bits to # of words */
 	opts->bloomLength = (DEFAULT_BLOOM_LENGTH + SIGNWORDBITS - 1) / SIGNWORDBITS;
-	for (i = 0; i < INDEX_MAX_KEYS; i++)
-		opts->bitSize[i] = DEFAULT_BLOOM_BITS;
 	SET_VARSIZE(opts, sizeof(BloomOptions));
 	return opts;
 }
@@ -131,7 +112,7 @@ blhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = blcostestimate;
 	amroutine->amoptions = bloptions;
-	amroutine->amattoptions = NULL;
+	amroutine->amattoptions = blattoptions;
 	amroutine->amproperty = NULL;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = blvalidate;
@@ -418,6 +399,7 @@ void
 BloomFillMetapage(Relation index, Page metaPage)
 {
 	BloomOptions *opts;
+	BloomAttOptions **attopts;
 	BloomMetaPageData *metadata;
 
 	/*
@@ -428,6 +410,11 @@ BloomFillMetapage(Relation index, Page metaPage)
 	if (!opts)
 		opts = makeDefaultBloomOptions();
 
+	attopts = (BloomAttOptions **) RelationGetIndexAttOptions(index, false);
+
+	for (int i = 0; i < RelationGetNumberOfAttributes(index); i++)
+		opts->bitSize[i] = attopts[i]->bitSize;
+
 	/*
 	 * Initialize contents of meta page, including a copy of the options,
 	 * which are now frozen for the life of the index.
@@ -491,3 +478,18 @@ bloptions(Datum reloptions, bool validate)
 
 	return (bytea *) rdopts;
 }
+
+/*
+ * Parse per-attribute reloptions for bloom index, producing a BloomOptions struct.
+ */
+void
+blattoptions(local_relopts *relopts, int attno)
+{
+	BloomAttOptions *attopts = NULL;
+
+	init_local_reloptions(relopts, attopts, sizeof(*attopts));
+	add_local_int_reloption(relopts, "bits",
+							"Number of bits generated for index column",
+							DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS,
+							&attopts->bitSize);
+}
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
index 5ab9e34..3ade0b3 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -4,7 +4,7 @@ CREATE TABLE tst (
 	t	text
 );
 INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
 SET enable_seqscan=on;
 SET enable_bitmapscan=off;
 SET enable_indexscan=off;
@@ -144,7 +144,7 @@ CREATE UNLOGGED TABLE tstu (
 	t	text
 );
 INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
+CREATE INDEX bloomidxu ON tstu USING bloom (i, t DEFAULT (bits = 4));
 SET enable_seqscan=off;
 SET enable_bitmapscan=on;
 SET enable_indexscan=on;
@@ -214,16 +214,23 @@ ORDER BY 1;
 -- relation options
 --
 DROP INDEX bloomidx;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
+CREATE INDEX bloomidx ON tst USING bloom (i  DEFAULT (bits=4), t) WITH (length=7);
 SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
-    reloptions     
--------------------
- {length=7,col1=4}
+ reloptions 
+------------
+ {length=7}
 (1 row)
 
+SELECT attnum, attoptions FROM pg_attribute WHERE attrelid = 'bloomidx'::regclass ORDER BY 1;
+ attnum | attoptions 
+--------+------------
+      1 | {bits=4}
+      2 | 
+(2 rows)
+
 -- check for min and max values
 \set VERBOSITY terse
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
 ERROR:  value 0 out of bounds for option "length"
-CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
-ERROR:  value 0 out of bounds for option "col1"
+CREATE INDEX bloomidx2 ON tst USING bloom (i DEFAULT (bits=0), t);
+ERROR:  value 0 out of bounds for option "bits"
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 32755f2..362647a 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -6,7 +6,7 @@ CREATE TABLE tst (
 );
 
 INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
 
 SET enable_seqscan=on;
 SET enable_bitmapscan=off;
@@ -58,7 +58,7 @@ CREATE UNLOGGED TABLE tstu (
 );
 
 INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
+CREATE INDEX bloomidxu ON tstu USING bloom (i, t DEFAULT (bits = 4));
 
 SET enable_seqscan=off;
 SET enable_bitmapscan=on;
@@ -86,9 +86,10 @@ ORDER BY 1;
 -- relation options
 --
 DROP INDEX bloomidx;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
+CREATE INDEX bloomidx ON tst USING bloom (i  DEFAULT (bits=4), t) WITH (length=7);
 SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+SELECT attnum, attoptions FROM pg_attribute WHERE attrelid = 'bloomidx'::regclass ORDER BY 1;
 -- check for min and max values
 \set VERBOSITY terse
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
-CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i DEFAULT (bits=0), t);
-- 
2.7.4

0001-Introduce-opclass-parameters-20190910.patchtext/x-patch; name=0001-Introduce-opclass-parameters-20190910.patchDownload
From 3a9b74780956951bdbe60cdaa96e9752675d3a34 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 1/5] Introduce opclass parameters

---
 contrib/bloom/blvalidate.c                       |   3 +
 doc/src/sgml/indices.sgml                        |   2 +-
 doc/src/sgml/ref/create_index.sgml               |  16 +-
 src/backend/access/brin/brin_validate.c          |   3 +
 src/backend/access/common/reloptions.c           | 410 +++++++++++++++++++----
 src/backend/access/gin/ginvalidate.c             |   3 +
 src/backend/access/gist/gistvalidate.c           |   3 +
 src/backend/access/hash/hashvalidate.c           |   2 +
 src/backend/access/index/indexam.c               |  74 +++-
 src/backend/access/nbtree/nbtvalidate.c          |   3 +
 src/backend/access/spgist/spgvalidate.c          |   3 +
 src/backend/catalog/heap.c                       |   8 +-
 src/backend/catalog/index.c                      |  22 +-
 src/backend/catalog/toasting.c                   |   1 +
 src/backend/commands/indexcmds.c                 |  65 +++-
 src/backend/commands/opclasscmds.c               |  41 ++-
 src/backend/commands/tablecmds.c                 |   2 +-
 src/backend/nodes/copyfuncs.c                    |   1 +
 src/backend/nodes/equalfuncs.c                   |   1 +
 src/backend/nodes/makefuncs.c                    |   3 +
 src/backend/nodes/outfuncs.c                     |   1 +
 src/backend/optimizer/util/plancat.c             |   3 +
 src/backend/parser/gram.y                        |  72 ++--
 src/backend/parser/parse_utilcmd.c               |   8 +
 src/backend/utils/adt/ruleutils.c                | 135 +++++---
 src/backend/utils/adt/selfuncs.c                 |  23 +-
 src/backend/utils/cache/lsyscache.c              |  35 ++
 src/backend/utils/cache/relcache.c               | 151 ++++++++-
 src/backend/utils/fmgr/fmgr.c                    |  28 ++
 src/include/access/genam.h                       |   3 +
 src/include/access/reloptions.h                  |  36 ++
 src/include/catalog/heap.h                       |   1 +
 src/include/catalog/pg_amproc.h                  |   2 +
 src/include/fmgr.h                               |   4 +
 src/include/nodes/execnodes.h                    |   2 +
 src/include/nodes/parsenodes.h                   |   1 +
 src/include/nodes/pathnodes.h                    |   1 +
 src/include/utils/lsyscache.h                    |   1 +
 src/include/utils/rel.h                          |   1 +
 src/include/utils/relcache.h                     |   3 +
 src/include/utils/ruleutils.h                    |   1 +
 src/test/regress/expected/alter_generic.out      |  22 +-
 src/test/regress/expected/btree_index.out        |   5 +
 src/test/regress/expected/opr_sanity.out         |   2 +-
 src/test/regress/input/create_function_1.source  |   5 +
 src/test/regress/output/create_function_1.source |   4 +
 src/test/regress/regress.c                       |   7 +
 src/test/regress/sql/alter_generic.sql           |  13 +-
 src/test/regress/sql/btree_index.sql             |   4 +
 src/test/regress/sql/opr_sanity.sql              |   2 +-
 50 files changed, 1050 insertions(+), 192 deletions(-)

diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
index e9bd1b4..dbd675b 100644
--- a/contrib/bloom/blvalidate.c
+++ b/contrib/bloom/blvalidate.c
@@ -105,6 +105,9 @@ blvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
+			case OPCLASS_OPTIONS_PROC:
+				ok = true;
+				break;
 			case BLOOM_HASH_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
 											1, 1, opckeytype);
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 95c0a19..ea3acea 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1253,7 +1253,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
    An index definition can specify an <firstterm>operator
    class</firstterm> for each column of an index.
 <synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
 </synopsis>
    The operator class identifies the operators to be used by the index
    for that column.  For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 629a31e..61401f3 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
-    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
      </varlistentry>
 
      <varlistentry>
+      <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+      <listitem>
+       <para>
+        The name of an operator class parameter. See below for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><literal>ASC</literal></term>
       <listitem>
        <para>
@@ -646,8 +655,9 @@ Indexes:
   </para>
 
   <para>
-   An <firstterm>operator class</firstterm> can be specified for each
-   column of an index. The operator class identifies the operators to be
+   An <firstterm>operator class</firstterm> with its optional parameters 
+   can be specified for each column of an index.
+   The operator class identifies the operators to be
    used by the index for that column. For example, a B-tree index on
    four-byte integers would use the <literal>int4_ops</literal> class;
    this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c
index 0129338..9e6694e 100644
--- a/src/backend/access/brin/brin_validate.c
+++ b/src/backend/access/brin/brin_validate.c
@@ -87,6 +87,9 @@ brinvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
+			case OPCLASS_OPTIONS_PROC:
+				ok = true;
+				break;
 			case BRIN_PROCNUM_OPCINFO:
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
 											1, 1, INTERNALOID);
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 20f4ed3..3ad1324 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -445,6 +445,7 @@ static relopt_string stringRelOpts[] =
 		4,
 		false,
 		gistValidateBufferingOption,
+		NULL,
 		"auto"
 	},
 	{
@@ -457,6 +458,7 @@ static relopt_string stringRelOpts[] =
 		0,
 		true,
 		validateWithCheckOption,
+		NULL,
 		NULL
 	},
 	/* list terminator */
@@ -615,6 +617,50 @@ add_reloption(relopt_gen *newoption)
 	need_initialization = true;
 }
 
+typedef struct local_relopt
+{
+	relopt_gen *option;
+	int			offset;
+} local_relopt;
+
+void
+init_local_reloptions(local_relopts *opts, void *base, Size base_size)
+{
+	opts->options = NIL;
+	opts->validators = NIL;
+	opts->base = base;
+	opts->base_size = base_size;
+}
+
+void
+extend_local_reloptions(local_relopts *opts, void *base, Size base_size)
+{
+	Assert(opts->base_size < base_size);
+	opts->base = base;
+	opts->base_size = base_size;
+}
+
+void
+register_reloptions_validator(local_relopts *opts, relopts_validator validator)
+{
+	opts->validators = lappend(opts->validators, validator);
+}
+
+/*
+ * add_local_reloption
+ *		Add an already-created custom reloption to the local list.
+ */
+static void
+add_local_reloption(local_relopts *relopts, relopt_gen *newoption, void *pval)
+{
+	local_relopt *opt = palloc(sizeof(*opt));
+
+	opt->option = newoption;
+	opt->offset = (char *) pval - (char *) relopts->base;
+
+	relopts->options = lappend(relopts->options, opt);
+}
+
 /*
  * allocate_reloption
  *		Allocate a new reloption and initialize the type-agnostic fields
@@ -627,7 +673,10 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
 	size_t		size;
 	relopt_gen *newoption;
 
-	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+	if (kinds != RELOPT_KIND_LOCAL)
+		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+	else
+		oldcxt = NULL;
 
 	switch (type)
 	{
@@ -659,17 +708,19 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
 	newoption->namelen = strlen(name);
 	newoption->type = type;
 
-	MemoryContextSwitchTo(oldcxt);
+	if (oldcxt != NULL)
+		MemoryContextSwitchTo(oldcxt);
 
 	return newoption;
 }
 
 /*
- * add_bool_reloption
- *		Add a new boolean reloption
+ * init_bool_reloption
+ *		Allocate and initialize a new boolean reloption
  */
-void
-add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
+static relopt_bool *
+init_bool_reloption(bits32 kinds, const char *name, const char *desc,
+					bool default_val)
 {
 	relopt_bool *newoption;
 
@@ -677,16 +728,43 @@ add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool defaul
 												   name, desc);
 	newoption->default_val = default_val;
 
+	return newoption;
+}
+
+/*
+ * add_bool_reloption
+ *		Add a new boolean reloption
+ */
+void
+add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
+{
+	relopt_bool *newoption = init_bool_reloption(kinds, name, desc, default_val);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
 /*
- * add_int_reloption
- *		Add a new integer reloption
+ * add_local_bool_reloption
+ *		Add a new boolean local reloption
  */
 void
-add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
-				  int min_val, int max_val)
+add_local_bool_reloption(local_relopts *relopts, const char *name,
+						 const char *desc, bool default_val, bool *pval)
+{
+	relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL, name, desc,
+												 default_val);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+
+/*
+ * init_real_reloption
+ *		Allocate and initialize a new integer reloption
+ */
+static relopt_int *
+init_int_reloption(bits32 kinds, const char *name, const char *desc,
+				   int default_val, int min_val, int max_val)
 {
 	relopt_int *newoption;
 
@@ -696,16 +774,45 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_
 	newoption->min = min_val;
 	newoption->max = max_val;
 
+	return newoption;
+}
+
+/*
+ * add_int_reloption
+ *		Add a new integer reloption
+ */
+void
+add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
+				  int min_val, int max_val)
+{
+	relopt_int *newoption = init_int_reloption(kinds, name, desc, default_val,
+											   min_val, max_val);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
 /*
- * add_real_reloption
- *		Add a new float reloption
+ * add_local_int_reloption
+ *		Add a new local integer reloption
  */
 void
-add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
-				   double min_val, double max_val)
+add_local_int_reloption(local_relopts *relopts, const char *name,
+						const char *desc, int default_val, int min_val,
+						int max_val, int *pval)
+{
+	relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL, name, desc,
+											   default_val, min_val, max_val);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+/*
+ * init_real_reloption
+ *		Allocate and initialize a new real reloption
+ */
+static relopt_real *
+init_real_reloption(bits32 kinds, const char *name, const char *desc,
+					double default_val, double min_val, double max_val)
 {
 	relopt_real *newoption;
 
@@ -715,21 +822,46 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc, double defa
 	newoption->min = min_val;
 	newoption->max = max_val;
 
+	return newoption;
+}
+
+/*
+ * add_real_reloption
+ *		Add a new float reloption
+ */
+void
+add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
+				   double min_val, double max_val)
+{
+	relopt_real *newoption = init_real_reloption(kinds, name, desc, default_val,
+												 min_val, max_val);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
 /*
- * add_string_reloption
- *		Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values.  It must elog(ERROR) when the argument string is
- * not acceptable for the variable.  Note that the default value must pass
- * the validation.
+ * add_local_real_reloption
+ *		Add a new local float reloption
  */
 void
-add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
-					 validate_string_relopt validator)
+add_local_real_reloption(local_relopts *relopts, const char *name, const char *desc,
+						 double default_val, double min_val, double max_val,
+						 double *pval)
+{
+	relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL, name, desc,
+												 default_val, min_val, max_val);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+/*
+ * init_string_reloption
+ *		Allocate and initialize a new string reloption
+ */
+static relopt_string *
+init_string_reloption(bits32 kinds, const char *name, const char *desc,
+					  const char *default_val, validate_string_relopt validator,
+					  fill_string_relopt filler)
 {
 	relopt_string *newoption;
 
@@ -740,10 +872,13 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
 	newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
 													 name, desc);
 	newoption->validate_cb = validator;
+	newoption->fill_cb = filler;
 	if (default_val)
 	{
-		newoption->default_val = MemoryContextStrdup(TopMemoryContext,
-													 default_val);
+		if (kinds == RELOPT_KIND_LOCAL)
+			newoption->default_val = strdup(default_val);
+		else
+			newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
 		newoption->default_len = strlen(default_val);
 		newoption->default_isnull = false;
 	}
@@ -754,10 +889,46 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
 		newoption->default_isnull = true;
 	}
 
+	return newoption;
+}
+
+/*
+ * add_string_reloption
+ *		Add a new string reloption
+ *
+ * "validator" is an optional function pointer that can be used to test the
+ * validity of the values.  It must elog(ERROR) when the argument string is
+ * not acceptable for the variable.  Note that the default value must pass
+ * the validation.
+ */
+void
+add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
+					 validate_string_relopt validator)
+{
+	relopt_string *newoption = init_string_reloption(kinds, name, desc,
+													 default_val, validator, NULL);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
 /*
+ * add_local_string_reloption
+ *		Add a new local string reloption
+ */
+void
+add_local_string_reloption(local_relopts *relopts, const char *name,
+						   const char *desc, const char *default_val,
+						   validate_string_relopt validator,
+						   fill_string_relopt filler, int *poffset)
+{
+	relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL, name,
+													 desc, default_val,
+													 validator, filler);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, poffset);
+}
+
+/*
  * Transform a relation options list (list of DefElem) into the text array
  * format that is kept in pg_class.reloptions, including only those options
  * that are in the passed namespace.  The output values do not include the
@@ -1051,6 +1222,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 	return options;
 }
 
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+						relopt_value *reloptions, int numoptions)
+{
+	ArrayType  *array = DatumGetArrayTypeP(options);
+	Datum	   *optiondatums;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(array, TEXTOID, -1, false, 'i',
+					  &optiondatums, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *text_str = VARDATA(optiondatums[i]);
+		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		int			j;
+
+		/* Search for a match in reloptions */
+		for (j = 0; j < numoptions; j++)
+		{
+			int			kw_len = reloptions[j].gen->namelen;
+
+			if (text_len > kw_len && text_str[kw_len] == '=' &&
+				strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+			{
+				parse_one_reloption(&reloptions[j], text_str, text_len,
+									validate);
+				break;
+			}
+		}
+
+		if (j >= numoptions && validate)
+		{
+			char	   *s;
+			char	   *p;
+
+			s = TextDatumGetCString(optiondatums[i]);
+			p = strchr(s, '=');
+			if (p)
+				*p = '\0';
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized parameter \"%s\"", s)));
+		}
+	}
+
+	/* It's worth avoiding memory leaks in this function */
+	pfree(optiondatums);
+
+	if (((void *) array) != DatumGetPointer(options))
+		pfree(array);
+}
+
 /*
  * Interpret reloptions that are given in text-array format.
  *
@@ -1105,57 +1330,74 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
 
 	/* Done if no options */
 	if (PointerIsValid(DatumGetPointer(options)))
+		parseRelOptionsInternal(options, validate, reloptions, numoptions);
+
+	*numrelopts = numoptions;
+	return reloptions;
+}
+
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
+{
+	int			nopts = list_length(relopts->options);
+	relopt_value *values = palloc(sizeof(*values) * nopts);
+	ListCell   *lc;
+	int			i = 0;
+
+	foreach(lc, relopts->options)
 	{
-		ArrayType  *array = DatumGetArrayTypeP(options);
-		Datum	   *optiondatums;
-		int			noptions;
+		local_relopt *opt = lfirst(lc);
 
-		deconstruct_array(array, TEXTOID, -1, false, 'i',
-						  &optiondatums, NULL, &noptions);
+		values[i].gen = opt->option;
+		values[i].isset = false;
 
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *text_str = VARDATA(optiondatums[i]);
-			int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
-			int			j;
+		i++;
+	}
 
-			/* Search for a match in reloptions */
-			for (j = 0; j < numoptions; j++)
-			{
-				int			kw_len = reloptions[j].gen->namelen;
+	if (options != (Datum) 0)
+		parseRelOptionsInternal(options, validate, values, nopts);
 
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
-				{
-					parse_one_reloption(&reloptions[j], text_str, text_len,
-										validate);
-					break;
-				}
-			}
+	return values;
+}
 
-			if (j >= numoptions && validate)
-			{
-				char	   *s;
-				char	   *p;
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
+{
+	int			noptions = list_length(relopts->options);
+	relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+	relopt_value *vals;
+	void	   *opts;
+	int			i = 0;
+	ListCell   *lc;
+
+	foreach(lc, relopts->options)
+	{
+		local_relopt *opt = lfirst(lc);
 
-				s = TextDatumGetCString(optiondatums[i]);
-				p = strchr(s, '=');
-				if (p)
-					*p = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("unrecognized parameter \"%s\"", s)));
-			}
-		}
+		elems[i].optname = opt->option->name;
+		elems[i].opttype = opt->option->type;
+		elems[i].offset = opt->offset;
 
-		/* It's worth avoiding memory leaks in this function */
-		pfree(optiondatums);
-		if (((void *) array) != DatumGetPointer(options))
-			pfree(array);
+		i++;
 	}
 
-	*numrelopts = numoptions;
-	return reloptions;
+	vals = parseLocalRelOptions(relopts, options, validate);
+	opts = allocateReloptStruct(relopts->base_size, vals, noptions);
+	fillRelOptions(opts, relopts->base_size, vals, noptions, validate, elems, noptions);
+
+	foreach(lc, relopts->validators)
+		((relopts_validator) lfirst(lc))(opts, vals, noptions);
+
+	if (elems)
+		pfree(elems);
+
+	return opts;
 }
 
 /*
@@ -1271,8 +1513,24 @@ allocateReloptStruct(Size base, relopt_value *options, int numoptions)
 	int			i;
 
 	for (i = 0; i < numoptions; i++)
-		if (options[i].gen->type == RELOPT_TYPE_STRING)
-			size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
+	{
+		relopt_value *optval = &options[i];
+
+		if (optval->gen->type == RELOPT_TYPE_STRING)
+		{
+			relopt_string *optstr = (relopt_string *) optval->gen;
+
+			if (optstr->fill_cb)
+			{
+				const char *val = optval->isset ? optval->values.string_val :
+					optstr->default_isnull ? NULL : optstr->default_val;
+
+				size += optstr->fill_cb(val, NULL);
+			}
+			else
+				size += GET_STRING_RELOPTION_LEN(*optval) + 1;
+		}
+	}
 
 	return palloc0(size);
 }
@@ -1336,7 +1594,21 @@ fillRelOptions(void *rdopts, Size basesize,
 						else
 							string_val = NULL;
 
-						if (string_val == NULL)
+						if (optstring->fill_cb)
+						{
+							Size		size =
+								optstring->fill_cb(string_val,
+												   (char *) rdopts + offset);
+
+							if (size)
+							{
+								*(int *) itempos = offset;
+								offset += size;
+							}
+							else
+								*(int *) itempos = 0;
+						}
+						else if (string_val == NULL)
 							*(int *) itempos = 0;
 						else
 						{
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 63bd7f2..337eeec 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -106,6 +106,9 @@ ginvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
+			case OPCLASS_OPTIONS_PROC:
+				ok = true;
+				break;
 			case GIN_COMPARE_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
 											2, 2, opckeytype, opckeytype);
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index dfc1a87..ea8cd0e 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -106,6 +106,9 @@ gistvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
+			case OPCLASS_OPTIONS_PROC:
+				ok = true;
+				break;
 			case GIST_CONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
 											5, 5, INTERNALOID, opcintype,
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 9315872..7650a30 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -105,6 +105,8 @@ hashvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
+			case OPCLASS_OPTIONS_PROC:
+				break;
 			case HASHSTANDARD_PROC:
 			case HASHEXTENDED_PROC:
 				if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 2e8f53a..bbed726 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -45,17 +45,23 @@
 
 #include "access/amapi.h"
 #include "access/heapam.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "catalog/index.h"
+#include "catalog/pg_amproc.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "nodes/makefuncs.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 
 
 /* ----------------------------------------------------------------
@@ -767,9 +773,9 @@ index_getprocid(Relation irel,
 
 	nproc = irel->rd_indam->amsupport;
 
-	Assert(procnum > 0 && procnum <= (uint16) nproc);
+	Assert(procnum >= 0 && procnum <= (uint16) nproc);
 
-	procindex = (nproc * (attnum - 1)) + (procnum - 1);
+	procindex = ((nproc + 1) * (attnum - 1)) + procnum;
 
 	loc = irel->rd_support;
 
@@ -801,9 +807,9 @@ index_getprocinfo(Relation irel,
 
 	nproc = irel->rd_indam->amsupport;
 
-	Assert(procnum > 0 && procnum <= (uint16) nproc);
+	Assert(procnum >= 0 && procnum <= (uint16) nproc);
 
-	procindex = (nproc * (attnum - 1)) + (procnum - 1);
+	procindex = ((nproc + 1) * (attnum - 1)) + procnum;
 
 	locinfo = irel->rd_supportinfo;
 
@@ -832,6 +838,17 @@ index_getprocinfo(Relation irel,
 				 procnum, attnum, RelationGetRelationName(irel));
 
 		fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
+
+		if (procnum != OPCLASS_OPTIONS_PROC)
+		{
+			/* Initialize locinfo->fn_expr with opclass options Const */
+			bytea	  **attoptions = RelationGetIndexAttOptions(irel, false);
+			MemoryContext oldcxt = MemoryContextSwitchTo(irel->rd_indexcxt);
+
+			set_fn_opclass_options(locinfo, attoptions[attnum - 1]);
+
+			MemoryContextSwitchTo(oldcxt);
+		}
 	}
 
 	return locinfo;
@@ -911,3 +928,52 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
 		}
 	}
 }
+
+/* ----------------
+ *      index_opclass_options
+ *
+ *      Parse opclass-specific options for index column.
+ * ----------------
+ */
+bytea *
+index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
+					  bool validate)
+{
+	Oid			procid = index_getprocid(indrel, attnum, 0);
+	FmgrInfo   *procinfo;
+	local_relopts relopts;
+
+	if (!OidIsValid(procid))
+	{
+		Oid			opclass;
+		Datum		indclassDatum;
+		oidvector  *indclass;
+		bool		isnull;
+
+		if (!DatumGetPointer(attoptions))
+			return NULL;	/* ok, no options, no procedure */
+
+		/*
+		 * Report an error if the opclass's options-parsing procedure does not
+		 * exist but the opclass options are specified.
+		 */
+		indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+										Anum_pg_index_indclass, &isnull);
+		Assert(!isnull);
+		indclass = (oidvector *) DatumGetPointer(indclassDatum);
+		opclass = indclass->values[attnum - 1];
+
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("operator class %s has no options",
+						generate_opclass_name(opclass))));
+	}
+
+	init_local_reloptions(&relopts, NULL, 0);
+
+	procinfo = index_getprocinfo(indrel, attnum, 0);
+
+	(void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+
+	return parseAndFillLocalRelOptions(&relopts, attoptions, validate);
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 0148ea7..418edc8 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -87,6 +87,9 @@ btvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
+			case OPCLASS_OPTIONS_PROC:
+				ok = true;
+				break;
 			case BTORDER_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, true,
 											2, 2, procform->amproclefttype,
diff --git a/src/backend/access/spgist/spgvalidate.c b/src/backend/access/spgist/spgvalidate.c
index 4b9fdbd..a5bb850 100644
--- a/src/backend/access/spgist/spgvalidate.c
+++ b/src/backend/access/spgist/spgvalidate.c
@@ -105,6 +105,9 @@ spgvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
+			case OPCLASS_OPTIONS_PROC:
+				ok = true;
+				break;
 			case SPGIST_CONFIG_PROC:
 				ok = check_amproc_signature(procform->amproc, VOIDOID, true,
 											2, 2, INTERNALOID, INTERNALOID);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index b7bcdd9..d5a4900 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -693,6 +693,7 @@ CheckAttributeType(const char *attname,
 void
 InsertPgAttributeTuple(Relation pg_attribute_rel,
 					   Form_pg_attribute new_attribute,
+					   Datum attoptions,
 					   CatalogIndexState indstate)
 {
 	Datum		values[Natts_pg_attribute];
@@ -724,10 +725,11 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
 	values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
 	values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
 	values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+	values[Anum_pg_attribute_attoptions - 1] = attoptions;
 
 	/* start out with empty permissions and empty options */
 	nulls[Anum_pg_attribute_attacl - 1] = true;
-	nulls[Anum_pg_attribute_attoptions - 1] = true;
+	nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0;
 	nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
 	nulls[Anum_pg_attribute_attmissingval - 1] = true;
 
@@ -781,7 +783,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		/* Make sure this is OK, too */
 		attr->attstattarget = -1;
 
-		InsertPgAttributeTuple(rel, attr, indstate);
+		InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate);
 
 		/* Add dependency info */
 		myself.classId = RelationRelationId;
@@ -819,7 +821,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 			/* Fill in the correct relation OID in the copied tuple */
 			attStruct.attrelid = new_rel_oid;
 
-			InsertPgAttributeTuple(rel, &attStruct, indstate);
+			InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate);
 		}
 	}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 3e1d406..cb7f0ba 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
 #include "access/amapi.h"
 #include "access/heapam.h"
 #include "access/multixact.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
@@ -106,7 +107,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
 										  Oid *classObjectId);
 static void InitializeAttributeOids(Relation indexRelation,
 									int numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, int numatts);
+static void AppendAttributeTuples(Relation indexRelation, int numatts,
+								  Datum *attopts);
 static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 								Oid parentIndexId,
 								IndexInfo *indexInfo,
@@ -486,7 +488,7 @@ InitializeAttributeOids(Relation indexRelation,
  * ----------------------------------------------------------------
  */
 static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
 {
 	Relation	pg_attribute;
 	CatalogIndexState indstate;
@@ -508,10 +510,11 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
 	for (i = 0; i < numatts; i++)
 	{
 		Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
+		Datum		attoptions = attopts ? attopts[i] : (Datum) 0;
 
 		Assert(attr->attnum == i + 1);
 
-		InsertPgAttributeTuple(pg_attribute, attr, indstate);
+		InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate);
 	}
 
 	CatalogCloseIndexes(indstate);
@@ -591,6 +594,7 @@ UpdateIndexRelation(Oid indexoid,
 	else
 		predDatum = (Datum) 0;
 
+
 	/*
 	 * open the system catalog index relation
 	 */
@@ -933,7 +937,8 @@ index_create(Relation heapRelation,
 	/*
 	 * append ATTRIBUTE tuples for the index
 	 */
-	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs,
+						  indexInfo->ii_OpclassOptions);
 
 	/* ----------------
 	 *	  update pg_index
@@ -1146,6 +1151,13 @@ index_create(Relation heapRelation,
 
 	indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
 
+	/* Validate opclass-specific options */
+	if (indexInfo->ii_OpclassOptions)
+		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+			(void) index_opclass_options(indexRelation, i + 1,
+										 indexInfo->ii_OpclassOptions[i],
+										 true);
+
 	/*
 	 * If this is bootstrap (initdb) time, then we don't actually fill in the
 	 * index yet.  We'll be creating more indexes and classes later, so we
@@ -2266,6 +2278,8 @@ BuildIndexInfo(Relation index)
 								 &ii->ii_ExclusionStrats);
 	}
 
+	ii->ii_OpclassOptions = RelationGetIndexRawAttOptions(index);
+
 	return ii;
 }
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index de6282a..7290731 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -304,6 +304,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;
 	indexInfo->ii_Unique = true;
 	indexInfo->ii_ReadyForInserts = true;
 	indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 1477755..04148e5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -90,6 +90,7 @@ static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
 static bool ReindexRelationConcurrently(Oid relationOid, int options);
 static void ReindexPartitionedIndex(Relation parentIdx);
 static void update_relispartition(Oid relationId, bool newval);
+static bool CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts);
 
 /*
  * callback argument type for RangeVarCallbackForReindexIndex()
@@ -268,6 +269,18 @@ CheckIndexCompatible(Oid oldId,
 		}
 	}
 
+	/* Any change in opclass options break compatibility. */
+	if (ret)
+	{
+		Datum	   *opclassOptions = RelationGetIndexRawAttOptions(irel);
+
+		ret = CompareOpclassOptions(opclassOptions,
+									indexInfo->ii_OpclassOptions, old_natts);
+
+		if (opclassOptions)
+			pfree(opclassOptions);
+	}
+
 	/* Any change in exclusion operator selections breaks compatibility. */
 	if (ret && indexInfo->ii_ExclusionOps != NULL)
 	{
@@ -302,6 +315,42 @@ CheckIndexCompatible(Oid oldId,
 	return ret;
 }
 
+/*
+ * CompareOpclassOptions
+ *
+ * Compare per-column opclass options which are represented by arrays of text[]
+ * datums.  Both elements of arrays and array themselves can be NULL.
+ */
+static bool
+CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts)
+{
+	int			i;
+
+	if (!opts1 && !opts2)
+		return true;
+
+	for (i = 0; i < natts; i++)
+	{
+		Datum		opt1 = opts1 ? opts1[i] : (Datum) 0;
+		Datum		opt2 = opts2 ? opts2[i] : (Datum) 0;
+
+		if (opt1 == (Datum) 0)
+		{
+			if (opt2 == (Datum) 0)
+				continue;
+			else
+				return false;
+		}
+		else if (opt2 == (Datum) 0)
+			return false;
+
+		/* Compare non-NULL text[] datums. */
+		if (!DatumGetBool(DirectFunctionCall2(array_eq, opt1, opt2)))
+			return false;
+	}
+
+	return true;
+}
 
 /*
  * WaitForOlderSnapshots
@@ -1509,7 +1558,7 @@ CheckPredicate(Expr *predicate)
 
 /*
  * Compute per-index-column information, including indexed column numbers
- * or index expressions, opclasses, and indoptions. Note, all output vectors
+ * or index expressions, opclasses and their options. Note, all output vectors
  * should be allocated for all columns, including "including" ones.
  */
 static void
@@ -1810,6 +1859,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 								accessMethodName)));
 		}
 
+		/* Set up the per-column opclass options (attoptions field). */
+		if (attribute->opclassopts)
+		{
+			Assert(attn < nkeycols);
+
+			if (!indexInfo->ii_OpclassOptions)
+				indexInfo->ii_OpclassOptions =
+					palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+
+			indexInfo->ii_OpclassOptions[attn] =
+				transformRelOptions((Datum) 0, attribute->opclassopts,
+									NULL, NULL, false, false);
+		}
+
 		attn++;
 	}
 }
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 6a1ccdeb..f2777a5 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -523,11 +523,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
 				addFamilyMember(&operators, member, false);
 				break;
 			case OPCLASS_ITEM_FUNCTION:
-				if (item->number <= 0 || item->number > maxProcNumber)
+				if (item->number < 0 || item->number > maxProcNumber)
 					ereport(ERROR,
 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 							 errmsg("invalid function number %d,"
-									" must be between 1 and %d",
+									" must be between 0 and %d",
 									item->number, maxProcNumber)));
 				funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
 #ifdef NOT_USED
@@ -537,7 +537,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
 								   get_func_name(funcOid));
 #endif
-
 				/* Save the info */
 				member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
 				member->object = funcOid;
@@ -902,11 +901,11 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
 				addFamilyMember(&operators, member, false);
 				break;
 			case OPCLASS_ITEM_FUNCTION:
-				if (item->number <= 0 || item->number > maxProcNumber)
+				if (item->number < 0 || item->number > maxProcNumber)
 					ereport(ERROR,
 							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 							 errmsg("invalid function number %d,"
-									" must be between 1 and %d",
+									" must be between 0 and %d",
 									item->number, maxProcNumber)));
 				funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
 #ifdef NOT_USED
@@ -1141,6 +1140,36 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 		elog(ERROR, "cache lookup failed for function %u", member->object);
 	procform = (Form_pg_proc) GETSTRUCT(proctup);
 
+	/* Check the signature of the opclass options parsing function */
+	if (member->number == OPCLASS_OPTIONS_PROC)
+	{
+		if (OidIsValid(typeoid))
+		{
+			if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) ||
+				(OidIsValid(member->righttype) && member->righttype != typeoid))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("associated data types for opclass options "
+								"parsing functions must match opclass input type")));
+		}
+		else
+		{
+			if (member->lefttype != member->righttype)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("left and right associated data types for "
+								"opclass options parsing functions must match")));
+		}
+
+		if (procform->prorettype != VOIDOID ||
+			procform->pronargs != 1 ||
+			procform->proargtypes.values[0] != INTERNALOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("invalid opclass options parsing function"),
+					 errhint("opclass options parsing function must have signature '%s'",
+							 "(internal) RETURNS void")));
+	}
 	/*
 	 * btree comparison procs must be 2-arg procs returning int4.  btree
 	 * sortsupport procs must take internal and return void.  btree in_range
@@ -1148,7 +1177,7 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 	 * a 1-arg proc returning int4, while proc 2 must be a 2-arg proc
 	 * returning int8.  Otherwise we don't know.
 	 */
-	if (amoid == BTREE_AM_OID)
+	else if (amoid == BTREE_AM_OID)
 	{
 		if (member->number == BTORDER_PROC)
 		{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 05593f3..afef6a9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5737,7 +5737,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	ReleaseSysCache(typeTuple);
 
-	InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+	InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
 
 	table_close(attrdesc, RowExclusiveLock);
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a2617c7..d44a5b0 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2868,6 +2868,7 @@ _copyIndexElem(const IndexElem *from)
 	COPY_STRING_FIELD(indexcolname);
 	COPY_NODE_FIELD(collation);
 	COPY_NODE_FIELD(opclass);
+	COPY_NODE_FIELD(opclassopts);
 	COPY_SCALAR_FIELD(ordering);
 	COPY_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4f2ebe5..6b14095 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2546,6 +2546,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
 	COMPARE_STRING_FIELD(indexcolname);
 	COMPARE_NODE_FIELD(collation);
 	COMPARE_NODE_FIELD(opclass);
+	COMPARE_NODE_FIELD(opclassopts);
 	COMPARE_SCALAR_FIELD(ordering);
 	COMPARE_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 18466ac..12d43bd 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -763,6 +763,9 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
 	n->ii_ExclusionProcs = NULL;
 	n->ii_ExclusionStrats = NULL;
 
+	/* opclass options */
+	n->ii_OpclassOptions = NULL;
+
 	/* speculative inserts */
 	n->ii_UniqueOps = NULL;
 	n->ii_UniqueProcs = NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e6ce8e2..37b6d6f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2850,6 +2850,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
 	WRITE_STRING_FIELD(indexcolname);
 	WRITE_NODE_FIELD(collation);
 	WRITE_NODE_FIELD(opclass);
+	WRITE_NODE_FIELD(opclassopts);
 	WRITE_ENUM_FIELD(ordering, SortByDir);
 	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
 }
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index cf17614..de9b6f4 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -279,6 +279,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 			info->amcostestimate = amroutine->amcostestimate;
 			Assert(info->amcostestimate != NULL);
 
+			/* Fetch index opclass options */
+			info->opclassoptions = RelationGetIndexAttOptions(indexRelation, true);
+
 			/*
 			 * Fetch the ordering information for the index, if any.
 			 */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a954acf..c8b5d91 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -491,7 +491,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <alias>	alias_clause opt_alias_clause
 %type <list>	func_alias_clause
 %type <sortby>	sortby
-%type <ielem>	index_elem
+%type <ielem>	index_elem index_elem_options
 %type <node>	table_ref
 %type <jexpr>	joined_table
 %type <range>	relation_expr
@@ -7420,43 +7420,65 @@ index_params:	index_elem							{ $$ = list_make1($1); }
 			| index_params ',' index_elem			{ $$ = lappend($1, $3); }
 		;
 
+
+index_elem_options:
+	opt_collate opt_class opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = NIL;
+			$$->ordering = $3;
+			$$->nulls_ordering = $4;
+		}
+	| opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	| opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = NIL;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	;
+
 /*
  * Index attributes can be either simple column references, or arbitrary
  * expressions in parens.  For backwards-compatibility reasons, we allow
  * an expression that's just a function call to be written without parens.
  */
-index_elem:	ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
 				{
-					$$ = makeNode(IndexElem);
+					$$ = $2;
 					$$->name = $1;
-					$$->expr = NULL;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+			| func_expr_windowless index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $2;
 					$$->expr = $1;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+			| '(' a_expr ')' index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $4;
 					$$->expr = $2;
-					$$->indexcolname = NULL;
-					$$->collation = $4;
-					$$->opclass = $5;
-					$$->ordering = $6;
-					$$->nulls_ordering = $7;
 				}
 		;
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6e5768c..5872376 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1577,6 +1577,8 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 
 		/* Add the operator class name, if non-default */
 		iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
+		iparam->opclassopts =
+			untransformRelOptions(get_attoptions(source_relid, keyno + 1));
 
 		iparam->ordering = SORTBY_DEFAULT;
 		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
@@ -2153,9 +2155,13 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 				 * semantics as you'd get from a normally-created constraint;
 				 * and there's also the dump/reload problem mentioned above.
 				 */
+				Datum		attoptions =
+					get_attoptions(RelationGetRelid(index_rel), i + 1);
+
 				defopclass = GetDefaultOpClass(attform->atttypid,
 											   index_rel->rd_rel->relam);
 				if (indclass->values[i] != defopclass ||
+					attoptions != (Datum) 0 ||
 					index_rel->rd_indoption[i] != 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -2335,6 +2341,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 			iparam->indexcolname = NULL;
 			iparam->collation = NIL;
 			iparam->opclass = NIL;
+			iparam->opclassopts = NIL;
 			iparam->ordering = SORTBY_DEFAULT;
 			iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
 			index->indexParams = lappend(index->indexParams, iparam);
@@ -2448,6 +2455,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		iparam->indexcolname = NULL;
 		iparam->collation = NIL;
 		iparam->opclass = NIL;
+		iparam->opclassopts = NIL;
 		index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
 	}
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3e64390..faecf04 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -469,6 +469,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1371,6 +1372,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 		{
 			int16		opt = indoption->values[keyno];
 			Oid			indcoll = indcollation->values[keyno];
+			Datum		attoptions = get_attoptions(indexrelid, keyno + 1);
+			bool		has_options = attoptions != (Datum) 0;
 
 			/* Add collation, if not default for column */
 			if (OidIsValid(indcoll) && indcoll != keycolcollation)
@@ -1378,7 +1381,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 								 generate_collation_name((indcoll)));
 
 			/* Add the operator class name, if not default */
-			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+			get_opclass_name(indclass->values[keyno],
+							 has_options ? InvalidOid : keycoltype, &buf);
+
+			if (has_options)
+			{
+				appendStringInfoString(&buf, " (");
+				get_reloptions(&buf, attoptions);
+				appendStringInfoChar(&buf, ')');
+			}
 
 			/* Add options if relevant */
 			if (amroutine->amcanorder)
@@ -10506,6 +10517,23 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 }
 
 /*
+ * generate_opclass_name
+ *		Compute the name to display for a opclass specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+char *
+generate_opclass_name(Oid opclass)
+{
+	StringInfoData buf;
+
+	initStringInfo(&buf);
+	get_opclass_name(opclass, InvalidOid, &buf);
+
+	return &buf.data[1];	/* get_opclass_name() prepends space */
+}
+
+/*
  * processIndirection - take care of array and subfield assignment
  *
  * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
@@ -11183,6 +11211,62 @@ string_to_text(char *str)
 }
 
 /*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+	Datum	   *options;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(reloptions),
+					  TEXTOID, -1, false, 'i',
+					  &options, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *option = TextDatumGetCString(options[i]);
+		char	   *name;
+		char	   *separator;
+		char	   *value;
+
+		/*
+		 * Each array element should have the form name=value.  If the "="
+		 * is missing for some reason, treat it like an empty value.
+		 */
+		name = option;
+		separator = strchr(option, '=');
+		if (separator)
+		{
+			*separator = '\0';
+			value = separator + 1;
+		}
+		else
+			value = "";
+
+		if (i > 0)
+			appendStringInfoString(buf, ", ");
+		appendStringInfo(buf, "%s=", quote_identifier(name));
+
+		/*
+		 * In general we need to quote the value; but to avoid unnecessary
+		 * clutter, do not quote if it is an identifier that would not
+		 * need quoting.  (We could also allow numbers, but that is a bit
+		 * trickier than it looks --- for example, are leading zeroes
+		 * significant?  We don't want to assume very much here about what
+		 * custom reloptions might mean.)
+		 */
+		if (quote_identifier(value) == value)
+			appendStringInfoString(buf, value);
+		else
+			simple_quote_literal(buf, value);
+
+		pfree(option);
+	}
+}
+
+/*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
 static char *
@@ -11202,56 +11286,9 @@ flatten_reloptions(Oid relid)
 	if (!isnull)
 	{
 		StringInfoData buf;
-		Datum	   *options;
-		int			noptions;
-		int			i;
 
 		initStringInfo(&buf);
-
-		deconstruct_array(DatumGetArrayTypeP(reloptions),
-						  TEXTOID, -1, false, 'i',
-						  &options, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *option = TextDatumGetCString(options[i]);
-			char	   *name;
-			char	   *separator;
-			char	   *value;
-
-			/*
-			 * Each array element should have the form name=value.  If the "="
-			 * is missing for some reason, treat it like an empty value.
-			 */
-			name = option;
-			separator = strchr(option, '=');
-			if (separator)
-			{
-				*separator = '\0';
-				value = separator + 1;
-			}
-			else
-				value = "";
-
-			if (i > 0)
-				appendStringInfoString(&buf, ", ");
-			appendStringInfo(&buf, "%s=", quote_identifier(name));
-
-			/*
-			 * In general we need to quote the value; but to avoid unnecessary
-			 * clutter, do not quote if it is an identifier that would not
-			 * need quoting.  (We could also allow numbers, but that is a bit
-			 * trickier than it looks --- for example, are leading zeroes
-			 * significant?  We don't want to assume very much here about what
-			 * custom reloptions might mean.)
-			 */
-			if (quote_identifier(value) == value)
-				appendStringInfoString(&buf, value);
-			else
-				simple_quote_literal(&buf, value);
-
-			pfree(option);
-		}
+		get_reloptions(&buf, reloptions);
 
 		result = buf.data;
 	}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 1710129..638d5ad 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -6260,6 +6260,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 				Oid clause_op, Datum query,
 				GinQualCounts *counts)
 {
+	FmgrInfo	flinfo;
 	Oid			extractProcOid;
 	Oid			collation;
 	int			strategy_op;
@@ -6309,15 +6310,19 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	else
 		collation = DEFAULT_COLLATION_OID;
 
-	OidFunctionCall7Coll(extractProcOid,
-						 collation,
-						 query,
-						 PointerGetDatum(&nentries),
-						 UInt16GetDatum(strategy_op),
-						 PointerGetDatum(&partial_matches),
-						 PointerGetDatum(&extra_data),
-						 PointerGetDatum(&nullFlags),
-						 PointerGetDatum(&searchMode));
+	fmgr_info(extractProcOid, &flinfo);
+
+	set_fn_opclass_options(&flinfo, index->opclassoptions[indexcol]);
+
+	FunctionCall7Coll(&flinfo,
+					  collation,
+					  query,
+					  PointerGetDatum(&nentries),
+					  UInt16GetDatum(strategy_op),
+					  PointerGetDatum(&partial_matches),
+					  PointerGetDatum(&extra_data),
+					  PointerGetDatum(&nullFlags),
+					  PointerGetDatum(&searchMode));
 
 	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
 	{
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa..8fbf5e3 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -908,6 +908,41 @@ get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
 	ReleaseSysCache(tp);
 }
 
+/*
+ * get_attoptions
+ *
+ *		Given the relation id and the attribute number,
+ *		return the attribute options text[] datum, if any.
+ */
+Datum
+get_attoptions(Oid relid, int16 attnum)
+{
+	HeapTuple	tuple;
+	Datum		attopts;
+	Datum		result;
+	bool		isnull;
+
+	tuple = SearchSysCache2(ATTNUM,
+							ObjectIdGetDatum(relid),
+							Int16GetDatum(attnum));
+
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 attnum, relid);
+
+	attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+							  &isnull);
+
+	if (isnull)
+		result = (Datum) 0;
+	else
+		result = datumCopy(attopts, false, -1);		/* text[] */
+
+	ReleaseSysCache(tuple);
+
+	return result;
+}
+
 /*				---------- COLLATION CACHE ----------					 */
 
 /*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 585dcee..7f96935 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1438,7 +1438,7 @@ RelationInitIndexAccessInfo(Relation relation)
 	amsupport = relation->rd_indam->amsupport;
 	if (amsupport > 0)
 	{
-		int			nsupport = indnatts * amsupport;
+		int			nsupport = indnatts * (amsupport + 1);
 
 		relation->rd_support = (RegProcedure *)
 			MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
@@ -1502,6 +1502,10 @@ RelationInitIndexAccessInfo(Relation relation)
 	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
 	memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
 
+#if 0
+	(void) RelationGetIndexAttOptions(relation, false);
+#endif
+
 	/*
 	 * expressions, predicate, exclusion caches will be filled later
 	 */
@@ -1551,9 +1555,9 @@ IndexSupportInitialize(oidvector *indclass,
 		opFamily[attIndex] = opcentry->opcfamily;
 		opcInType[attIndex] = opcentry->opcintype;
 		if (maxSupportNumber > 0)
-			memcpy(&indexSupport[attIndex * maxSupportNumber],
+			memcpy(&indexSupport[attIndex * (maxSupportNumber + 1)],
 				   opcentry->supportProcs,
-				   maxSupportNumber * sizeof(RegProcedure));
+				   (maxSupportNumber + 1) * sizeof(RegProcedure));
 	}
 }
 
@@ -1618,7 +1622,7 @@ LookupOpclassInfo(Oid operatorClassOid,
 		if (numSupport > 0)
 			opcentry->supportProcs = (RegProcedure *)
 				MemoryContextAllocZero(CacheMemoryContext,
-									   numSupport * sizeof(RegProcedure));
+									   (numSupport + 1) * sizeof(RegProcedure));
 		else
 			opcentry->supportProcs = NULL;
 	}
@@ -1705,13 +1709,12 @@ LookupOpclassInfo(Oid operatorClassOid,
 		{
 			Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
 
-			if (amprocform->amprocnum <= 0 ||
+			if (amprocform->amprocnum < 0 ||
 				(StrategyNumber) amprocform->amprocnum > numSupport)
 				elog(ERROR, "invalid amproc number %d for opclass %u",
 					 amprocform->amprocnum, operatorClassOid);
 
-			opcentry->supportProcs[amprocform->amprocnum - 1] =
-				amprocform->amproc;
+			opcentry->supportProcs[amprocform->amprocnum] = amprocform->amproc;
 		}
 
 		systable_endscan(scan);
@@ -3998,6 +4001,10 @@ load_critical_index(Oid indexoid, Oid heapoid)
 	ird->rd_refcnt = 1;
 	UnlockRelationOid(indexoid, AccessShareLock);
 	UnlockRelationOid(heapoid, AccessShareLock);
+
+#if 0
+	(void) RelationGetIndexAttOptions(ird, false);
+#endif
 }
 
 /*
@@ -5153,6 +5160,99 @@ GetRelationPublicationActions(Relation relation)
 }
 
 /*
+ * RelationGetIndexRawAttOptions -- get AM/opclass-specific options for the index
+ */
+Datum *
+RelationGetIndexRawAttOptions(Relation indexrel)
+{
+	Oid			indexrelid = RelationGetRelid(indexrel);
+	int16		natts = RelationGetNumberOfAttributes(indexrel);
+	Datum	   *options = NULL;
+	int16		attnum;
+
+	for (attnum = 1; attnum <= natts; attnum++)
+	{
+		if (!OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
+			continue;
+
+		if (!options)
+			options = palloc0(sizeof(Datum) * natts);
+
+		options[attnum - 1] = get_attoptions(indexrelid, attnum);
+	}
+
+	return options;
+}
+
+static bytea **
+CopyIndexAttOptions(bytea **srcopts, int natts)
+{
+	bytea	  **opts = palloc(sizeof(*opts) * natts);
+
+	for (int i = 0; i < natts; i++)
+	{
+		bytea	   *opt = srcopts[i];
+
+		opts[i] = !opt ? NULL : (bytea *)
+			DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+	}
+
+	return opts;
+}
+
+/*
+ * RelationGetIndexAttOptions
+ *		get AM/opclass-specific options for an index parsed into a binary form
+ */
+bytea **
+RelationGetIndexAttOptions(Relation relation, bool copy)
+{
+	MemoryContext oldcxt;
+	bytea	  **opts = relation->rd_opcoptions;
+	Oid			relid = RelationGetRelid(relation);
+	int			natts = RelationGetNumberOfAttributes(relation);	/* XXX IndexRelationGetNumberOfKeyAttributes */
+	int			i;
+
+	/* Try to copy cached options. */
+	if (opts)
+		return copy ? CopyIndexAttOptions(opts, natts) : opts;
+
+	/* Get and parse opclass options. */
+	opts = palloc0(sizeof(*opts) * natts);
+
+	for (i = 0; i < natts; i++)
+	{
+		if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId)
+		{
+			Datum		attoptions = get_attoptions(relid, i + 1);
+
+			opts[i] = index_opclass_options(relation, i + 1, attoptions, false);
+
+			if (attoptions != (Datum) 0)
+				pfree(DatumGetPointer(attoptions));
+		}
+	}
+
+	/* Copy parsed options to the cache. */
+	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
+	relation->rd_opcoptions = CopyIndexAttOptions(opts, natts);
+	MemoryContextSwitchTo(oldcxt);
+
+	if (copy)
+		return opts;
+
+	for (i = 0; i < natts; i++)
+	{
+		if (opts[i])
+			pfree(opts[i]);
+	}
+
+	pfree(opts);
+
+	return relation->rd_opcoptions;
+}
+
+/*
  * Routines to support ereport() reports of relation-related errors
  *
  * These could have been put into elog.c, but it seems like a module layering
@@ -5513,8 +5613,27 @@ load_relcache_init_file(bool shared)
 
 			rel->rd_indoption = indoption;
 
+#if 0
+			/* finally, read the vector of opcoptions values */
+			rel->rd_opcoptions = (bytea **)
+				MemoryContextAllocZero(indexcxt, sizeof(*rel->rd_opcoptions) * relform->relnatts);
+
+			for (i = 0; i < relform->relnatts; i++)
+			{
+				if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
+					goto read_failed;
+
+				if (len > 0)
+				{
+					rel->rd_opcoptions[i] = (bytea *) MemoryContextAlloc(indexcxt, len);
+					if (fread(rel->rd_opcoptions[i], 1, len, fp) != len)
+						goto read_failed;
+				}
+			}
+#endif
+
 			/* set up zeroed fmgr-info vector */
-			nsupport = relform->relnatts * rel->rd_indam->amsupport;
+			nsupport = relform->relnatts * (rel->rd_indam->amsupport + 1);
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
@@ -5541,6 +5660,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_opcoptions == NULL);
 		}
 
 		/*
@@ -5568,6 +5688,7 @@ load_relcache_init_file(bool shared)
 		rel->rd_exclprocs = NULL;
 		rel->rd_exclstrats = NULL;
 		rel->rd_fdwroutine = NULL;
+		rel->rd_opcoptions = NULL;
 
 		/*
 		 * Reset transient-state fields in the relcache entry
@@ -5814,7 +5935,7 @@ write_relcache_init_file(bool shared)
 
 			/* next, write the vector of support procedure OIDs */
 			write_item(rel->rd_support,
-					   relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)),
+					   relform->relnatts * ((rel->rd_indam->amsupport + 1) * sizeof(RegProcedure)),
 					   fp);
 
 			/* next, write the vector of collation OIDs */
@@ -5826,6 +5947,18 @@ write_relcache_init_file(bool shared)
 			write_item(rel->rd_indoption,
 					   relform->relnatts * sizeof(int16),
 					   fp);
+
+#if 0
+			Assert(rel->rd_opcoptions);
+
+			/* finally, write the vector of opcoptions values */
+			for (i = 0; i < relform->relnatts; i++)
+			{
+				bytea	   *opt = rel->rd_opcoptions[i];
+
+				write_item(opt, opt ? VARSIZE(opt) : 0, fp);
+			}
+#endif
 		}
 	}
 
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 0484adb..e11146a 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -18,9 +18,11 @@
 #include "access/detoast.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
 #include "executor/functions.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "pgstat.h"
 #include "utils/acl.h"
@@ -1967,6 +1969,32 @@ get_fn_expr_variadic(FmgrInfo *flinfo)
 		return false;
 }
 
+void
+set_fn_opclass_options(FmgrInfo *flinfo, bytea *options)
+{
+	flinfo->fn_expr = (Node *) makeConst(BYTEAOID, -1, InvalidOid, -1,
+										 PointerGetDatum(options),
+										 options == NULL, false);
+}
+
+bytea *
+get_fn_opclass_options(FmgrInfo *flinfo)
+{
+	if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const))
+	{
+		Const	   *expr = (Const *) flinfo->fn_expr;
+
+		if (expr->consttype == BYTEAOID)
+			return expr->constisnull ? NULL : DatumGetByteaP(expr->constvalue);
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("opclass options info is absent in function call context")));
+
+	return NULL;
+}
+
 /*-------------------------------------------------------------------------
  *		Support routines for procedural language implementations
  *-------------------------------------------------------------------------
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 6c56717..778d17c 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -182,6 +182,9 @@ extern void index_store_float8_orderby_distances(IndexScanDesc scan,
 												 double *distanceValues,
 												 bool *distanceNulls,
 												 bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+									Datum attoptions, bool validate);
+
 
 /*
  * index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 6d392e4..70238f9 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -37,6 +37,7 @@ typedef enum relopt_type
 /* kinds supported by reloptions */
 typedef enum relopt_kind
 {
+	RELOPT_KIND_LOCAL = 0,
 	RELOPT_KIND_HEAP = (1 << 0),
 	RELOPT_KIND_TOAST = (1 << 1),
 	RELOPT_KIND_BTREE = (1 << 2),
@@ -109,6 +110,9 @@ typedef struct relopt_real
 
 /* validation routines for strings */
 typedef void (*validate_string_relopt) (const char *value);
+typedef Size (*fill_string_relopt) (const char *value, void *ptr);
+
+typedef void (*relopts_validator)(void *parsed_options, relopt_value *vals, int nvals);
 
 typedef struct relopt_string
 {
@@ -116,6 +120,7 @@ typedef struct relopt_string
 	int			default_len;
 	bool		default_isnull;
 	validate_string_relopt validate_cb;
+	fill_string_relopt fill_cb;
 	char	   *default_val;
 } relopt_string;
 
@@ -127,6 +132,13 @@ typedef struct
 	int			offset;			/* offset of field in result struct */
 } relopt_parse_elt;
 
+typedef struct local_relopts
+{
+	List	   *options;
+	List	   *validators;
+	void	   *base;
+	Size		base_size;
+} local_relopts;
 
 /*
  * These macros exist for the convenience of amoptions writers (but consider
@@ -255,6 +267,26 @@ extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
 extern void add_string_reloption(bits32 kinds, const char *name, const char *desc,
 								 const char *default_val, validate_string_relopt validator);
 
+extern void init_local_reloptions(local_relopts *opts, void *base, Size base_size);
+extern void extend_local_reloptions(local_relopts *opts, void *base, Size base_size);
+extern void register_reloptions_validator(local_relopts *opts,
+										  relopts_validator validator);
+extern void add_local_bool_reloption(local_relopts *opts, const char *name,
+									 const char *desc, bool default_val,
+									 bool *pval);
+extern void add_local_int_reloption(local_relopts *opts, const char *name,
+									const char *desc, int default_val,
+									int min_val, int max_val, int *pval);
+extern void add_local_real_reloption(local_relopts *opts, const char *name,
+									 const char *desc, double default_val,
+									 double min_val, double max_val,
+									 double *pval);
+extern void add_local_string_reloption(local_relopts *opts, const char *name,
+									   const char *desc,
+									   const char *default_val,
+									   validate_string_relopt validator,
+									   fill_string_relopt filler, int *poffset);
+
 extern Datum transformRelOptions(Datum oldOptions, List *defList,
 								 const char *namspace, char *validnsps[],
 								 bool acceptOidsOff, bool isReset);
@@ -263,12 +295,16 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 								amoptions_function amoptions);
 extern relopt_value *parseRelOptions(Datum options, bool validate,
 									 relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(local_relopts *relopts, Datum options,
+										  bool validate);
 extern void *allocateReloptStruct(Size base, relopt_value *options,
 								  int numoptions);
 extern void fillRelOptions(void *rdopts, Size basesize,
 						   relopt_value *options, int numoptions,
 						   bool validate,
 						   const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(local_relopts *relopts, Datum options,
+										 bool validate);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
 								 relopt_kind kind);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index eec71c2..866e94d 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -94,6 +94,7 @@ extern List *heap_truncate_find_FKs(List *relationIds);
 
 extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 								   Form_pg_attribute new_attribute,
+								   Datum attoptions,
 								   CatalogIndexState indstate);
 
 extern void InsertPgClassTuple(Relation pg_class_desc,
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 62ca564..75cb340 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -67,4 +67,6 @@ CATALOG(pg_amproc,2603,AccessMethodProcedureRelationId)
  */
 typedef FormData_pg_amproc *Form_pg_amproc;
 
+#define OPCLASS_OPTIONS_PROC	0	/* amprocnum for opclass options */
+
 #endif							/* PG_AMPROC_H */
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 29ae467..568a9c6 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -331,6 +331,8 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 #define PG_GETARG_BPCHAR_P(n)		DatumGetBpCharP(PG_GETARG_DATUM(n))
 #define PG_GETARG_VARCHAR_P(n)		DatumGetVarCharP(PG_GETARG_DATUM(n))
 
+#define PG_GET_OPCLASS_OPTIONS()	get_fn_opclass_options(fcinfo->flinfo)
+
 /* To return a NULL do this: */
 #define PG_RETURN_NULL()  \
 	do { fcinfo->isnull = true; return (Datum) 0; } while (0)
@@ -699,6 +701,8 @@ extern Oid	get_call_expr_argtype(fmNodePtr expr, int argnum);
 extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
 extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
 extern bool get_fn_expr_variadic(FmgrInfo *flinfo);
+extern bytea *get_fn_opclass_options(FmgrInfo *flinfo);
+extern void set_fn_opclass_options(FmgrInfo *flinfo, bytea *options);
 extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
 
 /*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index b593d22..967c818 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -139,6 +139,7 @@ typedef struct ExprState
  *		UniqueProcs
  *		UniqueStrats
  *		Unique				is it a unique index?
+ *		OpclassOptions		opclass-specific options, or NULL if none
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
  *		BrokenHotChain		did we detect any broken HOT chains?
@@ -167,6 +168,7 @@ typedef struct IndexInfo
 	Oid		   *ii_UniqueOps;	/* array with one entry per column */
 	Oid		   *ii_UniqueProcs; /* array with one entry per column */
 	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	Datum	   *ii_OpclassOptions;	/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 94ded3c..aa266b2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -701,6 +701,7 @@ typedef struct IndexElem
 	char	   *indexcolname;	/* name for index column; NULL = default */
 	List	   *collation;		/* name of collation; NIL = default */
 	List	   *opclass;		/* name of desired opclass; NIL = default */
+	List	   *opclassopts;	/* opclass-specific options, or NIL */
 	SortByDir	ordering;		/* ASC/DESC/default */
 	SortByNulls nulls_ordering; /* FIRST/LAST/default */
 } IndexElem;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 23a06d7..a07c44d 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -806,6 +806,7 @@ struct IndexOptInfo
 	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
 	bool	   *reverse_sort;	/* is sort order descending? */
 	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
+	bytea	 **opclassoptions;	/* opclass-specific options for columns */
 	bool	   *canreturn;		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bf..2fa9965 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -90,6 +90,7 @@ extern char get_attgenerated(Oid relid, AttrNumber attnum);
 extern Oid	get_atttype(Oid relid, AttrNumber attnum);
 extern void get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
 								  Oid *typid, int32 *typmod, Oid *collid);
+extern Datum get_attoptions(Oid relid, int16 attnum);
 extern char *get_collation_name(Oid colloid);
 extern bool get_collation_isdeterministic(Oid colloid);
 extern char *get_constraint_name(Oid conoid);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 91b3b1b..552d9eb 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -166,6 +166,7 @@ typedef struct RelationData
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
+	bytea	  **rd_opcoptions;	/* parsed opclass-specific options */
 
 	/*
 	 * rd_amcache is available for index and table AMs to cache private data
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 2f2ace3..adc2b26 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
 #ifndef RELCACHE_H
 #define RELCACHE_H
 
+#include "postgres.h"
 #include "access/tupdesc.h"
 #include "nodes/bitmapset.h"
 
@@ -49,6 +50,8 @@ extern Oid	RelationGetPrimaryKeyIndex(Relation relation);
 extern Oid	RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
+extern Datum *RelationGetIndexRawAttOptions(Relation relation);
+extern bytea **RelationGetIndexAttOptions(Relation relation, bool copy);
 
 typedef enum IndexAttrBitmapKind
 {
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index d34cad2..e295052 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,6 +34,7 @@ extern List *set_deparse_context_planstate(List *dpcontext,
 extern List *select_rtable_names_for_explain(List *rtable,
 											 Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
+extern char *generate_opclass_name(Oid opclass);
 extern char *get_range_partbound_string(List *bound_datums);
 
 #endif							/* RULEUTILS_H */
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 8663f0c..40b3267 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -353,10 +353,11 @@ ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- ope
 ERROR:  invalid operator number 0, must be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
 ERROR:  operator argument types must be specified in ALTER OPERATOR FAMILY
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR:  invalid function number 0, must be between 1 and 3
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR:  invalid function number 6, must be between 1 and 3
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
+ERROR:  invalid opclass options parsing function
+HINT:  opclass options parsing function must have signature '(internal) RETURNS void'
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 4 btint42cmp(int4, int2); -- function number should be between 0 and 3
+ERROR:  invalid function number 4, must be between 0 and 3
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
 ERROR:  STORAGE cannot be specified in ALTER OPERATOR FAMILY
 DROP OPERATOR FAMILY alt_opf4 USING btree;
@@ -496,6 +497,19 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
 ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
 ERROR:  function 2(integer,integer) does not exist in operator family "alt_opf18"
 DROP OPERATOR FAMILY alt_opf18 USING btree;
+-- Should fail. Invalid opclass options function (#0) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 test_opclass_options_func(internal, text[], bool);
+ERROR:  function test_opclass_options_func(internal, text[], boolean) does not exist
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) btint42cmp(int4, int2);
+ERROR:  invalid opclass options parsing function
+HINT:  opclass options parsing function must have signature '(internal) RETURNS void'
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4, int2) btint42cmp(int4, int2);
+ERROR:  left and right associated data types for opclass options parsing functions must match
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 0 (int4, int4);
+ERROR:  invalid function number 0, must be between 1 and 3
+DROP OPERATOR FAMILY alt_opf19 USING btree;
 --
 -- Statistics
 --
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index acab8e0..b4023de 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -262,3 +262,8 @@ VACUUM delete_test_table;
 -- The vacuum above should've turned the leaf page into a fast root. We just
 -- need to insert some rows to cause the fast root page to split.
 INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  operator class int4_ops has no options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR:  operator class int4_ops has no options
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 33c058f..1f53a9c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2054,7 +2054,7 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
 SELECT p1.amprocfamily, p1.amprocnum
 FROM pg_amproc as p1
 WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
-    OR p1.amprocnum < 1 OR p1.amproc = 0;
+    OR p1.amprocnum < 0 OR p1.amproc = 0;
  amprocfamily | amprocnum 
 --------------+-----------
 (0 rows)
diff --git a/src/test/regress/input/create_function_1.source b/src/test/regress/input/create_function_1.source
index 223454a..412e339 100644
--- a/src/test/regress/input/create_function_1.source
+++ b/src/test/regress/input/create_function_1.source
@@ -73,6 +73,11 @@ CREATE FUNCTION test_support_func(internal)
     AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
     LANGUAGE C STRICT;
 
+CREATE FUNCTION test_opclass_options_func(internal)
+    RETURNS void
+    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+    LANGUAGE C;
+
 -- Things that shouldn't work:
 
 CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
diff --git a/src/test/regress/output/create_function_1.source b/src/test/regress/output/create_function_1.source
index 5f43e8d..4d78fa1 100644
--- a/src/test/regress/output/create_function_1.source
+++ b/src/test/regress/output/create_function_1.source
@@ -64,6 +64,10 @@ CREATE FUNCTION test_support_func(internal)
     RETURNS internal
     AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
     LANGUAGE C STRICT;
+CREATE FUNCTION test_opclass_options_func(internal)
+    RETURNS void
+    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+    LANGUAGE C;
 -- Things that shouldn't work:
 CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
     AS 'SELECT ''not an integer'';';
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 826556e..3ade2d6 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -940,3 +940,10 @@ test_support_func(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(ret);
 }
+
+PG_FUNCTION_INFO_V1(test_opclass_options_func);
+Datum
+test_opclass_options_func(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_NULL();
+}
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index ce9dbb1..31ad01c 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -298,8 +298,8 @@ ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD  OPERATOR 1 < (int
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 4 btint42cmp(int4, int2); -- function number should be between 0 and 3
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
 DROP OPERATOR FAMILY alt_opf4 USING btree;
 
@@ -433,6 +433,15 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
 ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
 DROP OPERATOR FAMILY alt_opf18 USING btree;
 
+-- Should fail. Invalid opclass options function (#0) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 test_opclass_options_func(internal, text[], bool);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4, int2) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 0 (int4, int4);
+DROP OPERATOR FAMILY alt_opf19 USING btree;
+
 --
 -- Statistics
 --
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 48eaf4f..7290c86 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -140,3 +140,7 @@ VACUUM delete_test_table;
 -- The vacuum above should've turned the leaf page into a fast root. We just
 -- need to insert some rows to cause the fast root page to split.
 INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 1227ef7..cee6a40 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1306,7 +1306,7 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
 SELECT p1.amprocfamily, p1.amprocnum
 FROM pg_amproc as p1
 WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
-    OR p1.amprocnum < 1 OR p1.amproc = 0;
+    OR p1.amprocnum < 0 OR p1.amproc = 0;
 
 -- Support routines that are primary members of opfamilies must be immutable
 -- (else it suggests that the index ordering isn't fixed).  But cross-type
-- 
2.7.4

0002-Introduce-amattoptions-20190910.patchtext/x-patch; name=0002-Introduce-amattoptions-20190910.patchDownload
From c9acb5bf6033c1d18ff35c58000aa0fd62bdbf73 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 2/5] Introduce amattoptions

---
 contrib/bloom/blutils.c              |  1 +
 src/backend/access/brin/brin.c       |  1 +
 src/backend/access/gin/ginutil.c     |  1 +
 src/backend/access/gist/gist.c       |  1 +
 src/backend/access/hash/hash.c       |  1 +
 src/backend/access/index/indexam.c   | 16 +++++++++++-----
 src/backend/access/nbtree/nbtree.c   |  1 +
 src/backend/access/spgist/spgutils.c |  1 +
 src/backend/utils/cache/relcache.c   |  3 ++-
 src/include/access/amapi.h           |  7 +++++++
 10 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index cc16709..b62ba7f 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -131,6 +131,7 @@ blhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = blcostestimate;
 	amroutine->amoptions = bloptions;
+	amroutine->amattoptions = NULL;
 	amroutine->amproperty = NULL;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = blvalidate;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ae7b729..e86f1f9 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -111,6 +111,7 @@ brinhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = brincostestimate;
 	amroutine->amoptions = brinoptions;
+	amroutine->amattoptions = NULL;
 	amroutine->amproperty = NULL;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = brinvalidate;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index cf9699a..1154239 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = gincostestimate;
 	amroutine->amoptions = ginoptions;
+	amroutine->amattoptions = NULL;
 	amroutine->amproperty = NULL;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = ginvalidate;
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 0cc8791..a1f96b1 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -85,6 +85,7 @@ gisthandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = gistcanreturn;
 	amroutine->amcostestimate = gistcostestimate;
 	amroutine->amoptions = gistoptions;
+	amroutine->amattoptions = NULL;
 	amroutine->amproperty = gistproperty;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = gistvalidate;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 5cc30da..84fb4d6 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -84,6 +84,7 @@ hashhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = hashcostestimate;
 	amroutine->amoptions = hashoptions;
+	amroutine->amattoptions = NULL;
 	amroutine->amproperty = NULL;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = hashvalidate;
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bbed726..58b0bda 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -940,10 +940,10 @@ index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
 					  bool validate)
 {
 	Oid			procid = index_getprocid(indrel, attnum, 0);
-	FmgrInfo   *procinfo;
 	local_relopts relopts;
+	amattoptions_function amattoptions = indrel->rd_indam->amattoptions;
 
-	if (!OidIsValid(procid))
+	if (!OidIsValid(procid) && !amattoptions)
 	{
 		Oid			opclass;
 		Datum		indclassDatum;
@@ -969,11 +969,17 @@ index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
 						generate_opclass_name(opclass))));
 	}
 
-	init_local_reloptions(&relopts, NULL, 0);
+	if (amattoptions)
+		amattoptions(&relopts, attnum);
+	else
+		init_local_reloptions(&relopts, NULL, 0);
 
-	procinfo = index_getprocinfo(indrel, attnum, 0);
+	if (OidIsValid(procid))
+	{
+		FmgrInfo   *procinfo = index_getprocinfo(indrel, attnum, 0);
 
-	(void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+		(void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+	}
 
 	return parseAndFillLocalRelOptions(&relopts, attoptions, validate);
 }
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 4cfd528..0741107 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -133,6 +133,7 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = btcanreturn;
 	amroutine->amcostestimate = btcostestimate;
 	amroutine->amoptions = btoptions;
+	amroutine->amattoptions = NULL;
 	amroutine->amproperty = btproperty;
 	amroutine->ambuildphasename = btbuildphasename;
 	amroutine->amvalidate = btvalidate;
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 45472db..0bf5734 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -66,6 +66,7 @@ spghandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = spgcanreturn;
 	amroutine->amcostestimate = spgcostestimate;
 	amroutine->amoptions = spgoptions;
+	amroutine->amattoptions = NULL;
 	amroutine->amproperty = spgproperty;
 	amroutine->ambuildphasename = NULL;
 	amroutine->amvalidate = spgvalidate;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7f96935..d36b31b 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5172,7 +5172,8 @@ RelationGetIndexRawAttOptions(Relation indexrel)
 
 	for (attnum = 1; attnum <= natts; attnum++)
 	{
-		if (!OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
+		if (!indexrel->rd_indam->amattoptions &&
+			!OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
 			continue;
 
 		if (!options)
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 6e3db06..4e79e81 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -25,6 +25,8 @@ struct IndexPath;
 /* Likewise, this file shouldn't depend on execnodes.h. */
 struct IndexInfo;
 
+struct local_relopts;
+
 
 /*
  * Properties for amproperty API.  This list covers properties known to the
@@ -103,6 +105,10 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
 typedef bytea *(*amoptions_function) (Datum reloptions,
 									  bool validate);
 
+/* initialize per-attribute reloptions */
+typedef void (*amattoptions_function) (struct local_relopts *options,
+									   int attno);
+
 /* report AM, index, or index column property */
 typedef bool (*amproperty_function) (Oid index_oid, int attno,
 									 IndexAMProperty prop, const char *propname,
@@ -215,6 +221,7 @@ typedef struct IndexAmRoutine
 	amcanreturn_function amcanreturn;	/* can be NULL */
 	amcostestimate_function amcostestimate;
 	amoptions_function amoptions;
+	amattoptions_function amattoptions; /* can be NULL */
 	amproperty_function amproperty; /* can be NULL */
 	ambuildphasename_function ambuildphasename; /* can be NULL */
 	amvalidate_function amvalidate;
-- 
2.7.4

0004-Use-opclass-parameters-in-GiST-tsvector_ops-20190910.patchtext/x-patch; name=0004-Use-opclass-parameters-in-GiST-tsvector_ops-20190910.patchDownload
From d0b53a9ab7dbd04206be08c03ae19ded15e7e921 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 4/5] Use opclass parameters in GiST tsvector_ops

---
 doc/src/sgml/textsearch.sgml          |   9 +-
 src/backend/utils/adt/tsgistidx.c     | 266 ++++++++++++++++++----------------
 src/include/catalog/pg_amproc.dat     |   3 +
 src/include/catalog/pg_proc.dat       |   3 +
 src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
 src/test/regress/sql/tsearch.sql      |  45 ++++++
 6 files changed, 379 insertions(+), 123 deletions(-)

diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 3f00ae3..2e881f7 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3637,7 +3637,7 @@ SELECT plainto_tsquery('supernovae stars');
       <tertiary>text search</tertiary>
      </indexterm>
 
-      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
      </term>
 
      <listitem>
@@ -3645,6 +3645,8 @@ SELECT plainto_tsquery('supernovae stars');
        Creates a GiST (Generalized Search Tree)-based index.
        The <replaceable>column</replaceable> can be of <type>tsvector</type> or
        <type>tsquery</type> type.
+       Optional integer parameter <literal>siglen</literal> determines
+       signature length in bytes (see below for details).
       </para>
      </listitem>
     </varlistentry>
@@ -3668,7 +3670,10 @@ SELECT plainto_tsquery('supernovae stars');
    to check the actual table row to eliminate such false matches.
    (<productname>PostgreSQL</productname> does this automatically when needed.)
    GiST indexes are lossy because each document is represented in the
-   index by a fixed-length signature. The signature is generated by hashing
+   index by a fixed-length signature.  Signature length in bytes is determined
+   by the value of the optional integer parameter <literal>siglen</literal>.
+   Default signature length (when <literal>siglen</literal> is not specied) is
+   124 bytes, maximal length is 484 bytes. The signature is generated by hashing
    each word into a single bit in an n-bit string, with all these bits OR-ed
    together to produce an n-bit document signature.  When two words hash to
    the same bit position there will be a false match.  If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 6ff71a4..85582cd 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -16,23 +16,29 @@
 
 #include "access/gist.h"
 #include "access/heaptoast.h"
+#include "access/reloptions.h"
 #include "port/pg_bitutils.h"
 #include "tsearch/ts_utils.h"
 #include "utils/builtins.h"
 #include "utils/pg_crc.h"
 
 
-#define SIGLENINT  31			/* >121 => key will toast, so it will not work
-								 * !!! */
+#define SIGLEN_DEFAULT	(31 * 4)
+#define SIGLEN_MAX		(121 * 4)	/* key will toast, so it will not work !!! */
 
-#define SIGLEN	( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length */
+}	GistTsVectorOptions;
 
-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) / BITS_PER_BYTE ) ) )
 #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -40,8 +46,8 @@ typedef char *BITVECP;
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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 GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
 
@@ -65,13 +71,14 @@ typedef struct
 #define ISALLTRUE(x)	( ((SignTSVector*)(x))->flag & ALLISTRUE )
 
 #define GTHDRSIZE	( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
 
 #define GETSIGN(x)	( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
 #define GETARR(x)	( (int32*)( (char*)(x)+GTHDRSIZE ) )
 #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
 
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
 
 Datum
 gtsvectorin(PG_FUNCTION_ARGS)
@@ -102,9 +109,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
 		sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
 	else
 	{
-		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+		int			siglen = GETSIGLEN(key);
+		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
 
-		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
 	}
 
 	PG_FREE_IF_COPY(key, 0);
@@ -148,36 +156,49 @@ uniqueint(int32 *a, int32 l)
 }
 
 static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
 {
 	int32		k,
 				len = ARRNELEM(a);
 	int32	   *ptr = GETARR(a);
 
-	MemSet((void *) sign, 0, sizeof(BITVEC));
+	MemSet((void *) sign, 0, siglen);
 	for (k = 0; k < len; k++)
-		HASH(sign, ptr[k]);
+		HASH(sign, ptr[k], siglen);
 }
 
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+	int			size = CALCGTSIZE(flag, len);
+	SignTSVector *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+		memcpy(GETSIGN(res), sign, len);
+
+	return res;
+}
+
+
 Datum
 gtsvector_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{							/* tsvector */
-		SignTSVector *res;
 		TSVector	val = DatumGetTSVector(entry->key);
+		SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
 		int32		len;
 		int32	   *arr;
 		WordEntry  *ptr = ARRPTR(val);
 		char	   *words = STRPTR(val);
 
-		len = CALCGTSIZE(ARRKEY, val->size);
-		res = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = ARRKEY;
 		arr = GETARR(res);
 		len = val->size;
 		while (len--)
@@ -208,13 +229,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 		/* make signature, if array is too long */
 		if (VARSIZE(res) > TOAST_INDEX_TARGET)
 		{
-			SignTSVector *ressign;
+			SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
 
-			len = CALCGTSIZE(SIGNKEY, 0);
-			ressign = (SignTSVector *) palloc(len);
-			SET_VARSIZE(ressign, len);
-			ressign->flag = SIGNKEY;
-			makesign(GETSIGN(ressign), res);
+			makesign(GETSIGN(ressign), res, siglen);
 			res = ressign;
 		}
 
@@ -226,22 +243,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 	else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
 			 !ISALLTRUE(DatumGetPointer(entry->key)))
 	{
-		int32		i,
-					len;
+		int32		i;
 		SignTSVector *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 = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = SIGNKEY | ALLISTRUE;
-
+		res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -315,12 +327,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
 static bool
 checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
 {
+	void *key = (SignTSVector *) checkval;
+
 	/*
 	 * we are not able to find a prefix in signature tree
 	 */
 	if (val->prefix)
 		return true;
-	return GETBIT(checkval, HASHVAL(val->valcrc));
+	return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
 }
 
 Datum
@@ -347,7 +361,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 
 		/* since signature is lossy, cannot specify CALC_NOT here */
 		PG_RETURN_BOOL(TS_execute(GETQUERY(query),
-								  (void *) GETSIGN(key),
+								  key,
 								  TS_EXEC_PHRASE_NO_POS,
 								  checkcondition_bit));
 	}
@@ -365,7 +379,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
 {
 	int32		i;
 
@@ -376,7 +390,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		Assert(GETSIGLEN(add) == siglen);
+
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -384,7 +400,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		int32	   *ptr = GETARR(add);
 
 		for (i = 0; i < ARRNELEM(add); i++)
-			HASH(sbase, ptr[i]);
+			HASH(sbase, ptr[i], siglen);
 	}
 	return 0;
 }
@@ -395,30 +411,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	SignTSVector *result;
+	int			siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
+	SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
+	int32		i;
+
+	memset(base, 0, siglen);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = ALLISTRUE;
+			result->flag |= ALLISTRUE;
+			SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
 			break;
 		}
 	}
 
-	flag |= SIGNKEY;
-	len = CALCGTSIZE(flag, 0);
-	result = (SignTSVector *) palloc(len);
-	*size = len;
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!ISALLTRUE(result))
-		memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -429,6 +439,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
 	SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
 	SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -444,8 +455,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
 			BITVECP		sa = GETSIGN(a),
 						sb = GETSIGN(b);
 
+			Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -482,19 +495,19 @@ gtsvector_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
-	return pg_popcount(sign, SIGLEN);
+	return pg_popcount(sign, siglen);
 }
 
 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]);
 		/* Using the popcount functions here isn't likely to win */
@@ -506,17 +519,22 @@ hemdistsign(BITVECP a, BITVECP b)
 static int
 hemdist(SignTSVector *a, SignTSVector *b)
 {
+	int siglena = GETSIGLEN(a);
+	int siglenb = GETSIGLEN(b);
+
 	if (ISALLTRUE(a))
 	{
 		if (ISALLTRUE(b))
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(GETSIGN(b));
+			return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
 	}
 	else if (ISALLTRUE(b))
-		return SIGLENBIT - sizebitvec(GETSIGN(a));
+		return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
+
+	Assert(siglena == siglenb);
 
-	return hemdistsign(GETSIGN(a), GETSIGN(b));
+	return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
 }
 
 Datum
@@ -525,6 +543,7 @@ gtsvector_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);
+	int			siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
 	SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
 	SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -533,14 +552,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 
 	if (ISARRKEY(newval))
 	{
-		BITVEC		sign;
+		BITVECP		sign = palloc(siglen);
 
-		makesign(sign, newval);
+		makesign(sign, newval, siglen);
 
 		if (ISALLTRUE(origval))
-			*penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+		{
+			int			siglenbit = SIGLENBIT(siglen);
+
+			*penalty =
+				(float) (siglenbit - sizebitvec(sign, siglen)) /
+				(float) (siglenbit + 1);
+		}
 		else
-			*penalty = hemdistsign(sign, orig);
+			*penalty = hemdistsign(sign, orig, siglen);
+
+		pfree(sign);
 	}
 	else
 		*penalty = hemdist(origval, newval);
@@ -550,19 +577,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 typedef struct
 {
 	bool		allistrue;
-	BITVEC		sign;
+	BITVECP		sign;
 } CACHESIGN;
 
 static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
 {
 	item->allistrue = false;
 	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) )
@@ -586,19 +613,19 @@ comparecost(const void *va, const void *vb)
 
 
 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
@@ -606,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
 	OffsetNumber k,
 				j;
 	SignTSVector *datum_l,
@@ -625,6 +653,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	BITVECP		ptr;
 	int			i;
 	CACHESIGN  *cache;
+	char	   *cache_sign;
 	SPLITCOST  *costvector;
 
 	maxoff = entryvec->n - 2;
@@ -633,16 +662,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	v->spl_right = (OffsetNumber *) palloc(nbytes);
 
 	cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
-	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+	cache_sign = palloc(siglen * (maxoff + 2));
+
+	for (j = 0; j < maxoff + 2; j++)
+		cache[j].sign = &cache_sign[siglen * j];
+
+	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+			  siglen);
 
 	for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
 	{
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
 			if (k == FirstOffsetNumber)
-				fillcache(&cache[j], GETENTRY(entryvec, j));
+				fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
 
-			size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+			size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -664,44 +699,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (cache[seed_1].allistrue)
-	{
-		datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_l->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (SignTSVector *) 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 = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_r->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (SignTSVector *) 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 = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+							  siglen, cache[seed_1].sign);
+	datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+							  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), 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);
@@ -727,36 +739,34 @@ gtsvector_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;
@@ -767,12 +777,12 @@ gtsvector_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;
@@ -799,3 +809,17 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
 {
 	return gtsvector_consistent(fcinfo);
 }
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+	GistTsVectorOptions *options = NULL;
+
+	extend_local_reloptions(relopts, options, sizeof(*options));
+	add_local_int_reloption(relopts, "siglen", "signature length",
+							SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+							&options->siglen);
+
+	PG_RETURN_VOID();
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e70501..53a64ed 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -481,6 +481,9 @@
   amproc => 'gtsvector_picksplit' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+  amprocrighttype => 'tsvector', amprocnum => '0',
+  amproc => 'gtsvector_options' },
 { amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
   amprocrighttype => 'tsquery', amprocnum => '1',
   amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e6645f1..ac4440b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8646,6 +8646,9 @@
   proname => 'gtsvector_consistent', prorettype => 'bool',
   proargtypes => 'internal gtsvector int4 oid internal',
   prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3434', descr => 'GiST tsvector support',
+  proname => 'gtsvector_options', prorettype => 'void', proisstrict => 'f',
+  proargtypes => 'internal', prosrc => 'gtsvector_options' },
 
 { oid => '3656', descr => 'GIN tsvector support',
   proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index 7af2899..ff8ec8e 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
    508
 (1 row)
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR:  value 485 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR:  parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a)
+    "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx2
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index ece80b9..1bdbfd8 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
 SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
 SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
-- 
2.7.4

0005-Remove-pg_index.indoption-20190910.patchtext/x-patch; name=0005-Remove-pg_index.indoption-20190910.patchDownload
From da324f0e2aae09f185ceca3473e8ca8eeae3c317 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:40 +0300
Subject: [PATCH 5/5] Remove pg_index.indoption

---
 doc/src/sgml/catalogs.sgml                | 11 -------
 src/backend/access/common/reloptions.c    | 37 +++++++++++++++++++++
 src/backend/access/nbtree/nbtree.c        |  3 +-
 src/backend/access/nbtree/nbtutils.c      | 36 +++++++++++++--------
 src/backend/catalog/index.c               | 19 ++---------
 src/backend/catalog/toasting.c            |  6 +---
 src/backend/commands/indexcmds.c          | 51 ++++++++++++++---------------
 src/backend/optimizer/util/plancat.c      | 16 ++++++----
 src/backend/parser/parse_utilcmd.c        | 26 +--------------
 src/backend/utils/adt/amutils.c           | 45 ++++++++++++++------------
 src/backend/utils/adt/ruleutils.c         | 53 ++++++++++++++++++-------------
 src/backend/utils/cache/relcache.c        | 41 +++++-------------------
 src/include/access/genam.h                |  7 ++++
 src/include/access/nbtree.h               | 11 +++----
 src/include/access/reloptions.h           |  3 ++
 src/include/catalog/index.h               |  1 -
 src/include/catalog/pg_index.h            |  9 ++----
 src/include/utils/rel.h                   |  1 -
 src/test/regress/expected/btree_index.out |  4 +--
 src/test/regress/expected/opr_sanity.out  |  3 +-
 src/test/regress/sql/opr_sanity.sql       |  3 +-
 21 files changed, 183 insertions(+), 203 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 5e71a2e..5b95131 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3916,17 +3916,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
      </row>
 
      <row>
-      <entry><structfield>indoption</structfield></entry>
-      <entry><type>int2vector</type></entry>
-      <entry></entry>
-      <entry>
-       This is an array of <structfield>indnkeyatts</structfield> values that
-       store per-column flag bits.  The meaning of the bits is defined by
-       the index's access method.
-      </entry>
-     </row>
-
-     <row>
       <entry><structfield>indexprs</structfield></entry>
       <entry><type>pg_node_tree</type></entry>
       <entry></entry>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 3ad1324..5d7e704 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1888,3 +1888,40 @@ AlterTableGetRelOptionsLockLevel(List *defList)
 
 	return lockmode;
 }
+
+static void
+ordattoptions_validator(void *parsed_options, relopt_value *vals, int nvals)
+{
+	OrderedAttOptions *ordopts = parsed_options;
+
+	for (int i = 0; i < nvals; i++)
+		if (!strcmp(vals[i].gen->name, "nulls_first") &&
+			!vals[i].isset)
+			ordopts->nulls_first = ordopts->desc;
+}
+
+void
+ordered_index_attoptions(local_relopts *relopts, int attno)
+{
+	OrderedAttOptions *attopts = NULL;
+
+	init_local_reloptions(relopts, attopts, sizeof(*attopts));
+	add_local_bool_reloption(relopts, INDOPTION_DESC,
+							 "Descending ordering",
+							 false, &attopts->desc);
+	add_local_bool_reloption(relopts, INDOPTION_NULLS_FIRST,
+							 "NULLs sorted first",
+							 false, &attopts->nulls_first);
+	register_reloptions_validator(relopts, ordattoptions_validator);
+}
+
+OrderedAttOptions *
+get_ordered_attoptions(Datum attoptions)
+{
+	local_relopts relopts;
+
+	ordered_index_attoptions(&relopts, 0);
+
+	return (OrderedAttOptions *)
+		parseAndFillLocalRelOptions(&relopts, attoptions, false);
+}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 0741107..d4ed36d 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -20,6 +20,7 @@
 
 #include "access/nbtree.h"
 #include "access/nbtxlog.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/xlog.h"
 #include "commands/progress.h"
@@ -133,7 +134,7 @@ bthandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = btcanreturn;
 	amroutine->amcostestimate = btcostestimate;
 	amroutine->amoptions = btoptions;
-	amroutine->amattoptions = NULL;
+	amroutine->amattoptions = ordered_index_attoptions;
 	amroutine->amproperty = btproperty;
 	amroutine->ambuildphasename = btbuildphasename;
 	amroutine->amvalidate = btvalidate;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 4c7b2d0..8d093de 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -46,7 +46,7 @@ static int	_bt_compare_array_elements(const void *a, const void *b, void *arg);
 static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
 									 ScanKey leftarg, ScanKey rightarg,
 									 bool *result);
-static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption);
+static bool _bt_fix_scankey_strategy(ScanKey skey, bytea **attoptions);
 static void _bt_mark_scankey_required(ScanKey skey);
 static bool _bt_check_rowcompare(ScanKey skey,
 								 IndexTuple tuple, int tupnatts, TupleDesc tupdesc,
@@ -54,6 +54,9 @@ static bool _bt_check_rowcompare(ScanKey skey,
 static int	_bt_keep_natts(Relation rel, IndexTuple lastleft,
 						   IndexTuple firstright, BTScanInsert itup_key);
 
+#define _bt_scankey_ordering_flags(ordopts) \
+	((ordopts->desc ? SK_BT_DESC : 0) | \
+	 (ordopts->nulls_first ? SK_BT_NULLS_FIRST : 0))
 
 /*
  * _bt_mkscankey
@@ -88,13 +91,13 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 	ScanKey		skey;
 	TupleDesc	itupdesc;
 	int			indnkeyatts;
-	int16	   *indoption;
+	bytea	  **attoptions;
 	int			tupnatts;
 	int			i;
 
 	itupdesc = RelationGetDescr(rel);
 	indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
-	indoption = rel->rd_indoption;
+	attoptions = RelationGetIndexAttOptions(rel, false);
 	tupnatts = itup ? BTreeTupleGetNAtts(itup, rel) : 0;
 
 	Assert(tupnatts <= IndexRelationGetNumberOfAttributes(rel));
@@ -117,6 +120,7 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 	for (i = 0; i < indnkeyatts; i++)
 	{
 		FmgrInfo   *procinfo;
+		OrderedAttOptions *ordopts = (OrderedAttOptions *) attoptions[i];
 		Datum		arg;
 		bool		null;
 		int			flags;
@@ -139,7 +143,9 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
 			arg = (Datum) 0;
 			null = true;
 		}
-		flags = (null ? SK_ISNULL : 0) | (indoption[i] << SK_BT_INDOPTION_SHIFT);
+
+		flags = (null ? SK_ISNULL : 0) | _bt_scankey_ordering_flags(ordopts);
+
 		ScanKeyEntryInitializeWithInfo(&skey[i],
 									   flags,
 									   (AttrNumber) (i + 1),
@@ -192,7 +198,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan)
 {
 	BTScanOpaque so = (BTScanOpaque) scan->opaque;
 	int			numberOfKeys = scan->numberOfKeys;
-	int16	   *indoption = scan->indexRelation->rd_indoption;
+	bytea	  **attopts = RelationGetIndexAttOptions(scan->indexRelation, false);
 	int			numArrayKeys;
 	ScanKey		cur;
 	int			i;
@@ -335,7 +341,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan)
 		 * successive primitive indexscans produce data in index order.
 		 */
 		num_elems = _bt_sort_array_elements(scan, cur,
-											(indoption[cur->sk_attno - 1] & INDOPTION_DESC) != 0,
+											((OrderedAttOptions *) attopts[cur->sk_attno - 1])->desc,
 											elem_values, num_nonnulls);
 
 		/*
@@ -745,7 +751,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
 {
 	BTScanOpaque so = (BTScanOpaque) scan->opaque;
 	int			numberOfKeys = scan->numberOfKeys;
-	int16	   *indoption = scan->indexRelation->rd_indoption;
+	bytea	  **attoptions = RelationGetIndexAttOptions(scan->indexRelation, false);
 	int			new_numberOfKeys;
 	int			numberOfEqualCols;
 	ScanKey		inkeys;
@@ -782,7 +788,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
 	if (numberOfKeys == 1)
 	{
 		/* Apply indoption to scankey (might change sk_strategy!) */
-		if (!_bt_fix_scankey_strategy(cur, indoption))
+		if (!_bt_fix_scankey_strategy(cur, attoptions))
 			so->qual_ok = false;
 		memcpy(outkeys, cur, sizeof(ScanKeyData));
 		so->numberOfKeys = 1;
@@ -817,7 +823,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
 		if (i < numberOfKeys)
 		{
 			/* Apply indoption to scankey (might change sk_strategy!) */
-			if (!_bt_fix_scankey_strategy(cur, indoption))
+			if (!_bt_fix_scankey_strategy(cur, attoptions))
 			{
 				/* NULL can't be matched, so give up */
 				so->qual_ok = false;
@@ -1195,11 +1201,10 @@ _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
  * not going to change while the scankey survives.
  */
 static bool
-_bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
+_bt_fix_scankey_strategy(ScanKey skey, bytea **attopts)
 {
-	int			addflags;
-
-	addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
+	OrderedAttOptions *ordopts = (OrderedAttOptions *) attopts[skey->sk_attno - 1];
+	int			addflags = _bt_scankey_ordering_flags(ordopts);
 
 	/*
 	 * We treat all btree operators as strict (even if they're not so marked
@@ -1268,7 +1273,10 @@ _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
 		for (;;)
 		{
 			Assert(subkey->sk_flags & SK_ROW_MEMBER);
-			addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
+
+			ordopts = (OrderedAttOptions *) attopts[subkey->sk_attno - 1];
+			addflags = _bt_scankey_ordering_flags(ordopts);
+
 			if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC))
 				subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy);
 			subkey->sk_flags |= addflags;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index cb7f0ba..60ec7ab 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -114,7 +114,6 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 								IndexInfo *indexInfo,
 								Oid *collationOids,
 								Oid *classOids,
-								int16 *coloptions,
 								bool primary,
 								bool isexclusion,
 								bool immediate,
@@ -535,7 +534,6 @@ UpdateIndexRelation(Oid indexoid,
 					IndexInfo *indexInfo,
 					Oid *collationOids,
 					Oid *classOids,
-					int16 *coloptions,
 					bool primary,
 					bool isexclusion,
 					bool immediate,
@@ -545,7 +543,6 @@ UpdateIndexRelation(Oid indexoid,
 	int2vector *indkey;
 	oidvector  *indcollation;
 	oidvector  *indclass;
-	int2vector *indoption;
 	Datum		exprsDatum;
 	Datum		predDatum;
 	Datum		values[Natts_pg_index];
@@ -555,7 +552,7 @@ UpdateIndexRelation(Oid indexoid,
 	int			i;
 
 	/*
-	 * Copy the index key, opclass, and indoption info into arrays (should we
+	 * Copy the index key and opclass info into arrays (should we
 	 * make the caller pass them like this to start with?)
 	 */
 	indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs);
@@ -563,7 +560,6 @@ UpdateIndexRelation(Oid indexoid,
 		indkey->values[i] = indexInfo->ii_IndexAttrNumbers[i];
 	indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexKeyAttrs);
 	indclass = buildoidvector(classOids, indexInfo->ii_NumIndexKeyAttrs);
-	indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexKeyAttrs);
 
 	/*
 	 * Convert the index expressions (if any) to a text datum
@@ -622,7 +618,6 @@ UpdateIndexRelation(Oid indexoid,
 	values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
 	values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
 	values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
-	values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
 	values[Anum_pg_index_indexprs - 1] = exprsDatum;
 	if (exprsDatum == (Datum) 0)
 		nulls[Anum_pg_index_indexprs - 1] = true;
@@ -665,7 +660,6 @@ UpdateIndexRelation(Oid indexoid,
  * tableSpaceId: OID of tablespace to use
  * collationObjectId: array of collation OIDs, one per index column
  * classObjectId: array of index opclass OIDs, one per index column
- * coloptions: array of per-index-column indoption settings
  * reloptions: AM-specific options
  * flags: bitmask that can include any combination of these bits:
  *		INDEX_CREATE_IS_PRIMARY
@@ -705,7 +699,6 @@ index_create(Relation heapRelation,
 			 Oid tableSpaceId,
 			 Oid *collationObjectId,
 			 Oid *classObjectId,
-			 int16 *coloptions,
 			 Datum reloptions,
 			 bits16 flags,
 			 bits16 constr_flags,
@@ -950,7 +943,7 @@ index_create(Relation heapRelation,
 	 */
 	UpdateIndexRelation(indexRelationId, heapRelationId, parentIndexRelid,
 						indexInfo,
-						collationObjectId, classObjectId, coloptions,
+						collationObjectId, classObjectId,
 						isprimary, is_exclusion,
 						(constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) == 0,
 						!concurrent && !invalid,
@@ -1215,10 +1208,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
 	HeapTuple	indexTuple,
 				classTuple;
 	Datum		indclassDatum,
-				colOptionDatum,
 				optionDatum;
 	oidvector  *indclass;
-	int2vector *indcoloptions;
 	bool		isnull;
 	List	   *indexColNames = NIL;
 	List	   *indexExprs = NIL;
@@ -1247,11 +1238,6 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
 	Assert(!isnull);
 	indclass = (oidvector *) DatumGetPointer(indclassDatum);
 
-	colOptionDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
-									 Anum_pg_index_indoption, &isnull);
-	Assert(!isnull);
-	indcoloptions = (int2vector *) DatumGetPointer(colOptionDatum);
-
 	/* Fetch options of index if any */
 	classTuple = SearchSysCache1(RELOID, oldIndexId);
 	if (!HeapTupleIsValid(classTuple))
@@ -1339,7 +1325,6 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
 							  indexRelation->rd_rel->reltablespace,
 							  indexRelation->rd_indcollation,
 							  indclass->values,
-							  indcoloptions->values,
 							  optionDatum,
 							  INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT,
 							  0,
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 7290731..5361955 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -142,7 +142,6 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	IndexInfo  *indexInfo;
 	Oid			collationObjectId[2];
 	Oid			classObjectId[2];
-	int16		coloptions[2];
 	ObjectAddress baseobject,
 				toastobject;
 
@@ -320,16 +319,13 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	classObjectId[0] = OID_BTREE_OPS_OID;
 	classObjectId[1] = INT4_BTREE_OPS_OID;
 
-	coloptions[0] = 0;
-	coloptions[1] = 0;
-
 	index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid,
 				 InvalidOid, InvalidOid,
 				 indexInfo,
 				 list_make2("chunk_id", "chunk_seq"),
 				 BTREE_AM_OID,
 				 rel->rd_rel->reltablespace,
-				 collationObjectId, classObjectId, coloptions, (Datum) 0,
+				 collationObjectId, classObjectId, (Datum) 0,
 				 INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL);
 
 	table_close(toast_rel, NoLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 04148e5..1e08100 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -73,7 +73,6 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
 							  Oid *typeOidP,
 							  Oid *collationOidP,
 							  Oid *classOidP,
-							  int16 *colOptionP,
 							  List *attList,
 							  List *exclusionOpNames,
 							  Oid relId,
@@ -155,7 +154,6 @@ CheckIndexCompatible(Oid oldId,
 	Form_pg_am	accessMethodForm;
 	IndexAmRoutine *amRoutine;
 	bool		amcanorder;
-	int16	   *coloptions;
 	IndexInfo  *indexInfo;
 	int			numberOfAttributes;
 	int			old_natts;
@@ -208,11 +206,9 @@ CheckIndexCompatible(Oid oldId,
 	typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
 	collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
 	classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
 	ComputeIndexAttrs(indexInfo,
 					  typeObjectId, collationObjectId, classObjectId,
-					  coloptions, attributeList,
-					  exclusionOpNames, relationId,
+					  attributeList, exclusionOpNames, relationId,
 					  accessMethodName, accessMethodId,
 					  amcanorder, isconstraint);
 
@@ -504,7 +500,6 @@ DefineIndex(Oid relationId,
 	amoptions_function amoptions;
 	bool		partitioned;
 	Datum		reloptions;
-	int16	   *coloptions;
 	IndexInfo  *indexInfo;
 	bits16		flags;
 	bits16		constr_flags;
@@ -834,11 +829,9 @@ DefineIndex(Oid relationId,
 	typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
 	collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
 	classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-	coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
 	ComputeIndexAttrs(indexInfo,
 					  typeObjectId, collationObjectId, classObjectId,
-					  coloptions, allIndexParams,
-					  stmt->excludeOpNames, relationId,
+					  allIndexParams, stmt->excludeOpNames, relationId,
 					  accessMethodName, accessMethodId,
 					  amcanorder, stmt->isconstraint);
 
@@ -1036,7 +1029,7 @@ DefineIndex(Oid relationId,
 					 stmt->oldNode, indexInfo, indexColNames,
 					 accessMethodId, tablespaceId,
 					 collationObjectId, classObjectId,
-					 coloptions, reloptions,
+					 reloptions,
 					 flags, constr_flags,
 					 allowSystemTableMods, !check_rights,
 					 &createdConstraintId);
@@ -1566,7 +1559,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 				  Oid *typeOidP,
 				  Oid *collationOidP,
 				  Oid *classOidP,
-				  int16 *colOptionP,
 				  List *attList,	/* list of IndexElem's */
 				  List *exclusionOpNames,
 				  Oid relId,
@@ -1714,7 +1706,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 						 errmsg("including column does not support NULLS FIRST/LAST options")));
 
 			classOidP[attn] = InvalidOid;
-			colOptionP[attn] = 0;
 			collationOidP[attn] = InvalidOid;
 			attn++;
 
@@ -1829,22 +1820,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 		 * zero for any un-ordered index, while ordered indexes have DESC and
 		 * NULLS FIRST/LAST options.
 		 */
-		colOptionP[attn] = 0;
-		if (amcanorder)
-		{
-			/* default ordering is ASC */
-			if (attribute->ordering == SORTBY_DESC)
-				colOptionP[attn] |= INDOPTION_DESC;
-			/* default null ordering is LAST for ASC, FIRST for DESC */
-			if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
-			{
-				if (attribute->ordering == SORTBY_DESC)
-					colOptionP[attn] |= INDOPTION_NULLS_FIRST;
-			}
-			else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
-				colOptionP[attn] |= INDOPTION_NULLS_FIRST;
-		}
-		else
+		if (!amcanorder)
 		{
 			/* index AM does not support ordering */
 			if (attribute->ordering != SORTBY_DEFAULT)
@@ -1859,6 +1835,25 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 								accessMethodName)));
 		}
 
+		if (attribute->ordering == SORTBY_DESC)
+		{
+			attribute->opclassopts =
+				lappend(attribute->opclassopts,
+						makeDefElem(INDOPTION_DESC, NULL, -1));
+
+			if (attribute->nulls_ordering == SORTBY_NULLS_LAST)
+				attribute->opclassopts =
+					lappend(attribute->opclassopts,
+							makeDefElem(INDOPTION_NULLS_FIRST,
+										(Node *) makeString("false"), -1));
+		}
+		else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
+		{
+			attribute->opclassopts =
+				lappend(attribute->opclassopts,
+						makeDefElem(INDOPTION_NULLS_FIRST, NULL, -1));
+		}
+
 		/* Set up the per-column opclass options (attoptions field). */
 		if (attribute->opclassopts)
 		{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index de9b6f4..6845079 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -299,10 +299,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 				for (i = 0; i < nkeycolumns; i++)
 				{
-					int16		opt = indexRelation->rd_indoption[i];
+					OrderedAttOptions *opt = (OrderedAttOptions *) info->opclassoptions[i];
 
-					info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
-					info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
+					Assert(opt);
+
+					info->reverse_sort[i] = opt->desc;
+					info->nulls_first[i] = opt->nulls_first;
 				}
 			}
 			else if (amroutine->amcanorder)
@@ -327,14 +329,16 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 				for (i = 0; i < nkeycolumns; i++)
 				{
-					int16		opt = indexRelation->rd_indoption[i];
+					OrderedAttOptions *opt = (OrderedAttOptions *) info->opclassoptions[i];
 					Oid			ltopr;
 					Oid			btopfamily;
 					Oid			btopcintype;
 					int16		btstrategy;
 
-					info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
-					info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
+					Assert(opt);
+
+					info->reverse_sort[i] = opt->desc;
+					info->nulls_first[i] = opt->nulls_first;
 
 					ltopr = get_opfamily_member(info->opfamily[i],
 												info->opcintype[i],
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 5872376..8badc74 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1523,7 +1523,6 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 		AttrNumber	attnum = idxrec->indkey.values[keyno];
 		Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(source_idx),
 											   keyno);
-		int16		opt = source_idx->rd_indoption[keyno];
 
 		iparam = makeNode(IndexElem);
 
@@ -1583,28 +1582,6 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 		iparam->ordering = SORTBY_DEFAULT;
 		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
 
-		/* Adjust options if necessary */
-		if (source_idx->rd_indam->amcanorder)
-		{
-			/*
-			 * If it supports sort ordering, copy DESC and NULLS opts. Don't
-			 * set non-default settings unnecessarily, though, so as to
-			 * improve the chance of recognizing equivalence to constraint
-			 * indexes.
-			 */
-			if (opt & INDOPTION_DESC)
-			{
-				iparam->ordering = SORTBY_DESC;
-				if ((opt & INDOPTION_NULLS_FIRST) == 0)
-					iparam->nulls_ordering = SORTBY_NULLS_LAST;
-			}
-			else
-			{
-				if (opt & INDOPTION_NULLS_FIRST)
-					iparam->nulls_ordering = SORTBY_NULLS_FIRST;
-			}
-		}
-
 		index->indexParams = lappend(index->indexParams, iparam);
 	}
 
@@ -2161,8 +2138,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 				defopclass = GetDefaultOpClass(attform->atttypid,
 											   index_rel->rd_rel->relam);
 				if (indclass->values[i] != defopclass ||
-					attoptions != (Datum) 0 ||
-					index_rel->rd_indoption[i] != 0)
+					attoptions != (Datum) 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 							 errmsg("index \"%s\" column number %d does not have default sorting behavior", index_name, i + 1),
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index e81d6cc..932f60c 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -15,9 +15,11 @@
 
 #include "access/amapi.h"
 #include "access/htup_details.h"
+#include "access/reloptions.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_index.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -114,14 +116,11 @@ lookup_prop_name(const char *name)
  * otherwise sets *res to the boolean value to return.
  */
 static bool
-test_indoption(HeapTuple tuple, int attno, bool guard,
-			   int16 iopt_mask, int16 iopt_expect,
-			   bool *res)
+test_indoption(Oid indexrelid, int attno, bool guard,
+			   int offset, bool expect, bool *res)
 {
-	Datum		datum;
-	bool		isnull;
-	int2vector *indoption;
-	int16		indoption_val;
+	OrderedAttOptions *ordopts;
+	Datum		relopts;
 
 	if (!guard)
 	{
@@ -129,14 +128,14 @@ test_indoption(HeapTuple tuple, int attno, bool guard,
 		return true;
 	}
 
-	datum = SysCacheGetAttr(INDEXRELID, tuple,
-							Anum_pg_index_indoption, &isnull);
-	Assert(!isnull);
+	relopts = get_attoptions(indexrelid, attno);
+	ordopts = get_ordered_attoptions(relopts);
 
-	indoption = ((int2vector *) DatumGetPointer(datum));
-	indoption_val = indoption->values[attno - 1];
+	*res = *((bool *)((char *) ordopts + offset)) == expect;
 
-	*res = (indoption_val & iopt_mask) == iopt_expect;
+	pfree(ordopts);
+	if (relopts != (Datum) 0)
+		pfree(DatumGetPointer(relopts));
 
 	return true;
 }
@@ -249,29 +248,33 @@ indexam_property(FunctionCallInfo fcinfo,
 		{
 			case AMPROP_ASC:
 				if (iskey &&
-					test_indoption(tuple, attno, routine->amcanorder,
-								   INDOPTION_DESC, 0, &res))
+					test_indoption(index_oid, attno, routine->amcanorder,
+								   offsetof(OrderedAttOptions, desc), false,
+								   &res))
 					isnull = false;
 				break;
 
 			case AMPROP_DESC:
 				if (iskey &&
-					test_indoption(tuple, attno, routine->amcanorder,
-								   INDOPTION_DESC, INDOPTION_DESC, &res))
+					test_indoption(index_oid, attno, routine->amcanorder,
+								   offsetof(OrderedAttOptions, desc), true,
+								   &res))
 					isnull = false;
 				break;
 
 			case AMPROP_NULLS_FIRST:
 				if (iskey &&
-					test_indoption(tuple, attno, routine->amcanorder,
-								   INDOPTION_NULLS_FIRST, INDOPTION_NULLS_FIRST, &res))
+					test_indoption(index_oid, attno, routine->amcanorder,
+								   offsetof(OrderedAttOptions, nulls_first),
+								   true, &res))
 					isnull = false;
 				break;
 
 			case AMPROP_NULLS_LAST:
 				if (iskey &&
-					test_indoption(tuple, attno, routine->amcanorder,
-								   INDOPTION_NULLS_FIRST, 0, &res))
+					test_indoption(index_oid, attno, routine->amcanorder,
+								   offsetof(OrderedAttOptions, nulls_first),
+								   false, &res))
 					isnull = false;
 				break;
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index faecf04..a649e4b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -22,6 +22,7 @@
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/relation.h"
+#include "access/reloptions.h"
 #include "access/sysattr.h"
 #include "access/table.h"
 #include "catalog/dependency.h"
@@ -469,7 +470,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
-static void get_reloptions(StringInfo buf, Datum reloptions);
+static int get_reloptions(StringInfo buf, Datum reloptions, bool skip_ord_opts);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1194,11 +1195,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	int			keyno;
 	Datum		indcollDatum;
 	Datum		indclassDatum;
-	Datum		indoptionDatum;
 	bool		isnull;
 	oidvector  *indcollation;
 	oidvector  *indclass;
-	int2vector *indoption;
 	StringInfoData buf;
 	char	   *str;
 	char	   *sep;
@@ -1218,7 +1217,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	indrelid = idxrec->indrelid;
 	Assert(indexrelid == idxrec->indexrelid);
 
-	/* Must get indcollation, indclass, and indoption the hard way */
+	/* Must get indcollation and indclass the hard way */
 	indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
 								   Anum_pg_index_indcollation, &isnull);
 	Assert(!isnull);
@@ -1229,11 +1228,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 	Assert(!isnull);
 	indclass = (oidvector *) DatumGetPointer(indclassDatum);
 
-	indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
-									 Anum_pg_index_indoption, &isnull);
-	Assert(!isnull);
-	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
-
 	/*
 	 * Fetch the pg_class tuple of the index relation
 	 */
@@ -1370,7 +1364,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 		if (!attrsOnly && keyno < idxrec->indnkeyatts &&
 			(!colno || colno == keyno + 1))
 		{
-			int16		opt = indoption->values[keyno];
 			Oid			indcoll = indcollation->values[keyno];
 			Datum		attoptions = get_attoptions(indexrelid, keyno + 1);
 			bool		has_options = attoptions != (Datum) 0;
@@ -1384,27 +1377,32 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 			get_opclass_name(indclass->values[keyno],
 							 has_options ? InvalidOid : keycoltype, &buf);
 
-			if (has_options)
+			if (has_options)	/* FIXME default opclass */
 			{
 				appendStringInfoString(&buf, " (");
-				get_reloptions(&buf, attoptions);
-				appendStringInfoChar(&buf, ')');
+				if (get_reloptions(&buf, attoptions, amroutine->amcanorder) > 0)
+					appendStringInfoChar(&buf, ')');
+				else
+					buf.len -= 2;
 			}
 
 			/* Add options if relevant */
-			if (amroutine->amcanorder)
+			if (amroutine->amcanorder && has_options)
 			{
+				OrderedAttOptions *ordopts =
+					get_ordered_attoptions(attoptions);
+
 				/* if it supports sort ordering, report DESC and NULLS opts */
-				if (opt & INDOPTION_DESC)
+				if (ordopts->desc)
 				{
 					appendStringInfoString(&buf, " DESC");
 					/* NULLS FIRST is the default in this case */
-					if (!(opt & INDOPTION_NULLS_FIRST))
+					if (!ordopts->nulls_first)
 						appendStringInfoString(&buf, " NULLS LAST");
 				}
 				else
 				{
-					if (opt & INDOPTION_NULLS_FIRST)
+					if (ordopts->nulls_first)
 						appendStringInfoString(&buf, " NULLS FIRST");
 				}
 			}
@@ -11213,18 +11211,19 @@ string_to_text(char *str)
 /*
  * Generate a C string representing a relation options from text[] datum.
  */
-static void
-get_reloptions(StringInfo buf, Datum reloptions)
+static int
+get_reloptions(StringInfo buf, Datum reloptions, bool skip_ord_opts)
 {
 	Datum	   *options;
 	int			noptions;
 	int			i;
+	int			j;
 
 	deconstruct_array(DatumGetArrayTypeP(reloptions),
 					  TEXTOID, -1, false, 'i',
 					  &options, NULL, &noptions);
 
-	for (i = 0; i < noptions; i++)
+	for (i = j = 0; i < noptions; i++)
 	{
 		char	   *option = TextDatumGetCString(options[i]);
 		char	   *name;
@@ -11245,7 +11244,15 @@ get_reloptions(StringInfo buf, Datum reloptions)
 		else
 			value = "";
 
-		if (i > 0)
+		if (skip_ord_opts &&
+			(!strcmp(name, INDOPTION_DESC) ||
+			 !strcmp(name, INDOPTION_NULLS_FIRST)))
+		{
+			pfree(option);
+			continue;
+		}
+
+		if (j++ > 0)
 			appendStringInfoString(buf, ", ");
 		appendStringInfo(buf, "%s=", quote_identifier(name));
 
@@ -11264,6 +11271,8 @@ get_reloptions(StringInfo buf, Datum reloptions)
 
 		pfree(option);
 	}
+
+	return j;
 }
 
 /*
@@ -11288,7 +11297,7 @@ flatten_reloptions(Oid relid)
 		StringInfoData buf;
 
 		initStringInfo(&buf);
-		get_reloptions(&buf, reloptions);
+		get_reloptions(&buf, reloptions, false);
 
 		result = buf.data;
 	}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d36b31b..ee32087 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1365,11 +1365,9 @@ RelationInitIndexAccessInfo(Relation relation)
 	Form_pg_am	aform;
 	Datum		indcollDatum;
 	Datum		indclassDatum;
-	Datum		indoptionDatum;
 	bool		isnull;
 	oidvector  *indcoll;
 	oidvector  *indclass;
-	int2vector *indoption;
 	MemoryContext indexcxt;
 	MemoryContext oldcontext;
 	int			indnatts;
@@ -1454,9 +1452,6 @@ RelationInitIndexAccessInfo(Relation relation)
 	relation->rd_indcollation = (Oid *)
 		MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
 
-	relation->rd_indoption = (int16 *)
-		MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(int16));
-
 	/*
 	 * indcollation cannot be referenced directly through the C struct,
 	 * because it comes after the variable-width indkey field.  Must extract
@@ -1491,17 +1486,6 @@ RelationInitIndexAccessInfo(Relation relation)
 						   relation->rd_opfamily, relation->rd_opcintype,
 						   amsupport, indnkeyatts);
 
-	/*
-	 * Similarly extract indoption and copy it to the cache entry
-	 */
-	indoptionDatum = fastgetattr(relation->rd_indextuple,
-								 Anum_pg_index_indoption,
-								 GetPgIndexDescriptor(),
-								 &isnull);
-	Assert(!isnull);
-	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
-	memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
-
 #if 0
 	(void) RelationGetIndexAttOptions(relation, false);
 #endif
@@ -5232,6 +5216,14 @@ RelationGetIndexAttOptions(Relation relation, bool copy)
 			if (attoptions != (Datum) 0)
 				pfree(DatumGetPointer(attoptions));
 		}
+		else
+		{
+			OrderedAttOptions *opt = palloc0(sizeof(*opt));
+
+			SET_VARSIZE(opt, sizeof(*opt));
+
+			opts[i] = (bytea *) opt;
+		}
 	}
 
 	/* Copy parsed options to the cache. */
@@ -5527,7 +5519,6 @@ load_relcache_init_file(bool shared)
 			Oid		   *opcintype;
 			RegProcedure *support;
 			int			nsupport;
-			int16	   *indoption;
 			Oid		   *indcollation;
 
 			/* Count nailed indexes to ensure we have 'em all */
@@ -5604,16 +5595,6 @@ load_relcache_init_file(bool shared)
 
 			rel->rd_indcollation = indcollation;
 
-			/* finally, read the vector of indoption values */
-			if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
-				goto read_failed;
-
-			indoption = (int16 *) MemoryContextAlloc(indexcxt, len);
-			if (fread(indoption, 1, len, fp) != len)
-				goto read_failed;
-
-			rel->rd_indoption = indoption;
-
 #if 0
 			/* finally, read the vector of opcoptions values */
 			rel->rd_opcoptions = (bytea **)
@@ -5659,7 +5640,6 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_opcintype == NULL);
 			Assert(rel->rd_support == NULL);
 			Assert(rel->rd_supportinfo == NULL);
-			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
 			Assert(rel->rd_opcoptions == NULL);
 		}
@@ -5944,11 +5924,6 @@ write_relcache_init_file(bool shared)
 					   relform->relnatts * sizeof(Oid),
 					   fp);
 
-			/* finally, write the vector of indoption values */
-			write_item(rel->rd_indoption,
-					   relform->relnatts * sizeof(int16),
-					   fp);
-
 #if 0
 			Assert(rel->rd_opcoptions);
 
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 778d17c..322577d 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -117,6 +117,13 @@ typedef enum IndexUniqueCheck
 	UNIQUE_CHECK_EXISTING		/* Check if existing tuple is unique */
 } IndexUniqueCheck;
 
+/* Common attribute options for ordered indexes */
+typedef struct OrderedAttOptions
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	bool		desc;			/* DESCending ordering */
+	bool		nulls_first;	/* NULLS FIRST/LAST */
+} OrderedAttOptions;
 
 /*
  * generalized index_ interface routines (in indexam.c)
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 52eafe6..6901d6e 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -672,13 +672,12 @@ typedef BTScanOpaqueData *BTScanOpaque;
 /*
  * We use some private sk_flags bits in preprocessed scan keys.  We're allowed
  * to use bits 16-31 (see skey.h).  The uppermost bits are copied from the
- * index's indoption[] array entry for the index attribute.
+ * index's attoptions for the index attribute.
  */
-#define SK_BT_REQFWD	0x00010000	/* required to continue forward scan */
-#define SK_BT_REQBKWD	0x00020000	/* required to continue backward scan */
-#define SK_BT_INDOPTION_SHIFT  24	/* must clear the above bits */
-#define SK_BT_DESC			(INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
-#define SK_BT_NULLS_FIRST	(INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
+#define SK_BT_REQFWD		0x00010000	/* required to continue forward scan */
+#define SK_BT_REQBKWD		0x00020000	/* required to continue backward scan */
+#define SK_BT_DESC			0x00040000
+#define SK_BT_NULLS_FIRST	0x00080000
 
 /*
  * Constant definition for progress reporting.  Phase numbers must match
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 70238f9..5592da5 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -316,4 +316,7 @@ extern bytea *attribute_reloptions(Datum reloptions, bool validate);
 extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
 extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
 
+extern void ordered_index_attoptions(local_relopts *relopts, int attno);
+extern OrderedAttOptions *get_ordered_attoptions(Datum attoptions);
+
 #endif							/* RELOPTIONS_H */
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 1113d25..b306882 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -64,7 +64,6 @@ extern Oid	index_create(Relation heapRelation,
 						 Oid tableSpaceId,
 						 Oid *collationObjectId,
 						 Oid *classObjectId,
-						 int16 *coloptions,
 						 Datum reloptions,
 						 bits16 flags,
 						 bits16 constr_flags,
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 2438374..28cd920 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -49,7 +49,6 @@ CATALOG(pg_index,2610,IndexRelationId) BKI_SCHEMA_MACRO
 #ifdef CATALOG_VARLEN
 	oidvector	indcollation;	/* collation identifiers */
 	oidvector	indclass;		/* opclass identifiers */
-	int2vector	indoption;		/* per-column flags (AM-specific meanings) */
 	pg_node_tree indexprs;		/* expression trees for index attributes that
 								 * are not simple column references; one for
 								 * each zero entry in indkey[] */
@@ -68,12 +67,10 @@ typedef FormData_pg_index *Form_pg_index;
 #ifdef EXPOSE_TO_CLIENT_CODE
 
 /*
- * Index AMs that support ordered scans must support these two indoption
- * bits.  Otherwise, the content of the per-column indoption fields is
- * open for future definition.
+ * Index AMs that support ordered scans must support these two reloptions.
  */
-#define INDOPTION_DESC			0x0001	/* values are in reverse order */
-#define INDOPTION_NULLS_FIRST	0x0002	/* NULLs are first instead of last */
+#define INDOPTION_DESC			"desc"			/* values are in reverse order */
+#define INDOPTION_NULLS_FIRST	"nulls_first"	/* NULLs are first instead of last */
 
 #endif							/* EXPOSE_TO_CLIENT_CODE */
 
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 552d9eb..54b3642 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -159,7 +159,6 @@ typedef struct RelationData
 	Oid		   *rd_opcintype;	/* OIDs of opclass declared input data types */
 	RegProcedure *rd_support;	/* OIDs of support procedures */
 	struct FmgrInfo *rd_supportinfo; /* lookup info for support procedures */
-	int16	   *rd_indoption;	/* per-column AM-specific flags */
 	List	   *rd_indexprs;	/* index expression trees, if any */
 	List	   *rd_indpred;		/* index predicate tree, if any */
 	Oid		   *rd_exclops;		/* OIDs of exclusion operators, if any */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index b4023de..d80d9cd 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -264,6 +264,6 @@ VACUUM delete_test_table;
 INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
 -- Test unsupported btree opclass parameters
 create index on btree_tall_tbl (id int4_ops(foo=1));
-ERROR:  operator class int4_ops has no options
+ERROR:  unrecognized parameter "foo"
 create index on btree_tall_tbl (id default(foo=1));
-ERROR:  operator class int4_ops has no options
+ERROR:  unrecognized parameter "foo"
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1f53a9c..a8329b6 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2096,8 +2096,7 @@ SELECT p1.indexrelid, p1.indrelid
 FROM pg_index as p1
 WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
     array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
-    array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
-    array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
+    array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1;
  indexrelid | indrelid 
 ------------+----------
 (0 rows)
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index cee6a40..7dd42a3 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1341,8 +1341,7 @@ SELECT p1.indexrelid, p1.indrelid
 FROM pg_index as p1
 WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
     array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
-    array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
-    array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
+    array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1;
 
 -- Check that opclasses and collations match the underlying columns.
 -- (As written, this test ignores expression indexes.)
-- 
2.7.4

#20Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Nikita Glukhov (#19)
Re: [PATCH] Opclass parameters

On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:

On 04.09.2019 1:02, Alvaro Herrera wrote:

On 2019-Jun-11, Tomas Vondra wrote:

1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:

I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.

API was simplified in the new version of the patches (see below).

2) The 0001 part does this in index_opclass_options_generic:

get_opclass_name(opclass, InvalidOid, &str);

ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));

But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.

Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.

I decided to add new function generate_opclass_name() like existing
generate_collation_name(), and to reuse static get_opclass_name().

Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.

New version of rebased patches is attached:

1. Opclass parameters infrastructure.

API was completely refactored since the previous version:

- API was generalized for all AMs. Previously, each AM should implement
opclass options parsing/passing in its own way using its own support
�function numbers.
Now each AMs uses 0th support function (discussable). Binary bytea values
of parsed options are passed to support functions using special expression
node initialized in FmgrInfo.fn_expr (see macro PG_GET_OPCLASS_OPTIONS(),
get_fn_opclass_options(), set_fn_opclass_options).

I very much doubt these changes are in the right direction. Firstly, using
0 as procnum is weird - AFAICS you picked 0 because after moving it from
individual AMs to pg_amproc.h it's hard to guarantee the procnum does not
clash with other am procs. But ISTM that's more a hint that the move to
pg_amproc.h itself was a bad idea. I suggest we undo that move, instead of
trying to fix the symptoms. That is, each AM should have a custom procnum.

Also, looking at fn_expr definition in FmgrInfo, I see this

fmNodePtr fn_expr; /* expression parse tree for call, or NULL */

it seems like a rather bad idea to reuse that to pass options when it's
clearly not meant for that purpose.

- Introduced new structure local_relopts (needs a better name, of course)
with a set of functions for opclass/AM options definition. The parsing
was moved into index_opclass_option(). That was necessary for mixing of
�AM- and opclass-specific options. Opclasses now extend the structure with
AM's options adding their own options. See patch #4 for an example.

OK. No opinion on this change yet.

2. New AM method amattoptions().

amattoptions() is used to specify per-column AM-specific options.
The example is signature length for bloom indexes (patch #3).

I'm somewhat confused how am I supposed to use this, considering the patch
set only defines this for the contrib/bloom index AM. So let's say I want
to create a custom BRIN opclass with per-attribute options (like the two
BRIN opclasses I work on in the other thread). Clearly, I can't tweak the
IndexAmRoutine from the extension. ISTM the patch series should modify all
existing index AMs to have a valid amattoptions() implementation, calling
the new amproc if defined.

Or what is the correct way to define custom opclass for existing index AM
(e.g. BRIN) with attribute options?

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#21Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Tomas Vondra (#20)
Re: [PATCH] Opclass parameters

On Wed, Sep 11, 2019 at 12:03:58AM +0200, Tomas Vondra wrote:

On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:

On 04.09.2019 1:02, Alvaro Herrera wrote:

On 2019-Jun-11, Tomas Vondra wrote:

1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:

I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.

API was simplified in the new version of the patches (see below).

2) The 0001 part does this in index_opclass_options_generic:

get_opclass_name(opclass, InvalidOid, &str);

ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));

But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.

Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.

I decided to add new function generate_opclass_name() like existing
generate_collation_name(), and to reuse static get_opclass_name().

Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.

New version of rebased patches is attached:

1. Opclass parameters infrastructure.

API was completely refactored since the previous version:

- API was generalized for all AMs. Previously, each AM should implement
opclass options parsing/passing in its own way using its own support
�function numbers.
Now each AMs uses 0th support function (discussable). Binary bytea values
of parsed options are passed to support functions using special expression
node initialized in FmgrInfo.fn_expr (see macro PG_GET_OPCLASS_OPTIONS(),
get_fn_opclass_options(), set_fn_opclass_options).

I very much doubt these changes are in the right direction. Firstly, using
0 as procnum is weird - AFAICS you picked 0 because after moving it from
individual AMs to pg_amproc.h it's hard to guarantee the procnum does not
clash with other am procs. But ISTM that's more a hint that the move to
pg_amproc.h itself was a bad idea. I suggest we undo that move, instead of
trying to fix the symptoms. That is, each AM should have a custom procnum.

Also, looking at fn_expr definition in FmgrInfo, I see this

fmNodePtr fn_expr; /* expression parse tree for call, or NULL */

it seems like a rather bad idea to reuse that to pass options when it's
clearly not meant for that purpose.

BTW, is there a place where we actually verify the signature of the new am
proc? Because I only see code like this:

+    case OPCLASS_OPTIONS_PROC:
+        ok = true;
+        break;

in all "validate" functions.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#22Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Tomas Vondra (#21)
Re: [PATCH] Opclass parameters

On 11.09.2019 1:14, Tomas Vondra wrote:

BTW, is there a place where we actually verify the signature of the
new am
proc? Because I only see code like this:

+    case OPCLASS_OPTIONS_PROC:
+        ok = true;
+        break;

in all "validate" functions.

See assignProcTypes() at src/backend/commands/opclasscmds.c

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#23Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Tomas Vondra (#20)
Re: [PATCH] Opclass parameters

On 11.09.2019 1:03, Tomas Vondra wrote:

On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:

2. New AM method amattoptions().

  amattoptions() is used to specify per-column AM-specific options.
  The example is signature length for bloom indexes (patch #3).

I'm somewhat confused how am I supposed to use this, considering the
patch
set only defines this for the contrib/bloom index AM. So let's say I want
to create a custom BRIN opclass with per-attribute options (like the two
BRIN opclasses I work on in the other thread). Clearly, I can't tweak the
IndexAmRoutine from the extension. ISTM the patch series should modify
all
existing index AMs to have a valid amattoptions() implementation, calling
the new amproc if defined.

Or what is the correct way to define custom opclass for existing index AM
(e.g. BRIN) with attribute options?

Per-attribute opclass options are implemented independently from per-attribute
AM options. amattoptions() is optional and needs to be defined only if AM has
per-attribute options. amproc #0 is called regardless of whether amattoptions
is defined or not. That was the main reason why uniform procnum 0 was picked.

You should simply define function like that and use it as amproc #0:

Datum
brin_bloom_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
BloomOptions *blopts = NULL;

extend_local_reloptions(relopts, blopts, sizeof(*blopts));

add_local_real_reloption(relopts, "n_distinct_per_range", "desc",
-0.1, -1.0, INT_MAX, &blopts->nDistinctPerRange);

add_local_real_reloption(relopts, "false_positive_rate", "desc",
0.01, 0.001, 1.0, &blopts->falsePositiveRate);

PG_RETURN_VOID();
}

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#24Tomas Vondra
tomas.vondra@2ndquadrant.com
In reply to: Nikita Glukhov (#23)
Re: [PATCH] Opclass parameters

On Wed, Sep 11, 2019 at 01:44:28AM +0300, Nikita Glukhov wrote:

On 11.09.2019 1:03, Tomas Vondra wrote:

On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:

2. New AM method amattoptions().

� amattoptions() is used to specify per-column AM-specific options.
� The example is signature length for bloom indexes (patch #3).

I'm somewhat confused how am I supposed to use this, considering the
patch
set only defines this for the contrib/bloom index AM. So let's say I want
to create a custom BRIN opclass with per-attribute options (like the two
BRIN opclasses I work on in the other thread). Clearly, I can't tweak the
IndexAmRoutine from the extension. ISTM the patch series should
modify all
existing index AMs to have a valid amattoptions() implementation, calling
the new amproc if defined.

Or what is the correct way to define custom opclass for existing index AM
(e.g. BRIN) with attribute options?

Per-attribute opclass options are implemented independently from per-attribute
AM options. amattoptions() is optional and needs to be defined only if AM has
per-attribute options.

OK, thanks for the explanation - so the per-attribute opclass options will
work even when the AM does not have amattoptions() defined. Is there any
practical reason why not to just define everything as opclass options and
get rid of the amattoptions() entirely?

amproc #0 is called regardless of whether amattoptions
is defined or not. That was the main reason why uniform procnum 0 was
picked.

I still think using procnum 0 and passing the data through fn_expr are not
the right solution. Firstly, traditionally the amprocs are either required
or optional, with required procs having low procnums and optional starting
at 11 or so. The 0 breaks this, because it's optional but it contradicts
the procnum rule. Also, what happens if we need to add another optional
amproc defined for all AMs? Surely we won't use -1.

IMHO we should keep AM-specific procnum and pass it somehow to the AM
machinery.

FWIW there seems to be a bug in identify_opfamily_groups() which does
this:

/* Ignore strategy numbers outside supported range */
if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;

but then identify_opfamily_groups() computes allfuncs without any such
restriction, i.e. it includes procnum 0. And then it fails on this check

if (thisgroup->functionset != allfuncs) {...}

None of the built-in brin opclasses defines the new amproc, so the code
does not hit this issue. I only noticed this with the opclasses added in
the other thread.

As for the fn_expr, I still think this seems like a misuse of a field
which was intended for something else. I wonder if it might be breaking
some exising code - either in code or in some extension. It seems quite
possible.

It just seems like we're inventing a new way to novel way to pass data
into a function while we already have parameters for that purpose. Adding
parameters may require care so as not to break existing opclasses, but it
seems like the right approach.

You should simply define function like that and use it as amproc #0:

Datum
brin_bloom_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
BloomOptions *blopts = NULL;

extend_local_reloptions(relopts, blopts, sizeof(*blopts));

add_local_real_reloption(relopts, "n_distinct_per_range", "desc",
-0.1, -1.0, INT_MAX, &blopts->nDistinctPerRange);

add_local_real_reloption(relopts, "false_positive_rate", "desc",
0.01, 0.001, 1.0, &blopts->falsePositiveRate);

PG_RETURN_VOID();
}

OK, this did the trick. Thanks. I don't have a clear opinion on the API,
but it certainly looks like an improvement.

regards

--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

#25Michael Paquier
michael@paquier.xyz
In reply to: Tomas Vondra (#24)
Re: [PATCH] Opclass parameters

On Thu, Sep 12, 2019 at 02:16:34AM +0200, Tomas Vondra wrote:

I still think using procnum 0 and passing the data through fn_expr are not
the right solution. Firstly, traditionally the amprocs are either required
or optional, with required procs having low procnums and optional starting
at 11 or so. The 0 breaks this, because it's optional but it contradicts
the procnum rule. Also, what happens if we need to add another optional
amproc defined for all AMs? Surely we won't use -1.

IMHO we should keep AM-specific procnum and pass it somehow to the AM
machinery.

The latest review has not been addressed, and this was 7 weeks ago.
So I am marking the patch as returned with feedback.
--
Michael

#26Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Tomas Vondra (#24)
10 attachment(s)
Re: [PATCH] Opclass parameters

Attached new version of the patches.

On 12.09.2019 3:16, Tomas Vondra wrote:

On Wed, Sep 11, 2019 at 01:44:28AM +0300, Nikita Glukhov wrote:

On 11.09.2019 1:03, Tomas Vondra wrote:

On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:

2. New AM method amattoptions().

  amattoptions() is used to specify per-column AM-specific options.
  The example is signature length for bloom indexes (patch #3).

I'm somewhat confused how am I supposed to use this, considering the
patch
set only defines this for the contrib/bloom index AM. So let's say I
want
to create a custom BRIN opclass with per-attribute options (like the
two
BRIN opclasses I work on in the other thread). Clearly, I can't
tweak the
IndexAmRoutine from the extension. ISTM the patch series should
modify all
existing index AMs to have a valid amattoptions() implementation,
calling
the new amproc if defined.

Or what is the correct way to define custom opclass for existing
index AM
(e.g. BRIN) with attribute options?

Per-attribute opclass options are implemented independently from
per-attribute
AM options.  amattoptions() is optional and needs to be defined only
if AM has
per-attribute options.

OK, thanks for the explanation - so the per-attribute opclass options
will
work even when the AM does not have amattoptions() defined. Is there any
practical reason why not to just define everything as opclass options and
get rid of the amattoptions() entirely?

The reason is that it would be no need to duplicate AM-specific
per-attribute
options in each opclass. For example, contrib/bloom AM has per-column
options
(signature length), but its opclasses have no options now.

amproc #0 is called regardless of whether amattoptions
is defined or not.  That was the main reason why uniform procnum 0 was
picked.

I still think using procnum 0 and passing the data through fn_expr are
not
the right solution. Firstly, traditionally the amprocs are either
required
or optional, with required procs having low procnums and optional
starting
at 11 or so. The 0 breaks this, because it's optional but it contradicts
the procnum rule. Also, what happens if we need to add another optional
amproc defined for all AMs? Surely we won't use -1.

IMHO we should keep AM-specific procnum and pass it somehow to the AM
machinery.

As you suggested, I introduced AM-specific procnum
IndexAmRoutine.attoptsprocnum, that has different values for each AM.
This was not a problem, because there are only a few AMs now.

FWIW there seems to be a bug in identify_opfamily_groups() which does
this:
   /* Ignore strategy numbers outside supported range */
   if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
       thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;

but then identify_opfamily_groups() computes allfuncs without any such
restriction, i.e. it includes procnum 0. And then it fails on this check

   if (thisgroup->functionset != allfuncs) {...}

None of the built-in brin opclasses defines the new amproc, so the code
does not hit this issue. I only noticed this with the opclasses added in
the other thread.

This really seems to be bug in previous patches, but now procnum can't be 0.

As for the fn_expr, I still think this seems like a misuse of a field
which was intended for something else. I wonder if it might be breaking
some exising code - either in code or in some extension. It seems quite
possible.

It just seems like we're inventing a new way to novel way to pass data
into a function while we already have parameters for that purpose. Adding
parameters may require care so as not to break existing opclasses, but it
seems like the right approach.

fn_expr is used to pass metainformation about user function calls. It is
not
used In AM method calls, so it seems safe to reuse it.  Opclass parameters
can be considered here as some kind metainfo about AM calls.

Of course, we could add special parameter to each support function of
each AM.
But it looks too complicated, and every external AM needs to do that to
enable
opclass parameters. So I think it is desirable to implement a more general
solution.

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0001-Introduce-opclass-parameters-20200229.patch.gzapplication/gzip; name=0001-Introduce-opclass-parameters-20200229.patch.gzDownload
0002-Introduce-amattoptions-20200229.patch.gzapplication/gzip; name=0002-Introduce-amattoptions-20200229.patch.gzDownload
0003-Use-amattoptions-in-contrib_bloom-20200229.patch.gzapplication/gzip; name=0003-Use-amattoptions-in-contrib_bloom-20200229.patch.gzDownload
0004-Use-opclass-parameters-in-GiST-tsvector_ops-20200229.patch.gzapplication/gzip; name=0004-Use-opclass-parameters-in-GiST-tsvector_ops-20200229.patch.gzDownload
0005-Use-opclass-parameters-in-contrib_intarray-20200229.patch.gzapplication/gzip; name=0005-Use-opclass-parameters-in-contrib_intarray-20200229.patch.gzDownload
0006-Use-opclass-parameters-in-contrib_ltree-20200229.patch.gzapplication/gzip; name=0006-Use-opclass-parameters-in-contrib_ltree-20200229.patch.gzDownload
0007-Use-opclass-parameters-in-contrib_pg_trgm-20200229.patch.gzapplication/gzip; name=0007-Use-opclass-parameters-in-contrib_pg_trgm-20200229.patch.gzDownload
0008-Use-opclass-parameters-in-contrib_hstore-20200229.patch.gzapplication/gzip; name=0008-Use-opclass-parameters-in-contrib_hstore-20200229.patch.gzDownload
���Y^0008-Use-opclass-parameters-in-contrib_hstore-20200229.patch�;kw�����WTr�N�%�$0��qC�x�������� [ "	'�I��oU?�B��ggw��A������n�{��C�6&�c�n��]m���11�zu���ZchV�j��	W�z����k�?��n�#���q����swu?��xQ��+��f�������U��i�����g�����"S����|m���U]/�V�;{����[���7�
�w�
l��#�
XZ�5�C��Y��[��3�����b�\.e�>o����������|��k���\6*u���_\6LP�M����	��N	�L� ��bJ��B��)g>E{������+���Y��h=b;���ZL��F�D��:�"P����m��
�����*�{c]��j���X;�	��S'k���z��bl�fu�Ri6���W���X�(�'0�ye�������c�����>��_��N����aD���oE��b�Y���Z�5D��8`�95���Q���M�#��n.Ik��U����L$�m�	�*}l�u{�T�?��+��y�$~]C74��7o����NN��_T��<_�:�3�`�;��o���1�%�k�[���!Dc����e�}���~�G3�����>!v��F�����WH���z�z����$�
|��rB����M��o;g}�1��-�39R�-��v�������V^]u��^��>tZW�W���UT/[����y�������z{����M���}	�������^�oZ��
�o]]\~
2�e����9k����tc�U8\)��L�`�Q�`�_���O����R��c��0�t��'�/�a�����������o���C�������_�-�d������A:�)v�7��~���rW(���������X+7<`:Bt�mU���xU$O[��`����<F��:������]o��]���$��h��y�2d�� %�-U�����T�c�8���]��� ���fb��iU���Y�����P��^R<�;����}�����S6v�?1$,V�L/I����������6�n\�����?�tR�z��P��P������(��v9PQ��
zY�,��k/G�b�||�`f[ck����bDZ�f���e�����f!�B!p�8��x|XX�
�������1�9]?����R(�:SD�U��Yph������0�5�e���8�lw.:}����0M89����.�lX��7<B�z__�xA�3X

R���M(���b��,$
��'��0���k�o�^�J��j����{���������XQ�T�:J�D�� "�eiX�����}���QR�{�������.��k����2������G�1Gr��j�Q	C��Q��~���'����?��M�C�|k��&f�h���1��1�:K�6�`�,��-���P�����}n�|/x�2��6��MsP���"$ZR��@e�������y��c���kMP�
����T#g;��!��d�����O�o��(l6����Xz8n��';b9O`A������H"G��������K%O���\X��B�VBi23��`��$ybM��H"NXN�Y�����+-(�)�?F�����`:|�O��p��|5���s_��N�����K�������n�����������
��������.������uC�D3M����Z�g�}�W/�:����xcb��tx-�G�n�����8#=dXj#��I���?P��P(j���0��a�UpzU3���
u*��Y�Z7�=S�6^m����Rt�\�e� ��1��+���b��=,]��C��5�2��&��5���(iS<#5���P�J_c�Kd�?���$^�h������4[b�~Mw�� ����O��N��8<.��yH{��g���mn�G�GE�1�a�y�
l	�uq^O�`��6� �h���&H*����;�h(RXEH'�s�����%"
����$[�%U����Ve���%V`n��m��3���������6��$��`D�I�����y�����?�E������'�25�1'���{%�U�C�]���7���>�5#��b�P��JT����e���o�����2U+"��������������qf���|����4�R���KJ��%�X��pG���|}d:NL,t-

���K�C���GP�)e�9�C�q-I%2��65����B[77�����F�}������"y+6�Cop���������#�}��dK4��T�!���$�W�@p\U�A����������7����?)�?
��%�
�O���r-���w:����r���H�OL����G�J���8;O3@��
	�<�ba~��"��f�2�h44��L�O���E3�"�1�#��dH�����O�fK���R���.+2�+��o�����LJ����������3��9�?�l�7��q�����ga�y3�c��~^d����T:�&R8�nQW�39���tN�� ���>������f�*"�0���P���~R�VV���>=h�}��QU6��5�j��!�_�Q�(k����E�/�j}�C�i\�M��M��5v�=}�a����X����V�E>�����cD!�U�5����F�2�1[W�d�C'|�GJ�������E�!��D���x)Q��F�l��	��J,�DD�*Z?��P�k��$C{�y��qp ��������=��Qwk�+�!�cH�\HB4�%�X,})�xE+�B�z�r��x������@�P��(�:����lmE"���!!�� y���a��5�K	�Y[����x[���q�v"�f����
�R��r1M��w���<�+E�&�'������3tr�����/X	��3��N(}<Fc�	ns��(��O6Ai���0��%���Y�hk�)0�(�~�P��'M�����5��
@�Su�47�^x"bZ>]�)P���t6����F�@�%+�X5e�[�L:5e*L�l9�<����Rc��	�s4���y���U�Ene�&��8^ieW5���j�F2��p���y��H�� [��'�@'�|�Bn�Y���J��Zw��E���6��/ ��m��zZ��\��M�#Wd��So��P�%E�����Pe���Uh��M��)1�i
2�]�m� ���x�c�F5��������k�FR�����v��2[nUx�3}��
�tX�W�1�2�x��0;�H!]�_w������Y!�;�t�H����"O/5Hp�5�����4�o���������g��)��,hIp����`��a����rm_��u]�n�C����\��N&�vV�������M&�t����
�d1�]_2���)�D�&[��"���(�����N._W�5Z�U�����,�yz���{@Y��C�$E�[� �����;=�@�%#?��W��	�����;Y�>oP�X�)�����D���C�Y��UPk����%�
G:6G����5��J���g����KLjB��Rth���#�20�=o��������9���S���@F8K��6�E�L���	i��Mf�����b�)��Y����?G)[g���R�g)��R��p^�������'���&3~v�kb�>wZ�g�5K%Q�E�k-|����&�ahO�%�
�(�@�������sr'#/��6��v�������(��TS����;n#,bZ�rf%"s���$����|�nG�>���2��Y�:�Jb^����F�_H�����4��"fU�Y��GC����	Z���ju����v`�����@o:�"K��J��\�	�c�c$������{�K��;X���X�	K�>�]��Rfo8�U�(�������.}��Y��A<?���l�K��1NN�Rw�=���=A?��Dj-�I��M�1���ATbcGN�G�TU���<:�(�5��%�����3"�����)����"�7��mR��N�\m7�G��%�<��<�u��o�!{[�$�j�^�S�X���s�<j4�G�OH����#R�4������3e�L^t����A��v��������?�vJ�����C����S���	�������4H�O�s���'a�-�u7Y;�ss��5�'���3q|q�]�����-`Y�
R�l����p��tPc������j�^b��ee�m�����_���k�1�oX���A��v@0��j���_�P=R��l~�������dW�:*!����C���=Ef���R��p�\�'�x-��=&�:{�Q}�t������
�c�t}�A�l�l`$�L;V`�]Oa����o�\���4g���3��L:���#����;�(�������74�y�*��{��^��8���ZT�k{j���<�uS������R��~������(��7F���� ��&f�d�J��#�X��0�����R/_n���J������7b�_29V�6��#�%�:j�������]��T�62k�F�r�V'�Q���������nV���T#��q@v'.^t����3����By����;���Bo��������5���������nQ�U�X������c,tm�rO3w���e����-��r��]��K?1�]�[�#g����mR9C��t9`u����i��j�&�,�.��Y���D�(��/Q�����hA���	���=��t/����S��x�=F`��r���@@C��Pfb��d#�:U(Q�9����O"�����U�OXi3�k�K��sW��E3����&����m���o�1z��c`�����M���~J@�;@��4*�b���X���@
0009-Use-opclass-parameters-in-GIN-tsvector_ops-20200229.patch.gzapplication/gzip; name=0009-Use-opclass-parameters-in-GIN-tsvector_ops-20200229.patch.gzDownload
0010-Remove-pg_index.indoption-20200229.patch.gzapplication/gzip; name=0010-Remove-pg_index.indoption-20200229.patch.gzDownload
#27Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Nikita Glukhov (#26)
Re: [PATCH] Opclass parameters

Hi!

I took a look on this patchset. There is a first set of questions.

* Patchset badly needs comments. I've to literally reverse engineer
to get what's going on. But I still don't understand many things.

* I'm curious about what local_relopts.base field means.

void
extend_local_reloptions(local_relopts *opts, void *base, Size base_size)
{
Assert(opts->base_size < base_size);
opts->base = base;
opts->base_size = base_size;
}

/*
* add_local_reloption
* Add an already-created custom reloption to the local list.
*/
static void
add_local_reloption(local_relopts *relopts, relopt_gen *newoption, void *pval)
{
local_relopt *opt = palloc(sizeof(*opt));

opt->option = newoption;
opt->offset = (char *) pval - (char *) relopts->base;

relopts->options = lappend(relopts->options, opt);
}

Datum
ghstore_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
GistHstoreOptions *options = NULL;

extend_local_reloptions(relopts, options, sizeof(*options));
add_local_int_reloption(relopts, "siglen",
"signature length in bytes",
SIGLEN_DEFAULT, 1, SIGLEN_MAX,
&options->siglen);

PG_RETURN_VOID();
}

It's not commented, but I guess it's used to calculate offsets from
pointers passed to add_local_*_reloption(). Is it better to just pass
offsets to add_local_*_reloption()?

* It's generally unclear how does amattoptions and opclass options
interact. As I get we now don't have an example where both
amattoptions and opclass options involved. What is general benefit
from putting both two kind of options into single bytea? Can opclass
options method do something useful with amattoptions? For instance,
some amattoptions can be calculated from opclass options? That would
be some point for putting these options together, but it doesn't look
like opclass options method can do this?

* It current opclass code safe for introduction new atattoptions. For
instace, would ghstore_*() work the same way expecting
GistHstoreOptions struct to be passed as opclass options if gist would
introduce own attoptions? I guess not. If I'm wrong, please clarify
this. And patchset needs comment one could get this without guessing.

------
Alexander Korotkov

Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#28Nikita Glukhov
n.gluhov@postgrespro.ru
In reply to: Alexander Korotkov (#27)
9 attachment(s)
Re: [PATCH] Opclass parameters

Attached new version of reordered patches.

Questionable patches for AM-specific per-attribute options were moved to
the end, so they can be skipped now.

On 16.03.2020 18:22, Alexander Korotkov wrote:

Hi!

I took a look on this patchset. There is a first set of questions.

* Patchset badly needs comments. I've to literally reverse engineer
to get what's going on. But I still don't understand many things.

* I'm curious about what local_relopts.base field means.

void
extend_local_reloptions(local_relopts *opts, void *base, Size base_size)
{
Assert(opts->base_size < base_size);
opts->base = base;
opts->base_size = base_size;
}

/*
* add_local_reloption
* Add an already-created custom reloption to the local list.
*/
static void
add_local_reloption(local_relopts *relopts, relopt_gen *newoption, void *pval)
{
local_relopt *opt = palloc(sizeof(*opt));

opt->option = newoption;
opt->offset = (char *) pval - (char *) relopts->base;

relopts->options = lappend(relopts->options, opt);
}

Datum
ghstore_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
GistHstoreOptions *options = NULL;

extend_local_reloptions(relopts, options, sizeof(*options));
add_local_int_reloption(relopts, "siglen",
"signature length in bytes",
SIGLEN_DEFAULT, 1, SIGLEN_MAX,
&options->siglen);

PG_RETURN_VOID();
}

It's not commented, but I guess it's used to calculate offsets from
pointers passed to add_local_*_reloption(). Is it better to just pass
offsets to add_local_*_reloption()?

Yes, 'base' field was used to calculate offsets. Now I started to pass offsets
instead of pointers to the fields of template structure (that gave us
additional type checking). Some comments were added.

* It's generally unclear how does amattoptions and opclass options
interact. As I get we now don't have an example where both
amattoptions and opclass options involved. What is general benefit
from putting both two kind of options into single bytea? Can opclass
options method do something useful with amattoptions? For instance,
some amattoptions can be calculated from opclass options? That would
be some point for putting these options together, but it doesn't look
like opclass options method can do this?

There are no examples for AM and opclass options interaction now. But AM and
opclass can register custom callbacks that will be called after parsing in
their registration order. In these callbacks it is possible to post-process
option values, check presence or absence of some options.

The main benefit of putting both option into single bytea is that it does not
require major modifications of reloption processing code. And it also does
not require to split reloption list obtained from SQL into two separate lists
for AM and opclass options.

* It current opclass code safe for introduction new atattoptions.
For instace, would ghstore_*() work the same way expecting
GistHstoreOptions struct to be passed as opclass options if gist would
introduce own attoptions? I guess not. If I'm wrong, please clarify
this. And patchset needs comment one could get this without guessing.

Yes, the code will be broken after introduction of GiST per-attribute options.
GistHstoreOptions should include GistAttOptions which simply did not exist in
the previous version of the patches. I added empty XxxAttOptions for all AMs
in patch #7, and GistAttOptions and GinAttOptions now are included into
corresponding structures for opclass options.

--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0002-Use-opclass-parameters-in-GiST-tsvector_ops-20200318.patch.gzapplication/gzip; name=0002-Use-opclass-parameters-in-GiST-tsvector_ops-20200318.patch.gzDownload
�Djq^0002-Use-opclass-parameters-in-GiST-tsvector_ops-20200318.patch�\}[����;�j�s[�8�!@y�eiJy,I������q0�8k;��w�3#Y�_B�=��[H��h43�����]���N���
�v6����l�����M����;�����L6�������57X����g�F�Y~l�����lv��o���lgZ�������F�g�_�{��v������bM�j���`������N�U�F�������a����Y�Uo6.���3�xv����<�A��);t������YX.W��2�N=����W��}r;p�j������o�U�iG�s����<r��n��z^�a���k��om4Y%�SM�~����c^w�������rh��>������mVYH�"��G<��G�u~?
�q=��?�����-�~
x�y�;�VCv�Y���&��C�\��K4e{�Vy��44*&X��fc�q��j��cw2a���1���aF�6�����������Z�i�Z�M�5�n��v_���X6��?�j����6XE��G��q�`�f��N#��y�`��3L��6gad�ks�,���V��a�`b���z*�v�4+x|��U��s�������=vt����	8H�p{���)���������I"�����?:9d�G�l���{��i��������+��Dc�����n����T�3B���S��l~;�Af������%6��6D���#�"�u��.7��"� �Cf�8f�)�����t�a� ��������T��g����S6��9�|�����`�����b��q;����g�3\���D��I8NC�>�(���;���4S;���et��|���0B�A`��c?��6�cSM{��C&fJ�"|�|���2a���X�
��x��"�wnXJ��h* f
��sa�`M��l���ukG�+�;��s'���w&�c��c���2(!�r�+��\���vw�<�|��5����O��*�0|�9��6��n~�`��=�!W��P�Qu�^8q���*-�LT#OK,�.�Q�����+wzI�]��/�=pV�2&~�	5���������
~�'�����g�v{06��dY��MxO������S��� S<��u6e���C�����-^eC�)��F�<AM���U|F4X�����?P1!�����
�^{��!@N��5t���z��|�!$���`��=�,�\�>�)�� D[w|`w4!�3w���1�h9���Vc��k�nc��"I���\2XN�������`����n���8���v���������Es%�@�#�3�=wT�k>u!LeZf~!V����f	n`C���h�-���V�j�hI1s`�qC\��1���d���������h�T*���^��d�{��?G�	Z,������'�)WK����/^��:h!�z(�z�h7��c�~���T2pPlF	V9-7���o�OXq��	L�9�Y�_� M��@��������?����*q���*��\�)�L\�H_ -a���!W@��A�����!�K�R�Y�J��!7�&aJ�S����!c������n\��&���>B�ma�\�D�o��Q���P&8����OB%��j]�g��a�OO�P7���/w����&�n��);�V�7L�z1���f.�arB�:����{�}o�`&�f0C�d�&�0��Bs=m;f�iVG)n���DF&��#�X����n�
;M�������4�����,-��]�����W^���J�3����&�f�������
�����|�C(4�O��Zf�'�mj1�,gH����k�[,Ya���$�)?a�XE�u�����O���A�3�����L�V��!��|�-���fuO,�O���A$#������r�e5;�{��~��?>���%#3�9_uo���`<���
hYZ������������s|�/p�t`��v�|p8�2��0A��
����4������	.����k)�&{�T���H��6aC��*�WBP��S��N��U�`��I��D����'�hH�4�9Um
�%3���k����A�(w~E���E#����%E ��;����I�J��*t6���Xev'?wj��}89�����"N6mk0W3��T�a��f�.�@0�O,�����=Q�9������/�uDt�,6Y�HV��O$���>��!$�J)�Q�Ubw$�(VM����Z���yU�>K�9f��dN��IH�JR�+}C���h����������O�b
  {.m#�n�����o���0�����g����6����i����\��o8�`��!����a����LG��}�T�V.f��
���YG�mK;��o�<��@31��
+�B$�S�����F �@@n��Oh���ts!��{�sD��+=��rE-�!|����DT�W��b����*������9J�+�!��0�i��7��-��<n+�;�wI&A�N�����`n���}��������X�R���:�����XrS%2
�y����G�x7D������^���,�ghCcC����g�P�����9s��'���g���>�E�B��3b��K���8���V�<nO0a���V�`���3(R����/���T�,r����O�'�7�����>�5+'�-O;���~0����%����-��/u[�/�'+�9wN&�:1�l��.u�us�����D��$�}��
�������8��jU���u���F��J��G]�l��4�!L�b?��W������T����9A���������������<C>Q�T�r]��-q g3E��.%��Z,k2=Rh����X�Q%$-���D����(�b?SJ�G�d����j��[
�Z�����[��F���/��W$	{�����M
���u-��D+��<���|($& ���q�7�8Rv�A�$N��	2��^P�<�����x!@���7�p~�b����BQ�(HM�
h�(hp����P�x9���ag�0��KK��mT�@��������!������^d7�z�r!
K�(���}�-��[PG�s�YO����1�d!8I8E
4�ox*t:��=%�	�z��9�
���6[�oS����{�,g����q��������"�`y�[cw���p����"�A58f6�u<q��8!�����_]�s����VD8� G���H�������r��L���V
:Y�*�����Pt�E���7�\���������O�;u2�2t�f���:��<�Z��u���,�_NO��A��z����~��;���E���������S�XI�����G�`x��|��) t�Sy_3e�E:��HG����H/�(����)����O�s��xye���s1���<��	rHh�d|j�,H��,���!^h1�^X�4ZY�
J$f��@���,y���8�n��6���'NhA	���v��*i�8:����XX���,hHA��T_�V%Mj�e�0��M��I�Mpa�NH;�(En�I#����a��A��j�*�B�&�>��R	���3����z*3d�����~p�UP�����(������w��D�
����G��z#����n��2[,�~�=�{��E�9X���G��w������:��A�qQ������
	���3�	H-����1����A�v�(��A=�K`;e�e�.vx��Eb����{!UR����P��[��%i,�q����X��P2XA��Y�N���;�\����H&p�pQI��V�����(%��G�]Z?|�F������&��1�}�����[�(�����/��^U�Z��&�|�=��qm�S��`q�-ieS/rNt�t
/N��P#5�6&i(���=��Ehb}�jn��l���������\Y�xs�B�x��9������a�����m5���+~;OKmt����\FR�J�@I�x�G�#�=1jv*����	���@�N��AF�o6PE��xC�.'��g���k��N_G�so���e�T>��o�k0�������F�dL�	v��d��,Y/���S��T�H�}��J_��Q�6HS�>��S����i$��r��T#sQ���F�|Y^�����R���D��(���2�(���&�Mw�YF7+S�bdk��F��z���n3��E��W�����	G�����a:��N���<E��x���D��=��*�eR7�En;-���4�-dy�6Vd�-��2$���B)�<�\�������C+9Bh���&��q�������jN?.V��"�H����)U+��Q<QQs��!�������?�Ko]PVXS������Ix?u7
-�vM����'�sb$���R"}K\m�z�lQ;2�.��2�I���6Z�+��A�'9^k����z'vq��U�/1�-�C�a����r?�P���������O\�sl������M�lr����S���PV���X�M�����U"z
3���E������(��
�Je�����>R�FcQu�<6)d�3����~�������x�t>��i��zi@f�b�\+x/S��\���[n5�[����pd����m1��{�Q9���,�g�L����H��qdf��m�)�K�������`�E([�P��
t9���|�W�f_��M8������1��i�?;�Ec*��"\����d���>���J�y�3F�
=qn���~��X?��?+�h����h��L�M
z2��II�����c�qk��T(j[��z-����M��S=*�gP��B���m����
�)}�G����0�=�TM�)CJ_a-�6�����|z�a��}Q���#�=Qc��G8A���b����������������&��J��x__H�?l>����a����������z���'�>2n��Y����4��XR]���%�,,���
9�\+�:����#{����
��0�	��������\��>�c��	m�%L��M����2�l;M��Pw�k��OY��^)��`�[�\��j5q�E�������Z*�Q�$�#����e�o\��t�K�����b��#�gK�d��J���������m��N����������	���0x�	�&��%^*�
&����	!�$^��>����~�>���������U�P���6n�;�m�qY��,i��E�����Z��e�R�D[q���H#�����x��"6�q9D���ILp9�Bp9@���)�$'��U32��D�O���f>^e�&��c{�+{Q��^�����v���G��m��V�=���\��:�|�G��������*����I|HX����@bfK����FC|�c���:��$��Vc���QA�L����(���;���6
x���{^��]�����V��X�����g�T�G���e�.��>���,re*�S��\����3�?���P���"G�����G.�b�����=����t�������T,SA�"'b�-q�t0[�A�������4��I���rwS�
L=>�,����U�f�,�N��R���"�S����X�������{��]���%��	*� ��,.c�oc_�(��H��W
���P���H��+:����m�������G��h�������,�yy�P��'��j}w������a�E��r��b��q�_l�V����}�P~?F�
�����N��e��I3-?FG;�n4��iD�UY�`[V���eoT�8$^
#��R_��2	���F�X3������������/)����wO��&}L1�=���h9�����u��\o�j�t��V�z��|A��t���i��
��E�m����o�y]1^�ox������� hA�u��Y^[�#��YL��J���R@!���[j�PO2��U��i��z���;�b�����1gD�4�J�4�H���2�$H�"Q����m|r�Fi%xhwv������*d�WaO���wQ@7�*��6zb�vD��i6AP7�G5.�sx��6.�sEP�p���<�K�o�`����:,������n��[��B�Q�70���	v�@,����]8x]O�o���0��q���W��Tf+g�]	�
�����I+jE}t��h������\�X���	��Q�Q���B������.�HC���%����w-�Uk~�n�n������6��<Wa���Cq�����CC�Mb��!x�����i��i��<(� |�Mn(dF~'+d����o��^b?.;���Z�`Nz������g�UW-7rL��o(�����x
8F@|����r���a�W����Y�$��AN&m�m�����[���x4	�s-_J!���v�'��&������j�`vk(������5��1�5������gi��Y`����6T����j��4���j��Q�]��L|��F�N8�����G��+�i��������f��90"UR�D*���]�W�YK��
/��x4(��9��l	���@����F��b7��V.�g����p{�#�A��A�&|f��}���Zs��l�����-n��}*8����!�����m�v���{{��$���w���n$��Z������j���o�|/z�����X�.$z���GH��� M>~b��h����K��(\cC�kpx�������h���"��(����j��`���#���eoO�1�jgAR��E[�xk�s
��9�l��������L��anLO����Y-��&��Tn�H��J"����(��b����D{�AL��j�[��i�4���TS���7&�������"e$PC^\P��H	���b����	���YdF��P����*R�����Mc�&8����i��#�)��"�)��"�S$�v�;�01��,cj��q�T�D��>����Em�����U]�z����rs����
2�iy��[[�^m�X��ou�Bi�,������gk-��Y����5��B��U�F73�-���I���@���>m�����m�<�:E��~1�����}����u�?]'���Pm��R���rT�-�t
0003-Use-opclass-parameters-in-contrib_intarray-20200318.patch.gzapplication/gzip; name=0003-Use-opclass-parameters-in-contrib_intarray-20200318.patch.gzDownload
0004-Use-opclass-parameters-in-contrib_ltree-20200318.patch.gzapplication/gzip; name=0004-Use-opclass-parameters-in-contrib_ltree-20200318.patch.gzDownload
0005-Use-opclass-parameters-in-contrib_pg_trgm-20200318.patch.gzapplication/gzip; name=0005-Use-opclass-parameters-in-contrib_pg_trgm-20200318.patch.gzDownload
0001-Introduce-opclass-parameters-20200318.patch.gzapplication/gzip; name=0001-Introduce-opclass-parameters-20200318.patch.gzDownload
0006-Use-opclass-parameters-in-contrib_hstore-20200318.patch.gzapplication/gzip; name=0006-Use-opclass-parameters-in-contrib_hstore-20200318.patch.gzDownload
0007-Introduce-amattoptions-20200318.patch.gzapplication/gzip; name=0007-Introduce-amattoptions-20200318.patch.gzDownload
0008-Use-amattoptions-in-contrib_bloom-20200318.patch.gzapplication/gzip; name=0008-Use-amattoptions-in-contrib_bloom-20200318.patch.gzDownload
0009-Remove-pg_index.indoption-20200318.patch.gzapplication/gzip; name=0009-Remove-pg_index.indoption-20200318.patch.gzDownload
#29Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Nikita Glukhov (#28)
1 attachment(s)
Re: [PATCH] Opclass parameters

On Wed, Mar 18, 2020 at 3:28 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:

Attached new version of reordered patches.

Thank you, Nikita.

I've merged 0001-0006 into single patch. I think this part is
committable to pg13 and I don't see much point in splitting it into
multiple patches.

I've done a lot of adjustments to this patch. Major things are:
* Now contribs can correctly work when extension is not yet upgraded
to the new version. So, if no options is available, default values
are used. This is important for pg_upgrade.
* Regression tests are provided for each modified opclass.
* Improvements to comment, documentation, coding style.

I'm going to push this if no objections.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

0001-Implement-operator-class-parameters-20200328.patchapplication/octet-stream; name=0001-Implement-operator-class-parameters-20200328.patchDownload
From 48f52ab7772b8a466defbe8e9000df692eae4345 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Fri, 27 Mar 2020 18:33:23 +0300
Subject: [PATCH] Implement operator class parameters

PostgreSQL provides set of template index access methods, where opclasses have
much freedom semantics of indexing.  These index AMs are GiST, GIN, SP-GiST and
BRIN.  There opclasses define representation of keys, operations on them and
supported search strategies.  So, it's natural that opclasses may be faced some
tradeoffs, which require user-side decision.  This commit implements opclass
parameters allowing users to set some values, which tell opclass how to index
the particular dataset.

This commit doesn't introduce new storage in system catalog.  Instead it uses
pg_attribute.attoptions, which was used for table column storage options but
unused for index attributes.

In order to evade changing signature of each opclass support function, we
implement unified way to pass options to opclass support functions.  Options
are set to fn_expr as constant bytea expression.  It's possible due to the
fact that opclass support functions are executed outside of expressions, so
fn_expr is unused for them.

This commit comes with some examples of opclass options usage.  We parametrize
signature length in GiST.  That applies to multiple opclasses: tsvector_ops,
gist__intbig_ops, gist_ltree_ops, gist__ltree_ops, gist_trgm_ops,
gist_hstore_ops.  Also we parametrize maximum number of integer ranges for
gist__int_ops.  However, the main future usage of this feature is expected
to be in json, where users would be able to specify which way to index
particular json parts.

Discussion: https://postgr.es/m/d22c3a18-31c7-1879-fc11-4c1ce2f5e5af%40postgrespro.ru
Author: Nikita Glukhov, revised by me
Reviwed-by: Nikolay Shaplov, Robert Haas, Tom Lane, Tomas Vondra, Alvaro Herrera
---
 contrib/bloom/bloom.h                            |    3 +-
 contrib/bloom/blutils.c                          |    1 +
 contrib/bloom/blvalidate.c                       |    5 +
 contrib/hstore/Makefile                          |    1 +
 contrib/hstore/expected/hstore.out               |   45 +
 contrib/hstore/hstore--1.6--1.7.sql              |   12 +
 contrib/hstore/hstore.control                    |    2 +-
 contrib/hstore/hstore_gist.c                     |  192 ++--
 contrib/hstore/sql/hstore.sql                    |   13 +
 contrib/intarray/Makefile                        |    3 +-
 contrib/intarray/_int.h                          |   45 +-
 contrib/intarray/_int_bool.c                     |   34 +-
 contrib/intarray/_int_gist.c                     |   30 +-
 contrib/intarray/_int_tool.c                     |    4 +-
 contrib/intarray/_intbig_gist.c                  |  189 ++--
 contrib/intarray/expected/_int.out               |  160 +++
 contrib/intarray/intarray--1.2--1.3.sql          |   20 +
 contrib/intarray/intarray.control                |    2 +-
 contrib/intarray/sql/_int.sql                    |   36 +
 contrib/ltree/Makefile                           |    2 +-
 contrib/ltree/_ltree_gist.c                      |  182 ++--
 contrib/ltree/expected/ltree.out                 |  154 +++
 contrib/ltree/ltree--1.1--1.2.sql                |   21 +
 contrib/ltree/ltree.control                      |    2 +-
 contrib/ltree/ltree.h                            |   54 +-
 contrib/ltree/ltree_gist.c                       |  257 ++---
 contrib/ltree/sql/ltree.sql                      |   35 +
 contrib/pg_trgm/Makefile                         |    2 +-
 contrib/pg_trgm/expected/pg_trgm.out             | 1157 ++++++++++++++++++++++
 contrib/pg_trgm/pg_trgm--1.4--1.5.sql            |   12 +
 contrib/pg_trgm/pg_trgm.control                  |    2 +-
 contrib/pg_trgm/sql/pg_trgm.sql                  |   14 +
 contrib/pg_trgm/trgm.h                           |   17 +-
 contrib/pg_trgm/trgm_gist.c                      |  227 +++--
 doc/src/sgml/hstore.sgml                         |   17 +
 doc/src/sgml/indices.sgml                        |    2 +-
 doc/src/sgml/intarray.sgml                       |   25 +-
 doc/src/sgml/ltree.sgml                          |   37 +-
 doc/src/sgml/pgtrgm.sgml                         |   17 +
 doc/src/sgml/ref/create_index.sgml               |   16 +-
 doc/src/sgml/textsearch.sgml                     |   13 +-
 src/backend/access/brin/brin.c                   |    1 +
 src/backend/access/brin/brin_validate.c          |    3 +
 src/backend/access/common/reloptions.c           |  488 +++++++--
 src/backend/access/gin/ginutil.c                 |    1 +
 src/backend/access/gin/ginvalidate.c             |    6 +-
 src/backend/access/gist/gist.c                   |    1 +
 src/backend/access/gist/gistvalidate.c           |    6 +-
 src/backend/access/hash/hash.c                   |    1 +
 src/backend/access/hash/hashvalidate.c           |    4 +
 src/backend/access/index/amvalidate.c            |   11 +
 src/backend/access/index/indexam.c               |   77 +-
 src/backend/access/nbtree/nbtree.c               |    1 +
 src/backend/access/nbtree/nbtvalidate.c          |    3 +
 src/backend/access/spgist/spgvalidate.c          |    5 +
 src/backend/catalog/heap.c                       |    8 +-
 src/backend/catalog/index.c                      |   22 +-
 src/backend/catalog/toasting.c                   |    1 +
 src/backend/commands/indexcmds.c                 |   65 +-
 src/backend/commands/opclasscmds.c               |   55 +-
 src/backend/commands/tablecmds.c                 |    2 +-
 src/backend/nodes/copyfuncs.c                    |    1 +
 src/backend/nodes/equalfuncs.c                   |    1 +
 src/backend/nodes/makefuncs.c                    |    3 +
 src/backend/nodes/outfuncs.c                     |    1 +
 src/backend/optimizer/util/plancat.c             |    3 +
 src/backend/parser/gram.y                        |   60 +-
 src/backend/parser/parse_utilcmd.c               |    8 +
 src/backend/utils/adt/ruleutils.c                |  135 ++-
 src/backend/utils/adt/selfuncs.c                 |   23 +-
 src/backend/utils/adt/tsgistidx.c                |  274 ++---
 src/backend/utils/cache/lsyscache.c              |   35 +
 src/backend/utils/cache/relcache.c               |  143 ++-
 src/backend/utils/fmgr/fmgr.c                    |   53 +
 src/include/access/amapi.h                       |    2 +
 src/include/access/amvalidate.h                  |    1 +
 src/include/access/brin_internal.h               |    1 +
 src/include/access/genam.h                       |    3 +
 src/include/access/gin.h                         |    3 +-
 src/include/access/gist.h                        |   22 +-
 src/include/access/hash.h                        |    3 +-
 src/include/access/nbtree.h                      |    3 +-
 src/include/access/reloptions.h                  |   47 +
 src/include/access/spgist.h                      |    3 +-
 src/include/catalog/heap.h                       |    1 +
 src/include/catalog/pg_amproc.dat                |    3 +
 src/include/catalog/pg_proc.dat                  |    3 +
 src/include/fmgr.h                               |    7 +
 src/include/nodes/execnodes.h                    |    2 +
 src/include/nodes/parsenodes.h                   |    1 +
 src/include/nodes/pathnodes.h                    |    1 +
 src/include/utils/lsyscache.h                    |    1 +
 src/include/utils/rel.h                          |    1 +
 src/include/utils/relcache.h                     |    3 +
 src/include/utils/ruleutils.h                    |    1 +
 src/test/regress/expected/alter_generic.out      |   18 +-
 src/test/regress/expected/btree_index.out        |    3 +
 src/test/regress/expected/opr_sanity.out         |    2 +-
 src/test/regress/expected/tsearch.out            |  176 ++++
 src/test/regress/input/create_function_1.source  |    5 +
 src/test/regress/output/create_function_1.source |    4 +
 src/test/regress/regress.c                       |    7 +
 src/test/regress/sql/alter_generic.sql           |   11 +-
 src/test/regress/sql/btree_index.sql             |    3 +
 src/test/regress/sql/opr_sanity.sql              |    2 +-
 src/test/regress/sql/tsearch.sql                 |   45 +
 src/tools/pgindent/typedefs.list                 |   11 +
 107 files changed, 4062 insertions(+), 900 deletions(-)
 create mode 100644 contrib/hstore/hstore--1.6--1.7.sql
 create mode 100644 contrib/intarray/intarray--1.2--1.3.sql
 create mode 100644 contrib/ltree/ltree--1.1--1.2.sql
 create mode 100644 contrib/pg_trgm/pg_trgm--1.4--1.5.sql

diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index d8fb36831f8..23aa7ac4416 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -22,7 +22,8 @@
 
 /* Support procedures numbers */
 #define BLOOM_HASH_PROC			1
-#define BLOOM_NPROC				1
+#define BLOOM_OPTIONS_PROC		2
+#define BLOOM_NPROC				2
 
 /* Scan strategies */
 #define BLOOM_EQUAL_STRATEGY	1
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 0104d02f675..d3bf8665df1 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -109,6 +109,7 @@ blhandler(PG_FUNCTION_ARGS)
 
 	amroutine->amstrategies = BLOOM_NSTRATEGIES;
 	amroutine->amsupport = BLOOM_NPROC;
+	amroutine->amoptsprocnum = BLOOM_OPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = false;
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
index 12773fcf5d5..3c05e5b01c9 100644
--- a/contrib/bloom/blvalidate.c
+++ b/contrib/bloom/blvalidate.c
@@ -108,6 +108,9 @@ blvalidate(Oid opclassoid)
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
 											1, 1, opckeytype);
 				break;
+			case BLOOM_OPTIONS_PROC:
+				ok = check_amoptsproc_signature(procform->amproc);
+				break;
 			default:
 				ereport(INFO,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -204,6 +207,8 @@ blvalidate(Oid opclassoid)
 		if (opclassgroup &&
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
+		if (i == BLOOM_OPTIONS_PROC)
+			continue;			/* optional method */
 		ereport(INFO,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("bloom opclass %s is missing support function %d",
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 24a9b02d061..872ca03cd1f 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -11,6 +11,7 @@ OBJS = \
 
 EXTENSION = hstore
 DATA = hstore--1.4.sql \
+	hstore--1.6--1.7.sql \
 	hstore--1.5--1.6.sql \
 	hstore--1.4--1.5.sql \
 	hstore--1.3--1.4.sql hstore--1.2--1.3.sql \
diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out
index 4f1db01b3eb..89010794380 100644
--- a/contrib/hstore/expected/hstore.out
+++ b/contrib/hstore/expected/hstore.out
@@ -1344,6 +1344,51 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled'];
     42
 (1 row)
 
+drop index hidx;
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=2025));
+ERROR:  value 2025 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=2024));
+set enable_seqscan=off;
+select count(*) from testhstore where h @> 'wait=>NULL';
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC';
+ count 
+-------
+    15
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count 
+-------
+     2
+(1 row)
+
+select count(*) from testhstore where h ? 'public';
+ count 
+-------
+   194
+(1 row)
+
+select count(*) from testhstore where h ?| ARRAY['public','disabled'];
+ count 
+-------
+   337
+(1 row)
+
+select count(*) from testhstore where h ?& ARRAY['public','disabled'];
+ count 
+-------
+    42
+(1 row)
+
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
diff --git a/contrib/hstore/hstore--1.6--1.7.sql b/contrib/hstore/hstore--1.6--1.7.sql
new file mode 100644
index 00000000000..0d126ef8a9c
--- /dev/null
+++ b/contrib/hstore/hstore--1.6--1.7.sql
@@ -0,0 +1,12 @@
+/* contrib/hstore/hstore--1.6--1.7.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION hstore UPDATE TO '1.7'" to load this file. \quit
+
+CREATE FUNCTION ghstore_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', 'ghstore_options'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_hstore_ops USING gist
+ADD FUNCTION 10 (hstore) ghstore_options (internal);
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index e0fbb8bb3c5..f0da7724295 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,6 +1,6 @@
 # hstore extension
 comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.6'
+default_version = '1.7'
 module_pathname = '$libdir/hstore'
 relocatable = true
 trusted = true
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index d198c4b7d77..102c9cea729 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -4,25 +4,36 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "catalog/pg_type.h"
 #include "hstore.h"
 #include "utils/pg_crc.h"
 
+/* gist_hstore_ops opclass options */
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+} GistHstoreOptions;
+
 /* bigint defines */
 #define BITBYTE 8
-#define SIGLENINT  4			/* >122 => key will toast, so very slow!!! */
-#define SIGLEN	( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
+#define SIGLEN_DEFAULT	(sizeof(int32) * 4)
+#define SIGLEN_MAX		GISTMaxIndexKeySize
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
+#define GET_SIGLEN()	(PG_HAS_OPCLASS_OPTIONS() ? \
+						 ((GistHstoreOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
+						 SIGLEN_DEFAULT)
+
 
-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 LOOPBIT \
-			for(i=0;i<SIGLENBIT;i++)
+#define LOOPBIT(siglen) \
+			for (i = 0; i < SIGLENBIT(siglen); i++)
 
 /* beware of multiple evaluation of arguments to these macros! */
 #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
@@ -30,8 +41,8 @@ typedef char *BITVECP;
 #define CLRBIT(x,i)   GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) )
 #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))
 
 typedef struct
 {
@@ -45,7 +56,7 @@ typedef struct
 #define ISALLTRUE(x)	( ((GISTTYPE*)x)->flag & ALLISTRUE )
 
 #define GTHDRSIZE		(VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
 
 #define GETSIGN(x)		( (BITVECP)( (char*)x+GTHDRSIZE ) )
 
@@ -96,6 +107,27 @@ ghstore_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+static GISTTYPE *
+ghstore_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+	int			flag = allistrue ? ALLISTRUE : 0;
+	int			size = CALCGTSIZE(flag, siglen);
+	GISTTYPE   *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if (!allistrue)
+	{
+		if (sign)
+			memcpy(GETSIGN(res), sign, siglen);
+		else
+			memset(GETSIGN(res), 0, siglen);
+	}
+
+	return res;
+}
+
 PG_FUNCTION_INFO_V1(ghstore_consistent);
 PG_FUNCTION_INFO_V1(ghstore_compress);
 PG_FUNCTION_INFO_V1(ghstore_decompress);
@@ -103,36 +135,36 @@ PG_FUNCTION_INFO_V1(ghstore_penalty);
 PG_FUNCTION_INFO_V1(ghstore_picksplit);
 PG_FUNCTION_INFO_V1(ghstore_union);
 PG_FUNCTION_INFO_V1(ghstore_same);
+PG_FUNCTION_INFO_V1(ghstore_options);
 
 Datum
 ghstore_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = GET_SIGLEN();
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{
-		GISTTYPE   *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		GISTTYPE   *res = ghstore_alloc(false, siglen, NULL);
 		HStore	   *val = DatumGetHStoreP(entry->key);
 		HEntry	   *hsent = ARRPTR(val);
 		char	   *ptr = STRPTR(val);
 		int			count = HS_COUNT(val);
 		int			i;
 
-		SET_VARSIZE(res, CALCGTSIZE(0));
-
 		for (i = 0; i < count; ++i)
 		{
 			int			h;
 
 			h = crc32_sz((char *) HSTORE_KEY(hsent, ptr, i),
 						 HSTORE_KEYLEN(hsent, i));
-			HASH(GETSIGN(res), h);
+			HASH(GETSIGN(res), h, siglen);
 			if (!HSTORE_VALISNULL(hsent, i))
 			{
 				h = crc32_sz((char *) HSTORE_VAL(hsent, ptr, i),
 							 HSTORE_VALLEN(hsent, i));
-				HASH(GETSIGN(res), h);
+				HASH(GETSIGN(res), h, siglen);
 			}
 		}
 
@@ -148,15 +180,13 @@ ghstore_compress(PG_FUNCTION_ARGS)
 		GISTTYPE   *res;
 		BITVECP		sign = GETSIGN(DatumGetPointer(entry->key));
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(retval);
 		}
 
-		res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
-		SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
-		res->flag = ALLISTRUE;
+		res = ghstore_alloc(true, siglen, NULL);
 
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
@@ -184,6 +214,8 @@ ghstore_same(PG_FUNCTION_ARGS)
 	GISTTYPE   *a = (GISTTYPE *) PG_GETARG_POINTER(0);
 	GISTTYPE   *b = (GISTTYPE *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = GET_SIGLEN();
+
 
 	if (ISALLTRUE(a) && ISALLTRUE(b))
 		*result = true;
@@ -198,7 +230,7 @@ ghstore_same(PG_FUNCTION_ARGS)
 					sb = GETSIGN(b);
 
 		*result = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -211,12 +243,12 @@ ghstore_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
 	int32		size = 0,
 				i;
 
-	LOOPBYTE
+	LOOPBYTE(siglen)
 	{
 		size += SUMBIT(sign);
 		sign = (BITVECP) (((char *) sign) + 1);
@@ -225,12 +257,12 @@ sizebitvec(BITVECP sign)
 }
 
 static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
 {
 	int			i,
 				dist = 0;
 
-	LOOPBIT
+	LOOPBIT(siglen)
 	{
 		if (GETBIT(a, i) != GETBIT(b, i))
 			dist++;
@@ -239,30 +271,30 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *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);
 }
 
 static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = GETSIGN(add);
 
 	if (ISALLTRUE(add))
 		return 1;
-	LOOPBYTE
+	LOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -274,28 +306,22 @@ ghstore_union(PG_FUNCTION_ARGS)
 	int32		len = entryvec->n;
 
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
+	int			siglen = GET_SIGLEN();
 	int32		i;
-	int32		flag = 0;
-	GISTTYPE   *result;
+	GISTTYPE   *result = ghstore_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;
 		}
 	}
 
-	len = CALCGTSIZE(flag);
-	result = (GISTTYPE *) 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);
 }
@@ -306,10 +332,11 @@ ghstore_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);
+	int			siglen = GET_SIGLEN();
 	GISTTYPE   *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
 	GISTTYPE   *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -334,6 +361,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	OffsetNumber maxoff = entryvec->n - 2;
 
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = GET_SIGLEN();
 	OffsetNumber k,
 				j;
 	GISTTYPE   *datum_l,
@@ -364,7 +392,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -386,33 +414,10 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_l, GTHDRSIZE);
-		datum_l->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
-			;
-	}
-	if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_r, GTHDRSIZE);
-		datum_r->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
-	}
+	datum_l = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_1)));
+	datum_r = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_2)));
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -421,8 +426,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -446,20 +451,20 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.0001))
 		{
 			if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -470,12 +475,12 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -500,6 +505,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	int			siglen = GET_SIGLEN();
 	bool		res = true;
 	BITVECP		sign;
 
@@ -525,13 +531,13 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			int			crc = crc32_sz((char *) HSTORE_KEY(qe, qv, i),
 									   HSTORE_KEYLEN(qe, i));
 
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (GETBIT(sign, HASHVAL(crc, siglen)))
 			{
 				if (!HSTORE_VALISNULL(qe, i))
 				{
 					crc = crc32_sz((char *) HSTORE_VAL(qe, qv, i),
 								   HSTORE_VALLEN(qe, i));
-					if (!GETBIT(sign, HASHVAL(crc)))
+					if (!GETBIT(sign, HASHVAL(crc, siglen)))
 						res = false;
 				}
 			}
@@ -544,7 +550,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 		text	   *query = PG_GETARG_TEXT_PP(1);
 		int			crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
 
-		res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
+		res = (GETBIT(sign, HASHVAL(crc, siglen))) ? true : false;
 	}
 	else if (strategy == HStoreExistsAllStrategyNumber)
 	{
@@ -565,7 +571,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			if (key_nulls[i])
 				continue;
 			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (!(GETBIT(sign, HASHVAL(crc))))
+			if (!(GETBIT(sign, HASHVAL(crc, siglen))))
 				res = false;
 		}
 	}
@@ -590,7 +596,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 			if (key_nulls[i])
 				continue;
 			crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
-			if (GETBIT(sign, HASHVAL(crc)))
+			if (GETBIT(sign, HASHVAL(crc, siglen)))
 				res = true;
 		}
 	}
@@ -599,3 +605,17 @@ ghstore_consistent(PG_FUNCTION_ARGS)
 
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+ghstore_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+	init_local_reloptions(relopts, sizeof(GistHstoreOptions));
+	add_local_int_reloption(relopts, "siglen",
+							"signature length in bytes",
+							SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+							offsetof(GistHstoreOptions, siglen));
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql
index 76ac48b021d..a6c2f3a0ce0 100644
--- a/contrib/hstore/sql/hstore.sql
+++ b/contrib/hstore/sql/hstore.sql
@@ -304,6 +304,19 @@ select count(*) from testhstore where h ? 'public';
 select count(*) from testhstore where h ?| ARRAY['public','disabled'];
 select count(*) from testhstore where h ?& ARRAY['public','disabled'];
 
+drop index hidx;
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=0));
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=2025));
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=2024));
+set enable_seqscan=off;
+
+select count(*) from testhstore where h @> 'wait=>NULL';
+select count(*) from testhstore where h @> 'wait=>CC';
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+select count(*) from testhstore where h ? 'public';
+select count(*) from testhstore where h ?| ARRAY['public','disabled'];
+select count(*) from testhstore where h ?& ARRAY['public','disabled'];
+
 drop index hidx;
 create index hidx on testhstore using gin (h);
 set enable_seqscan=off;
diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile
index d02349c0493..b68959ebd64 100644
--- a/contrib/intarray/Makefile
+++ b/contrib/intarray/Makefile
@@ -12,7 +12,8 @@ OBJS = \
 	_intbig_gist.o
 
 EXTENSION = intarray
-DATA = intarray--1.2.sql intarray--1.1--1.2.sql intarray--1.0--1.1.sql
+DATA = intarray--1.2--1.3.sql intarray--1.2.sql intarray--1.1--1.2.sql \
+	intarray--1.0--1.1.sql
 PGFILEDESC = "intarray - functions and operators for arrays of integers"
 
 REGRESS = _int
diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index f03fdf9add9..304c29525c9 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -8,7 +8,19 @@
 #include "utils/memutils.h"
 
 /* number ranges for compression */
-#define MAXNUMRANGE 100
+#define G_INT_NUMRANGES_DEFAULT		100
+#define G_INT_NUMRANGES_MAX			((GISTMaxIndexKeySize - VARHDRSZ) / \
+									 (2 * sizeof(int32)))
+#define G_INT_GET_NUMRANGES()		(PG_HAS_OPCLASS_OPTIONS() ? \
+									 ((GISTIntArrayOptions *) PG_GET_OPCLASS_OPTIONS())->num_ranges : \
+									 G_INT_NUMRANGES_DEFAULT)
+
+/* gist_int_ops opclass options */
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			num_ranges;		/* number of ranges */
+} GISTIntArrayOptions;
 
 /* useful macros for accessing int4 arrays */
 #define ARRPTR(x)  ( (int32 *) ARR_DATA_PTR(x) )
@@ -47,15 +59,17 @@
 
 
 /* bigint defines */
-#define SIGLENINT  63			/* >122 => key will toast, so very slow!!! */
-#define SIGLEN	( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
+#define SIGLEN_DEFAULT		(63 * 4)
+#define SIGLEN_MAX			GISTMaxIndexKeySize
+#define SIGLENBIT(siglen)	((siglen) * BITS_PER_BYTE)
+#define GET_SIGLEN()		(PG_HAS_OPCLASS_OPTIONS() ? \
+							 ((GISTIntArrayBigOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
+							 SIGLEN_DEFAULT)
 
-typedef char BITVEC[SIGLEN];
 typedef char *BITVECP;
 
-#define LOOPBYTE \
-			for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+			for (i = 0; i < siglen; i++)
 
 /* beware of multiple evaluation of arguments to these macros! */
 #define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
@@ -63,8 +77,15 @@ typedef char *BITVECP;
 #define CLRBIT(x,i)   GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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))
+
+/* gist_intbig_ops opclass options */
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+} GISTIntArrayBigOptions;
 
 /*
  * type of index key
@@ -81,7 +102,7 @@ typedef struct
 #define ISALLTRUE(x)	( ((GISTTYPE*)x)->flag & ALLISTRUE )
 
 #define GTHDRSIZE		(VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
 
 #define GETSIGN(x)		( (BITVECP)( (char*)x+GTHDRSIZE ) )
 
@@ -103,7 +124,7 @@ bool		inner_int_contains(ArrayType *a, ArrayType *b);
 ArrayType  *inner_int_union(ArrayType *a, ArrayType *b);
 ArrayType  *inner_int_inter(ArrayType *a, ArrayType *b);
 void		rt__int_size(ArrayType *a, float *size);
-void		gensign(BITVEC sign, int *a, int len);
+void		gensign(BITVECP sign, int *a, int len, int siglen);
 
 
 /*****************************************************************************
@@ -149,7 +170,7 @@ typedef struct QUERYTYPE
 #define PG_GETARG_QUERYTYPE_P(n)	  DatumGetQueryTypeP(PG_GETARG_DATUM(n))
 #define PG_GETARG_QUERYTYPE_P_COPY(n) DatumGetQueryTypePCopy(PG_GETARG_DATUM(n))
 
-bool		signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot);
+bool		signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot);
 bool		execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
 
 bool		gin_bool_consistent(QUERYTYPE *query, bool *check);
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index fd976900b8a..58113892d3b 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -232,7 +232,7 @@ typedef struct
  * is there value 'val' in (sorted) array or not ?
  */
 static bool
-checkcondition_arr(void *checkval, ITEM *item)
+checkcondition_arr(void *checkval, ITEM *item, void *options)
 {
 	int32	   *StopLow = ((CHKVAL *) checkval)->arrb;
 	int32	   *StopHigh = ((CHKVAL *) checkval)->arre;
@@ -254,42 +254,42 @@ checkcondition_arr(void *checkval, ITEM *item)
 }
 
 static bool
-checkcondition_bit(void *checkval, ITEM *item)
+checkcondition_bit(void *checkval, ITEM *item, void *siglen)
 {
-	return GETBIT(checkval, HASHVAL(item->val));
+	return GETBIT(checkval, HASHVAL(item->val, (int)(intptr_t) siglen));
 }
 
 /*
  * evaluate boolean expression, using chkcond() to test the primitive cases
  */
 static bool
-execute(ITEM *curitem, void *checkval, bool calcnot,
-		bool (*chkcond) (void *checkval, ITEM *item))
+execute(ITEM *curitem, void *checkval, void *options, bool calcnot,
+		bool (*chkcond) (void *checkval, ITEM *item, void *options))
 {
 	/* since this function recurses, it could be driven to stack overflow */
 	check_stack_depth();
 
 	if (curitem->type == VAL)
-		return (*chkcond) (checkval, curitem);
+		return (*chkcond) (checkval, curitem, options);
 	else if (curitem->val == (int32) '!')
 	{
 		return calcnot ?
-			((execute(curitem - 1, checkval, calcnot, chkcond)) ? false : true)
+			((execute(curitem - 1, checkval, options, calcnot, chkcond)) ? false : true)
 			: true;
 	}
 	else if (curitem->val == (int32) '&')
 	{
-		if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
-			return execute(curitem - 1, checkval, calcnot, chkcond);
+		if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
+			return execute(curitem - 1, checkval, options, calcnot, chkcond);
 		else
 			return false;
 	}
 	else
 	{							/* |-operator */
-		if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
+		if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
 			return true;
 		else
-			return execute(curitem - 1, checkval, calcnot, chkcond);
+			return execute(curitem - 1, checkval, options, calcnot, chkcond);
 	}
 }
 
@@ -297,10 +297,10 @@ execute(ITEM *curitem, void *checkval, bool calcnot,
  * signconsistent & execconsistent called by *_consistent
  */
 bool
-signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot)
+signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot)
 {
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) sign, calcnot,
+				   (void *) sign, (void *)(intptr_t) siglen, calcnot,
 				   checkcondition_bit);
 }
 
@@ -314,7 +314,7 @@ execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot)
 	chkval.arrb = ARRPTR(array);
 	chkval.arre = chkval.arrb + ARRNELEMS(array);
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) &chkval, calcnot,
+				   (void *) &chkval, NULL, calcnot,
 				   checkcondition_arr);
 }
 
@@ -325,7 +325,7 @@ typedef struct
 } GinChkVal;
 
 static bool
-checkcondition_gin(void *checkval, ITEM *item)
+checkcondition_gin(void *checkval, ITEM *item, void *options)
 {
 	GinChkVal  *gcv = (GinChkVal *) checkval;
 
@@ -356,7 +356,7 @@ gin_bool_consistent(QUERYTYPE *query, bool *check)
 	}
 
 	return execute(GETQUERY(query) + query->size - 1,
-				   (void *) &gcv, true,
+				   (void *) &gcv, NULL, true,
 				   checkcondition_gin);
 }
 
@@ -428,7 +428,7 @@ boolop(PG_FUNCTION_ARGS)
 	chkval.arrb = ARRPTR(val);
 	chkval.arre = chkval.arrb + ARRNELEMS(val);
 	result = execute(GETQUERY(query) + query->size - 1,
-					 &chkval, true,
+					 &chkval, NULL, true,
 					 checkcondition_arr);
 	pfree(val);
 
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index 50effc3ca57..fb05b06af9e 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -7,6 +7,7 @@
 
 #include "_int.h"
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 
 #define GETENTRY(vec,pos) ((ArrayType *) DatumGetPointer((vec)->vector[(pos)].key))
@@ -32,6 +33,7 @@ PG_FUNCTION_INFO_V1(g_int_penalty);
 PG_FUNCTION_INFO_V1(g_int_picksplit);
 PG_FUNCTION_INFO_V1(g_int_union);
 PG_FUNCTION_INFO_V1(g_int_same);
+PG_FUNCTION_INFO_V1(g_int_options);
 
 
 /*
@@ -156,6 +158,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 	GISTENTRY  *retval;
 	ArrayType  *r;
+	int			num_ranges = G_INT_GET_NUMRANGES();
 	int			len,
 				lenr;
 	int		   *dr;
@@ -170,9 +173,9 @@ g_int_compress(PG_FUNCTION_ARGS)
 		CHECKARRVALID(r);
 		PREPAREARR(r);
 
-		if (ARRNELEMS(r) >= 2 * MAXNUMRANGE)
+		if (ARRNELEMS(r) >= 2 * num_ranges)
 			elog(NOTICE, "input array is too big (%d maximum allowed, %d current), use gist__intbig_ops opclass instead",
-				 2 * MAXNUMRANGE - 1, ARRNELEMS(r));
+				 2 * num_ranges - 1, ARRNELEMS(r));
 
 		retval = palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(r),
@@ -195,7 +198,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 		PG_RETURN_POINTER(entry);
 	}
 
-	if ((len = ARRNELEMS(r)) >= 2 * MAXNUMRANGE)
+	if ((len = ARRNELEMS(r)) >= 2 * num_ranges)
 	{							/* compress */
 		if (r == (ArrayType *) DatumGetPointer(entry->key))
 			r = DatumGetArrayTypePCopy(entry->key);
@@ -208,7 +211,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 		 * "lenr" is the number of ranges we must eventually remove by
 		 * merging, we must be careful to remove no more than this number.
 		 */
-		lenr = len - MAXNUMRANGE;
+		lenr = len - num_ranges;
 
 		/*
 		 * Initially assume we can merge consecutive ints into a range. but we
@@ -241,7 +244,7 @@ g_int_compress(PG_FUNCTION_ARGS)
 		 */
 		len = 2 * (len - j);
 		cand = 1;
-		while (len > MAXNUMRANGE * 2)
+		while (len > num_ranges * 2)
 		{
 			min = PG_INT64_MAX;
 			for (i = 2; i < len; i += 2)
@@ -278,6 +281,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 	GISTENTRY  *retval;
 	ArrayType  *r;
+	int			num_ranges = G_INT_GET_NUMRANGES();
 	int		   *dr,
 				lenr;
 	ArrayType  *in;
@@ -304,7 +308,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
 
 	lenin = ARRNELEMS(in);
 
-	if (lenin < 2 * MAXNUMRANGE)
+	if (lenin < 2 * num_ranges)
 	{							/* not compressed value */
 		if (in != (ArrayType *) DatumGetPointer(entry->key))
 		{
@@ -604,3 +608,17 @@ g_int_picksplit(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(v);
 }
+
+Datum
+g_int_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+	init_local_reloptions(relopts, sizeof(GISTIntArrayOptions));
+	add_local_int_reloption(relopts, "numranges",
+							"number of ranges for compression",
+							G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX,
+							offsetof(GISTIntArrayOptions, num_ranges));
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index e5f4bd40643..91690aff51f 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -319,14 +319,14 @@ _int_unique(ArrayType *r)
 }
 
 void
-gensign(BITVEC sign, int *a, int len)
+gensign(BITVECP sign, int *a, int len, int siglen)
 {
 	int			i;
 
 	/* we assume that the sign vector is previously zeroed */
 	for (i = 0; i < len; i++)
 	{
-		HASH(sign, *a);
+		HASH(sign, *a, siglen);
 		a++;
 	}
 }
diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c
index be51dac1fa7..67c44e99a9a 100644
--- a/contrib/intarray/_intbig_gist.c
+++ b/contrib/intarray/_intbig_gist.c
@@ -5,6 +5,7 @@
 
 #include "_int.h"
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "port/pg_bitutils.h"
 
@@ -19,6 +20,8 @@ PG_FUNCTION_INFO_V1(g_intbig_penalty);
 PG_FUNCTION_INFO_V1(g_intbig_picksplit);
 PG_FUNCTION_INFO_V1(g_intbig_union);
 PG_FUNCTION_INFO_V1(g_intbig_same);
+PG_FUNCTION_INFO_V1(g_intbig_options);
+
 PG_FUNCTION_INFO_V1(_intbig_in);
 PG_FUNCTION_INFO_V1(_intbig_out);
 
@@ -40,12 +43,33 @@ _intbig_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+static GISTTYPE *
+_intbig_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+	int			flag = allistrue ? ALLISTRUE : 0;
+	int			size = CALCGTSIZE(flag, siglen);
+	GISTTYPE   *res = (GISTTYPE *) palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if (!allistrue)
+	{
+		if (sign)
+			memcpy(GETSIGN(res), sign, siglen);
+		else
+			memset(GETSIGN(res), 0, siglen);
+	}
+
+	return res;
+}
+
 
 /*********************************************************************
 ** intbig functions
 *********************************************************************/
 static bool
-_intbig_overlap(GISTTYPE *a, ArrayType *b)
+_intbig_overlap(GISTTYPE *a, ArrayType *b, int siglen)
 {
 	int			num = ARRNELEMS(b);
 	int32	   *ptr = ARRPTR(b);
@@ -54,7 +78,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
 
 	while (num--)
 	{
-		if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+		if (GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
 			return true;
 		ptr++;
 	}
@@ -63,7 +87,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
 }
 
 static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglen)
 {
 	int			num = ARRNELEMS(b);
 	int32	   *ptr = ARRPTR(b);
@@ -72,7 +96,7 @@ _intbig_contains(GISTTYPE *a, ArrayType *b)
 
 	while (num--)
 	{
-		if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+		if (!GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
 			return false;
 		ptr++;
 	}
@@ -86,6 +110,7 @@ g_intbig_same(PG_FUNCTION_ARGS)
 	GISTTYPE   *a = (GISTTYPE *) PG_GETARG_POINTER(0);
 	GISTTYPE   *b = (GISTTYPE *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = GET_SIGLEN();
 
 	if (ISALLTRUE(a) && ISALLTRUE(b))
 		*result = true;
@@ -100,7 +125,7 @@ g_intbig_same(PG_FUNCTION_ARGS)
 					sb = GETSIGN(b);
 
 		*result = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -116,6 +141,7 @@ Datum
 g_intbig_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = GET_SIGLEN();
 
 	if (entry->leafkey)
 	{
@@ -123,7 +149,7 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 		ArrayType  *in = DatumGetArrayTypeP(entry->key);
 		int32	   *ptr;
 		int			num;
-		GISTTYPE   *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+		GISTTYPE   *res = _intbig_alloc(false, siglen, NULL);
 
 		CHECKARRVALID(in);
 		if (ARRISEMPTY(in))
@@ -136,11 +162,10 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 			ptr = ARRPTR(in);
 			num = ARRNELEMS(in);
 		}
-		SET_VARSIZE(res, CALCGTSIZE(0));
 
 		while (num--)
 		{
-			HASH(GETSIGN(res), *ptr);
+			HASH(GETSIGN(res), *ptr, siglen);
 			ptr++;
 		}
 
@@ -161,16 +186,13 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 		BITVECP		sign = GETSIGN(DatumGetPointer(entry->key));
 		GISTTYPE   *res;
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(entry);
 		}
 
-		res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
-		SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
-		res->flag = ALLISTRUE;
-
+		res = _intbig_alloc(true, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -184,19 +206,19 @@ g_intbig_compress(PG_FUNCTION_ARGS)
 
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
-	return pg_popcount(sign, SIGLEN);
+	return pg_popcount(sign, siglen);
 }
 
 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]);
 		/* Using the popcount functions here isn't likely to win */
@@ -206,19 +228,19 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *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
@@ -228,14 +250,14 @@ g_intbig_decompress(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = GETSIGN(add);
 
 	if (ISALLTRUE(add))
 		return 1;
-	LOOPBYTE
+	LOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -245,29 +267,22 @@ g_intbig_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	GISTTYPE   *result;
+	int			siglen = GET_SIGLEN();
+	int32		i;
+	GISTTYPE   *result = _intbig_alloc(false, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; 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;
 		}
 	}
 
-	len = CALCGTSIZE(flag);
-	result = (GISTTYPE *) 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);
 }
@@ -280,8 +295,9 @@ g_intbig_penalty(PG_FUNCTION_ARGS)
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
 	GISTTYPE   *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
 	GISTTYPE   *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
+	int			siglen = GET_SIGLEN();
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -304,6 +320,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = GET_SIGLEN();
 	OffsetNumber k,
 				j;
 	GISTTYPE   *datum_l,
@@ -336,7 +353,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -358,32 +375,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_l, GTHDRSIZE);
-		datum_l->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC));
-	}
-	if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
-		SET_VARSIZE(datum_r, GTHDRSIZE);
-		datum_r->flag = ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
-		SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
-	}
+	datum_l = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_1)));
+	datum_r = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+							GETSIGN(GETENTRY(entryvec, seed_2)));
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -392,8 +387,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = Abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -417,20 +412,20 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
 		{
 			if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -441,12 +436,12 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
 			{
 				if (!ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = GETSIGN(_j);
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -472,6 +467,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	int			siglen = GET_SIGLEN();
 	bool		retval;
 
 	/* All cases served by this function are inexact */
@@ -484,6 +480,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	{
 		retval = signconsistent((QUERYTYPE *) query,
 								GETSIGN(DatumGetPointer(entry->key)),
+								siglen,
 								false);
 		PG_FREE_IF_COPY(query, 1);
 		PG_RETURN_BOOL(retval);
@@ -494,7 +491,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	switch (strategy)
 	{
 		case RTOverlapStrategyNumber:
-			retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+			retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+									 query, siglen);
 			break;
 		case RTSameStrategyNumber:
 			if (GIST_LEAF(entry))
@@ -502,22 +500,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				int			i,
 							num = ARRNELEMS(query);
 				int32	   *ptr = ARRPTR(query);
-				BITVEC		qp;
-				BITVECP		dq,
+				BITVECP		dq = palloc0(siglen),
 							de;
 
-				memset(qp, 0, sizeof(BITVEC));
-
 				while (num--)
 				{
-					HASH(qp, *ptr);
+					HASH(dq, *ptr, siglen);
 					ptr++;
 				}
 
 				de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
-				dq = qp;
 				retval = true;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 				{
 					if (de[i] != dq[i])
 					{
@@ -526,13 +520,16 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 					}
 				}
 
+				pfree(dq);
 			}
 			else
-				retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+				retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+										  query, siglen);
 			break;
 		case RTContainsStrategyNumber:
 		case RTOldContainsStrategyNumber:
-			retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+			retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+									  query, siglen);
 			break;
 		case RTContainedByStrategyNumber:
 		case RTOldContainedByStrategyNumber:
@@ -541,22 +538,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 				int			i,
 							num = ARRNELEMS(query);
 				int32	   *ptr = ARRPTR(query);
-				BITVEC		qp;
-				BITVECP		dq,
+				BITVECP		dq = palloc0(siglen),
 							de;
 
-				memset(qp, 0, sizeof(BITVEC));
-
 				while (num--)
 				{
-					HASH(qp, *ptr);
+					HASH(dq, *ptr, siglen);
 					ptr++;
 				}
 
 				de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
-				dq = qp;
 				retval = true;
-				LOOPBYTE
+				LOOPBYTE(siglen)
 				{
 					if (de[i] & ~dq[i])
 					{
@@ -580,3 +573,17 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(retval);
 }
+
+Datum
+g_intbig_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+	init_local_reloptions(relopts, sizeof(GISTIntArrayBigOptions));
+	add_local_int_reloption(relopts, "siglen",
+							"signature length in bytes",
+							SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+							offsetof(GISTIntArrayBigOptions, siglen));
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/intarray/expected/_int.out b/contrib/intarray/expected/_int.out
index c92a56524a3..a09d40efa17 100644
--- a/contrib/intarray/expected/_int.out
+++ b/contrib/intarray/expected/_int.out
@@ -547,6 +547,166 @@ SELECT count(*) from test__int WHERE a @@ '!20 & !21';
   6343
 (1 row)
 
+DROP INDEX text_idx;
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 0));
+ERROR:  value 0 out of bounds for option "numranges"
+DETAIL:  Valid values are between "1" and "252".
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 253));
+ERROR:  value 253 out of bounds for option "numranges"
+DETAIL:  Valid values are between "1" and "252".
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 252));
+SELECT count(*) from test__int WHERE a && '{23,50}';
+ count 
+-------
+   403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23|50';
+ count 
+-------
+   403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{23,50}';
+ count 
+-------
+    12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23&50';
+ count 
+-------
+    12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}';
+ count 
+-------
+    12
+(1 row)
+
+SELECT count(*) from test__int WHERE a <@ '{73,23,20}';
+ count 
+-------
+    10
+(1 row)
+
+SELECT count(*) from test__int WHERE a = '{73,23,20}';
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '50&68';
+ count 
+-------
+     9
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
+ count 
+-------
+    21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
+ count 
+-------
+    21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '20 | !21';
+ count 
+-------
+  6566
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '!20 & !21';
+ count 
+-------
+  6343
+(1 row)
+
+DROP INDEX text_idx;
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 2025));
+ERROR:  value 2025 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 2024));
+SELECT count(*) from test__int WHERE a && '{23,50}';
+ count 
+-------
+   403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23|50';
+ count 
+-------
+   403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{23,50}';
+ count 
+-------
+    12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23&50';
+ count 
+-------
+    12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}';
+ count 
+-------
+    12
+(1 row)
+
+SELECT count(*) from test__int WHERE a <@ '{73,23,20}';
+ count 
+-------
+    10
+(1 row)
+
+SELECT count(*) from test__int WHERE a = '{73,23,20}';
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '50&68';
+ count 
+-------
+     9
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
+ count 
+-------
+    21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
+ count 
+-------
+    21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '20 | !21';
+ count 
+-------
+  6566
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '!20 & !21';
+ count 
+-------
+  6343
+(1 row)
+
 DROP INDEX text_idx;
 CREATE INDEX text_idx on test__int using gist ( a gist__intbig_ops );
 SELECT count(*) from test__int WHERE a && '{23,50}';
diff --git a/contrib/intarray/intarray--1.2--1.3.sql b/contrib/intarray/intarray--1.2--1.3.sql
new file mode 100644
index 00000000000..790d1591770
--- /dev/null
+++ b/contrib/intarray/intarray--1.2--1.3.sql
@@ -0,0 +1,20 @@
+/* contrib/intarray/intarray--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION g_int_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', 'g_int_options'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist__int_ops USING gist
+ADD FUNCTION 10 (_int4) g_int_options (internal);
+
+ALTER OPERATOR FAMILY gist__intbig_ops USING gist
+ADD FUNCTION 10 (_int4) g_intbig_options (internal);
diff --git a/contrib/intarray/intarray.control b/contrib/intarray/intarray.control
index bf28804dec9..db7746b6c7a 100644
--- a/contrib/intarray/intarray.control
+++ b/contrib/intarray/intarray.control
@@ -1,6 +1,6 @@
 # intarray extension
 comment = 'functions, operators, and index support for 1-D arrays of integers'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/_int'
 relocatable = true
 trusted = true
diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql
index 6ca7e3cca7e..b26fc57e4dd 100644
--- a/contrib/intarray/sql/_int.sql
+++ b/contrib/intarray/sql/_int.sql
@@ -110,6 +110,42 @@ SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
 SELECT count(*) from test__int WHERE a @@ '20 | !21';
 SELECT count(*) from test__int WHERE a @@ '!20 & !21';
 
+DROP INDEX text_idx;
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 0));
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 253));
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 252));
+
+SELECT count(*) from test__int WHERE a && '{23,50}';
+SELECT count(*) from test__int WHERE a @@ '23|50';
+SELECT count(*) from test__int WHERE a @> '{23,50}';
+SELECT count(*) from test__int WHERE a @@ '23&50';
+SELECT count(*) from test__int WHERE a @> '{20,23}';
+SELECT count(*) from test__int WHERE a <@ '{73,23,20}';
+SELECT count(*) from test__int WHERE a = '{73,23,20}';
+SELECT count(*) from test__int WHERE a @@ '50&68';
+SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
+SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
+SELECT count(*) from test__int WHERE a @@ '20 | !21';
+SELECT count(*) from test__int WHERE a @@ '!20 & !21';
+
+DROP INDEX text_idx;
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 0));
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 2025));
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 2024));
+
+SELECT count(*) from test__int WHERE a && '{23,50}';
+SELECT count(*) from test__int WHERE a @@ '23|50';
+SELECT count(*) from test__int WHERE a @> '{23,50}';
+SELECT count(*) from test__int WHERE a @@ '23&50';
+SELECT count(*) from test__int WHERE a @> '{20,23}';
+SELECT count(*) from test__int WHERE a <@ '{73,23,20}';
+SELECT count(*) from test__int WHERE a = '{73,23,20}';
+SELECT count(*) from test__int WHERE a @@ '50&68';
+SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
+SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
+SELECT count(*) from test__int WHERE a @@ '20 | !21';
+SELECT count(*) from test__int WHERE a @@ '!20 & !21';
+
 DROP INDEX text_idx;
 CREATE INDEX text_idx on test__int using gist ( a gist__intbig_ops );
 
diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index 70c5e371c8e..b16a5668523 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -15,7 +15,7 @@ OBJS = \
 PG_CPPFLAGS = -DLOWER_NODE
 
 EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql
+DATA = ltree--1.1--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql
 PGFILEDESC = "ltree - hierarchical label data type"
 
 HEADERS = ltree.h
diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c
index 50f54f2eeca..95cc367dd81 100644
--- a/contrib/ltree/_ltree_gist.c
+++ b/contrib/ltree/_ltree_gist.c
@@ -8,6 +8,7 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "crc32.h"
 #include "ltree.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(_ltree_union);
 PG_FUNCTION_INFO_V1(_ltree_penalty);
 PG_FUNCTION_INFO_V1(_ltree_picksplit);
 PG_FUNCTION_INFO_V1(_ltree_consistent);
+PG_FUNCTION_INFO_V1(_ltree_gist_options);
 
 #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
 #define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
@@ -27,7 +29,7 @@ PG_FUNCTION_INFO_V1(_ltree_consistent);
 
 
 static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
 {
 	int			tlen = t->numlevel;
 	ltree_level *cur = LTREE_FIRST(t);
@@ -36,7 +38,7 @@ hashing(BITVECP sign, ltree *t)
 	while (tlen > 0)
 	{
 		hash = ltree_crc32_sz(cur->name, cur->len);
-		AHASH(sign, hash);
+		AHASH(sign, hash, siglen);
 		cur = LEVEL_NEXT(cur);
 		tlen--;
 	}
@@ -47,12 +49,12 @@ _ltree_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 	GISTENTRY  *retval = entry;
+	int			siglen = LTREE_GET_ASIGLEN();
 
 	if (entry->leafkey)
 	{							/* ltree */
 		ltree_gist *key;
 		ArrayType  *val = DatumGetArrayTypeP(entry->key);
-		int32		len = LTG_HDRSIZE + ASIGLEN;
 		int			num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
 		ltree	   *item = (ltree *) ARR_DATA_PTR(val);
 
@@ -65,14 +67,11 @@ _ltree_compress(PG_FUNCTION_ARGS)
 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 					 errmsg("array must not contain nulls")));
 
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = 0;
+		key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
 
-		MemSet(LTG_SIGN(key), 0, ASIGLEN);
 		while (num > 0)
 		{
-			hashing(LTG_SIGN(key), item);
+			hashing(LTG_SIGN(key), item, siglen);
 			num--;
 			item = NEXTVAL(item);
 		}
@@ -84,22 +83,17 @@ _ltree_compress(PG_FUNCTION_ARGS)
 	}
 	else if (!LTG_ISALLTRUE(entry->key))
 	{
-		int32		i,
-					len;
+		int32		i;
 		ltree_gist *key;
-
 		BITVECP		sign = LTG_SIGN(DatumGetPointer(entry->key));
 
-		ALOOPBYTE
+		ALOOPBYTE(siglen)
 		{
 			if ((sign[i] & 0xff) != 0xff)
 				PG_RETURN_POINTER(retval);
 		}
-		len = LTG_HDRSIZE;
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = LTG_ALLTRUE;
 
+		key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(key),
 					  entry->rel, entry->page,
@@ -114,6 +108,7 @@ _ltree_same(PG_FUNCTION_ARGS)
 	ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
 	ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = LTREE_GET_ASIGLEN();
 
 	if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
 		*result = true;
@@ -128,7 +123,7 @@ _ltree_same(PG_FUNCTION_ARGS)
 					sb = LTG_SIGN(b);
 
 		*result = true;
-		ALOOPBYTE
+		ALOOPBYTE(siglen)
 		{
 			if (sa[i] != sb[i])
 			{
@@ -141,7 +136,7 @@ _ltree_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, ltree_gist *add)
+unionkey(BITVECP sbase, ltree_gist *add, int siglen)
 {
 	int32		i;
 	BITVECP		sadd = LTG_SIGN(add);
@@ -149,7 +144,7 @@ unionkey(BITVECP sbase, ltree_gist *add)
 	if (LTG_ISALLTRUE(add))
 		return 1;
 
-	ALOOPBYTE
+	ALOOPBYTE(siglen)
 		sbase[i] |= sadd[i];
 	return 0;
 }
@@ -159,47 +154,40 @@ _ltree_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	ABITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	ltree_gist *result;
+	int			siglen = LTREE_GET_ASIGLEN();
+	int32		i;
+	ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
+	BITVECP		base = LTG_SIGN(result);
 
-	MemSet((void *) base, 0, sizeof(ABITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = LTG_ALLTRUE;
+			result->flag |= LTG_ALLTRUE;
+			SET_VARSIZE(result, LTG_HDRSIZE);
 			break;
 		}
 	}
 
-	len = LTG_HDRSIZE + ((flag & LTG_ALLTRUE) ? 0 : ASIGLEN);
-	result = (ltree_gist *) palloc0(len);
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!LTG_ISALLTRUE(result))
-		memcpy((void *) LTG_SIGN(result), (void *) base, sizeof(ABITVEC));
-	*size = len;
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
-	return pg_popcount((const char *) sign, ASIGLEN);
+	return pg_popcount((const char *) sign, siglen);
 }
 
 static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
 {
 	int			i,
 				diff,
 				dist = 0;
 
-	ALOOPBYTE
+	ALOOPBYTE(siglen)
 	{
 		diff = (unsigned char) (a[i] ^ b[i]);
 		/* Using the popcount functions here isn't likely to win */
@@ -209,19 +197,19 @@ hemdistsign(BITVECP a, BITVECP b)
 }
 
 static int
-hemdist(ltree_gist *a, ltree_gist *b)
+hemdist(ltree_gist *a, ltree_gist *b, int siglen)
 {
 	if (LTG_ISALLTRUE(a))
 	{
 		if (LTG_ISALLTRUE(b))
 			return 0;
 		else
-			return ASIGLENBIT - sizebitvec(LTG_SIGN(b));
+			return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
 	}
 	else if (LTG_ISALLTRUE(b))
-		return ASIGLENBIT - sizebitvec(LTG_SIGN(a));
+		return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
 
-	return hemdistsign(LTG_SIGN(a), LTG_SIGN(b));
+	return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
 }
 
 
@@ -231,8 +219,9 @@ _ltree_penalty(PG_FUNCTION_ARGS)
 	ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
 	ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	int			siglen = LTREE_GET_ASIGLEN();
 
-	*penalty = hemdist(origval, newval);
+	*penalty = hemdist(origval, newval, siglen);
 	PG_RETURN_POINTER(penalty);
 }
 
@@ -253,6 +242,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = LTREE_GET_ASIGLEN();
 	OffsetNumber k,
 				j;
 	ltree_gist *datum_l,
@@ -285,7 +275,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 		_k = GETENTRY(entryvec, k);
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
-			size_waste = hemdist(_k, GETENTRY(entryvec, j));
+			size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -307,32 +297,13 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)))
-	{
-		datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE);
-		SET_VARSIZE(datum_l, LTG_HDRSIZE);
-		datum_l->flag = LTG_ALLTRUE;
-	}
-	else
-	{
-		datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
-		SET_VARSIZE(datum_l, LTG_HDRSIZE + ASIGLEN);
-		datum_l->flag = 0;
-		memcpy((void *) LTG_SIGN(datum_l), (void *) LTG_SIGN(GETENTRY(entryvec, seed_1)), sizeof(ABITVEC));
-	}
-	if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)))
-	{
-		datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE);
-		SET_VARSIZE(datum_r, LTG_HDRSIZE);
-		datum_r->flag = LTG_ALLTRUE;
-	}
-	else
-	{
-		datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
-		SET_VARSIZE(datum_r, LTG_HDRSIZE + ASIGLEN);
-		datum_r->flag = 0;
-		memcpy((void *) LTG_SIGN(datum_r), (void *) LTG_SIGN(GETENTRY(entryvec, seed_2)), sizeof(ABITVEC));
-	}
+	datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
+							   LTG_SIGN(GETENTRY(entryvec, seed_1)),
+							   siglen, NULL, NULL);
+
+	datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
+							   LTG_SIGN(GETENTRY(entryvec, seed_2)),
+							   siglen, NULL, NULL);
 
 	maxoff = OffsetNumberNext(maxoff);
 	/* sort before ... */
@@ -341,8 +312,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 	{
 		costvector[j - 1].pos = j;
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 		costvector[j - 1].cost = Abs(size_alpha - size_beta);
 	}
 	qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -366,20 +337,20 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 			continue;
 		}
 		_j = GETENTRY(entryvec, j);
-		size_alpha = hemdist(datum_l, _j);
-		size_beta = hemdist(datum_r, _j);
+		size_alpha = hemdist(datum_l, _j, siglen);
+		size_beta = hemdist(datum_r, _j, siglen);
 
 		if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
 		{
 			if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
 			{
 				if (!LTG_ISALLTRUE(datum_l))
-					MemSet((void *) union_l, 0xff, sizeof(ABITVEC));
+					MemSet((void *) union_l, 0xff, siglen);
 			}
 			else
 			{
 				ptr = LTG_SIGN(_j);
-				ALOOPBYTE
+				ALOOPBYTE(siglen)
 					union_l[i] |= ptr[i];
 			}
 			*left++ = j;
@@ -390,12 +361,12 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 			if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
 			{
 				if (!LTG_ISALLTRUE(datum_r))
-					MemSet((void *) union_r, 0xff, sizeof(ABITVEC));
+					MemSet((void *) union_r, 0xff, siglen);
 			}
 			else
 			{
 				ptr = LTG_SIGN(_j);
-				ALOOPBYTE
+				ALOOPBYTE(siglen)
 					union_r[i] |= ptr[i];
 			}
 			*right++ = j;
@@ -412,7 +383,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
 }
 
 static bool
-gist_te(ltree_gist *key, ltree *query)
+gist_te(ltree_gist *key, ltree *query, int siglen)
 {
 	ltree_level *curq = LTREE_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -425,7 +396,7 @@ gist_te(ltree_gist *key, ltree *query)
 	while (qlen > 0)
 	{
 		hv = ltree_crc32_sz(curq->name, curq->len);
-		if (!GETBIT(sign, AHASHVAL(hv)))
+		if (!GETBIT(sign, AHASHVAL(hv, siglen)))
 			return false;
 		curq = LEVEL_NEXT(curq);
 		qlen--;
@@ -434,25 +405,38 @@ gist_te(ltree_gist *key, ltree *query)
 	return true;
 }
 
+typedef struct LtreeSignature
+{
+	BITVECP	sign;
+	int		siglen;
+} LtreeSignature;
+
 static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
 {
-	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, AHASHVAL(val->val)) : true;
+	LtreeSignature *sig = cxt;
+
+	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
 }
 
 static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
 {
+	LtreeSignature sig;
+
 	if (LTG_ISALLTRUE(key))
 		return true;
 
+	sig.sign = LTG_SIGN(key);
+	sig.siglen = siglen;
+
 	return ltree_execute(GETQUERY(query),
-						 (void *) LTG_SIGN(key), false,
+						 &sig, false,
 						 checkcondition_bit);
 }
 
 static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
 {
 	lquery_level *curq = LQUERY_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -471,7 +455,7 @@ gist_qe(ltree_gist *key, lquery *query)
 
 			while (vlen > 0)
 			{
-				if (GETBIT(sign, AHASHVAL(curv->val)))
+				if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
 				{
 					isexist = true;
 					break;
@@ -491,7 +475,7 @@ gist_qe(ltree_gist *key, lquery *query)
 }
 
 static bool
-_arrq_cons(ltree_gist *key, ArrayType *_query)
+_arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -507,7 +491,7 @@ _arrq_cons(ltree_gist *key, ArrayType *_query)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query))
+		if (gist_qe(key, query, siglen))
 			return true;
 		num--;
 		query = (lquery *) NEXTVAL(query);
@@ -524,6 +508,7 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	int			siglen = LTREE_GET_ASIGLEN();
 	ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
 	bool		res = false;
 
@@ -534,19 +519,19 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 	{
 		case 10:
 		case 11:
-			res = gist_te(key, (ltree *) query);
+			res = gist_te(key, (ltree *) query, siglen);
 			break;
 		case 12:
 		case 13:
-			res = gist_qe(key, (lquery *) query);
+			res = gist_qe(key, (lquery *) query, siglen);
 			break;
 		case 14:
 		case 15:
-			res = gist_qtxt(key, (ltxtquery *) query);
+			res = gist_qtxt(key, (ltxtquery *) query, siglen);
 			break;
 		case 16:
 		case 17:
-			res = _arrq_cons(key, (ArrayType *) query);
+			res = _arrq_cons(key, (ArrayType *) query, siglen);
 			break;
 		default:
 			/* internal error */
@@ -555,3 +540,16 @@ _ltree_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+_ltree_gist_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+	init_local_reloptions(relopts, sizeof(LtreeGistOptions));
+	add_local_int_reloption(relopts, "siglen", "signature length",
+							LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX,
+							offsetof(LtreeGistOptions, siglen));
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out
index 82269309056..ebfa12e9640 100644
--- a/contrib/ltree/expected/ltree.out
+++ b/contrib/ltree/expected/ltree.out
@@ -7567,6 +7567,98 @@ SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc;
  23.3.32.21.5.14.10.17.1
 (4 rows)
 
+drop index tstidx;
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2025));
+ERROR:  value 2025 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2024));
+SELECT count(*) FROM ltreetest WHERE t <  '12.3';
+ count 
+-------
+   123
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t <= '12.3';
+ count 
+-------
+   124
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t =  '12.3';
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t >= '12.3';
+ count 
+-------
+   883
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t >  '12.3';
+ count 
+-------
+   882
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t @> '1.1.1';
+ count 
+-------
+     4
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t <@ '1.1.1';
+ count 
+-------
+     4
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t @ '23 & 1';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t ~ '1.1.1.*';
+ count 
+-------
+     4
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t ~ '*.1';
+ count 
+-------
+    34
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t ~ '23.*{1}.1';
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t ~ '23.*.1';
+ count 
+-------
+     3
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t ~ '23.*.2';
+ count 
+-------
+     1
+(1 row)
+
+SELECT count(*) FROM ltreetest WHERE t ? '{23.*.1,23.*.2}';
+ count 
+-------
+     4
+(1 row)
+
 create table _ltreetest (t ltree[]);
 \copy _ltreetest FROM 'data/_ltree.data'
 SELECT count(*) FROM _ltreetest WHERE t @> '1.1.1' ;
@@ -7679,3 +7771,65 @@ SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ;
     15
 (1 row)
 
+drop index _tstidx;
+create index _tstidx on _ltreetest using gist (t gist__ltree_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index _tstidx on _ltreetest using gist (t gist__ltree_ops(siglen=2025));
+ERROR:  value 2025 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index _tstidx on _ltreetest using gist (t gist__ltree_ops(siglen=2024));
+SELECT count(*) FROM _ltreetest WHERE t @> '1.1.1' ;
+ count 
+-------
+    15
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t <@ '1.1.1' ;
+ count 
+-------
+    19
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ;
+ count 
+-------
+   147
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ;
+ count 
+-------
+    19
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t ~ '*.1' ;
+ count 
+-------
+   109
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ;
+ count 
+-------
+     5
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ;
+ count 
+-------
+    11
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ;
+ count 
+-------
+     5
+(1 row)
+
+SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ;
+ count 
+-------
+    15
+(1 row)
+
diff --git a/contrib/ltree/ltree--1.1--1.2.sql b/contrib/ltree/ltree--1.1--1.2.sql
new file mode 100644
index 00000000000..7b4ea998679
--- /dev/null
+++ b/contrib/ltree/ltree--1.1--1.2.sql
@@ -0,0 +1,21 @@
+/* contrib/ltree/ltree--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION ltree UPDATE TO '1.2'" to load this file. \quit
+
+CREATE FUNCTION ltree_gist_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', 'ltree_gist_options'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_gist_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', '_ltree_gist_options'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_ltree_ops USING gist
+ADD FUNCTION 10 (ltree) ltree_gist_options (internal);
+
+ALTER OPERATOR FAMILY gist__ltree_ops USING gist
+ADD FUNCTION 10 (_ltree) _ltree_gist_options (internal);
+
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 3118df63d3f..b408d64781f 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,6 +1,6 @@
 # ltree extension
 comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
 module_pathname = '$libdir/ltree'
 relocatable = true
 trusted = true
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index 366e58004c7..ea10a7d2ec9 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -183,15 +183,16 @@ int			ltree_strncasecmp(const char *a, const char *b, size_t s);
 
 /* GiST support for ltree */
 
+#define SIGLEN_MAX		GISTMaxIndexKeySize
+#define SIGLEN_DEFAULT	(2 * sizeof(int32))
 #define BITBYTE 8
-#define SIGLENINT  2
-#define SIGLEN	( sizeof(int32)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
-typedef unsigned char BITVEC[SIGLEN];
+#define SIGLEN	(sizeof(int32) * SIGLENINT)
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
+
 typedef unsigned 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) ( ((unsigned char)(x)) >> i & 0x01 )
@@ -199,8 +200,8 @@ typedef unsigned 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))
 
 /*
  * type of index key for ltree. Tree are combined B-Tree and R-Tree
@@ -230,26 +231,37 @@ typedef struct
 #define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
 #define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
 #define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
-#define LTG_LNODE(x)	( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
-#define LTG_RENODE(x)	( (ltree*)( ((char*)LTG_LNODE(x)) + VARSIZE(LTG_LNODE(x))) )
-#define LTG_RNODE(x)	( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
+#define LTG_LNODE(x, siglen)	( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : (siglen) ) ) )
+#define LTG_RENODE(x, siglen)	( (ltree*)( ((char*)LTG_LNODE(x, siglen)) + VARSIZE(LTG_LNODE(x, siglen))) )
+#define LTG_RNODE(x, siglen)	( LTG_ISNORIGHT(x) ? LTG_LNODE(x, siglen) : LTG_RENODE(x, siglen) )
 
-#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
-#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
+#define LTG_GETLNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x, siglen) )
+#define LTG_GETRNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x, siglen) )
 
+extern ltree_gist *ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+				 ltree *left, ltree *right);
 
 /* GiST support for ltree[] */
 
-#define ASIGLENINT	(7)
-#define ASIGLEN		(sizeof(int32)*ASIGLENINT)
-#define ASIGLENBIT (ASIGLEN*BITBYTE)
-typedef unsigned char ABITVEC[ASIGLEN];
+#define LTREE_ASIGLEN_DEFAULT	(7 * sizeof(int32))
+#define LTREE_ASIGLEN_MAX		GISTMaxIndexKeySize
+#define LTREE_GET_ASIGLEN()		(PG_HAS_OPCLASS_OPTIONS() ? \
+								 ((LtreeGistOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
+								 LTREE_ASIGLEN_DEFAULT)
+#define ASIGLENBIT(siglen)		((siglen) * BITBYTE)
+
+#define ALOOPBYTE(siglen) \
+			for (i = 0; i < (siglen); i++)
 
-#define ALOOPBYTE \
-			for(i=0;i<ASIGLEN;i++)
+#define AHASHVAL(val, siglen) (((unsigned int)(val)) % ASIGLENBIT(siglen))
+#define AHASH(sign, val, siglen) SETBIT((sign), AHASHVAL(val, siglen))
 
-#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
-#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
+/* gist_ltree_ops and gist__ltree_ops opclass options */
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+} LtreeGistOptions;
 
 /* type of key is the same to ltree_gist */
 
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 6ff4c3548b2..041e28064ff 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -6,11 +6,13 @@
 #include "postgres.h"
 
 #include "access/gist.h"
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "crc32.h"
 #include "ltree.h"
 
 #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
 
 PG_FUNCTION_INFO_V1(ltree_gist_in);
 PG_FUNCTION_INFO_V1(ltree_gist_out);
@@ -33,6 +35,47 @@ ltree_gist_out(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(0);
 }
 
+ltree_gist *
+ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+				 ltree *left, ltree *right)
+{
+	int32		size = LTG_HDRSIZE + (isalltrue ? 0 : siglen) +
+		(left ? VARSIZE(left) + (right ? VARSIZE(right) : 0) : 0);
+	ltree_gist *result = palloc(size);
+
+	SET_VARSIZE(result, size);
+
+	if (siglen)
+	{
+		result->flag = 0;
+
+		if (isalltrue)
+			result->flag |= LTG_ALLTRUE;
+		else if (sign)
+			memcpy(LTG_SIGN(result), sign, siglen);
+		else
+			memset(LTG_SIGN(result), 0, siglen);
+
+		if (left)
+		{
+			memcpy(LTG_LNODE(result, siglen), left, VARSIZE(left));
+
+			if (!right || left == right || ISEQ(left, right))
+				result->flag |= LTG_NORIGHT;
+			else
+				memcpy(LTG_RNODE(result, siglen), right, VARSIZE(right));
+		}
+	}
+	else
+	{
+		Assert(left);
+		result->flag = LTG_ONENODE;
+		memcpy(LTG_NODE(result), left, VARSIZE(left));
+	}
+
+	return result;
+}
+
 PG_FUNCTION_INFO_V1(ltree_compress);
 PG_FUNCTION_INFO_V1(ltree_decompress);
 PG_FUNCTION_INFO_V1(ltree_same);
@@ -40,8 +83,8 @@ PG_FUNCTION_INFO_V1(ltree_union);
 PG_FUNCTION_INFO_V1(ltree_penalty);
 PG_FUNCTION_INFO_V1(ltree_picksplit);
 PG_FUNCTION_INFO_V1(ltree_consistent);
+PG_FUNCTION_INFO_V1(ltree_gist_options);
 
-#define ISEQ(a,b)	( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
 #define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
 
 Datum
@@ -52,14 +95,8 @@ ltree_compress(PG_FUNCTION_ARGS)
 
 	if (entry->leafkey)
 	{							/* ltree */
-		ltree_gist *key;
 		ltree	   *val = DatumGetLtreeP(entry->key);
-		int32		len = LTG_HDRSIZE + VARSIZE(val);
-
-		key = (ltree_gist *) palloc0(len);
-		SET_VARSIZE(key, len);
-		key->flag = LTG_ONENODE;
-		memcpy((void *) LTG_NODE(key), (void *) val, VARSIZE(val));
+		ltree_gist *key = ltree_gist_alloc(false, NULL, 0, val, 0);
 
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(key),
@@ -93,6 +130,7 @@ ltree_same(PG_FUNCTION_ARGS)
 	ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
 	ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = LTREE_GET_ASIGLEN();
 
 	*result = false;
 	if (LTG_ISONENODE(a) != LTG_ISONENODE(b))
@@ -109,15 +147,15 @@ ltree_same(PG_FUNCTION_ARGS)
 		if (LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b))
 			PG_RETURN_POINTER(result);
 
-		if (!ISEQ(LTG_LNODE(a), LTG_LNODE(b)))
+		if (!ISEQ(LTG_LNODE(a, siglen), LTG_LNODE(b, siglen)))
 			PG_RETURN_POINTER(result);
-		if (!ISEQ(LTG_RNODE(a), LTG_RNODE(b)))
+		if (!ISEQ(LTG_RNODE(a, siglen), LTG_RNODE(b, siglen)))
 			PG_RETURN_POINTER(result);
 
 		*result = true;
 		if (!LTG_ISALLTRUE(a))
 		{
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -132,7 +170,7 @@ ltree_same(PG_FUNCTION_ARGS)
 }
 
 static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
 {
 	int			tlen = t->numlevel;
 	ltree_level *cur = LTREE_FIRST(t);
@@ -141,7 +179,7 @@ hashing(BITVECP sign, ltree *t)
 	while (tlen > 0)
 	{
 		hash = ltree_crc32_sz(cur->name, cur->len);
-		HASH(sign, hash);
+		HASH(sign, hash, siglen);
 		cur = LEVEL_NEXT(cur);
 		tlen--;
 	}
@@ -152,7 +190,8 @@ ltree_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
+	int			siglen = LTREE_GET_ASIGLEN();
+	BITVECP		base = palloc0(siglen);
 	int32		i,
 				j;
 	ltree_gist *result,
@@ -161,16 +200,14 @@ ltree_union(PG_FUNCTION_ARGS)
 			   *right = NULL,
 			   *curtree;
 	bool		isalltrue = false;
-	bool		isleqr;
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (j = 0; j < entryvec->n; j++)
 	{
 		cur = GETENTRY(entryvec, j);
 		if (LTG_ISONENODE(cur))
 		{
 			curtree = LTG_NODE(cur);
-			hashing(base, curtree);
+			hashing(base, curtree, siglen);
 			if (!left || ltree_compare(left, curtree) > 0)
 				left = curtree;
 			if (!right || ltree_compare(right, curtree) < 0)
@@ -184,14 +221,14 @@ ltree_union(PG_FUNCTION_ARGS)
 			{
 				BITVECP		sc = LTG_SIGN(cur);
 
-				LOOPBYTE
+				LOOPBYTE(siglen)
 					((unsigned char *) base)[i] |= sc[i];
 			}
 
-			curtree = LTG_LNODE(cur);
+			curtree = LTG_LNODE(cur, siglen);
 			if (!left || ltree_compare(left, curtree) > 0)
 				left = curtree;
-			curtree = LTG_RNODE(cur);
+			curtree = LTG_RNODE(cur, siglen);
 			if (!right || ltree_compare(right, curtree) < 0)
 				right = curtree;
 		}
@@ -200,7 +237,7 @@ ltree_union(PG_FUNCTION_ARGS)
 	if (isalltrue == false)
 	{
 		isalltrue = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) base)[i] != 0xff)
 			{
@@ -210,23 +247,9 @@ ltree_union(PG_FUNCTION_ARGS)
 		}
 	}
 
-	isleqr = (left == right || ISEQ(left, right)) ? true : false;
-	*size = LTG_HDRSIZE + ((isalltrue) ? 0 : SIGLEN) + VARSIZE(left) + ((isleqr) ? 0 : VARSIZE(right));
+	result = ltree_gist_alloc(isalltrue, base, siglen, left, right);
 
-	result = (ltree_gist *) palloc0(*size);
-	SET_VARSIZE(result, *size);
-	result->flag = 0;
-
-	if (isalltrue)
-		result->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(result), base, SIGLEN);
-
-	memcpy((void *) LTG_LNODE(result), (void *) left, VARSIZE(left));
-	if (isleqr)
-		result->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(result), (void *) right, VARSIZE(right));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -237,11 +260,12 @@ ltree_penalty(PG_FUNCTION_ARGS)
 	ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
 	ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
+	int			siglen = LTREE_GET_ASIGLEN();
 	int32		cmpr,
 				cmpl;
 
-	cmpl = ltree_compare(LTG_GETLNODE(origval), LTG_GETLNODE(newval));
-	cmpr = ltree_compare(LTG_GETRNODE(newval), LTG_GETRNODE(origval));
+	cmpl = ltree_compare(LTG_GETLNODE(origval, siglen), LTG_GETLNODE(newval, siglen));
+	cmpr = ltree_compare(LTG_GETRNODE(newval, siglen), LTG_GETRNODE(origval, siglen));
 
 	*penalty = Max(cmpl, 0) + Max(cmpr, 0);
 
@@ -268,26 +292,23 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = LTREE_GET_ASIGLEN();
 	OffsetNumber j;
 	int32		i;
 	RIX		   *array;
 	OffsetNumber maxoff;
 	int			nbytes;
-	int			size;
 	ltree	   *lu_l,
 			   *lu_r,
 			   *ru_l,
 			   *ru_r;
 	ltree_gist *lu,
 			   *ru;
-	BITVEC		ls,
-				rs;
+	BITVECP		ls = palloc0(siglen),
+				rs = palloc0(siglen);
 	bool		lisat = false,
-				risat = false,
-				isleqr;
+				risat = false;
 
-	memset((void *) ls, 0, sizeof(BITVEC));
-	memset((void *) rs, 0, sizeof(BITVEC));
 	maxoff = entryvec->n - 1;
 	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
 	v->spl_left = (OffsetNumber *) palloc(nbytes);
@@ -301,7 +322,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	{
 		array[j].index = j;
 		lu = GETENTRY(entryvec, j); /* use as tmp val */
-		array[j].r = LTG_GETLNODE(lu);
+		array[j].r = LTG_GETLNODE(lu, siglen);
 	}
 
 	qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
@@ -315,10 +336,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		{
 			v->spl_left[v->spl_nleft] = array[j].index;
 			v->spl_nleft++;
-			if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu), lu_r) > 0)
-				lu_r = LTG_GETRNODE(lu);
+			if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), lu_r) > 0)
+				lu_r = LTG_GETRNODE(lu, siglen);
 			if (LTG_ISONENODE(lu))
-				hashing(ls, LTG_NODE(lu));
+				hashing(ls, LTG_NODE(lu), siglen);
 			else
 			{
 				if (lisat || LTG_ISALLTRUE(lu))
@@ -327,7 +348,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 				{
 					BITVECP		sc = LTG_SIGN(lu);
 
-					LOOPBYTE
+					LOOPBYTE(siglen)
 						((unsigned char *) ls)[i] |= sc[i];
 				}
 			}
@@ -336,10 +357,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		{
 			v->spl_right[v->spl_nright] = array[j].index;
 			v->spl_nright++;
-			if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu), ru_r) > 0)
-				ru_r = LTG_GETRNODE(lu);
+			if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), ru_r) > 0)
+				ru_r = LTG_GETRNODE(lu, siglen);
 			if (LTG_ISONENODE(lu))
-				hashing(rs, LTG_NODE(lu));
+				hashing(rs, LTG_NODE(lu), siglen);
 			else
 			{
 				if (risat || LTG_ISALLTRUE(lu))
@@ -348,7 +369,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 				{
 					BITVECP		sc = LTG_SIGN(lu);
 
-					LOOPBYTE
+					LOOPBYTE(siglen)
 						((unsigned char *) rs)[i] |= sc[i];
 				}
 			}
@@ -358,7 +379,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	if (lisat == false)
 	{
 		lisat = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) ls)[i] != 0xff)
 			{
@@ -371,7 +392,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 	if (risat == false)
 	{
 		risat = true;
-		LOOPBYTE
+		LOOPBYTE(siglen)
 		{
 			if (((unsigned char *) rs)[i] != 0xff)
 			{
@@ -381,38 +402,14 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 		}
 	}
 
-	lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index));
-	isleqr = (lu_l == lu_r || ISEQ(lu_l, lu_r)) ? true : false;
-	size = LTG_HDRSIZE + ((lisat) ? 0 : SIGLEN) + VARSIZE(lu_l) + ((isleqr) ? 0 : VARSIZE(lu_r));
-	lu = (ltree_gist *) palloc0(size);
-	SET_VARSIZE(lu, size);
-	lu->flag = 0;
-	if (lisat)
-		lu->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(lu), ls, SIGLEN);
-	memcpy((void *) LTG_LNODE(lu), (void *) lu_l, VARSIZE(lu_l));
-	if (isleqr)
-		lu->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(lu), (void *) lu_r, VARSIZE(lu_r));
+	lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index), siglen);
+	lu = ltree_gist_alloc(lisat, ls, siglen, lu_l, lu_r);
 
+	ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index), siglen);
+	ru = ltree_gist_alloc(risat, rs, siglen, ru_l, ru_r);
 
-	ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index));
-	isleqr = (ru_l == ru_r || ISEQ(ru_l, ru_r)) ? true : false;
-	size = LTG_HDRSIZE + ((risat) ? 0 : SIGLEN) + VARSIZE(ru_l) + ((isleqr) ? 0 : VARSIZE(ru_r));
-	ru = (ltree_gist *) palloc0(size);
-	SET_VARSIZE(ru, size);
-	ru->flag = 0;
-	if (risat)
-		ru->flag |= LTG_ALLTRUE;
-	else
-		memcpy((void *) LTG_SIGN(ru), rs, SIGLEN);
-	memcpy((void *) LTG_LNODE(ru), (void *) ru_l, VARSIZE(ru_l));
-	if (isleqr)
-		ru->flag |= LTG_NORIGHT;
-	else
-		memcpy((void *) LTG_RNODE(ru), (void *) ru_r, VARSIZE(ru_r));
+	pfree(ls);
+	pfree(rs);
 
 	v->spl_ldatum = PointerGetDatum(lu);
 	v->spl_rdatum = PointerGetDatum(ru);
@@ -421,7 +418,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
 }
 
 static bool
-gist_isparent(ltree_gist *key, ltree *query)
+gist_isparent(ltree_gist *key, ltree *query, int siglen)
 {
 	int32		numlevel = query->numlevel;
 	int			i;
@@ -429,7 +426,8 @@ gist_isparent(ltree_gist *key, ltree *query)
 	for (i = query->numlevel; i >= 0; i--)
 	{
 		query->numlevel = i;
-		if (ltree_compare(query, LTG_GETLNODE(key)) >= 0 && ltree_compare(query, LTG_GETRNODE(key)) <= 0)
+		if (ltree_compare(query, LTG_GETLNODE(key, siglen)) >= 0 &&
+			ltree_compare(query, LTG_GETRNODE(key, siglen)) <= 0)
 		{
 			query->numlevel = numlevel;
 			return true;
@@ -450,10 +448,10 @@ copy_ltree(ltree *src)
 }
 
 static bool
-gist_ischild(ltree_gist *key, ltree *query)
+gist_ischild(ltree_gist *key, ltree *query, int siglen)
 {
-	ltree	   *left = copy_ltree(LTG_GETLNODE(key));
-	ltree	   *right = copy_ltree(LTG_GETRNODE(key));
+	ltree	   *left = copy_ltree(LTG_GETLNODE(key, siglen));
+	ltree	   *right = copy_ltree(LTG_GETRNODE(key, siglen));
 	bool		res = true;
 
 	if (left->numlevel > query->numlevel)
@@ -475,7 +473,7 @@ gist_ischild(ltree_gist *key, ltree *query)
 }
 
 static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
 {
 	lquery_level *curq = LQUERY_FIRST(query);
 	BITVECP		sign = LTG_SIGN(key);
@@ -494,7 +492,7 @@ gist_qe(ltree_gist *key, lquery *query)
 
 			while (vlen > 0)
 			{
-				if (GETBIT(sign, HASHVAL(curv->val)))
+				if (GETBIT(sign, HASHVAL(curv->val, siglen)))
 				{
 					isexist = true;
 					break;
@@ -543,39 +541,52 @@ gist_tqcmp(ltree *t, lquery *q)
 }
 
 static bool
-gist_between(ltree_gist *key, lquery *query)
+gist_between(ltree_gist *key, lquery *query, int siglen)
 {
 	if (query->firstgood == 0)
 		return true;
 
-	if (gist_tqcmp(LTG_GETLNODE(key), query) > 0)
+	if (gist_tqcmp(LTG_GETLNODE(key, siglen), query) > 0)
 		return false;
 
-	if (gist_tqcmp(LTG_GETRNODE(key), query) < 0)
+	if (gist_tqcmp(LTG_GETRNODE(key, siglen), query) < 0)
 		return false;
 
 	return true;
 }
 
+typedef struct LtreeSignature
+{
+	BITVECP	sign;
+	int		siglen;
+} LtreeSignature;
+
 static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
 {
-	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, HASHVAL(val->val)) : true;
+	LtreeSignature *sig = cxt;
+
+	return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, HASHVAL(val->val, sig->siglen)) : true;
 }
 
 static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
 {
+	LtreeSignature sig;
+
 	if (LTG_ISALLTRUE(key))
 		return true;
 
+	sig.sign = LTG_SIGN(key);
+	sig.siglen = siglen;
+
 	return ltree_execute(GETQUERY(query),
-						 (void *) LTG_SIGN(key), false,
+						 &sig, false,
 						 checkcondition_bit);
 }
 
 static bool
-arrq_cons(ltree_gist *key, ArrayType *_query)
+arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
 {
 	lquery	   *query = (lquery *) ARR_DATA_PTR(_query);
 	int			num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -591,7 +602,7 @@ arrq_cons(ltree_gist *key, ArrayType *_query)
 
 	while (num > 0)
 	{
-		if (gist_qe(key, query) && gist_between(key, query))
+		if (gist_qe(key, query, siglen) && gist_between(key, query, siglen))
 			return true;
 		num--;
 		query = NEXTVAL(query);
@@ -607,6 +618,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	int			siglen = LTREE_GET_ASIGLEN();
 	ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
 	void	   *query = NULL;
 	bool		res = false;
@@ -621,45 +633,45 @@ ltree_consistent(PG_FUNCTION_ARGS)
 			res = (GIST_LEAF(entry)) ?
 				(ltree_compare((ltree *) query, LTG_NODE(key)) > 0)
 				:
-				(ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+				(ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
 			break;
 		case BTLessEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
-			res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+			res = (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
 			break;
 		case BTEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
 			if (GIST_LEAF(entry))
 				res = (ltree_compare((ltree *) query, LTG_NODE(key)) == 0);
 			else
-				res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0
+				res = (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0
 					   &&
-					   ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+					   ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
 			break;
 		case BTGreaterEqualStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
-			res = (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+			res = (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
 			break;
 		case BTGreaterStrategyNumber:
 			query = PG_GETARG_LTREE_P(1);
 			res = (GIST_LEAF(entry)) ?
-				(ltree_compare((ltree *) query, LTG_GETRNODE(key)) < 0)
+				(ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) < 0)
 				:
-				(ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+				(ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
 			break;
 		case 10:
 			query = PG_GETARG_LTREE_P_COPY(1);
 			res = (GIST_LEAF(entry)) ?
 				inner_isparent((ltree *) query, LTG_NODE(key))
 				:
-				gist_isparent(key, (ltree *) query);
+				gist_isparent(key, (ltree *) query, siglen);
 			break;
 		case 11:
 			query = PG_GETARG_LTREE_P(1);
 			res = (GIST_LEAF(entry)) ?
 				inner_isparent(LTG_NODE(key), (ltree *) query)
 				:
-				gist_ischild(key, (ltree *) query);
+				gist_ischild(key, (ltree *) query, siglen);
 			break;
 		case 12:
 		case 13:
@@ -670,7 +682,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((lquery *) query)
 													   ));
 			else
-				res = (gist_qe(key, (lquery *) query) && gist_between(key, (lquery *) query));
+				res = (gist_qe(key, (lquery *) query, siglen) &&
+					   gist_between(key, (lquery *) query, siglen));
 			break;
 		case 14:
 		case 15:
@@ -681,7 +694,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((ltxtquery *) query)
 													   ));
 			else
-				res = gist_qtxt(key, (ltxtquery *) query);
+				res = gist_qtxt(key, (ltxtquery *) query, siglen);
 			break;
 		case 16:
 		case 17:
@@ -692,7 +705,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
 													   PointerGetDatum((ArrayType *) query)
 													   ));
 			else
-				res = arrq_cons(key, (ArrayType *) query);
+				res = arrq_cons(key, (ArrayType *) query, siglen);
 			break;
 		default:
 			/* internal error */
@@ -702,3 +715,17 @@ ltree_consistent(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(query, 1);
 	PG_RETURN_BOOL(res);
 }
+
+Datum
+ltree_gist_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+	init_local_reloptions(relopts, sizeof(LtreeGistOptions));
+	add_local_int_reloption(relopts, "siglen",
+							"signature length in bytes",
+							SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+							offsetof(LtreeGistOptions, siglen));
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql
index 846b04e48ee..5669737a99b 100644
--- a/contrib/ltree/sql/ltree.sql
+++ b/contrib/ltree/sql/ltree.sql
@@ -266,6 +266,26 @@ SELECT * FROM ltreetest WHERE t ~ '23.*.1' order by t asc;
 SELECT * FROM ltreetest WHERE t ~ '23.*.2' order by t asc;
 SELECT * FROM ltreetest WHERE t ? '{23.*.1,23.*.2}' order by t asc;
 
+drop index tstidx;
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=0));
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2025));
+create index tstidx on ltreetest using gist (t gist_ltree_ops(siglen=2024));
+
+SELECT count(*) FROM ltreetest WHERE t <  '12.3';
+SELECT count(*) FROM ltreetest WHERE t <= '12.3';
+SELECT count(*) FROM ltreetest WHERE t =  '12.3';
+SELECT count(*) FROM ltreetest WHERE t >= '12.3';
+SELECT count(*) FROM ltreetest WHERE t >  '12.3';
+SELECT count(*) FROM ltreetest WHERE t @> '1.1.1';
+SELECT count(*) FROM ltreetest WHERE t <@ '1.1.1';
+SELECT count(*) FROM ltreetest WHERE t @ '23 & 1';
+SELECT count(*) FROM ltreetest WHERE t ~ '1.1.1.*';
+SELECT count(*) FROM ltreetest WHERE t ~ '*.1';
+SELECT count(*) FROM ltreetest WHERE t ~ '23.*{1}.1';
+SELECT count(*) FROM ltreetest WHERE t ~ '23.*.1';
+SELECT count(*) FROM ltreetest WHERE t ~ '23.*.2';
+SELECT count(*) FROM ltreetest WHERE t ? '{23.*.1,23.*.2}';
+
 create table _ltreetest (t ltree[]);
 \copy _ltreetest FROM 'data/_ltree.data'
 
@@ -291,3 +311,18 @@ SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ;
 SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ;
 SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ;
 SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ;
+
+drop index _tstidx;
+create index _tstidx on _ltreetest using gist (t gist__ltree_ops(siglen=0));
+create index _tstidx on _ltreetest using gist (t gist__ltree_ops(siglen=2025));
+create index _tstidx on _ltreetest using gist (t gist__ltree_ops(siglen=2024));
+
+SELECT count(*) FROM _ltreetest WHERE t @> '1.1.1' ;
+SELECT count(*) FROM _ltreetest WHERE t <@ '1.1.1' ;
+SELECT count(*) FROM _ltreetest WHERE t @ '23 & 1' ;
+SELECT count(*) FROM _ltreetest WHERE t ~ '1.1.1.*' ;
+SELECT count(*) FROM _ltreetest WHERE t ~ '*.1' ;
+SELECT count(*) FROM _ltreetest WHERE t ~ '23.*{1}.1' ;
+SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.1' ;
+SELECT count(*) FROM _ltreetest WHERE t ~ '23.*.2' ;
+SELECT count(*) FROM _ltreetest WHERE t ? '{23.*.1,23.*.2}' ;
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index f0a8d9cc777..d75e9ada2e4 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -9,7 +9,7 @@ OBJS = \
 	trgm_regexp.o
 
 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
 PGFILEDESC = "pg_trgm - trigram matching"
diff --git a/contrib/pg_trgm/expected/pg_trgm.out b/contrib/pg_trgm/expected/pg_trgm.out
index 91596f86458..5746be0dc41 100644
--- a/contrib/pg_trgm/expected/pg_trgm.out
+++ b/contrib/pg_trgm/expected/pg_trgm.out
@@ -2363,6 +2363,1163 @@ select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
   1000
 (1 row)
 
+drop index trgm_idx;
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2025));
+ERROR:  value 2025 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2024));
+set enable_seqscan=off;
+select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t;
+      t      |   sml    
+-------------+----------
+ qwertyu0988 |        1
+ qwertyu0980 | 0.714286
+ qwertyu0981 | 0.714286
+ qwertyu0982 | 0.714286
+ qwertyu0983 | 0.714286
+ qwertyu0984 | 0.714286
+ qwertyu0985 | 0.714286
+ qwertyu0986 | 0.714286
+ qwertyu0987 | 0.714286
+ qwertyu0989 | 0.714286
+ qwertyu0088 |      0.6
+ qwertyu0098 |      0.6
+ qwertyu0188 |      0.6
+ qwertyu0288 |      0.6
+ qwertyu0388 |      0.6
+ qwertyu0488 |      0.6
+ qwertyu0588 |      0.6
+ qwertyu0688 |      0.6
+ qwertyu0788 |      0.6
+ qwertyu0888 |      0.6
+ qwertyu0900 |      0.6
+ qwertyu0901 |      0.6
+ qwertyu0902 |      0.6
+ qwertyu0903 |      0.6
+ qwertyu0904 |      0.6
+ qwertyu0905 |      0.6
+ qwertyu0906 |      0.6
+ qwertyu0907 |      0.6
+ qwertyu0908 |      0.6
+ qwertyu0909 |      0.6
+ qwertyu0910 |      0.6
+ qwertyu0911 |      0.6
+ qwertyu0912 |      0.6
+ qwertyu0913 |      0.6
+ qwertyu0914 |      0.6
+ qwertyu0915 |      0.6
+ qwertyu0916 |      0.6
+ qwertyu0917 |      0.6
+ qwertyu0918 |      0.6
+ qwertyu0919 |      0.6
+ qwertyu0920 |      0.6
+ qwertyu0921 |      0.6
+ qwertyu0922 |      0.6
+ qwertyu0923 |      0.6
+ qwertyu0924 |      0.6
+ qwertyu0925 |      0.6
+ qwertyu0926 |      0.6
+ qwertyu0927 |      0.6
+ qwertyu0928 |      0.6
+ qwertyu0929 |      0.6
+ qwertyu0930 |      0.6
+ qwertyu0931 |      0.6
+ qwertyu0932 |      0.6
+ qwertyu0933 |      0.6
+ qwertyu0934 |      0.6
+ qwertyu0935 |      0.6
+ qwertyu0936 |      0.6
+ qwertyu0937 |      0.6
+ qwertyu0938 |      0.6
+ qwertyu0939 |      0.6
+ qwertyu0940 |      0.6
+ qwertyu0941 |      0.6
+ qwertyu0942 |      0.6
+ qwertyu0943 |      0.6
+ qwertyu0944 |      0.6
+ qwertyu0945 |      0.6
+ qwertyu0946 |      0.6
+ qwertyu0947 |      0.6
+ qwertyu0948 |      0.6
+ qwertyu0949 |      0.6
+ qwertyu0950 |      0.6
+ qwertyu0951 |      0.6
+ qwertyu0952 |      0.6
+ qwertyu0953 |      0.6
+ qwertyu0954 |      0.6
+ qwertyu0955 |      0.6
+ qwertyu0956 |      0.6
+ qwertyu0957 |      0.6
+ qwertyu0958 |      0.6
+ qwertyu0959 |      0.6
+ qwertyu0960 |      0.6
+ qwertyu0961 |      0.6
+ qwertyu0962 |      0.6
+ qwertyu0963 |      0.6
+ qwertyu0964 |      0.6
+ qwertyu0965 |      0.6
+ qwertyu0966 |      0.6
+ qwertyu0967 |      0.6
+ qwertyu0968 |      0.6
+ qwertyu0969 |      0.6
+ qwertyu0970 |      0.6
+ qwertyu0971 |      0.6
+ qwertyu0972 |      0.6
+ qwertyu0973 |      0.6
+ qwertyu0974 |      0.6
+ qwertyu0975 |      0.6
+ qwertyu0976 |      0.6
+ qwertyu0977 |      0.6
+ qwertyu0978 |      0.6
+ qwertyu0979 |      0.6
+ qwertyu0990 |      0.6
+ qwertyu0991 |      0.6
+ qwertyu0992 |      0.6
+ qwertyu0993 |      0.6
+ qwertyu0994 |      0.6
+ qwertyu0995 |      0.6
+ qwertyu0996 |      0.6
+ qwertyu0997 |      0.6
+ qwertyu0998 |      0.6
+ qwertyu0999 |      0.6
+ qwertyu0001 |      0.5
+ qwertyu0002 |      0.5
+ qwertyu0003 |      0.5
+ qwertyu0004 |      0.5
+ qwertyu0005 |      0.5
+ qwertyu0006 |      0.5
+ qwertyu0007 |      0.5
+ qwertyu0008 |      0.5
+ qwertyu0009 |      0.5
+ qwertyu0010 |      0.5
+ qwertyu0011 |      0.5
+ qwertyu0012 |      0.5
+ qwertyu0013 |      0.5
+ qwertyu0014 |      0.5
+ qwertyu0015 |      0.5
+ qwertyu0016 |      0.5
+ qwertyu0017 |      0.5
+ qwertyu0018 |      0.5
+ qwertyu0019 |      0.5
+ qwertyu0020 |      0.5
+ qwertyu0021 |      0.5
+ qwertyu0022 |      0.5
+ qwertyu0023 |      0.5
+ qwertyu0024 |      0.5
+ qwertyu0025 |      0.5
+ qwertyu0026 |      0.5
+ qwertyu0027 |      0.5
+ qwertyu0028 |      0.5
+ qwertyu0029 |      0.5
+ qwertyu0030 |      0.5
+ qwertyu0031 |      0.5
+ qwertyu0032 |      0.5
+ qwertyu0033 |      0.5
+ qwertyu0034 |      0.5
+ qwertyu0035 |      0.5
+ qwertyu0036 |      0.5
+ qwertyu0037 |      0.5
+ qwertyu0038 |      0.5
+ qwertyu0039 |      0.5
+ qwertyu0040 |      0.5
+ qwertyu0041 |      0.5
+ qwertyu0042 |      0.5
+ qwertyu0043 |      0.5
+ qwertyu0044 |      0.5
+ qwertyu0045 |      0.5
+ qwertyu0046 |      0.5
+ qwertyu0047 |      0.5
+ qwertyu0048 |      0.5
+ qwertyu0049 |      0.5
+ qwertyu0050 |      0.5
+ qwertyu0051 |      0.5
+ qwertyu0052 |      0.5
+ qwertyu0053 |      0.5
+ qwertyu0054 |      0.5
+ qwertyu0055 |      0.5
+ qwertyu0056 |      0.5
+ qwertyu0057 |      0.5
+ qwertyu0058 |      0.5
+ qwertyu0059 |      0.5
+ qwertyu0060 |      0.5
+ qwertyu0061 |      0.5
+ qwertyu0062 |      0.5
+ qwertyu0063 |      0.5
+ qwertyu0064 |      0.5
+ qwertyu0065 |      0.5
+ qwertyu0066 |      0.5
+ qwertyu0067 |      0.5
+ qwertyu0068 |      0.5
+ qwertyu0069 |      0.5
+ qwertyu0070 |      0.5
+ qwertyu0071 |      0.5
+ qwertyu0072 |      0.5
+ qwertyu0073 |      0.5
+ qwertyu0074 |      0.5
+ qwertyu0075 |      0.5
+ qwertyu0076 |      0.5
+ qwertyu0077 |      0.5
+ qwertyu0078 |      0.5
+ qwertyu0079 |      0.5
+ qwertyu0080 |      0.5
+ qwertyu0081 |      0.5
+ qwertyu0082 |      0.5
+ qwertyu0083 |      0.5
+ qwertyu0084 |      0.5
+ qwertyu0085 |      0.5
+ qwertyu0086 |      0.5
+ qwertyu0087 |      0.5
+ qwertyu0089 |      0.5
+ qwertyu0090 |      0.5
+ qwertyu0091 |      0.5
+ qwertyu0092 |      0.5
+ qwertyu0093 |      0.5
+ qwertyu0094 |      0.5
+ qwertyu0095 |      0.5
+ qwertyu0096 |      0.5
+ qwertyu0097 |      0.5
+ qwertyu0099 |      0.5
+ qwertyu0100 |      0.5
+ qwertyu0101 |      0.5
+ qwertyu0102 |      0.5
+ qwertyu0103 |      0.5
+ qwertyu0104 |      0.5
+ qwertyu0105 |      0.5
+ qwertyu0106 |      0.5
+ qwertyu0107 |      0.5
+ qwertyu0108 |      0.5
+ qwertyu0109 |      0.5
+ qwertyu0110 |      0.5
+ qwertyu0111 |      0.5
+ qwertyu0112 |      0.5
+ qwertyu0113 |      0.5
+ qwertyu0114 |      0.5
+ qwertyu0115 |      0.5
+ qwertyu0116 |      0.5
+ qwertyu0117 |      0.5
+ qwertyu0118 |      0.5
+ qwertyu0119 |      0.5
+ qwertyu0120 |      0.5
+ qwertyu0121 |      0.5
+ qwertyu0122 |      0.5
+ qwertyu0123 |      0.5
+ qwertyu0124 |      0.5
+ qwertyu0125 |      0.5
+ qwertyu0126 |      0.5
+ qwertyu0127 |      0.5
+ qwertyu0128 |      0.5
+ qwertyu0129 |      0.5
+ qwertyu0130 |      0.5
+ qwertyu0131 |      0.5
+ qwertyu0132 |      0.5
+ qwertyu0133 |      0.5
+ qwertyu0134 |      0.5
+ qwertyu0135 |      0.5
+ qwertyu0136 |      0.5
+ qwertyu0137 |      0.5
+ qwertyu0138 |      0.5
+ qwertyu0139 |      0.5
+ qwertyu0140 |      0.5
+ qwertyu0141 |      0.5
+ qwertyu0142 |      0.5
+ qwertyu0143 |      0.5
+ qwertyu0144 |      0.5
+ qwertyu0145 |      0.5
+ qwertyu0146 |      0.5
+ qwertyu0147 |      0.5
+ qwertyu0148 |      0.5
+ qwertyu0149 |      0.5
+ qwertyu0150 |      0.5
+ qwertyu0151 |      0.5
+ qwertyu0152 |      0.5
+ qwertyu0153 |      0.5
+ qwertyu0154 |      0.5
+ qwertyu0155 |      0.5
+ qwertyu0156 |      0.5
+ qwertyu0157 |      0.5
+ qwertyu0158 |      0.5
+ qwertyu0159 |      0.5
+ qwertyu0160 |      0.5
+ qwertyu0161 |      0.5
+ qwertyu0162 |      0.5
+ qwertyu0163 |      0.5
+ qwertyu0164 |      0.5
+ qwertyu0165 |      0.5
+ qwertyu0166 |      0.5
+ qwertyu0167 |      0.5
+ qwertyu0168 |      0.5
+ qwertyu0169 |      0.5
+ qwertyu0170 |      0.5
+ qwertyu0171 |      0.5
+ qwertyu0172 |      0.5
+ qwertyu0173 |      0.5
+ qwertyu0174 |      0.5
+ qwertyu0175 |      0.5
+ qwertyu0176 |      0.5
+ qwertyu0177 |      0.5
+ qwertyu0178 |      0.5
+ qwertyu0179 |      0.5
+ qwertyu0180 |      0.5
+ qwertyu0181 |      0.5
+ qwertyu0182 |      0.5
+ qwertyu0183 |      0.5
+ qwertyu0184 |      0.5
+ qwertyu0185 |      0.5
+ qwertyu0186 |      0.5
+ qwertyu0187 |      0.5
+ qwertyu0189 |      0.5
+ qwertyu0190 |      0.5
+ qwertyu0191 |      0.5
+ qwertyu0192 |      0.5
+ qwertyu0193 |      0.5
+ qwertyu0194 |      0.5
+ qwertyu0195 |      0.5
+ qwertyu0196 |      0.5
+ qwertyu0197 |      0.5
+ qwertyu0198 |      0.5
+ qwertyu0199 |      0.5
+ qwertyu0200 |      0.5
+ qwertyu0201 |      0.5
+ qwertyu0202 |      0.5
+ qwertyu0203 |      0.5
+ qwertyu0204 |      0.5
+ qwertyu0205 |      0.5
+ qwertyu0206 |      0.5
+ qwertyu0207 |      0.5
+ qwertyu0208 |      0.5
+ qwertyu0209 |      0.5
+ qwertyu0210 |      0.5
+ qwertyu0211 |      0.5
+ qwertyu0212 |      0.5
+ qwertyu0213 |      0.5
+ qwertyu0214 |      0.5
+ qwertyu0215 |      0.5
+ qwertyu0216 |      0.5
+ qwertyu0217 |      0.5
+ qwertyu0218 |      0.5
+ qwertyu0219 |      0.5
+ qwertyu0220 |      0.5
+ qwertyu0221 |      0.5
+ qwertyu0222 |      0.5
+ qwertyu0223 |      0.5
+ qwertyu0224 |      0.5
+ qwertyu0225 |      0.5
+ qwertyu0226 |      0.5
+ qwertyu0227 |      0.5
+ qwertyu0228 |      0.5
+ qwertyu0229 |      0.5
+ qwertyu0230 |      0.5
+ qwertyu0231 |      0.5
+ qwertyu0232 |      0.5
+ qwertyu0233 |      0.5
+ qwertyu0234 |      0.5
+ qwertyu0235 |      0.5
+ qwertyu0236 |      0.5
+ qwertyu0237 |      0.5
+ qwertyu0238 |      0.5
+ qwertyu0239 |      0.5
+ qwertyu0240 |      0.5
+ qwertyu0241 |      0.5
+ qwertyu0242 |      0.5
+ qwertyu0243 |      0.5
+ qwertyu0244 |      0.5
+ qwertyu0245 |      0.5
+ qwertyu0246 |      0.5
+ qwertyu0247 |      0.5
+ qwertyu0248 |      0.5
+ qwertyu0249 |      0.5
+ qwertyu0250 |      0.5
+ qwertyu0251 |      0.5
+ qwertyu0252 |      0.5
+ qwertyu0253 |      0.5
+ qwertyu0254 |      0.5
+ qwertyu0255 |      0.5
+ qwertyu0256 |      0.5
+ qwertyu0257 |      0.5
+ qwertyu0258 |      0.5
+ qwertyu0259 |      0.5
+ qwertyu0260 |      0.5
+ qwertyu0261 |      0.5
+ qwertyu0262 |      0.5
+ qwertyu0263 |      0.5
+ qwertyu0264 |      0.5
+ qwertyu0265 |      0.5
+ qwertyu0266 |      0.5
+ qwertyu0267 |      0.5
+ qwertyu0268 |      0.5
+ qwertyu0269 |      0.5
+ qwertyu0270 |      0.5
+ qwertyu0271 |      0.5
+ qwertyu0272 |      0.5
+ qwertyu0273 |      0.5
+ qwertyu0274 |      0.5
+ qwertyu0275 |      0.5
+ qwertyu0276 |      0.5
+ qwertyu0277 |      0.5
+ qwertyu0278 |      0.5
+ qwertyu0279 |      0.5
+ qwertyu0280 |      0.5
+ qwertyu0281 |      0.5
+ qwertyu0282 |      0.5
+ qwertyu0283 |      0.5
+ qwertyu0284 |      0.5
+ qwertyu0285 |      0.5
+ qwertyu0286 |      0.5
+ qwertyu0287 |      0.5
+ qwertyu0289 |      0.5
+ qwertyu0290 |      0.5
+ qwertyu0291 |      0.5
+ qwertyu0292 |      0.5
+ qwertyu0293 |      0.5
+ qwertyu0294 |      0.5
+ qwertyu0295 |      0.5
+ qwertyu0296 |      0.5
+ qwertyu0297 |      0.5
+ qwertyu0298 |      0.5
+ qwertyu0299 |      0.5
+ qwertyu0300 |      0.5
+ qwertyu0301 |      0.5
+ qwertyu0302 |      0.5
+ qwertyu0303 |      0.5
+ qwertyu0304 |      0.5
+ qwertyu0305 |      0.5
+ qwertyu0306 |      0.5
+ qwertyu0307 |      0.5
+ qwertyu0308 |      0.5
+ qwertyu0309 |      0.5
+ qwertyu0310 |      0.5
+ qwertyu0311 |      0.5
+ qwertyu0312 |      0.5
+ qwertyu0313 |      0.5
+ qwertyu0314 |      0.5
+ qwertyu0315 |      0.5
+ qwertyu0316 |      0.5
+ qwertyu0317 |      0.5
+ qwertyu0318 |      0.5
+ qwertyu0319 |      0.5
+ qwertyu0320 |      0.5
+ qwertyu0321 |      0.5
+ qwertyu0322 |      0.5
+ qwertyu0323 |      0.5
+ qwertyu0324 |      0.5
+ qwertyu0325 |      0.5
+ qwertyu0326 |      0.5
+ qwertyu0327 |      0.5
+ qwertyu0328 |      0.5
+ qwertyu0329 |      0.5
+ qwertyu0330 |      0.5
+ qwertyu0331 |      0.5
+ qwertyu0332 |      0.5
+ qwertyu0333 |      0.5
+ qwertyu0334 |      0.5
+ qwertyu0335 |      0.5
+ qwertyu0336 |      0.5
+ qwertyu0337 |      0.5
+ qwertyu0338 |      0.5
+ qwertyu0339 |      0.5
+ qwertyu0340 |      0.5
+ qwertyu0341 |      0.5
+ qwertyu0342 |      0.5
+ qwertyu0343 |      0.5
+ qwertyu0344 |      0.5
+ qwertyu0345 |      0.5
+ qwertyu0346 |      0.5
+ qwertyu0347 |      0.5
+ qwertyu0348 |      0.5
+ qwertyu0349 |      0.5
+ qwertyu0350 |      0.5
+ qwertyu0351 |      0.5
+ qwertyu0352 |      0.5
+ qwertyu0353 |      0.5
+ qwertyu0354 |      0.5
+ qwertyu0355 |      0.5
+ qwertyu0356 |      0.5
+ qwertyu0357 |      0.5
+ qwertyu0358 |      0.5
+ qwertyu0359 |      0.5
+ qwertyu0360 |      0.5
+ qwertyu0361 |      0.5
+ qwertyu0362 |      0.5
+ qwertyu0363 |      0.5
+ qwertyu0364 |      0.5
+ qwertyu0365 |      0.5
+ qwertyu0366 |      0.5
+ qwertyu0367 |      0.5
+ qwertyu0368 |      0.5
+ qwertyu0369 |      0.5
+ qwertyu0370 |      0.5
+ qwertyu0371 |      0.5
+ qwertyu0372 |      0.5
+ qwertyu0373 |      0.5
+ qwertyu0374 |      0.5
+ qwertyu0375 |      0.5
+ qwertyu0376 |      0.5
+ qwertyu0377 |      0.5
+ qwertyu0378 |      0.5
+ qwertyu0379 |      0.5
+ qwertyu0380 |      0.5
+ qwertyu0381 |      0.5
+ qwertyu0382 |      0.5
+ qwertyu0383 |      0.5
+ qwertyu0384 |      0.5
+ qwertyu0385 |      0.5
+ qwertyu0386 |      0.5
+ qwertyu0387 |      0.5
+ qwertyu0389 |      0.5
+ qwertyu0390 |      0.5
+ qwertyu0391 |      0.5
+ qwertyu0392 |      0.5
+ qwertyu0393 |      0.5
+ qwertyu0394 |      0.5
+ qwertyu0395 |      0.5
+ qwertyu0396 |      0.5
+ qwertyu0397 |      0.5
+ qwertyu0398 |      0.5
+ qwertyu0399 |      0.5
+ qwertyu0400 |      0.5
+ qwertyu0401 |      0.5
+ qwertyu0402 |      0.5
+ qwertyu0403 |      0.5
+ qwertyu0404 |      0.5
+ qwertyu0405 |      0.5
+ qwertyu0406 |      0.5
+ qwertyu0407 |      0.5
+ qwertyu0408 |      0.5
+ qwertyu0409 |      0.5
+ qwertyu0410 |      0.5
+ qwertyu0411 |      0.5
+ qwertyu0412 |      0.5
+ qwertyu0413 |      0.5
+ qwertyu0414 |      0.5
+ qwertyu0415 |      0.5
+ qwertyu0416 |      0.5
+ qwertyu0417 |      0.5
+ qwertyu0418 |      0.5
+ qwertyu0419 |      0.5
+ qwertyu0420 |      0.5
+ qwertyu0421 |      0.5
+ qwertyu0422 |      0.5
+ qwertyu0423 |      0.5
+ qwertyu0424 |      0.5
+ qwertyu0425 |      0.5
+ qwertyu0426 |      0.5
+ qwertyu0427 |      0.5
+ qwertyu0428 |      0.5
+ qwertyu0429 |      0.5
+ qwertyu0430 |      0.5
+ qwertyu0431 |      0.5
+ qwertyu0432 |      0.5
+ qwertyu0433 |      0.5
+ qwertyu0434 |      0.5
+ qwertyu0435 |      0.5
+ qwertyu0436 |      0.5
+ qwertyu0437 |      0.5
+ qwertyu0438 |      0.5
+ qwertyu0439 |      0.5
+ qwertyu0440 |      0.5
+ qwertyu0441 |      0.5
+ qwertyu0442 |      0.5
+ qwertyu0443 |      0.5
+ qwertyu0444 |      0.5
+ qwertyu0445 |      0.5
+ qwertyu0446 |      0.5
+ qwertyu0447 |      0.5
+ qwertyu0448 |      0.5
+ qwertyu0449 |      0.5
+ qwertyu0450 |      0.5
+ qwertyu0451 |      0.5
+ qwertyu0452 |      0.5
+ qwertyu0453 |      0.5
+ qwertyu0454 |      0.5
+ qwertyu0455 |      0.5
+ qwertyu0456 |      0.5
+ qwertyu0457 |      0.5
+ qwertyu0458 |      0.5
+ qwertyu0459 |      0.5
+ qwertyu0460 |      0.5
+ qwertyu0461 |      0.5
+ qwertyu0462 |      0.5
+ qwertyu0463 |      0.5
+ qwertyu0464 |      0.5
+ qwertyu0465 |      0.5
+ qwertyu0466 |      0.5
+ qwertyu0467 |      0.5
+ qwertyu0468 |      0.5
+ qwertyu0469 |      0.5
+ qwertyu0470 |      0.5
+ qwertyu0471 |      0.5
+ qwertyu0472 |      0.5
+ qwertyu0473 |      0.5
+ qwertyu0474 |      0.5
+ qwertyu0475 |      0.5
+ qwertyu0476 |      0.5
+ qwertyu0477 |      0.5
+ qwertyu0478 |      0.5
+ qwertyu0479 |      0.5
+ qwertyu0480 |      0.5
+ qwertyu0481 |      0.5
+ qwertyu0482 |      0.5
+ qwertyu0483 |      0.5
+ qwertyu0484 |      0.5
+ qwertyu0485 |      0.5
+ qwertyu0486 |      0.5
+ qwertyu0487 |      0.5
+ qwertyu0489 |      0.5
+ qwertyu0490 |      0.5
+ qwertyu0491 |      0.5
+ qwertyu0492 |      0.5
+ qwertyu0493 |      0.5
+ qwertyu0494 |      0.5
+ qwertyu0495 |      0.5
+ qwertyu0496 |      0.5
+ qwertyu0497 |      0.5
+ qwertyu0498 |      0.5
+ qwertyu0499 |      0.5
+ qwertyu0500 |      0.5
+ qwertyu0501 |      0.5
+ qwertyu0502 |      0.5
+ qwertyu0503 |      0.5
+ qwertyu0504 |      0.5
+ qwertyu0505 |      0.5
+ qwertyu0506 |      0.5
+ qwertyu0507 |      0.5
+ qwertyu0508 |      0.5
+ qwertyu0509 |      0.5
+ qwertyu0510 |      0.5
+ qwertyu0511 |      0.5
+ qwertyu0512 |      0.5
+ qwertyu0513 |      0.5
+ qwertyu0514 |      0.5
+ qwertyu0515 |      0.5
+ qwertyu0516 |      0.5
+ qwertyu0517 |      0.5
+ qwertyu0518 |      0.5
+ qwertyu0519 |      0.5
+ qwertyu0520 |      0.5
+ qwertyu0521 |      0.5
+ qwertyu0522 |      0.5
+ qwertyu0523 |      0.5
+ qwertyu0524 |      0.5
+ qwertyu0525 |      0.5
+ qwertyu0526 |      0.5
+ qwertyu0527 |      0.5
+ qwertyu0528 |      0.5
+ qwertyu0529 |      0.5
+ qwertyu0530 |      0.5
+ qwertyu0531 |      0.5
+ qwertyu0532 |      0.5
+ qwertyu0533 |      0.5
+ qwertyu0534 |      0.5
+ qwertyu0535 |      0.5
+ qwertyu0536 |      0.5
+ qwertyu0537 |      0.5
+ qwertyu0538 |      0.5
+ qwertyu0539 |      0.5
+ qwertyu0540 |      0.5
+ qwertyu0541 |      0.5
+ qwertyu0542 |      0.5
+ qwertyu0543 |      0.5
+ qwertyu0544 |      0.5
+ qwertyu0545 |      0.5
+ qwertyu0546 |      0.5
+ qwertyu0547 |      0.5
+ qwertyu0548 |      0.5
+ qwertyu0549 |      0.5
+ qwertyu0550 |      0.5
+ qwertyu0551 |      0.5
+ qwertyu0552 |      0.5
+ qwertyu0553 |      0.5
+ qwertyu0554 |      0.5
+ qwertyu0555 |      0.5
+ qwertyu0556 |      0.5
+ qwertyu0557 |      0.5
+ qwertyu0558 |      0.5
+ qwertyu0559 |      0.5
+ qwertyu0560 |      0.5
+ qwertyu0561 |      0.5
+ qwertyu0562 |      0.5
+ qwertyu0563 |      0.5
+ qwertyu0564 |      0.5
+ qwertyu0565 |      0.5
+ qwertyu0566 |      0.5
+ qwertyu0567 |      0.5
+ qwertyu0568 |      0.5
+ qwertyu0569 |      0.5
+ qwertyu0570 |      0.5
+ qwertyu0571 |      0.5
+ qwertyu0572 |      0.5
+ qwertyu0573 |      0.5
+ qwertyu0574 |      0.5
+ qwertyu0575 |      0.5
+ qwertyu0576 |      0.5
+ qwertyu0577 |      0.5
+ qwertyu0578 |      0.5
+ qwertyu0579 |      0.5
+ qwertyu0580 |      0.5
+ qwertyu0581 |      0.5
+ qwertyu0582 |      0.5
+ qwertyu0583 |      0.5
+ qwertyu0584 |      0.5
+ qwertyu0585 |      0.5
+ qwertyu0586 |      0.5
+ qwertyu0587 |      0.5
+ qwertyu0589 |      0.5
+ qwertyu0590 |      0.5
+ qwertyu0591 |      0.5
+ qwertyu0592 |      0.5
+ qwertyu0593 |      0.5
+ qwertyu0594 |      0.5
+ qwertyu0595 |      0.5
+ qwertyu0596 |      0.5
+ qwertyu0597 |      0.5
+ qwertyu0598 |      0.5
+ qwertyu0599 |      0.5
+ qwertyu0600 |      0.5
+ qwertyu0601 |      0.5
+ qwertyu0602 |      0.5
+ qwertyu0603 |      0.5
+ qwertyu0604 |      0.5
+ qwertyu0605 |      0.5
+ qwertyu0606 |      0.5
+ qwertyu0607 |      0.5
+ qwertyu0608 |      0.5
+ qwertyu0609 |      0.5
+ qwertyu0610 |      0.5
+ qwertyu0611 |      0.5
+ qwertyu0612 |      0.5
+ qwertyu0613 |      0.5
+ qwertyu0614 |      0.5
+ qwertyu0615 |      0.5
+ qwertyu0616 |      0.5
+ qwertyu0617 |      0.5
+ qwertyu0618 |      0.5
+ qwertyu0619 |      0.5
+ qwertyu0620 |      0.5
+ qwertyu0621 |      0.5
+ qwertyu0622 |      0.5
+ qwertyu0623 |      0.5
+ qwertyu0624 |      0.5
+ qwertyu0625 |      0.5
+ qwertyu0626 |      0.5
+ qwertyu0627 |      0.5
+ qwertyu0628 |      0.5
+ qwertyu0629 |      0.5
+ qwertyu0630 |      0.5
+ qwertyu0631 |      0.5
+ qwertyu0632 |      0.5
+ qwertyu0633 |      0.5
+ qwertyu0634 |      0.5
+ qwertyu0635 |      0.5
+ qwertyu0636 |      0.5
+ qwertyu0637 |      0.5
+ qwertyu0638 |      0.5
+ qwertyu0639 |      0.5
+ qwertyu0640 |      0.5
+ qwertyu0641 |      0.5
+ qwertyu0642 |      0.5
+ qwertyu0643 |      0.5
+ qwertyu0644 |      0.5
+ qwertyu0645 |      0.5
+ qwertyu0646 |      0.5
+ qwertyu0647 |      0.5
+ qwertyu0648 |      0.5
+ qwertyu0649 |      0.5
+ qwertyu0650 |      0.5
+ qwertyu0651 |      0.5
+ qwertyu0652 |      0.5
+ qwertyu0653 |      0.5
+ qwertyu0654 |      0.5
+ qwertyu0655 |      0.5
+ qwertyu0656 |      0.5
+ qwertyu0657 |      0.5
+ qwertyu0658 |      0.5
+ qwertyu0659 |      0.5
+ qwertyu0660 |      0.5
+ qwertyu0661 |      0.5
+ qwertyu0662 |      0.5
+ qwertyu0663 |      0.5
+ qwertyu0664 |      0.5
+ qwertyu0665 |      0.5
+ qwertyu0666 |      0.5
+ qwertyu0667 |      0.5
+ qwertyu0668 |      0.5
+ qwertyu0669 |      0.5
+ qwertyu0670 |      0.5
+ qwertyu0671 |      0.5
+ qwertyu0672 |      0.5
+ qwertyu0673 |      0.5
+ qwertyu0674 |      0.5
+ qwertyu0675 |      0.5
+ qwertyu0676 |      0.5
+ qwertyu0677 |      0.5
+ qwertyu0678 |      0.5
+ qwertyu0679 |      0.5
+ qwertyu0680 |      0.5
+ qwertyu0681 |      0.5
+ qwertyu0682 |      0.5
+ qwertyu0683 |      0.5
+ qwertyu0684 |      0.5
+ qwertyu0685 |      0.5
+ qwertyu0686 |      0.5
+ qwertyu0687 |      0.5
+ qwertyu0689 |      0.5
+ qwertyu0690 |      0.5
+ qwertyu0691 |      0.5
+ qwertyu0692 |      0.5
+ qwertyu0693 |      0.5
+ qwertyu0694 |      0.5
+ qwertyu0695 |      0.5
+ qwertyu0696 |      0.5
+ qwertyu0697 |      0.5
+ qwertyu0698 |      0.5
+ qwertyu0699 |      0.5
+ qwertyu0700 |      0.5
+ qwertyu0701 |      0.5
+ qwertyu0702 |      0.5
+ qwertyu0703 |      0.5
+ qwertyu0704 |      0.5
+ qwertyu0705 |      0.5
+ qwertyu0706 |      0.5
+ qwertyu0707 |      0.5
+ qwertyu0708 |      0.5
+ qwertyu0709 |      0.5
+ qwertyu0710 |      0.5
+ qwertyu0711 |      0.5
+ qwertyu0712 |      0.5
+ qwertyu0713 |      0.5
+ qwertyu0714 |      0.5
+ qwertyu0715 |      0.5
+ qwertyu0716 |      0.5
+ qwertyu0717 |      0.5
+ qwertyu0718 |      0.5
+ qwertyu0719 |      0.5
+ qwertyu0720 |      0.5
+ qwertyu0721 |      0.5
+ qwertyu0722 |      0.5
+ qwertyu0723 |      0.5
+ qwertyu0724 |      0.5
+ qwertyu0725 |      0.5
+ qwertyu0726 |      0.5
+ qwertyu0727 |      0.5
+ qwertyu0728 |      0.5
+ qwertyu0729 |      0.5
+ qwertyu0730 |      0.5
+ qwertyu0731 |      0.5
+ qwertyu0732 |      0.5
+ qwertyu0733 |      0.5
+ qwertyu0734 |      0.5
+ qwertyu0735 |      0.5
+ qwertyu0736 |      0.5
+ qwertyu0737 |      0.5
+ qwertyu0738 |      0.5
+ qwertyu0739 |      0.5
+ qwertyu0740 |      0.5
+ qwertyu0741 |      0.5
+ qwertyu0742 |      0.5
+ qwertyu0743 |      0.5
+ qwertyu0744 |      0.5
+ qwertyu0745 |      0.5
+ qwertyu0746 |      0.5
+ qwertyu0747 |      0.5
+ qwertyu0748 |      0.5
+ qwertyu0749 |      0.5
+ qwertyu0750 |      0.5
+ qwertyu0751 |      0.5
+ qwertyu0752 |      0.5
+ qwertyu0753 |      0.5
+ qwertyu0754 |      0.5
+ qwertyu0755 |      0.5
+ qwertyu0756 |      0.5
+ qwertyu0757 |      0.5
+ qwertyu0758 |      0.5
+ qwertyu0759 |      0.5
+ qwertyu0760 |      0.5
+ qwertyu0761 |      0.5
+ qwertyu0762 |      0.5
+ qwertyu0763 |      0.5
+ qwertyu0764 |      0.5
+ qwertyu0765 |      0.5
+ qwertyu0766 |      0.5
+ qwertyu0767 |      0.5
+ qwertyu0768 |      0.5
+ qwertyu0769 |      0.5
+ qwertyu0770 |      0.5
+ qwertyu0771 |      0.5
+ qwertyu0772 |      0.5
+ qwertyu0773 |      0.5
+ qwertyu0774 |      0.5
+ qwertyu0775 |      0.5
+ qwertyu0776 |      0.5
+ qwertyu0777 |      0.5
+ qwertyu0778 |      0.5
+ qwertyu0779 |      0.5
+ qwertyu0780 |      0.5
+ qwertyu0781 |      0.5
+ qwertyu0782 |      0.5
+ qwertyu0783 |      0.5
+ qwertyu0784 |      0.5
+ qwertyu0785 |      0.5
+ qwertyu0786 |      0.5
+ qwertyu0787 |      0.5
+ qwertyu0789 |      0.5
+ qwertyu0790 |      0.5
+ qwertyu0791 |      0.5
+ qwertyu0792 |      0.5
+ qwertyu0793 |      0.5
+ qwertyu0794 |      0.5
+ qwertyu0795 |      0.5
+ qwertyu0796 |      0.5
+ qwertyu0797 |      0.5
+ qwertyu0798 |      0.5
+ qwertyu0799 |      0.5
+ qwertyu0800 |      0.5
+ qwertyu0801 |      0.5
+ qwertyu0802 |      0.5
+ qwertyu0803 |      0.5
+ qwertyu0804 |      0.5
+ qwertyu0805 |      0.5
+ qwertyu0806 |      0.5
+ qwertyu0807 |      0.5
+ qwertyu0808 |      0.5
+ qwertyu0809 |      0.5
+ qwertyu0810 |      0.5
+ qwertyu0811 |      0.5
+ qwertyu0812 |      0.5
+ qwertyu0813 |      0.5
+ qwertyu0814 |      0.5
+ qwertyu0815 |      0.5
+ qwertyu0816 |      0.5
+ qwertyu0817 |      0.5
+ qwertyu0818 |      0.5
+ qwertyu0819 |      0.5
+ qwertyu0820 |      0.5
+ qwertyu0821 |      0.5
+ qwertyu0822 |      0.5
+ qwertyu0823 |      0.5
+ qwertyu0824 |      0.5
+ qwertyu0825 |      0.5
+ qwertyu0826 |      0.5
+ qwertyu0827 |      0.5
+ qwertyu0828 |      0.5
+ qwertyu0829 |      0.5
+ qwertyu0830 |      0.5
+ qwertyu0831 |      0.5
+ qwertyu0832 |      0.5
+ qwertyu0833 |      0.5
+ qwertyu0834 |      0.5
+ qwertyu0835 |      0.5
+ qwertyu0836 |      0.5
+ qwertyu0837 |      0.5
+ qwertyu0838 |      0.5
+ qwertyu0839 |      0.5
+ qwertyu0840 |      0.5
+ qwertyu0841 |      0.5
+ qwertyu0842 |      0.5
+ qwertyu0843 |      0.5
+ qwertyu0844 |      0.5
+ qwertyu0845 |      0.5
+ qwertyu0846 |      0.5
+ qwertyu0847 |      0.5
+ qwertyu0848 |      0.5
+ qwertyu0849 |      0.5
+ qwertyu0850 |      0.5
+ qwertyu0851 |      0.5
+ qwertyu0852 |      0.5
+ qwertyu0853 |      0.5
+ qwertyu0854 |      0.5
+ qwertyu0855 |      0.5
+ qwertyu0856 |      0.5
+ qwertyu0857 |      0.5
+ qwertyu0858 |      0.5
+ qwertyu0859 |      0.5
+ qwertyu0860 |      0.5
+ qwertyu0861 |      0.5
+ qwertyu0862 |      0.5
+ qwertyu0863 |      0.5
+ qwertyu0864 |      0.5
+ qwertyu0865 |      0.5
+ qwertyu0866 |      0.5
+ qwertyu0867 |      0.5
+ qwertyu0868 |      0.5
+ qwertyu0869 |      0.5
+ qwertyu0870 |      0.5
+ qwertyu0871 |      0.5
+ qwertyu0872 |      0.5
+ qwertyu0873 |      0.5
+ qwertyu0874 |      0.5
+ qwertyu0875 |      0.5
+ qwertyu0876 |      0.5
+ qwertyu0877 |      0.5
+ qwertyu0878 |      0.5
+ qwertyu0879 |      0.5
+ qwertyu0880 |      0.5
+ qwertyu0881 |      0.5
+ qwertyu0882 |      0.5
+ qwertyu0883 |      0.5
+ qwertyu0884 |      0.5
+ qwertyu0885 |      0.5
+ qwertyu0886 |      0.5
+ qwertyu0887 |      0.5
+ qwertyu0889 |      0.5
+ qwertyu0890 |      0.5
+ qwertyu0891 |      0.5
+ qwertyu0892 |      0.5
+ qwertyu0893 |      0.5
+ qwertyu0894 |      0.5
+ qwertyu0895 |      0.5
+ qwertyu0896 |      0.5
+ qwertyu0897 |      0.5
+ qwertyu0898 |      0.5
+ qwertyu0899 |      0.5
+ qwertyu1000 | 0.411765
+(1000 rows)
+
+select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t;
+      t      |   sml    
+-------------+----------
+ qwertyu0988 |      0.6
+ qwertyu0980 | 0.411765
+ qwertyu0981 | 0.411765
+ qwertyu0982 | 0.411765
+ qwertyu0983 | 0.411765
+ qwertyu0984 | 0.411765
+ qwertyu0985 | 0.411765
+ qwertyu0986 | 0.411765
+ qwertyu0987 | 0.411765
+ qwertyu0989 | 0.411765
+ qwertyu0088 | 0.333333
+ qwertyu0098 | 0.333333
+ qwertyu0188 | 0.333333
+ qwertyu0288 | 0.333333
+ qwertyu0388 | 0.333333
+ qwertyu0488 | 0.333333
+ qwertyu0588 | 0.333333
+ qwertyu0688 | 0.333333
+ qwertyu0788 | 0.333333
+ qwertyu0888 | 0.333333
+ qwertyu0900 | 0.333333
+ qwertyu0901 | 0.333333
+ qwertyu0902 | 0.333333
+ qwertyu0903 | 0.333333
+ qwertyu0904 | 0.333333
+ qwertyu0905 | 0.333333
+ qwertyu0906 | 0.333333
+ qwertyu0907 | 0.333333
+ qwertyu0908 | 0.333333
+ qwertyu0909 | 0.333333
+ qwertyu0910 | 0.333333
+ qwertyu0911 | 0.333333
+ qwertyu0912 | 0.333333
+ qwertyu0913 | 0.333333
+ qwertyu0914 | 0.333333
+ qwertyu0915 | 0.333333
+ qwertyu0916 | 0.333333
+ qwertyu0917 | 0.333333
+ qwertyu0918 | 0.333333
+ qwertyu0919 | 0.333333
+ qwertyu0920 | 0.333333
+ qwertyu0921 | 0.333333
+ qwertyu0922 | 0.333333
+ qwertyu0923 | 0.333333
+ qwertyu0924 | 0.333333
+ qwertyu0925 | 0.333333
+ qwertyu0926 | 0.333333
+ qwertyu0927 | 0.333333
+ qwertyu0928 | 0.333333
+ qwertyu0929 | 0.333333
+ qwertyu0930 | 0.333333
+ qwertyu0931 | 0.333333
+ qwertyu0932 | 0.333333
+ qwertyu0933 | 0.333333
+ qwertyu0934 | 0.333333
+ qwertyu0935 | 0.333333
+ qwertyu0936 | 0.333333
+ qwertyu0937 | 0.333333
+ qwertyu0938 | 0.333333
+ qwertyu0939 | 0.333333
+ qwertyu0940 | 0.333333
+ qwertyu0941 | 0.333333
+ qwertyu0942 | 0.333333
+ qwertyu0943 | 0.333333
+ qwertyu0944 | 0.333333
+ qwertyu0945 | 0.333333
+ qwertyu0946 | 0.333333
+ qwertyu0947 | 0.333333
+ qwertyu0948 | 0.333333
+ qwertyu0949 | 0.333333
+ qwertyu0950 | 0.333333
+ qwertyu0951 | 0.333333
+ qwertyu0952 | 0.333333
+ qwertyu0953 | 0.333333
+ qwertyu0954 | 0.333333
+ qwertyu0955 | 0.333333
+ qwertyu0956 | 0.333333
+ qwertyu0957 | 0.333333
+ qwertyu0958 | 0.333333
+ qwertyu0959 | 0.333333
+ qwertyu0960 | 0.333333
+ qwertyu0961 | 0.333333
+ qwertyu0962 | 0.333333
+ qwertyu0963 | 0.333333
+ qwertyu0964 | 0.333333
+ qwertyu0965 | 0.333333
+ qwertyu0966 | 0.333333
+ qwertyu0967 | 0.333333
+ qwertyu0968 | 0.333333
+ qwertyu0969 | 0.333333
+ qwertyu0970 | 0.333333
+ qwertyu0971 | 0.333333
+ qwertyu0972 | 0.333333
+ qwertyu0973 | 0.333333
+ qwertyu0974 | 0.333333
+ qwertyu0975 | 0.333333
+ qwertyu0976 | 0.333333
+ qwertyu0977 | 0.333333
+ qwertyu0978 | 0.333333
+ qwertyu0979 | 0.333333
+ qwertyu0990 | 0.333333
+ qwertyu0991 | 0.333333
+ qwertyu0992 | 0.333333
+ qwertyu0993 | 0.333333
+ qwertyu0994 | 0.333333
+ qwertyu0995 | 0.333333
+ qwertyu0996 | 0.333333
+ qwertyu0997 | 0.333333
+ qwertyu0998 | 0.333333
+ qwertyu0999 | 0.333333
+(110 rows)
+
+select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t;
+      t      |   sml    
+-------------+----------
+ qwertyu0988 | 0.333333
+(1 row)
+
+explain (costs off)
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+                    QUERY PLAN                     
+---------------------------------------------------
+ Limit
+   ->  Index Scan using trgm_idx on test_trgm
+         Order By: (t <-> 'q0987wertyu0988'::text)
+(3 rows)
+
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+ ?column? |      t      
+----------+-------------
+ 0.411765 | qwertyu0988
+      0.5 | qwertyu0987
+(2 rows)
+
+select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
+ count 
+-------
+  1000
+(1 row)
+
 drop index trgm_idx;
 create index trgm_idx on test_trgm using gin (t gin_trgm_ops);
 set enable_seqscan=off;
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 00000000000..3804c3bc692
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
@@ -0,0 +1,12 @@
+/* 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
+
+CREATE FUNCTION gtrgm_options(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', 'gtrgm_options'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_trgm_ops USING gist
+ADD FUNCTION 10 (text) gtrgm_options (internal);
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 831ba2391b9..ed4487e96b2 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,6 +1,6 @@
 # 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
 trusted = true
diff --git a/contrib/pg_trgm/sql/pg_trgm.sql b/contrib/pg_trgm/sql/pg_trgm.sql
index 2019d1f6be8..bc2a6d525cc 100644
--- a/contrib/pg_trgm/sql/pg_trgm.sql
+++ b/contrib/pg_trgm/sql/pg_trgm.sql
@@ -46,6 +46,20 @@ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988
 select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
 select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
 
+drop index trgm_idx;
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=0));
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2025));
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2024));
+set enable_seqscan=off;
+
+select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t;
+select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t;
+select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t;
+explain (costs off)
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
+
 drop index trgm_idx;
 create index trgm_idx on test_trgm using gin (t gin_trgm_ops);
 set enable_seqscan=off;
diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h
index 0fd600d5810..0c34b96d808 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		GISTMaxIndexKeySize
 #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 35a75c60668..9937ef92531 100644
--- a/contrib/pg_trgm/trgm_gist.c
+++ b/contrib/pg_trgm/trgm_gist.c
@@ -3,11 +3,23 @@
  */
 #include "postgres.h"
 
+#include "access/reloptions.h"
 #include "access/stratnum.h"
 #include "fmgr.h"
 #include "port/pg_bitutils.h"
 #include "trgm.h"
 
+/* gist_trgm_ops opclass options */
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length in bytes */
+} TrgmGistOptions;
+
+#define LTREE_GET_ASIGLEN()		(PG_HAS_OPCLASS_OPTIONS() ? \
+								 ((TrgmGistOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
+								 SIGLEN_DEFAULT)
+
 typedef struct
 {
 	/* most recent inputs to gtrgm_consistent */
@@ -37,6 +49,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);
 
 
 Datum
@@ -53,20 +66,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);
 	}
 }
 
@@ -74,6 +108,7 @@ Datum
 gtrgm_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = LTREE_GET_ASIGLEN();
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
@@ -90,22 +125,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,
@@ -139,7 +169,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,
@@ -150,7 +180,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;
@@ -165,6 +195,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	int			siglen = LTREE_GET_ASIGLEN();
 	TRGM	   *key = (TRGM *) DatumGetPointer(entry->key);
 	TRGM	   *qtrg;
 	bool		res;
@@ -292,7 +323,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)
@@ -334,7 +365,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;
@@ -387,7 +418,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);
@@ -417,6 +448,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
 
 	/* Oid		subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
+	int			siglen = LTREE_GET_ASIGLEN();
 	TRGM	   *key = (TRGM *) DatumGetPointer(entry->key);
 	TRGM	   *qtrg;
 	float8		res;
@@ -474,7 +506,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);
@@ -490,7 +522,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, TRGM *add)
+unionkey(BITVECP sbase, TRGM *add, int siglen)
 {
 	int32		i;
 
@@ -501,7 +533,7 @@ unionkey(BITVECP sbase, TRGM *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -512,7 +544,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;
@@ -525,29 +557,22 @@ 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;
+	int			siglen = LTREE_GET_ASIGLEN();
 	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);
 }
@@ -558,6 +583,7 @@ 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);
+	int			siglen = LTREE_GET_ASIGLEN();
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -574,7 +600,7 @@ gtrgm_same(PG_FUNCTION_ARGS)
 						sb = GETSIGN(b);
 
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -611,19 +637,19 @@ gtrgm_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
-	return pg_popcount(sign, SIGLEN);
+	return pg_popcount(sign, siglen);
 }
 
 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]);
 		/* Using the popcount functions here isn't likely to win */
@@ -633,19 +659,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
@@ -654,6 +680,7 @@ 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);
+	int			siglen = LTREE_GET_ASIGLEN();
 	TRGM	   *origval = (TRGM *) DatumGetPointer(origentry->key);
 	TRGM	   *newval = (TRGM *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -663,7 +690,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;
 
@@ -677,12 +704,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)
@@ -694,31 +721,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) )
@@ -739,19 +767,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
@@ -760,6 +788,7 @@ 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);
+	int			siglen = LTREE_GET_ASIGLEN();
 	OffsetNumber k,
 				j;
 	TRGM	   *datum_l,
@@ -778,19 +807,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;
@@ -815,44 +848,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);
@@ -878,36 +889,38 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 			if (ISALLTRUE(datum_l) && cache[j].allistrue)
 				size_alpha = 0;
 			else
-				size_alpha = SIGLENBIT -
+				size_alpha = SIGLENBIT(siglen) -
 					sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) :
-							   GETSIGN(cache[j].sign));
+							   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 -
+				size_beta = SIGLENBIT(siglen) -
 					sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) :
-							   GETSIGN(cache[j].sign));
+							   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;
@@ -918,12 +931,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;
@@ -937,3 +950,17 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(v);
 }
+
+Datum
+gtrgm_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+	init_local_reloptions(relopts, sizeof(TrgmGistOptions));
+	add_local_int_reloption(relopts, "siglen",
+							"signature length in bytes",
+							SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+							offsetof(TrgmGistOptions, siglen));
+
+	PG_RETURN_VOID();
+}
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 64c2477fffc..f1f2b08cd79 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -467,6 +467,23 @@ CREATE INDEX hidx ON testhstore USING GIST (h);
 CREATE INDEX hidx ON testhstore USING GIN (h);
 </programlisting>
 
+  <para>
+   <literal>gist_hstore_ops</literal> GiST opclass approximates set of
+   key/value pairs as a bitmap signature.  Optional integer parameter
+   <literal>siglen</literal> of <literal>gist_hstore_ops</literal> determines
+   signature length in bytes.  Default signature length is 16 bytes.
+   Valid values of signature length are between 1 and 2024 bytes.  Longer
+   signatures leads to more precise search (scan less fraction of index, scan
+   less heap pages), but larger index.
+  </para>
+
+  <para>
+   Example of creating such an index with a signature length of 32 bytes:
+  </para>
+<programlisting>
+  CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32));
+</programlisting>
+
   <para>
    <type>hstore</type> also supports <type>btree</type> or <type>hash</type> indexes for
    the <literal>=</literal> operator. This allows <type>hstore</type> columns to be
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 86539a781c5..1be209a2fe7 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1316,7 +1316,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
    An index definition can specify an <firstterm>operator
    class</firstterm> for each column of an index.
 <synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
 </synopsis>
    The operator class identifies the operators to be used by the index
    for that column.  For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index 025cbca616e..72b4b23c158 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -265,7 +265,7 @@
   </para>
 
   <para>
-   Two GiST index operator classes are provided:
+   Two parametrized GiST index operator classes are provided:
    <literal>gist__int_ops</literal> (used by default) is suitable for
    small- to medium-size data sets, while
    <literal>gist__intbig_ops</literal> uses a larger signature and is more
@@ -274,6 +274,25 @@
    The implementation uses an RD-tree data structure with
    built-in lossy compression.
   </para>
+   
+  <para>
+   <literal>gist__int_ops</literal> approximates integer set as an array of
+   integer ranges.  Optional integer parameter <literal>numranges</literal> of
+   <literal>gist__int_ops</literal> determines maximum number of ranges in
+   one index key.  Default value of <literal>numranges</literal> is 100.
+   Valid values are between 1 and 253.  Using larger arrays as GiST index
+   keys leads to more precise search (scan less fraction of index, scan less
+   heap pages), but larger index.
+  </para>
+   
+  <para>
+   <literal>gist__intbig_ops</literal> approximates integer set as a bitmap
+   signature.  Optional integer parameter <literal>siglen</literal> of
+   <literal>gist__intbig_ops</literal> determines signature length in bytes.
+   Default signature length is 16 bytes.  Valid values of signature length
+   are between 1 and 2024 bytes.  Longer signatures leads to more precise
+   search (scan less fraction of index, scan less heap pages), but larger index.
+  </para>
 
   <para>
    There is also a non-default GIN operator class
@@ -293,8 +312,8 @@
 -- a message can be in one or more <quote>sections</quote>
 CREATE TABLE message (mid INT PRIMARY KEY, sections INT[], ...);
 
--- create specialized index
-CREATE INDEX message_rdtree_idx ON message USING GIST (sections gist__int_ops);
+-- create specialized index with sigature length of 32 bytes
+CREATE INDEX message_rdtree_idx ON message USING GIST (sections gist__int_ops(siglen=32));
 
 -- select messages in section 1 OR 2 - OVERLAP operator
 SELECT message.mid FROM message WHERE message.sections &amp;&amp; '{1,2}';
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index b4e07f65109..0f99eecc8c1 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -499,30 +499,59 @@ Europe &amp; Russia*@ &amp; !Transportation
    </listitem>
    <listitem>
     <para>
-     GiST index over <type>ltree</type>:
+     GiST index over <type>ltree</type> (<literal>gist_ltree_ops</literal>
+     opclass):
      <literal>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
      <literal>&gt;=</literal>, <literal>&gt;</literal>,
      <literal>@&gt;</literal>, <literal>&lt;@</literal>,
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     Example of creating such an index:
+     <literal>gist_ltree_ops</literal> GiST opclass approximates set of
+     path labels as a bitmap signature.  Optional integer parameter
+     <literal>siglen</literal> of <literal>gist_ltree_ops</literal> determines
+     signature length in bytes.  Default signature length is 8 bytes.
+     Valid values of signature length are between 1 and 2024 bytes.  Longer
+     signatures leads to more precise search (scan less fraction of index, scan
+     less heap pages), but larger index.
+    </para>
+    <para>
+     Example of creating such an index with a default signature length of 8 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (path);
+</programlisting>
+    <para>
+     Example of creating such an index with a signature length of 100 bytes:
+    </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
 </programlisting>
    </listitem>
    <listitem>
     <para>
-     GiST index over <type>ltree[]</type>:
+     GiST index over <type>ltree[]</type> (<literal>gist__ltree_ops</literal>
+     opclass):
      <literal>ltree[] &lt;@ ltree</literal>, <literal>ltree @&gt; ltree[]</literal>,
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     Example of creating such an index:
+     <literal>gist__ltree_ops</literal> GiST opclass works similar to
+     <literal>gist_ltree_ops</literal> and also takes signature length as
+     a parameter.  Default value of <literal>siglen</literal> in
+      <literal>gist__ltree_ops</literal> is 28 bytes.
+    </para>
+    <para>
+     Example of creating such an index with a default signature length of 28 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (array_path);
+</programlisting>
+    <para>
+     Example of creating such an index with a signature length of 100 bytes:
+    </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
 </programlisting>
     <para>
      Note: This index type is lossy.
diff --git a/doc/src/sgml/pgtrgm.sgml b/doc/src/sgml/pgtrgm.sgml
index 049f496869c..dde02634ae4 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -390,6 +390,23 @@ CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
 </programlisting>
   </para>
 
+  <para>
+   <literal>gist_trgm_ops</literal> GiST opclass approximates set of
+   trigrams as a bitmap signature.  Optional integer parameter
+   <literal>siglen</literal> of <literal>gist_trgm_ops</literal> determines
+   signature length in bytes.  Default signature length is 12 bytes.
+   Valid values of signature length are between 1 and 2024 bytes.  Longer
+   signatures leads to more precise search (scan less fraction of index, scan
+   less heap pages), but larger index.
+  </para>
+
+  <para>
+   Example of creating such an index with a signature length of 32 bytes:
+  </para>
+<programlisting>
+CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));
+</programlisting>
+
   <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
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index f0fe6fb874b..3f902dcf84f 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
-    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+    ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
     [ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
     [ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
     [ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -285,6 +285,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+      <listitem>
+       <para>
+        The name of an operator class parameter. See below for details.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><literal>ASC</literal></term>
       <listitem>
@@ -679,8 +688,9 @@ Indexes:
   </para>
 
   <para>
-   An <firstterm>operator class</firstterm> can be specified for each
-   column of an index. The operator class identifies the operators to be
+   An <firstterm>operator class</firstterm> with its optional parameters 
+   can be specified for each column of an index.
+   The operator class identifies the operators to be
    used by the index for that column. For example, a B-tree index on
    four-byte integers would use the <literal>int4_ops</literal> class;
    this operator class includes comparison functions for four-byte
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index c5b254c7ca9..2217fcd6c2f 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3637,7 +3637,7 @@ SELECT plainto_tsquery('supernovae stars');
       <tertiary>text search</tertiary>
      </indexterm>
 
-      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+      <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
      </term>
 
      <listitem>
@@ -3645,6 +3645,8 @@ SELECT plainto_tsquery('supernovae stars');
        Creates a GiST (Generalized Search Tree)-based index.
        The <replaceable>column</replaceable> can be of <type>tsvector</type> or
        <type>tsquery</type> type.
+       Optional integer parameter <literal>siglen</literal> determines
+       signature length in bytes (see below for details).
       </para>
      </listitem>
     </varlistentry>
@@ -3668,12 +3670,17 @@ SELECT plainto_tsquery('supernovae stars');
    to check the actual table row to eliminate such false matches.
    (<productname>PostgreSQL</productname> does this automatically when needed.)
    GiST indexes are lossy because each document is represented in the
-   index by a fixed-length signature. The signature is generated by hashing
+   index by a fixed-length signature.  Signature length in bytes is determined
+   by the value of the optional integer parameter <literal>siglen</literal>.
+   Default signature length (when <literal>siglen</literal> is not specied) is
+   124 bytes, maximal length is 2024 bytes. The signature is generated by hashing
    each word into a single bit in an n-bit string, with all these bits OR-ed
    together to produce an n-bit document signature.  When two words hash to
    the same bit position there will be a false match.  If all words in
    the query have matches (real or false) then the table row must be
-   retrieved to see if the match is correct.
+   retrieved to see if the match is correct.  Longer signatures leads to more
+   precise search (scan less fraction of index, scan less heap pages), but
+   larger index.
   </para>
 
   <para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index c481838389c..7db3ae5ee0c 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -90,6 +90,7 @@ brinhandler(PG_FUNCTION_ARGS)
 
 	amroutine->amstrategies = 0;
 	amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
+	amroutine->amoptsprocnum = BRIN_PROCNUM_OPTIONS;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = false;
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c
index 1302ebb6681..fb0615463e0 100644
--- a/src/backend/access/brin/brin_validate.c
+++ b/src/backend/access/brin/brin_validate.c
@@ -105,6 +105,9 @@ brinvalidate(Oid opclassoid)
 											3, 3, INTERNALOID, INTERNALOID,
 											INTERNALOID);
 				break;
+			case BRIN_PROCNUM_OPTIONS:
+				ok = check_amoptsproc_signature(procform->amproc);
+				break;
 			default:
 				/* Complain if it's not a valid optional proc number */
 				if (procform->amprocnum < BRIN_FIRST_OPTIONAL_PROCNUM ||
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index e136116d7ba..8ccc228a8cc 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -701,6 +701,47 @@ add_reloption(relopt_gen *newoption)
 	need_initialization = true;
 }
 
+/*
+ * init_local_reloptions
+ *		Initialize local reloptions that will parsed into bytea structure of
+ * 		'relopt_struct_size'.
+ */
+void
+init_local_reloptions(local_relopts *opts, Size relopt_struct_size)
+{
+	opts->options = NIL;
+	opts->validators = NIL;
+	opts->relopt_struct_size = relopt_struct_size;
+}
+
+/*
+ * register_reloptions_validator
+ *		Register custom validation callback that will be called at the end of
+ *		build_local_reloptions().
+ */
+void
+register_reloptions_validator(local_relopts *opts, relopts_validator validator)
+{
+	opts->validators = lappend(opts->validators, validator);
+}
+
+/*
+ * add_local_reloption
+ *		Add an already-created custom reloption to the local list.
+ */
+static void
+add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
+{
+	local_relopt *opt = palloc(sizeof(*opt));
+
+	Assert(offset < relopts->relopt_struct_size);
+
+	opt->option = newoption;
+	opt->offset = offset;
+
+	relopts->options = lappend(relopts->options, opt);
+}
+
 /*
  * allocate_reloption
  *		Allocate a new reloption and initialize the type-agnostic fields
@@ -714,7 +755,10 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
 	size_t		size;
 	relopt_gen *newoption;
 
-	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+	if (kinds != RELOPT_KIND_LOCAL)
+		oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+	else
+		oldcxt = NULL;
 
 	switch (type)
 	{
@@ -750,18 +794,19 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
 	newoption->type = type;
 	newoption->lockmode = lockmode;
 
-	MemoryContextSwitchTo(oldcxt);
+	if (oldcxt != NULL)
+		MemoryContextSwitchTo(oldcxt);
 
 	return newoption;
 }
 
 /*
- * add_bool_reloption
- *		Add a new boolean reloption
+ * init_bool_reloption
+ *		Allocate and initialize a new boolean reloption
  */
-void
-add_bool_reloption(bits32 kinds, const char *name, const char *desc,
-				   bool default_val, LOCKMODE lockmode)
+static relopt_bool *
+init_bool_reloption(bits32 kinds, const char *name, const char *desc,
+					bool default_val, LOCKMODE lockmode)
 {
 	relopt_bool *newoption;
 
@@ -769,16 +814,49 @@ add_bool_reloption(bits32 kinds, const char *name, const char *desc,
 												   name, desc, lockmode);
 	newoption->default_val = default_val;
 
+	return newoption;
+}
+
+/*
+ * add_bool_reloption
+ *		Add a new boolean reloption
+ */
+void
+add_bool_reloption(bits32 kinds, const char *name, const char *desc,
+				   bool default_val, LOCKMODE lockmode)
+{
+	relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
+												 default_val, lockmode);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
 /*
- * add_int_reloption
- *		Add a new integer reloption
+ * add_local_bool_reloption
+ *		Add a new boolean local reloption
+ *
+ * 'offset' is offset of bool-typed field.
  */
 void
-add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
-				  int min_val, int max_val, LOCKMODE lockmode)
+add_local_bool_reloption(local_relopts *relopts, const char *name,
+						 const char *desc, bool default_val, int offset)
+{
+	relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
+												 name, desc,
+												 default_val, 0);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, offset);
+}
+
+
+/*
+ * init_real_reloption
+ *		Allocate and initialize a new integer reloption
+ */
+static relopt_int *
+init_int_reloption(bits32 kinds, const char *name, const char *desc,
+				   int default_val, int min_val, int max_val,
+				   LOCKMODE lockmode)
 {
 	relopt_int *newoption;
 
@@ -788,16 +866,50 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_
 	newoption->min = min_val;
 	newoption->max = max_val;
 
+	return newoption;
+}
+
+/*
+ * add_int_reloption
+ *		Add a new integer reloption
+ */
+void
+add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
+				  int min_val, int max_val, LOCKMODE lockmode)
+{
+	relopt_int *newoption = init_int_reloption(kinds, name, desc,
+											   default_val, min_val,
+											   max_val, lockmode);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
 /*
- * add_real_reloption
- *		Add a new float reloption
+ * add_local_int_reloption
+ *		Add a new local integer reloption
+ *
+ * 'offset' is offset of int-typed field.
  */
 void
-add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
-				   double min_val, double max_val, LOCKMODE lockmode)
+add_local_int_reloption(local_relopts *relopts, const char *name,
+						const char *desc, int default_val, int min_val,
+						int max_val, int offset)
+{
+	relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
+											   name, desc, default_val,
+											   min_val, max_val, 0);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, offset);
+}
+
+/*
+ * init_real_reloption
+ *		Allocate and initialize a new real reloption
+ */
+static relopt_real *
+init_real_reloption(bits32 kinds, const char *name, const char *desc,
+					double default_val, double min_val, double max_val,
+					LOCKMODE lockmode)
 {
 	relopt_real *newoption;
 
@@ -807,9 +919,65 @@ add_real_reloption(bits32 kinds, const char *name, const char *desc, double defa
 	newoption->min = min_val;
 	newoption->max = max_val;
 
+	return newoption;
+}
+
+/*
+ * add_real_reloption
+ *		Add a new float reloption
+ */
+void
+add_real_reloption(bits32 kinds, const char *name, const char *desc,
+				   double default_val, double min_val, double max_val,
+				   LOCKMODE lockmode)
+{
+	relopt_real *newoption = init_real_reloption(kinds, name, desc,
+												 default_val, min_val,
+												 max_val, lockmode);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
+/*
+ * add_local_real_reloption
+ *		Add a new local float reloption
+ *
+ * 'offset' is offset of double-typed field.
+ */
+void
+add_local_real_reloption(local_relopts *relopts, const char *name,
+						 const char *desc, double default_val,
+						 double min_val, double max_val, int offset)
+{
+	relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
+												 name, desc,
+												 default_val, min_val,
+												 max_val, 0);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, offset);
+}
+
+/*
+ * init_enum_reloption
+ *		Allocate and initialize a new enum reloption
+ */
+static relopt_enum *
+init_enum_reloption(bits32 kinds, const char *name, const char *desc,
+					relopt_enum_elt_def *members, int default_val,
+					const char *detailmsg, LOCKMODE lockmode)
+{
+	relopt_enum *newoption;
+
+	newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
+												   name, desc, lockmode);
+	newoption->members = members;
+	newoption->default_val = default_val;
+	newoption->detailmsg = detailmsg;
+
+	return newoption;
+}
+
+
 /*
  * add_enum_reloption
  *		Add a new enum reloption
@@ -827,29 +995,42 @@ add_enum_reloption(bits32 kinds, const char *name, const char *desc,
 				   relopt_enum_elt_def *members, int default_val,
 				   const char *detailmsg, LOCKMODE lockmode)
 {
-	relopt_enum *newoption;
-
-	newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
-												   name, desc, lockmode);
-	newoption->members = members;
-	newoption->default_val = default_val;
-	newoption->detailmsg = detailmsg;
+	relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
+												 members, default_val,
+												 detailmsg, lockmode);
 
 	add_reloption((relopt_gen *) newoption);
 }
 
 /*
- * add_string_reloption
- *		Add a new string reloption
+ * add_local_enum_reloption
+ *		Add a new local enum reloption
  *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values.  It must elog(ERROR) when the argument string is
- * not acceptable for the variable.  Note that the default value must pass
- * the validation.
+ * 'offset' is offset of int-typed field.
  */
 void
-add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
-					 validate_string_relopt validator, LOCKMODE lockmode)
+add_local_enum_reloption(local_relopts *relopts, const char *name,
+						 const char *desc, relopt_enum_elt_def *members,
+						 int default_val, const char *detailmsg, int offset)
+{
+	relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
+												 name, desc,
+												 members, default_val,
+												 detailmsg, 0);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, offset);
+}
+
+/*
+ * init_string_reloption
+ *		Allocate and initialize a new string reloption
+ */
+static relopt_string *
+init_string_reloption(bits32 kinds, const char *name, const char *desc,
+					  const char *default_val,
+					  validate_string_relopt validator,
+					  fill_string_relopt filler,
+					  LOCKMODE lockmode)
 {
 	relopt_string *newoption;
 
@@ -860,10 +1041,13 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
 	newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
 													 name, desc, lockmode);
 	newoption->validate_cb = validator;
+	newoption->fill_cb = filler;
 	if (default_val)
 	{
-		newoption->default_val = MemoryContextStrdup(TopMemoryContext,
-													 default_val);
+		if (kinds == RELOPT_KIND_LOCAL)
+			newoption->default_val = strdup(default_val);
+		else
+			newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
 		newoption->default_len = strlen(default_val);
 		newoption->default_isnull = false;
 	}
@@ -874,9 +1058,53 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
 		newoption->default_isnull = true;
 	}
 
+	return newoption;
+}
+
+/*
+ * add_string_reloption
+ *		Add a new string reloption
+ *
+ * "validator" is an optional function pointer that can be used to test the
+ * validity of the values.  It must elog(ERROR) when the argument string is
+ * not acceptable for the variable.  Note that the default value must pass
+ * the validation.
+ */
+void
+add_string_reloption(bits32 kinds, const char *name, const char *desc,
+					 const char *default_val, validate_string_relopt validator,
+					 LOCKMODE lockmode)
+{
+	relopt_string *newoption = init_string_reloption(kinds, name, desc,
+													 default_val,
+													 validator, NULL,
+													 lockmode);
+
 	add_reloption((relopt_gen *) newoption);
 }
 
+/*
+ * add_local_string_reloption
+ *		Add a new local string reloption
+ *
+ * 'offset' is offset of int-typed field that will store offset of string value
+ * in the resulting bytea structure.
+ */
+void
+add_local_string_reloption(local_relopts *relopts, const char *name,
+						   const char *desc, const char *default_val,
+						   validate_string_relopt validator,
+						   fill_string_relopt filler, int offset)
+{
+	relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
+													 name, desc,
+													 default_val,
+													 validator, filler,
+													 0);
+
+	add_local_reloption(relopts, (relopt_gen *) newoption, offset);
+}
+
 /*
  * Transform a relation options list (list of DefElem) into the text array
  * format that is kept in pg_class.reloptions, including only those options
@@ -1173,6 +1401,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 	return options;
 }
 
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+						relopt_value *reloptions, int numoptions)
+{
+	ArrayType  *array = DatumGetArrayTypeP(options);
+	Datum	   *optiondatums;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
+					  &optiondatums, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *text_str = VARDATA(optiondatums[i]);
+		int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+		int			j;
+
+		/* Search for a match in reloptions */
+		for (j = 0; j < numoptions; j++)
+		{
+			int			kw_len = reloptions[j].gen->namelen;
+
+			if (text_len > kw_len && text_str[kw_len] == '=' &&
+				strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+			{
+				parse_one_reloption(&reloptions[j], text_str, text_len,
+									validate);
+				break;
+			}
+		}
+
+		if (j >= numoptions && validate)
+		{
+			char	   *s;
+			char	   *p;
+
+			s = TextDatumGetCString(optiondatums[i]);
+			p = strchr(s, '=');
+			if (p)
+				*p = '\0';
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("unrecognized parameter \"%s\"", s)));
+		}
+	}
+
+	/* It's worth avoiding memory leaks in this function */
+	pfree(optiondatums);
+
+	if (((void *) array) != DatumGetPointer(options))
+		pfree(array);
+}
+
 /*
  * Interpret reloptions that are given in text-array format.
  *
@@ -1227,57 +1509,35 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
 
 	/* Done if no options */
 	if (PointerIsValid(DatumGetPointer(options)))
-	{
-		ArrayType  *array = DatumGetArrayTypeP(options);
-		Datum	   *optiondatums;
-		int			noptions;
+		parseRelOptionsInternal(options, validate, reloptions, numoptions);
 
-		deconstruct_array(array, TEXTOID, -1, false, TYPALIGN_INT,
-						  &optiondatums, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *text_str = VARDATA(optiondatums[i]);
-			int			text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
-			int			j;
-
-			/* Search for a match in reloptions */
-			for (j = 0; j < numoptions; j++)
-			{
-				int			kw_len = reloptions[j].gen->namelen;
+	*numrelopts = numoptions;
+	return reloptions;
+}
 
-				if (text_len > kw_len && text_str[kw_len] == '=' &&
-					strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
-				{
-					parse_one_reloption(&reloptions[j], text_str, text_len,
-										validate);
-					break;
-				}
-			}
+/* Parse local unregistered options. */
+static relopt_value *
+parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
+{
+	int			nopts = list_length(relopts->options);
+	relopt_value *values = palloc(sizeof(*values) * nopts);
+	ListCell   *lc;
+	int			i = 0;
 
-			if (j >= numoptions && validate)
-			{
-				char	   *s;
-				char	   *p;
+	foreach(lc, relopts->options)
+	{
+		local_relopt *opt = lfirst(lc);
 
-				s = TextDatumGetCString(optiondatums[i]);
-				p = strchr(s, '=');
-				if (p)
-					*p = '\0';
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-						 errmsg("unrecognized parameter \"%s\"", s)));
-			}
-		}
+		values[i].gen = opt->option;
+		values[i].isset = false;
 
-		/* It's worth avoiding memory leaks in this function */
-		pfree(optiondatums);
-		if (((void *) array) != DatumGetPointer(options))
-			pfree(array);
+		i++;
 	}
 
-	*numrelopts = numoptions;
-	return reloptions;
+	if (options != (Datum) 0)
+		parseRelOptionsInternal(options, validate, values, nopts);
+
+	return values;
 }
 
 /*
@@ -1424,8 +1684,24 @@ allocateReloptStruct(Size base, relopt_value *options, int numoptions)
 	int			i;
 
 	for (i = 0; i < numoptions; i++)
-		if (options[i].gen->type == RELOPT_TYPE_STRING)
-			size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
+	{
+		relopt_value *optval = &options[i];
+
+		if (optval->gen->type == RELOPT_TYPE_STRING)
+		{
+			relopt_string *optstr = (relopt_string *) optval->gen;
+
+			if (optstr->fill_cb)
+			{
+				const char *val = optval->isset ? optval->values.string_val :
+				optstr->default_isnull ? NULL : optstr->default_val;
+
+				size += optstr->fill_cb(val, NULL);
+			}
+			else
+				size += GET_STRING_RELOPTION_LEN(*optval) + 1;
+		}
+	}
 
 	return palloc0(size);
 }
@@ -1494,7 +1770,21 @@ fillRelOptions(void *rdopts, Size basesize,
 						else
 							string_val = NULL;
 
-						if (string_val == NULL)
+						if (optstring->fill_cb)
+						{
+							Size		size =
+							optstring->fill_cb(string_val,
+											   (char *) rdopts + offset);
+
+							if (size)
+							{
+								*(int *) itempos = offset;
+								offset += size;
+							}
+							else
+								*(int *) itempos = 0;
+						}
+						else if (string_val == NULL)
 							*(int *) itempos = 0;
 						else
 						{
@@ -1625,6 +1915,46 @@ build_reloptions(Datum reloptions, bool validate,
 	return rdopts;
 }
 
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
+{
+	int			noptions = list_length(relopts->options);
+	relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+	relopt_value *vals;
+	void	   *opts;
+	int			i = 0;
+	ListCell   *lc;
+
+	foreach(lc, relopts->options)
+	{
+		local_relopt *opt = lfirst(lc);
+
+		elems[i].optname = opt->option->name;
+		elems[i].opttype = opt->option->type;
+		elems[i].offset = opt->offset;
+
+		i++;
+	}
+
+	vals = parseLocalRelOptions(relopts, options, validate);
+	opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
+	fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
+				   elems, noptions);
+
+	foreach(lc, relopts->validators)
+		((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+
+	if (elems)
+		pfree(elems);
+
+	return opts;
+}
+
 /*
  * Option parser for partitioned tables
  */
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index a7e55caf28d..a400f1fedbc 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -41,6 +41,7 @@ ginhandler(PG_FUNCTION_ARGS)
 
 	amroutine->amstrategies = 0;
 	amroutine->amsupport = GINNProcs;
+	amroutine->amoptsprocnum = GIN_OPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = false;
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 0b62e0a8ae8..1e3046f4eb7 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -142,6 +142,9 @@ ginvalidate(Oid opclassoid)
 											INTERNALOID, INTERNALOID,
 											INTERNALOID);
 				break;
+			case GIN_OPTIONS_PROC:
+				ok = check_amoptsproc_signature(procform->amproc);
+				break;
 			default:
 				ereport(INFO,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -237,7 +240,8 @@ ginvalidate(Oid opclassoid)
 		if (opclassgroup &&
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
-		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+			i == GIN_OPTIONS_PROC)
 			continue;			/* optional method */
 		if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
 			continue;			/* don't need both, see check below loop */
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 90c46e86a19..9eee5381aea 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -62,6 +62,7 @@ gisthandler(PG_FUNCTION_ARGS)
 
 	amroutine->amstrategies = 0;
 	amroutine->amsupport = GISTNProcs;
+	amroutine->amoptsprocnum = GIST_OPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = true;
 	amroutine->amcanbackward = false;
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index 0c4fb8c1bf9..a285736a810 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -140,6 +140,9 @@ gistvalidate(Oid opclassoid)
 											5, 5, INTERNALOID, opcintype,
 											INT2OID, OIDOID, INTERNALOID);
 				break;
+			case GIST_OPTIONS_PROC:
+				ok = check_amoptsproc_signature(procform->amproc);
+				break;
 			default:
 				ereport(INFO,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -259,7 +262,8 @@ gistvalidate(Oid opclassoid)
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
 		if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
-			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+			i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+			i == GIST_OPTIONS_PROC)
 			continue;			/* optional methods */
 		ereport(INFO,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 4871b7ff4d6..3ec6d528e77 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -59,6 +59,7 @@ hashhandler(PG_FUNCTION_ARGS)
 
 	amroutine->amstrategies = HTMaxStrategyNumber;
 	amroutine->amsupport = HASHNProcs;
+	amroutine->amoptsprocnum = HASHOPTIONS_PROC;
 	amroutine->amcanorder = false;
 	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = true;
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 6346e658652..7b08ed53543 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -126,6 +126,10 @@ hashvalidate(Oid opclassoid)
 											   procform->amproclefttype);
 				}
 				break;
+			case HASHOPTIONS_PROC:
+				if (!check_amoptsproc_signature(procform->amproc))
+					result = false;
+				break;
 			default:
 				ereport(INFO,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/access/index/amvalidate.c b/src/backend/access/index/amvalidate.c
index 3eae6aa012f..24d49750ada 100644
--- a/src/backend/access/index/amvalidate.c
+++ b/src/backend/access/index/amvalidate.c
@@ -21,6 +21,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
 #include "parser/parse_coerce.h"
 #include "utils/syscache.h"
 
@@ -182,6 +183,16 @@ check_amproc_signature(Oid funcid, Oid restype, bool exact,
 	return result;
 }
 
+/*
+ * Validate the signature of an opclass options support function, that should
+ * be 'void(internal)'.
+ */
+bool
+check_amoptsproc_signature(Oid funcid)
+{
+	return check_amproc_signature(funcid, VOIDOID, true, 1, 1, INTERNALOID);
+}
+
 /*
  * Validate the signature (argument and result types) of an opclass operator.
  * Return true if OK, false if not.
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index a5210d0b342..f7e4c65d99f 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -45,17 +45,23 @@
 
 #include "access/amapi.h"
 #include "access/heapam.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/tableam.h"
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "catalog/index.h"
+#include "catalog/pg_amproc.h"
 #include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "nodes/makefuncs.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
 #include "storage/predicate.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
+#include "utils/syscache.h"
 
 
 /* ----------------------------------------------------------------
@@ -767,9 +773,9 @@ index_getprocid(Relation irel,
 
 	nproc = irel->rd_indam->amsupport;
 
-	Assert(procnum > 0 && procnum <= (uint16) nproc);
+	Assert(procnum >= 0 && procnum <= (uint16) nproc);
 
-	procindex = (nproc * (attnum - 1)) + (procnum - 1);
+	procindex = ((nproc + 1) * (attnum - 1)) + procnum;
 
 	loc = irel->rd_support;
 
@@ -797,13 +803,15 @@ index_getprocinfo(Relation irel,
 {
 	FmgrInfo   *locinfo;
 	int			nproc;
+	int			optsproc;
 	int			procindex;
 
 	nproc = irel->rd_indam->amsupport;
+	optsproc = irel->rd_indam->amoptsprocnum;
 
-	Assert(procnum > 0 && procnum <= (uint16) nproc);
+	Assert(procnum >= 0 && procnum <= (uint16) nproc);
 
-	procindex = (nproc * (attnum - 1)) + (procnum - 1);
+	procindex = ((nproc + 1) * (attnum - 1)) + procnum;
 
 	locinfo = irel->rd_supportinfo;
 
@@ -832,6 +840,17 @@ index_getprocinfo(Relation irel,
 				 procnum, attnum, RelationGetRelationName(irel));
 
 		fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
+
+		if (procnum != optsproc)
+		{
+			/* Initialize locinfo->fn_expr with opclass options Const */
+			bytea	  **attoptions = RelationGetIndexAttOptions(irel, false);
+			MemoryContext oldcxt = MemoryContextSwitchTo(irel->rd_indexcxt);
+
+			set_fn_opclass_options(locinfo, attoptions[attnum - 1]);
+
+			MemoryContextSwitchTo(oldcxt);
+		}
 	}
 
 	return locinfo;
@@ -906,3 +925,53 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
 		}
 	}
 }
+
+/* ----------------
+ *      index_opclass_options
+ *
+ *      Parse opclass-specific options for index column.
+ * ----------------
+ */
+bytea *
+index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
+					  bool validate)
+{
+	int			amoptsprocnum = indrel->rd_indam->amoptsprocnum;
+	Oid			procid = index_getprocid(indrel, attnum, amoptsprocnum);
+	FmgrInfo   *procinfo;
+	local_relopts relopts;
+
+	if (!OidIsValid(procid))
+	{
+		Oid			opclass;
+		Datum		indclassDatum;
+		oidvector  *indclass;
+		bool		isnull;
+
+		if (!DatumGetPointer(attoptions))
+			return NULL;	/* ok, no options, no procedure */
+
+		/*
+		 * Report an error if the opclass's options-parsing procedure does not
+		 * exist but the opclass options are specified.
+		 */
+		indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+										Anum_pg_index_indclass, &isnull);
+		Assert(!isnull);
+		indclass = (oidvector *) DatumGetPointer(indclassDatum);
+		opclass = indclass->values[attnum - 1];
+
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("operator class %s has no options",
+						generate_opclass_name(opclass))));
+	}
+
+	init_local_reloptions(&relopts, 0);
+
+	procinfo = index_getprocinfo(indrel, attnum, amoptsprocnum);
+
+	(void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+
+	return build_local_reloptions(&relopts, attoptions, validate);
+}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 4bb16297c31..36294789f3f 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -112,6 +112,7 @@ bthandler(PG_FUNCTION_ARGS)
 
 	amroutine->amstrategies = BTMaxStrategyNumber;
 	amroutine->amsupport = BTNProcs;
+	amroutine->amoptsprocnum = BTOPTIONS_PROC;
 	amroutine->amcanorder = true;
 	amroutine->amcanorderbyop = false;
 	amroutine->amcanbackward = true;
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 627f74407a3..02905f79c82 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -108,6 +108,9 @@ btvalidate(Oid opclassoid)
 				ok = check_amproc_signature(procform->amproc, BOOLOID, true,
 											1, 1, OIDOID);
 				break;
+			case BTOPTIONS_PROC:
+				ok = check_amoptsproc_signature(procform->amproc);
+				break;
 			default:
 				ereport(INFO,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/access/spgist/spgvalidate.c b/src/backend/access/spgist/spgvalidate.c
index e316d6eda22..3c433e94e76 100644
--- a/src/backend/access/spgist/spgvalidate.c
+++ b/src/backend/access/spgist/spgvalidate.c
@@ -159,6 +159,9 @@ spgvalidate(Oid opclassoid)
 												configOut.leafType, true,
 												1, 1, procform->amproclefttype);
 				break;
+			case SPGIST_OPTIONS_PROC:
+				ok = check_amoptsproc_signature(procform->amproc);
+				break;
 			default:
 				ereport(INFO,
 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -271,6 +274,8 @@ spgvalidate(Oid opclassoid)
 		{
 			if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
 				continue;		/* got it */
+			if (i == SPGIST_OPTIONS_PROC)
+				continue;			/* optional method */
 			ereport(INFO,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("operator family \"%s\" of access method %s is missing support function %d for type %s",
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 9d9e9159796..632c058b80a 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -725,6 +725,7 @@ CheckAttributeType(const char *attname,
 void
 InsertPgAttributeTuple(Relation pg_attribute_rel,
 					   Form_pg_attribute new_attribute,
+					   Datum attoptions,
 					   CatalogIndexState indstate)
 {
 	Datum		values[Natts_pg_attribute];
@@ -756,10 +757,11 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
 	values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
 	values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
 	values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+	values[Anum_pg_attribute_attoptions - 1] = attoptions;
 
 	/* start out with empty permissions and empty options */
 	nulls[Anum_pg_attribute_attacl - 1] = true;
-	nulls[Anum_pg_attribute_attoptions - 1] = true;
+	nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0;
 	nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
 	nulls[Anum_pg_attribute_attmissingval - 1] = true;
 
@@ -813,7 +815,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		/* Make sure this is OK, too */
 		attr->attstattarget = -1;
 
-		InsertPgAttributeTuple(rel, attr, indstate);
+		InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate);
 
 		/* Add dependency info */
 		myself.classId = RelationRelationId;
@@ -851,7 +853,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 			/* Fill in the correct relation OID in the copied tuple */
 			attStruct.attrelid = new_rel_oid;
 
-			InsertPgAttributeTuple(rel, &attStruct, indstate);
+			InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate);
 		}
 	}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 2d81bc3cbc9..bd7ec923e94 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -26,6 +26,7 @@
 #include "access/amapi.h"
 #include "access/heapam.h"
 #include "access/multixact.h"
+#include "access/reloptions.h"
 #include "access/relscan.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
@@ -105,7 +106,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
 										  Oid *classObjectId);
 static void InitializeAttributeOids(Relation indexRelation,
 									int numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, int numatts);
+static void AppendAttributeTuples(Relation indexRelation, int numatts,
+								  Datum *attopts);
 static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
 								Oid parentIndexId,
 								IndexInfo *indexInfo,
@@ -484,7 +486,7 @@ InitializeAttributeOids(Relation indexRelation,
  * ----------------------------------------------------------------
  */
 static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
 {
 	Relation	pg_attribute;
 	CatalogIndexState indstate;
@@ -506,10 +508,11 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
 	for (i = 0; i < numatts; i++)
 	{
 		Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
+		Datum		attoptions = attopts ? attopts[i] : (Datum) 0;
 
 		Assert(attr->attnum == i + 1);
 
-		InsertPgAttributeTuple(pg_attribute, attr, indstate);
+		InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate);
 	}
 
 	CatalogCloseIndexes(indstate);
@@ -589,6 +592,7 @@ UpdateIndexRelation(Oid indexoid,
 	else
 		predDatum = (Datum) 0;
 
+
 	/*
 	 * open the system catalog index relation
 	 */
@@ -976,7 +980,8 @@ index_create(Relation heapRelation,
 	/*
 	 * append ATTRIBUTE tuples for the index
 	 */
-	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+	AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs,
+						  indexInfo->ii_OpclassOptions);
 
 	/* ----------------
 	 *	  update pg_index
@@ -1189,6 +1194,13 @@ index_create(Relation heapRelation,
 
 	indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
 
+	/* Validate opclass-specific options */
+	if (indexInfo->ii_OpclassOptions)
+		for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+			(void) index_opclass_options(indexRelation, i + 1,
+										 indexInfo->ii_OpclassOptions[i],
+										 true);
+
 	/*
 	 * If this is bootstrap (initdb) time, then we don't actually fill in the
 	 * index yet.  We'll be creating more indexes and classes later, so we
@@ -2336,6 +2348,8 @@ BuildIndexInfo(Relation index)
 								 &ii->ii_ExclusionStrats);
 	}
 
+	ii->ii_OpclassOptions = RelationGetIndexRawAttOptions(index);
+
 	return ii;
 }
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3239185b425..3f7ab8d389b 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -304,6 +304,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
+	indexInfo->ii_OpclassOptions = NULL;
 	indexInfo->ii_Unique = true;
 	indexInfo->ii_ReadyForInserts = true;
 	indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 4e8263af4be..2e5997b5c3c 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -90,6 +90,7 @@ static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
 static bool ReindexRelationConcurrently(Oid relationOid, int options);
 static void ReindexPartitionedIndex(Relation parentIdx);
 static void update_relispartition(Oid relationId, bool newval);
+static bool CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts);
 
 /*
  * callback argument type for RangeVarCallbackForReindexIndex()
@@ -268,6 +269,18 @@ CheckIndexCompatible(Oid oldId,
 		}
 	}
 
+	/* Any change in opclass options break compatibility. */
+	if (ret)
+	{
+		Datum	   *opclassOptions = RelationGetIndexRawAttOptions(irel);
+
+		ret = CompareOpclassOptions(opclassOptions,
+									indexInfo->ii_OpclassOptions, old_natts);
+
+		if (opclassOptions)
+			pfree(opclassOptions);
+	}
+
 	/* Any change in exclusion operator selections breaks compatibility. */
 	if (ret && indexInfo->ii_ExclusionOps != NULL)
 	{
@@ -302,6 +315,42 @@ CheckIndexCompatible(Oid oldId,
 	return ret;
 }
 
+/*
+ * CompareOpclassOptions
+ *
+ * Compare per-column opclass options which are represented by arrays of text[]
+ * datums.  Both elements of arrays and array themselves can be NULL.
+ */
+static bool
+CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts)
+{
+	int			i;
+
+	if (!opts1 && !opts2)
+		return true;
+
+	for (i = 0; i < natts; i++)
+	{
+		Datum		opt1 = opts1 ? opts1[i] : (Datum) 0;
+		Datum		opt2 = opts2 ? opts2[i] : (Datum) 0;
+
+		if (opt1 == (Datum) 0)
+		{
+			if (opt2 == (Datum) 0)
+				continue;
+			else
+				return false;
+		}
+		else if (opt2 == (Datum) 0)
+			return false;
+
+		/* Compare non-NULL text[] datums. */
+		if (!DatumGetBool(DirectFunctionCall2(array_eq, opt1, opt2)))
+			return false;
+	}
+
+	return true;
+}
 
 /*
  * WaitForOlderSnapshots
@@ -1528,7 +1577,7 @@ CheckPredicate(Expr *predicate)
 
 /*
  * Compute per-index-column information, including indexed column numbers
- * or index expressions, opclasses, and indoptions. Note, all output vectors
+ * or index expressions, opclasses and their options. Note, all output vectors
  * should be allocated for all columns, including "including" ones.
  */
 static void
@@ -1829,6 +1878,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 								accessMethodName)));
 		}
 
+		/* Set up the per-column opclass options (attoptions field). */
+		if (attribute->opclassopts)
+		{
+			Assert(attn < nkeycols);
+
+			if (!indexInfo->ii_OpclassOptions)
+				indexInfo->ii_OpclassOptions =
+					palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+
+			indexInfo->ii_OpclassOptions[attn] =
+				transformRelOptions((Datum) 0, attribute->opclassopts,
+									NULL, NULL, false, false);
+		}
+
 		attn++;
 	}
 }
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 743511bdf21..f1026de7565 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -53,14 +53,15 @@
 static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt,
 							 Oid amoid, Oid opfamilyoid,
 							 int maxOpNumber, int maxProcNumber,
-							 List *items);
+							 int opclassOptsProcNumber, List *items);
 static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt,
 							  Oid amoid, Oid opfamilyoid,
 							  int maxOpNumber, int maxProcNumber,
 							  List *items);
 static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
 static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
-static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
+static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
+							int opclassOptsProcNum);
 static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
 static void storeOperators(List *opfamilyname, Oid amoid,
 						   Oid opfamilyoid, Oid opclassoid,
@@ -337,6 +338,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
 				opfamilyoid,	/* oid of containing opfamily */
 				opclassoid;		/* oid of opclass we create */
 	int			maxOpNumber,	/* amstrategies value */
+				optsProcNumber,	/* amoptsprocnum value */
 				maxProcNumber;	/* amsupport value */
 	bool		amstorage;		/* amstorage flag */
 	List	   *operators;		/* OpFamilyMember list for operators */
@@ -381,6 +383,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	if (maxOpNumber <= 0)
 		maxOpNumber = SHRT_MAX;
 	maxProcNumber = amroutine->amsupport;
+	optsProcNumber = amroutine->amoptsprocnum;
 	amstorage = amroutine->amstorage;
 
 	/* XXX Should we make any privilege check against the AM? */
@@ -536,7 +539,6 @@ DefineOpClass(CreateOpClassStmt *stmt)
 					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
 								   get_func_name(funcOid));
 #endif
-
 				/* Save the info */
 				member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
 				member->object = funcOid;
@@ -547,7 +549,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
 					processTypesSpec(item->class_args,
 									 &member->lefttype, &member->righttype);
 
-				assignProcTypes(member, amoid, typeoid);
+				assignProcTypes(member, amoid, typeoid, optsProcNumber);
 				addFamilyMember(&procedures, member, true);
 				break;
 			case OPCLASS_ITEM_STORAGETYPE:
@@ -777,6 +779,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
 	Oid			amoid,			/* our AM's oid */
 				opfamilyoid;	/* oid of opfamily */
 	int			maxOpNumber,	/* amstrategies value */
+				optsProcNumber,	/* amopclassopts value */
 				maxProcNumber;	/* amsupport value */
 	HeapTuple	tup;
 	Form_pg_am	amform;
@@ -800,6 +803,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
 	if (maxOpNumber <= 0)
 		maxOpNumber = SHRT_MAX;
 	maxProcNumber = amroutine->amsupport;
+	optsProcNumber = amroutine->amoptsprocnum;
 
 	/* XXX Should we make any privilege check against the AM? */
 
@@ -824,7 +828,8 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
 						  maxOpNumber, maxProcNumber, stmt->items);
 	else
 		AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
-						 maxOpNumber, maxProcNumber, stmt->items);
+						 maxOpNumber, maxProcNumber, optsProcNumber,
+						 stmt->items);
 
 	return opfamilyoid;
 }
@@ -834,7 +839,8 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
  */
 static void
 AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
-				 int maxOpNumber, int maxProcNumber, List *items)
+				 int maxOpNumber, int maxProcNumber, int optsProcNumber,
+				 List *items)
 {
 	List	   *operators;		/* OpFamilyMember list for operators */
 	List	   *procedures;		/* OpFamilyMember list for support procs */
@@ -926,7 +932,7 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
 					processTypesSpec(item->class_args,
 									 &member->lefttype, &member->righttype);
 
-				assignProcTypes(member, amoid, InvalidOid);
+				assignProcTypes(member, amoid, InvalidOid, optsProcNumber);
 				addFamilyMember(&procedures, member, true);
 				break;
 			case OPCLASS_ITEM_STORAGETYPE:
@@ -1129,7 +1135,8 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
  * and do any validity checking we can manage.
  */
 static void
-assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
+				int opclassOptsProcNum)
 {
 	HeapTuple	proctup;
 	Form_pg_proc procform;
@@ -1140,6 +1147,36 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 		elog(ERROR, "cache lookup failed for function %u", member->object);
 	procform = (Form_pg_proc) GETSTRUCT(proctup);
 
+	/* Check the signature of the opclass options parsing function */
+	if (member->number == opclassOptsProcNum)
+	{
+		if (OidIsValid(typeoid))
+		{
+			if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) ||
+				(OidIsValid(member->righttype) && member->righttype != typeoid))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("associated data types for opclass options "
+								"parsing functions must match opclass input type")));
+		}
+		else
+		{
+			if (member->lefttype != member->righttype)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("left and right associated data types for "
+								"opclass options parsing functions must match")));
+		}
+
+		if (procform->prorettype != VOIDOID ||
+			procform->pronargs != 1 ||
+			procform->proargtypes.values[0] != INTERNALOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("invalid opclass options parsing function"),
+					 errhint("opclass options parsing function must have signature '%s'",
+							 "(internal) RETURNS void")));
+	}
 	/*
 	 * btree comparison procs must be 2-arg procs returning int4.  btree
 	 * sortsupport procs must take internal and return void.  btree in_range
@@ -1148,7 +1185,7 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 	 * returning int4, while proc 2 must be a 2-arg proc returning int8.
 	 * Otherwise we don't know.
 	 */
-	if (amoid == BTREE_AM_OID)
+	else if (amoid == BTREE_AM_OID)
 	{
 		if (member->number == BTORDER_PROC)
 		{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8e35c5bd1a2..c8c88be2c9a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -6085,7 +6085,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 	ReleaseSysCache(typeTuple);
 
-	InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+	InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
 
 	table_close(attrdesc, RowExclusiveLock);
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index eaab97f7535..c9a90d11915 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2877,6 +2877,7 @@ _copyIndexElem(const IndexElem *from)
 	COPY_STRING_FIELD(indexcolname);
 	COPY_NODE_FIELD(collation);
 	COPY_NODE_FIELD(opclass);
+	COPY_NODE_FIELD(opclassopts);
 	COPY_SCALAR_FIELD(ordering);
 	COPY_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 88b912977e9..d05ca26fcf5 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2572,6 +2572,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
 	COMPARE_STRING_FIELD(indexcolname);
 	COMPARE_NODE_FIELD(collation);
 	COMPARE_NODE_FIELD(opclass);
+	COMPARE_NODE_FIELD(opclassopts);
 	COMPARE_SCALAR_FIELD(ordering);
 	COMPARE_SCALAR_FIELD(nulls_ordering);
 
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index e8cdc90c315..b442b5a29ef 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -763,6 +763,9 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
 	n->ii_ExclusionProcs = NULL;
 	n->ii_ExclusionStrats = NULL;
 
+	/* opclass options */
+	n->ii_OpclassOptions = NULL;
+
 	/* speculative inserts */
 	n->ii_UniqueOps = NULL;
 	n->ii_UniqueProcs = NULL;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e084c3f0699..eb168ffd6da 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2869,6 +2869,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
 	WRITE_STRING_FIELD(indexcolname);
 	WRITE_NODE_FIELD(collation);
 	WRITE_NODE_FIELD(opclass);
+	WRITE_NODE_FIELD(opclassopts);
 	WRITE_ENUM_FIELD(ordering, SortByDir);
 	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
 }
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d82fc5ab8b8..51470dd73e1 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -278,6 +278,9 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 			info->amcostestimate = amroutine->amcostestimate;
 			Assert(info->amcostestimate != NULL);
 
+			/* Fetch index opclass options */
+			info->opclassoptions = RelationGetIndexAttOptions(indexRelation, true);
+
 			/*
 			 * Fetch the ordering information for the index, if any.
 			 */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7e384f956c8..eb0bf12cd8b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -493,7 +493,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <alias>	alias_clause opt_alias_clause
 %type <list>	func_alias_clause
 %type <sortby>	sortby
-%type <ielem>	index_elem
+%type <ielem>	index_elem index_elem_options
 %type <node>	table_ref
 %type <jexpr>	joined_table
 %type <range>	relation_expr
@@ -7478,43 +7478,53 @@ index_params:	index_elem							{ $$ = list_make1($1); }
 			| index_params ',' index_elem			{ $$ = lappend($1, $3); }
 		;
 
+
+index_elem_options:
+	opt_collate opt_class opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = NIL;
+			$$->ordering = $3;
+			$$->nulls_ordering = $4;
+		}
+	| opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+		{
+			$$ = makeNode(IndexElem);
+			$$->name = NULL;
+			$$->expr = NULL;
+			$$->indexcolname = NULL;
+			$$->collation = $1;
+			$$->opclass = $2;
+			$$->opclassopts = $3;
+			$$->ordering = $4;
+			$$->nulls_ordering = $5;
+		}
+	;
+
 /*
  * Index attributes can be either simple column references, or arbitrary
  * expressions in parens.  For backwards-compatibility reasons, we allow
  * an expression that's just a function call to be written without parens.
  */
-index_elem:	ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
 				{
-					$$ = makeNode(IndexElem);
+					$$ = $2;
 					$$->name = $1;
-					$$->expr = NULL;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+			| func_expr_windowless index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $2;
 					$$->expr = $1;
-					$$->indexcolname = NULL;
-					$$->collation = $2;
-					$$->opclass = $3;
-					$$->ordering = $4;
-					$$->nulls_ordering = $5;
 				}
-			| '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+			| '(' a_expr ')' index_elem_options
 				{
-					$$ = makeNode(IndexElem);
-					$$->name = NULL;
+					$$ = $4;
 					$$->expr = $2;
-					$$->indexcolname = NULL;
-					$$->collation = $4;
-					$$->opclass = $5;
-					$$->ordering = $6;
-					$$->nulls_ordering = $7;
 				}
 		;
 
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index c1911411d0b..ae322aae567 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1591,6 +1591,8 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
 
 		/* Add the operator class name, if non-default */
 		iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
+		iparam->opclassopts =
+			untransformRelOptions(get_attoptions(source_relid, keyno + 1));
 
 		iparam->ordering = SORTBY_DEFAULT;
 		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
@@ -2168,10 +2170,14 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 				 * constraint; and there's also the dump/reload problem
 				 * mentioned above.
 				 */
+				Datum		attoptions =
+					get_attoptions(RelationGetRelid(index_rel), i + 1);
+
 				defopclass = GetDefaultOpClass(attform->atttypid,
 											   index_rel->rd_rel->relam);
 				if (indclass->values[i] != defopclass ||
 					attform->attcollation != index_rel->rd_indcollation[i] ||
+					attoptions != (Datum) 0 ||
 					index_rel->rd_indoption[i] != 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -2351,6 +2357,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 			iparam->indexcolname = NULL;
 			iparam->collation = NIL;
 			iparam->opclass = NIL;
+			iparam->opclassopts = NIL;
 			iparam->ordering = SORTBY_DEFAULT;
 			iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
 			index->indexParams = lappend(index->indexParams, iparam);
@@ -2464,6 +2471,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		iparam->indexcolname = NULL;
 		iparam->collation = NIL;
 		iparam->opclass = NIL;
+		iparam->opclassopts = NIL;
 		index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
 	}
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5e63238f030..f6cf7e72a1e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -480,6 +480,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1384,6 +1385,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 		{
 			int16		opt = indoption->values[keyno];
 			Oid			indcoll = indcollation->values[keyno];
+			Datum		attoptions = get_attoptions(indexrelid, keyno + 1);
+			bool		has_options = attoptions != (Datum) 0;
 
 			/* Add collation, if not default for column */
 			if (OidIsValid(indcoll) && indcoll != keycolcollation)
@@ -1391,7 +1394,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 								 generate_collation_name((indcoll)));
 
 			/* Add the operator class name, if not default */
-			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+			get_opclass_name(indclass->values[keyno],
+							 has_options ? InvalidOid : keycoltype, &buf);
+
+			if (has_options)
+			{
+				appendStringInfoString(&buf, " (");
+				get_reloptions(&buf, attoptions);
+				appendStringInfoChar(&buf, ')');
+			}
 
 			/* Add options if relevant */
 			if (amroutine->amcanorder)
@@ -10573,6 +10584,23 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 	ReleaseSysCache(ht_opc);
 }
 
+/*
+ * generate_opclass_name
+ *		Compute the name to display for a opclass specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+char *
+generate_opclass_name(Oid opclass)
+{
+	StringInfoData buf;
+
+	initStringInfo(&buf);
+	get_opclass_name(opclass, InvalidOid, &buf);
+
+	return &buf.data[1];	/* get_opclass_name() prepends space */
+}
+
 /*
  * processIndirection - take care of array and subfield assignment
  *
@@ -11250,6 +11278,62 @@ string_to_text(char *str)
 	return result;
 }
 
+/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+	Datum	   *options;
+	int			noptions;
+	int			i;
+
+	deconstruct_array(DatumGetArrayTypeP(reloptions),
+					  TEXTOID, -1, false, TYPALIGN_INT,
+					  &options, NULL, &noptions);
+
+	for (i = 0; i < noptions; i++)
+	{
+		char	   *option = TextDatumGetCString(options[i]);
+		char	   *name;
+		char	   *separator;
+		char	   *value;
+
+		/*
+		 * Each array element should have the form name=value.  If the "="
+		 * is missing for some reason, treat it like an empty value.
+		 */
+		name = option;
+		separator = strchr(option, '=');
+		if (separator)
+		{
+			*separator = '\0';
+			value = separator + 1;
+		}
+		else
+			value = "";
+
+		if (i > 0)
+			appendStringInfoString(buf, ", ");
+		appendStringInfo(buf, "%s=", quote_identifier(name));
+
+		/*
+		 * In general we need to quote the value; but to avoid unnecessary
+		 * clutter, do not quote if it is an identifier that would not
+		 * need quoting.  (We could also allow numbers, but that is a bit
+		 * trickier than it looks --- for example, are leading zeroes
+		 * significant?  We don't want to assume very much here about what
+		 * custom reloptions might mean.)
+		 */
+		if (quote_identifier(value) == value)
+			appendStringInfoString(buf, value);
+		else
+			simple_quote_literal(buf, value);
+
+		pfree(option);
+	}
+}
+
 /*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
@@ -11270,56 +11354,9 @@ flatten_reloptions(Oid relid)
 	if (!isnull)
 	{
 		StringInfoData buf;
-		Datum	   *options;
-		int			noptions;
-		int			i;
 
 		initStringInfo(&buf);
-
-		deconstruct_array(DatumGetArrayTypeP(reloptions),
-						  TEXTOID, -1, false, TYPALIGN_INT,
-						  &options, NULL, &noptions);
-
-		for (i = 0; i < noptions; i++)
-		{
-			char	   *option = TextDatumGetCString(options[i]);
-			char	   *name;
-			char	   *separator;
-			char	   *value;
-
-			/*
-			 * Each array element should have the form name=value.  If the "="
-			 * is missing for some reason, treat it like an empty value.
-			 */
-			name = option;
-			separator = strchr(option, '=');
-			if (separator)
-			{
-				*separator = '\0';
-				value = separator + 1;
-			}
-			else
-				value = "";
-
-			if (i > 0)
-				appendStringInfoString(&buf, ", ");
-			appendStringInfo(&buf, "%s=", quote_identifier(name));
-
-			/*
-			 * In general we need to quote the value; but to avoid unnecessary
-			 * clutter, do not quote if it is an identifier that would not
-			 * need quoting.  (We could also allow numbers, but that is a bit
-			 * trickier than it looks --- for example, are leading zeroes
-			 * significant?  We don't want to assume very much here about what
-			 * custom reloptions might mean.)
-			 */
-			if (quote_identifier(value) == value)
-				appendStringInfoString(&buf, value);
-			else
-				simple_quote_literal(&buf, value);
-
-			pfree(option);
-		}
+		get_reloptions(&buf, reloptions);
 
 		result = buf.data;
 	}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 8339f4cd7a2..e62b69d6f26 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -6367,6 +6367,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 				Oid clause_op, Datum query,
 				GinQualCounts *counts)
 {
+	FmgrInfo	flinfo;
 	Oid			extractProcOid;
 	Oid			collation;
 	int			strategy_op;
@@ -6416,15 +6417,19 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	else
 		collation = DEFAULT_COLLATION_OID;
 
-	OidFunctionCall7Coll(extractProcOid,
-						 collation,
-						 query,
-						 PointerGetDatum(&nentries),
-						 UInt16GetDatum(strategy_op),
-						 PointerGetDatum(&partial_matches),
-						 PointerGetDatum(&extra_data),
-						 PointerGetDatum(&nullFlags),
-						 PointerGetDatum(&searchMode));
+	fmgr_info(extractProcOid, &flinfo);
+
+	set_fn_opclass_options(&flinfo, index->opclassoptions[indexcol]);
+
+	FunctionCall7Coll(&flinfo,
+					  collation,
+					  query,
+					  PointerGetDatum(&nentries),
+					  UInt16GetDatum(strategy_op),
+					  PointerGetDatum(&partial_matches),
+					  PointerGetDatum(&extra_data),
+					  PointerGetDatum(&nullFlags),
+					  PointerGetDatum(&searchMode));
 
 	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
 	{
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 8320dcda800..2e0bc3ebd07 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -16,6 +16,7 @@
 
 #include "access/gist.h"
 #include "access/heaptoast.h"
+#include "access/reloptions.h"
 #include "lib/qunique.h"
 #include "port/pg_bitutils.h"
 #include "tsearch/ts_utils.h"
@@ -23,17 +24,25 @@
 #include "utils/pg_crc.h"
 
 
-#define SIGLENINT  31			/* >121 => key will toast, so it will not work
-								 * !!! */
+/* tsvector_ops opclass options */
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	int			siglen;			/* signature length */
+} GistTsVectorOptions;
 
-#define SIGLEN	( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLEN_DEFAULT	(31 * 4)
+#define SIGLEN_MAX		GISTMaxIndexKeySize
+#define GET_SIGLEN()	(PG_HAS_OPCLASS_OPTIONS() ? \
+						 ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
+						 SIGLEN_DEFAULT)
+
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
 
-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) / BITS_PER_BYTE ) ) )
 #define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -41,8 +50,8 @@ typedef char *BITVECP;
 #define SETBIT(x,i)   GETBYTE(x,i) |=  ( 0x01 << ( (i) % BITS_PER_BYTE ) )
 #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 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 GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
 
@@ -66,13 +75,14 @@ typedef struct
 #define ISALLTRUE(x)	( ((SignTSVector*)(x))->flag & ALLISTRUE )
 
 #define GTHDRSIZE	( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
 
 #define GETSIGN(x)	( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
 #define GETARR(x)	( (int32*)( (char*)(x)+GTHDRSIZE ) )
 #define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
 
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
 
 Datum
 gtsvectorin(PG_FUNCTION_ARGS)
@@ -103,9 +113,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
 		sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
 	else
 	{
-		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+		int			siglen = GETSIGLEN(key);
+		int			cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
 
-		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+		sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
 	}
 
 	PG_FREE_IF_COPY(key, 0);
@@ -124,36 +135,49 @@ compareint(const void *va, const void *vb)
 }
 
 static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
 {
 	int32		k,
 				len = ARRNELEM(a);
 	int32	   *ptr = GETARR(a);
 
-	MemSet((void *) sign, 0, sizeof(BITVEC));
+	MemSet((void *) sign, 0, siglen);
 	for (k = 0; k < len; k++)
-		HASH(sign, ptr[k]);
+		HASH(sign, ptr[k], siglen);
 }
 
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+	int			size = CALCGTSIZE(flag, len);
+	SignTSVector *res = palloc(size);
+
+	SET_VARSIZE(res, size);
+	res->flag = flag;
+
+	if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+		memcpy(GETSIGN(res), sign, len);
+
+	return res;
+}
+
+
 Datum
 gtsvector_compress(PG_FUNCTION_ARGS)
 {
 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	int			siglen = GET_SIGLEN();
 	GISTENTRY  *retval = entry;
 
 	if (entry->leafkey)
 	{							/* tsvector */
-		SignTSVector *res;
 		TSVector	val = DatumGetTSVector(entry->key);
+		SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
 		int32		len;
 		int32	   *arr;
 		WordEntry  *ptr = ARRPTR(val);
 		char	   *words = STRPTR(val);
 
-		len = CALCGTSIZE(ARRKEY, val->size);
-		res = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = ARRKEY;
 		arr = GETARR(res);
 		len = val->size;
 		while (len--)
@@ -185,13 +209,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 		/* make signature, if array is too long */
 		if (VARSIZE(res) > TOAST_INDEX_TARGET)
 		{
-			SignTSVector *ressign;
+			SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
 
-			len = CALCGTSIZE(SIGNKEY, 0);
-			ressign = (SignTSVector *) palloc(len);
-			SET_VARSIZE(ressign, len);
-			ressign->flag = SIGNKEY;
-			makesign(GETSIGN(ressign), res);
+			makesign(GETSIGN(ressign), res, siglen);
 			res = ressign;
 		}
 
@@ -203,22 +223,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
 	else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
 			 !ISALLTRUE(DatumGetPointer(entry->key)))
 	{
-		int32		i,
-					len;
+		int32		i;
 		SignTSVector *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 = (SignTSVector *) palloc(len);
-		SET_VARSIZE(res, len);
-		res->flag = SIGNKEY | ALLISTRUE;
-
+		res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
 		gistentryinit(*retval, PointerGetDatum(res),
 					  entry->rel, entry->page,
@@ -292,12 +307,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
 static bool
 checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
 {
+	void *key = (SignTSVector *) checkval;
+
 	/*
 	 * we are not able to find a prefix in signature tree
 	 */
 	if (val->prefix)
 		return true;
-	return GETBIT(checkval, HASHVAL(val->valcrc));
+	return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
 }
 
 Datum
@@ -324,7 +341,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 
 		/* since signature is lossy, cannot specify CALC_NOT here */
 		PG_RETURN_BOOL(TS_execute(GETQUERY(query),
-								  (void *) GETSIGN(key),
+								  key,
 								  TS_EXEC_PHRASE_NO_POS,
 								  checkcondition_bit));
 	}
@@ -342,7 +359,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
 }
 
 static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
 {
 	int32		i;
 
@@ -353,7 +370,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		if (ISALLTRUE(add))
 			return 1;
 
-		LOOPBYTE
+		Assert(GETSIGLEN(add) == siglen);
+
+		LOOPBYTE(siglen)
 			sbase[i] |= sadd[i];
 	}
 	else
@@ -361,7 +380,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
 		int32	   *ptr = GETARR(add);
 
 		for (i = 0; i < ARRNELEM(add); i++)
-			HASH(sbase, ptr[i]);
+			HASH(sbase, ptr[i], siglen);
 	}
 	return 0;
 }
@@ -372,30 +391,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	int		   *size = (int *) PG_GETARG_POINTER(1);
-	BITVEC		base;
-	int32		i,
-				len;
-	int32		flag = 0;
-	SignTSVector *result;
+	int			siglen = GET_SIGLEN();
+	SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+	BITVECP		base = GETSIGN(result);
+	int32		i;
+
+	memset(base, 0, siglen);
 
-	MemSet((void *) base, 0, sizeof(BITVEC));
 	for (i = 0; i < entryvec->n; i++)
 	{
-		if (unionkey(base, GETENTRY(entryvec, i)))
+		if (unionkey(base, GETENTRY(entryvec, i), siglen))
 		{
-			flag = ALLISTRUE;
+			result->flag |= ALLISTRUE;
+			SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
 			break;
 		}
 	}
 
-	flag |= SIGNKEY;
-	len = CALCGTSIZE(flag, 0);
-	result = (SignTSVector *) palloc(len);
-	*size = len;
-	SET_VARSIZE(result, len);
-	result->flag = flag;
-	if (!ISALLTRUE(result))
-		memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+	*size = VARSIZE(result);
 
 	PG_RETURN_POINTER(result);
 }
@@ -406,6 +419,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
 	SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
 	SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
+	int			siglen = GET_SIGLEN();
 
 	if (ISSIGNKEY(a))
 	{							/* then b also ISSIGNKEY */
@@ -421,8 +435,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
 			BITVECP		sa = GETSIGN(a),
 						sb = GETSIGN(b);
 
+			Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
 			*result = true;
-			LOOPBYTE
+			LOOPBYTE(siglen)
 			{
 				if (sa[i] != sb[i])
 				{
@@ -459,19 +475,19 @@ gtsvector_same(PG_FUNCTION_ARGS)
 }
 
 static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
 {
-	return pg_popcount(sign, SIGLEN);
+	return pg_popcount(sign, siglen);
 }
 
 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]);
 		/* Using the popcount functions here isn't likely to win */
@@ -483,17 +499,22 @@ hemdistsign(BITVECP a, BITVECP b)
 static int
 hemdist(SignTSVector *a, SignTSVector *b)
 {
+	int siglena = GETSIGLEN(a);
+	int siglenb = GETSIGLEN(b);
+
 	if (ISALLTRUE(a))
 	{
 		if (ISALLTRUE(b))
 			return 0;
 		else
-			return SIGLENBIT - sizebitvec(GETSIGN(b));
+			return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
 	}
 	else if (ISALLTRUE(b))
-		return SIGLENBIT - sizebitvec(GETSIGN(a));
+		return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
+
+	Assert(siglena == siglenb);
 
-	return hemdistsign(GETSIGN(a), GETSIGN(b));
+	return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
 }
 
 Datum
@@ -502,6 +523,7 @@ gtsvector_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);
+	int			siglen = GET_SIGLEN();
 	SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
 	SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
 	BITVECP		orig = GETSIGN(origval);
@@ -510,14 +532,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 
 	if (ISARRKEY(newval))
 	{
-		BITVEC		sign;
+		BITVECP		sign = palloc(siglen);
 
-		makesign(sign, newval);
+		makesign(sign, newval, siglen);
 
 		if (ISALLTRUE(origval))
-			*penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+		{
+			int			siglenbit = SIGLENBIT(siglen);
+
+			*penalty =
+				(float) (siglenbit - sizebitvec(sign, siglen)) /
+				(float) (siglenbit + 1);
+		}
 		else
-			*penalty = hemdistsign(sign, orig);
+			*penalty = hemdistsign(sign, orig, siglen);
+
+		pfree(sign);
 	}
 	else
 		*penalty = hemdist(origval, newval);
@@ -527,19 +557,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
 typedef struct
 {
 	bool		allistrue;
-	BITVEC		sign;
+	BITVECP		sign;
 } CACHESIGN;
 
 static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
 {
 	item->allistrue = false;
 	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) )
@@ -563,19 +593,19 @@ comparecost(const void *va, const void *vb)
 
 
 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
@@ -583,6 +613,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	int			siglen = GET_SIGLEN();
 	OffsetNumber k,
 				j;
 	SignTSVector *datum_l,
@@ -602,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	BITVECP		ptr;
 	int			i;
 	CACHESIGN  *cache;
+	char	   *cache_sign;
 	SPLITCOST  *costvector;
 
 	maxoff = entryvec->n - 2;
@@ -610,16 +642,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	v->spl_right = (OffsetNumber *) palloc(nbytes);
 
 	cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
-	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+	cache_sign = palloc(siglen * (maxoff + 2));
+
+	for (j = 0; j < maxoff + 2; j++)
+		cache[j].sign = &cache_sign[siglen * j];
+
+	fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+			  siglen);
 
 	for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
 	{
 		for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
 		{
 			if (k == FirstOffsetNumber)
-				fillcache(&cache[j], GETENTRY(entryvec, j));
+				fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
 
-			size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+			size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
 			if (size_waste > waste)
 			{
 				waste = size_waste;
@@ -641,44 +679,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
 	}
 
 	/* form initial .. */
-	if (cache[seed_1].allistrue)
-	{
-		datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_l->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_l = (SignTSVector *) 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 = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
-		datum_r->flag = SIGNKEY | ALLISTRUE;
-	}
-	else
-	{
-		datum_r = (SignTSVector *) 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 = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+							  siglen, cache[seed_1].sign);
+	datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+							  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), 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);
@@ -704,36 +719,40 @@ gtsvector_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;
@@ -744,12 +763,12 @@ gtsvector_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;
@@ -776,3 +795,16 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
 {
 	return gtsvector_consistent(fcinfo);
 }
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+	local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+	init_local_reloptions(relopts, sizeof(GistTsVectorOptions));
+	add_local_int_reloption(relopts, "siglen", "signature length",
+							SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+							offsetof(GistTsVectorOptions, siglen));
+
+	PG_RETURN_VOID();
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27bbb58f564..0a6db0d478e 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -909,6 +909,41 @@ get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
 	ReleaseSysCache(tp);
 }
 
+/*
+ * get_attoptions
+ *
+ *		Given the relation id and the attribute number,
+ *		return the attribute options text[] datum, if any.
+ */
+Datum
+get_attoptions(Oid relid, int16 attnum)
+{
+	HeapTuple	tuple;
+	Datum		attopts;
+	Datum		result;
+	bool		isnull;
+
+	tuple = SearchSysCache2(ATTNUM,
+							ObjectIdGetDatum(relid),
+							Int16GetDatum(attnum));
+
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+			 attnum, relid);
+
+	attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+							  &isnull);
+
+	if (isnull)
+		result = (Datum) 0;
+	else
+		result = datumCopy(attopts, false, -1);		/* text[] */
+
+	ReleaseSysCache(tuple);
+
+	return result;
+}
+
 /*				---------- PG_CAST CACHE ----------					 */
 
 /*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 76f41dbe36c..2ffc1a10782 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1426,7 +1426,7 @@ RelationInitIndexAccessInfo(Relation relation)
 	amsupport = relation->rd_indam->amsupport;
 	if (amsupport > 0)
 	{
-		int			nsupport = indnatts * amsupport;
+		int			nsupport = indnatts * (amsupport + 1);
 
 		relation->rd_support = (RegProcedure *)
 			MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
@@ -1490,6 +1490,8 @@ RelationInitIndexAccessInfo(Relation relation)
 	indoption = (int2vector *) DatumGetPointer(indoptionDatum);
 	memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
 
+	(void) RelationGetIndexAttOptions(relation, false);
+
 	/*
 	 * expressions, predicate, exclusion caches will be filled later
 	 */
@@ -1539,9 +1541,9 @@ IndexSupportInitialize(oidvector *indclass,
 		opFamily[attIndex] = opcentry->opcfamily;
 		opcInType[attIndex] = opcentry->opcintype;
 		if (maxSupportNumber > 0)
-			memcpy(&indexSupport[attIndex * maxSupportNumber],
+			memcpy(&indexSupport[attIndex * (maxSupportNumber + 1)],
 				   opcentry->supportProcs,
-				   maxSupportNumber * sizeof(RegProcedure));
+				   (maxSupportNumber + 1) * sizeof(RegProcedure));
 	}
 }
 
@@ -1606,7 +1608,7 @@ LookupOpclassInfo(Oid operatorClassOid,
 		if (numSupport > 0)
 			opcentry->supportProcs = (RegProcedure *)
 				MemoryContextAllocZero(CacheMemoryContext,
-									   numSupport * sizeof(RegProcedure));
+									   (numSupport + 1) * sizeof(RegProcedure));
 		else
 			opcentry->supportProcs = NULL;
 	}
@@ -1693,13 +1695,12 @@ LookupOpclassInfo(Oid operatorClassOid,
 		{
 			Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
 
-			if (amprocform->amprocnum <= 0 ||
+			if (amprocform->amprocnum < 0 ||
 				(StrategyNumber) amprocform->amprocnum > numSupport)
 				elog(ERROR, "invalid amproc number %d for opclass %u",
 					 amprocform->amprocnum, operatorClassOid);
 
-			opcentry->supportProcs[amprocform->amprocnum - 1] =
-				amprocform->amproc;
+			opcentry->supportProcs[amprocform->amprocnum] = amprocform->amproc;
 		}
 
 		systable_endscan(scan);
@@ -3980,6 +3981,8 @@ load_critical_index(Oid indexoid, Oid heapoid)
 	ird->rd_refcnt = 1;
 	UnlockRelationOid(indexoid, AccessShareLock);
 	UnlockRelationOid(heapoid, AccessShareLock);
+
+	(void) RelationGetIndexAttOptions(ird, false);
 }
 
 /*
@@ -5185,6 +5188,100 @@ GetRelationPublicationActions(Relation relation)
 	return pubactions;
 }
 
+/*
+ * RelationGetIndexRawAttOptions -- get AM/opclass-specific options for the index
+ */
+Datum *
+RelationGetIndexRawAttOptions(Relation indexrel)
+{
+	Oid			indexrelid = RelationGetRelid(indexrel);
+	int16		natts = RelationGetNumberOfAttributes(indexrel);
+	Datum	   *options = NULL;
+	int16		attnum;
+
+	for (attnum = 1; attnum <= natts; attnum++)
+	{
+		if (!OidIsValid(index_getprocid(indexrel, attnum,
+										indexrel->rd_indam->amoptsprocnum)))
+			continue;
+
+		if (!options)
+			options = palloc0(sizeof(Datum) * natts);
+
+		options[attnum - 1] = get_attoptions(indexrelid, attnum);
+	}
+
+	return options;
+}
+
+static bytea **
+CopyIndexAttOptions(bytea **srcopts, int natts)
+{
+	bytea	  **opts = palloc(sizeof(*opts) * natts);
+
+	for (int i = 0; i < natts; i++)
+	{
+		bytea	   *opt = srcopts[i];
+
+		opts[i] = !opt ? NULL : (bytea *)
+			DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+	}
+
+	return opts;
+}
+
+/*
+ * RelationGetIndexAttOptions
+ *		get AM/opclass-specific options for an index parsed into a binary form
+ */
+bytea **
+RelationGetIndexAttOptions(Relation relation, bool copy)
+{
+	MemoryContext oldcxt;
+	bytea	  **opts = relation->rd_opcoptions;
+	Oid			relid = RelationGetRelid(relation);
+	int			natts = RelationGetNumberOfAttributes(relation);	/* XXX IndexRelationGetNumberOfKeyAttributes */
+	int			i;
+
+	/* Try to copy cached options. */
+	if (opts)
+		return copy ? CopyIndexAttOptions(opts, natts) : opts;
+
+	/* Get and parse opclass options. */
+	opts = palloc0(sizeof(*opts) * natts);
+
+	for (i = 0; i < natts; i++)
+	{
+		if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId)
+		{
+			Datum		attoptions = get_attoptions(relid, i + 1);
+
+			opts[i] = index_opclass_options(relation, i + 1, attoptions, false);
+
+			if (attoptions != (Datum) 0)
+				pfree(DatumGetPointer(attoptions));
+		}
+	}
+
+	/* Copy parsed options to the cache. */
+	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
+	relation->rd_opcoptions = CopyIndexAttOptions(opts, natts);
+	MemoryContextSwitchTo(oldcxt);
+
+	if (copy)
+		return opts;
+
+	for (i = 0; i < natts; i++)
+	{
+		if (opts[i])
+			pfree(opts[i]);
+	}
+
+	pfree(opts);
+
+	return relation->rd_opcoptions;
+}
+
 /*
  * Routines to support ereport() reports of relation-related errors
  *
@@ -5546,8 +5643,25 @@ load_relcache_init_file(bool shared)
 
 			rel->rd_indoption = indoption;
 
+			/* finally, read the vector of opcoptions values */
+			rel->rd_opcoptions = (bytea **)
+				MemoryContextAllocZero(indexcxt, sizeof(*rel->rd_opcoptions) * relform->relnatts);
+
+			for (i = 0; i < relform->relnatts; i++)
+			{
+				if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
+					goto read_failed;
+
+				if (len > 0)
+				{
+					rel->rd_opcoptions[i] = (bytea *) MemoryContextAlloc(indexcxt, len);
+					if (fread(rel->rd_opcoptions[i], 1, len, fp) != len)
+						goto read_failed;
+				}
+			}
+
 			/* set up zeroed fmgr-info vector */
-			nsupport = relform->relnatts * rel->rd_indam->amsupport;
+			nsupport = relform->relnatts * (rel->rd_indam->amsupport + 1);
 			rel->rd_supportinfo = (FmgrInfo *)
 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
 		}
@@ -5574,6 +5688,7 @@ load_relcache_init_file(bool shared)
 			Assert(rel->rd_supportinfo == NULL);
 			Assert(rel->rd_indoption == NULL);
 			Assert(rel->rd_indcollation == NULL);
+			Assert(rel->rd_opcoptions == NULL);
 		}
 
 		/*
@@ -5847,7 +5962,7 @@ write_relcache_init_file(bool shared)
 
 			/* next, write the vector of support procedure OIDs */
 			write_item(rel->rd_support,
-					   relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)),
+					   relform->relnatts * ((rel->rd_indam->amsupport + 1) * sizeof(RegProcedure)),
 					   fp);
 
 			/* next, write the vector of collation OIDs */
@@ -5859,6 +5974,16 @@ write_relcache_init_file(bool shared)
 			write_item(rel->rd_indoption,
 					   relform->relnatts * sizeof(int16),
 					   fp);
+
+			Assert(rel->rd_opcoptions);
+
+			/* finally, write the vector of opcoptions values */
+			for (i = 0; i < relform->relnatts; i++)
+			{
+				bytea	   *opt = rel->rd_opcoptions[i];
+
+				write_item(opt, opt ? VARSIZE(opt) : 0, fp);
+			}
 		}
 	}
 
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 2b4226d3a81..03c614b234a 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -18,9 +18,11 @@
 #include "access/detoast.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
 #include "executor/functions.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "pgstat.h"
 #include "utils/acl.h"
@@ -1952,6 +1954,57 @@ get_fn_expr_variadic(FmgrInfo *flinfo)
 		return false;
 }
 
+/*
+ * Set options to FmgrInfo of opclass support function.
+ *
+ * Opclass support functions are called outside of expressions.  Thanks to that
+ * we can use fn_expr to store opclass options as bytea constant.
+ */
+void
+set_fn_opclass_options(FmgrInfo *flinfo, bytea *options)
+{
+	flinfo->fn_expr = (Node *) makeConst(BYTEAOID, -1, InvalidOid, -1,
+										 PointerGetDatum(options),
+										 options == NULL, false);
+}
+
+/*
+ * Check if options are defined for opclass support function.
+ */
+bool
+has_fn_opclass_options(FmgrInfo *flinfo)
+{
+	if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const))
+	{
+		Const	   *expr = (Const *) flinfo->fn_expr;
+
+		if (expr->consttype == BYTEAOID)
+			return !expr->constisnull;
+	}
+	return false;
+}
+
+/*
+ * Get options for opclass support function.
+ */
+bytea *
+get_fn_opclass_options(FmgrInfo *flinfo)
+{
+	if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const))
+	{
+		Const	   *expr = (Const *) flinfo->fn_expr;
+
+		if (expr->consttype == BYTEAOID)
+			return expr->constisnull ? NULL : DatumGetByteaP(expr->constvalue);
+	}
+
+	ereport(ERROR,
+			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+			 errmsg("opclass options info is absent in function call context")));
+
+	return NULL;
+}
+
 /*-------------------------------------------------------------------------
  *		Support routines for procedural language implementations
  *-------------------------------------------------------------------------
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 3b3e22f73de..4325faa460b 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -171,6 +171,8 @@ typedef struct IndexAmRoutine
 	uint16		amstrategies;
 	/* total number of support functions that this AM uses */
 	uint16		amsupport;
+	/* opclass options support function number or 0 */
+	uint16		amoptsprocnum;
 	/* does AM support ORDER BY indexed column's value? */
 	bool		amcanorder;
 	/* does AM support ORDER BY result of an operator on indexed column? */
diff --git a/src/include/access/amvalidate.h b/src/include/access/amvalidate.h
index c6c60e06b4b..f3a0e52d84e 100644
--- a/src/include/access/amvalidate.h
+++ b/src/include/access/amvalidate.h
@@ -29,6 +29,7 @@ typedef struct OpFamilyOpFuncGroup
 extern List *identify_opfamily_groups(CatCList *oprlist, CatCList *proclist);
 extern bool check_amproc_signature(Oid funcid, Oid restype, bool exact,
 								   int minargs, int maxargs,...);
+extern bool check_amoptsproc_signature(Oid funcid);
 extern bool check_amop_signature(Oid opno, Oid restype,
 								 Oid lefttype, Oid righttype);
 extern bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid);
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 28b4a63ef7e..9ffc9100c0b 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -69,6 +69,7 @@ typedef struct BrinDesc
 #define BRIN_PROCNUM_CONSISTENT		3
 #define BRIN_PROCNUM_UNION			4
 #define BRIN_MANDATORY_NPROCS		4
+#define BRIN_PROCNUM_OPTIONS 		5	/* optional */
 /* procedure numbers up to 10 are reserved for BRIN future expansion */
 #define BRIN_FIRST_OPTIONAL_PROCNUM 11
 #define BRIN_LAST_OPTIONAL_PROCNUM	15
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 7e9364a50c4..931257bd817 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -188,6 +188,9 @@ extern void index_store_float8_orderby_distances(IndexScanDesc scan,
 												 Oid *orderByTypes,
 												 IndexOrderByDistance *distances,
 												 bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+									Datum attoptions, bool validate);
+
 
 /*
  * index access method support routines (in genam.c)
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 59fad86c818..990e8b3e4fa 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
 #define GIN_CONSISTENT_PROC			   4
 #define GIN_COMPARE_PARTIAL_PROC	   5
 #define GIN_TRICONSISTENT_PROC		   6
-#define GINNProcs					   6
+#define GIN_OPTIONS_PROC	   7
+#define GINNProcs					   7
 
 /*
  * searchMode settings for extractQueryFn.
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 73e43e880ab..4994351697c 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -16,6 +16,7 @@
 #ifndef GIST_H
 #define GIST_H
 
+#include "access/itup.h"
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "access/xlogdefs.h"
@@ -35,7 +36,8 @@
 #define GIST_EQUAL_PROC					7
 #define GIST_DISTANCE_PROC				8
 #define GIST_FETCH_PROC					9
-#define GISTNProcs					9
+#define GIST_OPTIONS_PROC				10
+#define GISTNProcs						10
 
 /*
  * Page opaque data in a GiST index page.
@@ -73,6 +75,24 @@ typedef struct GISTPageOpaqueData
 
 typedef GISTPageOpaqueData *GISTPageOpaque;
 
+/*
+ * Maximum possible sizes for GiST index tuple and index key.  Calculation is
+ * based on assumption that GiST page should fit at least 4 tuples.  In theory,
+ * GiST index can be functional when page can fit 3 tuples.  But that seems
+ * rather inefficent, so we use a bit conservative estimate.
+ *
+ * The maximum size of index key is true for unicolumn index.  Therefore, this
+ * estimation should be used to figure out which maximum size of GiST index key
+ * makes sense at all.  For multicolumn indexes, user might be able to tune
+ * key size using opclass parameters.
+ */
+#define GISTMaxIndexTupleSize	\
+	MAXALIGN_DOWN((BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
+				  4 - sizeof(ItemIdData))
+
+#define GISTMaxIndexKeySize	\
+	(GISTMaxIndexTupleSize - MAXALIGN(sizeof(IndexTupleData)))
+
 /*
  * The page ID is for the convenience of pg_filedump and similar utilities,
  * which otherwise would have a hard time telling pages of different index
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 2707e1924b9..8cda938cbe4 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -352,7 +352,8 @@ typedef struct HashOptions
  */
 #define HASHSTANDARD_PROC		1
 #define HASHEXTENDED_PROC		2
-#define HASHNProcs				2
+#define HASHOPTIONS_PROC		3
+#define HASHNProcs				3
 
 
 /* public routines */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 18206a0c656..5f67fc04e09 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -587,7 +587,8 @@ BTreeTupleGetMaxHeapTID(IndexTuple itup)
 #define BTSORTSUPPORT_PROC	2
 #define BTINRANGE_PROC		3
 #define BTEQUALIMAGE_PROC	4
-#define BTNProcs			4
+#define BTOPTIONS_PROC		5
+#define BTNProcs			5
 
 /*
  *	We need to be able to tell the difference between read and write
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 36e6472768f..5964438cb0c 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -38,6 +38,7 @@ typedef enum relopt_type
 /* kinds supported by reloptions */
 typedef enum relopt_kind
 {
+	RELOPT_KIND_LOCAL = 0,
 	RELOPT_KIND_HEAP = (1 << 0),
 	RELOPT_KIND_TOAST = (1 << 1),
 	RELOPT_KIND_BTREE = (1 << 2),
@@ -130,6 +131,10 @@ typedef struct relopt_enum
 
 /* validation routines for strings */
 typedef void (*validate_string_relopt) (const char *value);
+typedef Size (*fill_string_relopt) (const char *value, void *ptr);
+
+/* validation routine for the whole option set */
+typedef void (*relopts_validator) (void *parsed_options, relopt_value *vals, int nvals);
 
 typedef struct relopt_string
 {
@@ -137,6 +142,7 @@ typedef struct relopt_string
 	int			default_len;
 	bool		default_isnull;
 	validate_string_relopt validate_cb;
+	fill_string_relopt fill_cb;
 	char	   *default_val;
 } relopt_string;
 
@@ -148,6 +154,21 @@ typedef struct
 	int			offset;			/* offset of field in result struct */
 } relopt_parse_elt;
 
+/* Local reloption definition */
+typedef struct local_relopt
+{
+	relopt_gen *option;			/* option definition */
+	int			offset;			/* offset of parsed value in bytea structure */
+} local_relopt;
+
+/* Structure to hold local reloption data for build_local_reloptions() */
+typedef struct local_relopts
+{
+	List	   *options;		/* list of local_relopt definitions */
+	List	   *validators;		/* list of relopts_validator callbacks */
+	Size		relopt_struct_size; /* size of parsed bytea structure */
+} local_relopts;
+
 /*
  * Utility macro to get a value for a string reloption once the options
  * are parsed.  This gets a pointer to the string value itself.  "optstruct"
@@ -174,6 +195,30 @@ extern void add_string_reloption(bits32 kinds, const char *name, const char *des
 								 const char *default_val, validate_string_relopt validator,
 								 LOCKMODE lockmode);
 
+extern void init_local_reloptions(local_relopts *opts, Size relopt_struct_size);
+extern void register_reloptions_validator(local_relopts *opts,
+										  relopts_validator validator);
+extern void add_local_bool_reloption(local_relopts *opts, const char *name,
+									 const char *desc, bool default_val,
+									 int offset);
+extern void add_local_int_reloption(local_relopts *opts, const char *name,
+									const char *desc, int default_val,
+									int min_val, int max_val, int offset);
+extern void add_local_real_reloption(local_relopts *opts, const char *name,
+									 const char *desc, double default_val,
+									 double min_val, double max_val,
+									 int offset);
+extern void add_local_enum_reloption(local_relopts *relopts,
+									 const char *name, const char *desc,
+									 relopt_enum_elt_def *members,
+									 int default_val, const char *detailmsg,
+									 int offset);
+extern void add_local_string_reloption(local_relopts *opts, const char *name,
+									   const char *desc,
+									   const char *default_val,
+									   validate_string_relopt validator,
+									   fill_string_relopt filler, int offset);
+
 extern Datum transformRelOptions(Datum oldOptions, List *defList,
 								 const char *namspace, char *validnsps[],
 								 bool acceptOidsOff, bool isReset);
@@ -185,6 +230,8 @@ extern void *build_reloptions(Datum reloptions, bool validate,
 							  Size relopt_struct_size,
 							  const relopt_parse_elt *relopt_elems,
 							  int num_relopt_elems);
+extern void *build_local_reloptions(local_relopts *relopts, Datum options,
+									bool validate);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
 								 relopt_kind kind);
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index f48080be943..852d1e2961a 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -26,8 +26,9 @@
 #define SPGIST_INNER_CONSISTENT_PROC	4
 #define SPGIST_LEAF_CONSISTENT_PROC		5
 #define SPGIST_COMPRESS_PROC			6
+#define SPGIST_OPTIONS_PROC				7
 #define SPGISTNRequiredProc				5
-#define SPGISTNProc						6
+#define SPGISTNProc						7
 
 /*
  * Argument structs for spg_config method
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index bd64024946c..cbfdfe2abe5 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -95,6 +95,7 @@ extern List *heap_truncate_find_FKs(List *relationIds);
 
 extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 								   Form_pg_attribute new_attribute,
+								   Datum attoptions,
 								   CatalogIndexState indstate);
 
 extern void InsertPgClassTuple(Relation pg_class_desc,
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 75c0152b666..cef63b2a716 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -541,6 +541,9 @@
   amproc => 'gtsvector_picksplit' },
 { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
   amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+  amprocrighttype => 'tsvector', amprocnum => '10',
+  amproc => 'gtsvector_options' },
 { amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
   amprocrighttype => 'tsquery', amprocnum => '1',
   amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index ac8ad8dbf08..a6a708cca92 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8735,6 +8735,9 @@
   proname => 'gtsvector_consistent', prorettype => 'bool',
   proargtypes => 'internal gtsvector int4 oid internal',
   prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3434', descr => 'GiST tsvector support',
+  proname => 'gtsvector_options', prorettype => 'void', proisstrict => 'f',
+  proargtypes => 'internal', prosrc => 'gtsvector_options' },
 
 { oid => '3656', descr => 'GIN tsvector support',
   proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 453df2220fc..a4249994b92 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -331,6 +331,10 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 #define PG_GETARG_BPCHAR_P(n)		DatumGetBpCharP(PG_GETARG_DATUM(n))
 #define PG_GETARG_VARCHAR_P(n)		DatumGetVarCharP(PG_GETARG_DATUM(n))
 
+/* To access options from opclass support functions use this: */
+#define PG_HAS_OPCLASS_OPTIONS()	has_fn_opclass_options(fcinfo->flinfo)
+#define PG_GET_OPCLASS_OPTIONS()	get_fn_opclass_options(fcinfo->flinfo)
+
 /* To return a NULL do this: */
 #define PG_RETURN_NULL()  \
 	do { fcinfo->isnull = true; return (Datum) 0; } while (0)
@@ -697,6 +701,9 @@ extern Oid	get_call_expr_argtype(fmNodePtr expr, int argnum);
 extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
 extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
 extern bool get_fn_expr_variadic(FmgrInfo *flinfo);
+extern bytea *get_fn_opclass_options(FmgrInfo *flinfo);
+extern bool has_fn_opclass_options(FmgrInfo *flinfo);
+extern void set_fn_opclass_options(FmgrInfo *flinfo, bytea *options);
 extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
 
 /*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 3d27d50f090..0fb5d61a3f6 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -139,6 +139,7 @@ typedef struct ExprState
  *		UniqueProcs
  *		UniqueStrats
  *		Unique				is it a unique index?
+ *		OpclassOptions		opclass-specific options, or NULL if none
  *		ReadyForInserts		is it valid for inserts?
  *		Concurrent			are we doing a concurrent index build?
  *		BrokenHotChain		did we detect any broken HOT chains?
@@ -167,6 +168,7 @@ typedef struct IndexInfo
 	Oid		   *ii_UniqueOps;	/* array with one entry per column */
 	Oid		   *ii_UniqueProcs; /* array with one entry per column */
 	uint16	   *ii_UniqueStrats;	/* array with one entry per column */
+	Datum	   *ii_OpclassOptions;	/* array with one entry per column */
 	bool		ii_Unique;
 	bool		ii_ReadyForInserts;
 	bool		ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2039b424499..77943f06376 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -701,6 +701,7 @@ typedef struct IndexElem
 	char	   *indexcolname;	/* name for index column; NULL = default */
 	List	   *collation;		/* name of collation; NIL = default */
 	List	   *opclass;		/* name of desired opclass; NIL = default */
+	List	   *opclassopts;	/* opclass-specific options, or NIL */
 	SortByDir	ordering;		/* ASC/DESC/default */
 	SortByNulls nulls_ordering; /* FIRST/LAST/default */
 } IndexElem;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 0ceb8096442..5334a73b535 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -808,6 +808,7 @@ struct IndexOptInfo
 	Oid		   *sortopfamily;	/* OIDs of btree opfamilies, if orderable */
 	bool	   *reverse_sort;	/* is sort order descending? */
 	bool	   *nulls_first;	/* do NULLs come first in the sort order? */
+	bytea	  **opclassoptions; /* opclass-specific options for columns */
 	bool	   *canreturn;		/* which index cols can be returned in an
 								 * index-only scan? */
 	Oid			relam;			/* OID of the access method (in pg_am) */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 4e646c55e90..374f57fb43a 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -90,6 +90,7 @@ extern char get_attgenerated(Oid relid, AttrNumber attnum);
 extern Oid	get_atttype(Oid relid, AttrNumber attnum);
 extern void get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
 								  Oid *typid, int32 *typmod, Oid *collid);
+extern Datum get_attoptions(Oid relid, int16 attnum);
 extern Oid	get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
 extern char *get_collation_name(Oid colloid);
 extern bool get_collation_isdeterministic(Oid colloid);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 2a13d8aad0c..74106b37314 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -177,6 +177,7 @@ typedef struct RelationData
 	Oid		   *rd_exclprocs;	/* OIDs of exclusion ops' procs, if any */
 	uint16	   *rd_exclstrats;	/* exclusion ops' strategy numbers, if any */
 	Oid		   *rd_indcollation;	/* OIDs of index collations */
+	bytea	  **rd_opcoptions;	/* parsed opclass-specific options */
 
 	/*
 	 * rd_amcache is available for index and table AMs to cache private data
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index d77f5beec68..d596c210b10 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
 #ifndef RELCACHE_H
 #define RELCACHE_H
 
+#include "postgres.h"
 #include "access/tupdesc.h"
 #include "nodes/bitmapset.h"
 
@@ -50,6 +51,8 @@ extern Oid	RelationGetReplicaIndex(Relation relation);
 extern List *RelationGetIndexExpressions(Relation relation);
 extern List *RelationGetDummyIndexExpressions(Relation relation);
 extern List *RelationGetIndexPredicate(Relation relation);
+extern Datum *RelationGetIndexRawAttOptions(Relation relation);
+extern bytea **RelationGetIndexAttOptions(Relation relation, bool copy);
 
 typedef enum IndexAttrBitmapKind
 {
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index abd9a4dfa0b..8306c760a9a 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -38,6 +38,7 @@ extern List *set_deparse_context_plan(List *dpcontext,
 extern List *select_rtable_names_for_explain(List *rtable,
 											 Bitmapset *rels_used);
 extern char *generate_collation_name(Oid collid);
+extern char *generate_opclass_name(Oid opclass);
 extern char *get_range_partbound_string(List *bound_datums);
 
 #endif							/* RULEUTILS_H */
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index ba5ce7a17e5..b2a451a83f3 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -353,10 +353,10 @@ ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- ope
 ERROR:  invalid operator number 0, must be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
 ERROR:  operator argument types must be specified in ALTER OPERATOR FAMILY
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR:  invalid function number 0, must be between 1 and 4
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
+ERROR:  invalid function number 0, must be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR:  invalid function number 6, must be between 1 and 4
+ERROR:  invalid function number 6, must be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
 ERROR:  STORAGE cannot be specified in ALTER OPERATOR FAMILY
 DROP OPERATOR FAMILY alt_opf4 USING btree;
@@ -500,6 +500,18 @@ ERROR:  btree equal image functions must not be cross-type
 ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
 ERROR:  function 2(integer,integer) does not exist in operator family "alt_opf18"
 DROP OPERATOR FAMILY alt_opf18 USING btree;
+-- Should fail. Invalid opclass options function (#5) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 test_opclass_options_func(internal, text[], bool);
+ERROR:  function test_opclass_options_func(internal, text[], boolean) does not exist
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) btint42cmp(int4, int2);
+ERROR:  invalid opclass options parsing function
+HINT:  opclass options parsing function must have signature '(internal) RETURNS void'
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4, int2) btint42cmp(int4, int2);
+ERROR:  left and right associated data types for opclass options parsing functions must match
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4);
+DROP OPERATOR FAMILY alt_opf19 USING btree;
 --
 -- Statistics
 --
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 1646deb0923..c0657020b01 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -348,3 +348,6 @@ VACUUM delete_test_table;
 -- The vacuum above should've turned the leaf page into a fast root. We just
 -- need to insert some rows to cause the fast root page to split.
 INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR:  operator class int4_ops has no options
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 3c0b21d633e..2efd7d7ec74 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2126,7 +2126,7 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
 SELECT p1.amprocfamily, p1.amprocnum
 FROM pg_amproc as p1
 WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
-    OR p1.amprocnum < 1 OR p1.amproc = 0;
+    OR p1.amprocnum < 0 OR p1.amproc = 0;
  amprocfamily | amprocnum 
 --------------+-----------
 (0 rows)
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index fe1cd9deb0e..2bfd58b0b96 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
    508
 (1 row)
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR:  value 0 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=2048));
+ERROR:  value 2048 out of bounds for option "siglen"
+DETAIL:  Valid values are between "1" and "2024".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR:  unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR:  parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a)
+    "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx2
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a tsvector_ops(siglen=484));
+\d test_tsvector
+            Table "public.test_tsvector"
+ Column |   Type   | Collation | Nullable | Default 
+--------+----------+-----------+----------+---------
+ t      | text     |           |          | 
+ a      | tsvector |           |          | 
+Indexes:
+    "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Bitmap Heap Scan on test_tsvector
+         Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+         ->  Bitmap Index Scan on wowidx
+               Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count 
+-------
+    17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count 
+-------
+     6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count 
+-------
+    98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count 
+-------
+    23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count 
+-------
+    39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count 
+-------
+   494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count 
+-------
+   158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count 
+-------
+   508
+(1 row)
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
diff --git a/src/test/regress/input/create_function_1.source b/src/test/regress/input/create_function_1.source
index 223454a5eab..412e339fcf2 100644
--- a/src/test/regress/input/create_function_1.source
+++ b/src/test/regress/input/create_function_1.source
@@ -73,6 +73,11 @@ CREATE FUNCTION test_support_func(internal)
     AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
     LANGUAGE C STRICT;
 
+CREATE FUNCTION test_opclass_options_func(internal)
+    RETURNS void
+    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+    LANGUAGE C;
+
 -- Things that shouldn't work:
 
 CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
diff --git a/src/test/regress/output/create_function_1.source b/src/test/regress/output/create_function_1.source
index 5f43e8de81f..4d78fa12289 100644
--- a/src/test/regress/output/create_function_1.source
+++ b/src/test/regress/output/create_function_1.source
@@ -64,6 +64,10 @@ CREATE FUNCTION test_support_func(internal)
     RETURNS internal
     AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
     LANGUAGE C STRICT;
+CREATE FUNCTION test_opclass_options_func(internal)
+    RETURNS void
+    AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+    LANGUAGE C;
 -- Things that shouldn't work:
 CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
     AS 'SELECT ''not an integer'';';
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index 3567361ad0a..960c155e5f2 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -888,3 +888,10 @@ test_support_func(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(ret);
 }
+
+PG_FUNCTION_INFO_V1(test_opclass_options_func);
+Datum
+test_opclass_options_func(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_NULL();
+}
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 223d66bc2d5..8c5d0e5e1f8 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -298,7 +298,7 @@ ALTER OPERATOR FAMILY alt_opf4 USING invalid_index_method ADD  OPERATOR 1 < (int
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
 ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
 DROP OPERATOR FAMILY alt_opf4 USING btree;
@@ -436,6 +436,15 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree
 ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
 DROP OPERATOR FAMILY alt_opf18 USING btree;
 
+-- Should fail. Invalid opclass options function (#5) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 test_opclass_options_func(internal, text[], bool);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4, int2) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4);
+DROP OPERATOR FAMILY alt_opf19 USING btree;
+
 --
 -- Statistics
 --
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 6e14b935cea..4245cbb80c4 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -180,3 +180,6 @@ VACUUM delete_test_table;
 -- The vacuum above should've turned the leaf page into a fast root. We just
 -- need to insert some rows to cause the fast root page to split.
 INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 389d5b2464a..9a1ea3d9991 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1335,7 +1335,7 @@ WHERE p1.amopopr = p2.oid AND p2.oprcode = p3.oid AND
 SELECT p1.amprocfamily, p1.amprocnum
 FROM pg_amproc as p1
 WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
-    OR p1.amprocnum < 1 OR p1.amproc = 0;
+    OR p1.amprocnum < 0 OR p1.amproc = 0;
 
 -- Support routines that are primary members of opfamilies must be immutable
 -- (else it suggests that the index ordering isn't fixed).  But cross-type
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 14da7edd841..c71cda5cf92 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
 SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
 SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
 
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=2048));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a tsvector_ops(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
 RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index ca2d9ec8fb0..55cfb71f92a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -841,6 +841,8 @@ GISTBuildState
 GISTENTRY
 GISTInsertStack
 GISTInsertState
+GISTIntArrayBigOptions
+GISTIntArrayOptions
 GISTNodeBuffer
 GISTNodeBufferPage
 GISTPageOpaque
@@ -908,11 +910,13 @@ GinVacuumState
 GistBufferingMode
 GistBulkDeleteResult
 GistEntryVector
+GistHstoreOptions
 GistInetKey
 GistNSN
 GistOptBufferingMode
 GistSplitUnion
 GistSplitVector
+GistTsVectorOptions
 GistVacState
 GlobalTransaction
 GrantRoleStmt
@@ -1303,6 +1307,8 @@ LogicalRepWorkerId
 LogicalRewriteMappingData
 LogicalTape
 LogicalTapeSet
+LtreeGistOptions
+LtreeSignature
 MAGIC
 MBuf
 MCVItem
@@ -2477,6 +2483,7 @@ TrgmArcInfo
 TrgmBound
 TrgmColor
 TrgmColorInfo
+TrgmGistOptions
 TrgmNFA
 TrgmPackArcInfo
 TrgmPackedArc
@@ -2874,6 +2881,7 @@ file_action_t
 file_entry_t
 file_type_t
 filemap_t
+fill_string_relopt
 finalize_primnode_context
 find_dependent_phvs_context
 find_expr_references_context
@@ -2997,6 +3005,8 @@ leaf_item
 line_t
 lineno_t
 list_qsort_comparator
+local_relopt
+local_relopts
 locale_t
 locate_agg_of_level_context
 locate_var_of_level_context
@@ -3195,6 +3205,7 @@ relopt_real
 relopt_string
 relopt_type
 relopt_value
+relopts_validator
 remoteConn
 remoteConnHashEnt
 remoteDep
-- 
2.14.3

#30Justin Pryzby
pryzby@telsasoft.com
In reply to: Alexander Korotkov (#29)
1 attachment(s)
Re: [PATCH] Opclass parameters

On Sat, Mar 28, 2020 at 06:05:51PM +0300, Alexander Korotkov wrote:

On Wed, Mar 18, 2020 at 3:28 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:

Attached new version of reordered patches.

I'm going to push this if no objections.

Find attached patch with editorial corrections to docs for this commit.

--word-diff to follow.

commit d3f077b813efa90b25a162bf8d227f3e4218c248
Author: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon Mar 30 20:55:06 2020 -0500

Doc review: Implement operator class parameters

commit 911e70207703799605f5a0e8aad9f06cff067c63
Author: Alexander Korotkov <akorotkov@postgresql.org>

diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index f1f2b08cd7..b2e04d0815 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -468,13 +468,13 @@ CREATE INDEX hidx ON testhstore USING GIN (h);
</programlisting>

<para>
<literal>gist_hstore_ops</literal> GiST opclass approximates {+a+} set of
key/value pairs as a bitmap signature. [-Optional-]{+Its optional+} integer parameter
<literal>siglen</literal>[-of <literal>gist_hstore_ops</literal>-] determines {+the+}
signature length in bytes. [-Default signature-]{+The default+} length is 16 bytes.
Valid values of signature length are between 1 and 2024 bytes. Longer
signatures [-leads-]{+lead+} to {+a+} more precise search [-(scan less-]{+(scanning a smaller+} fraction of [-index, scan-]
[- less-]{+the index and+}
{+ fewer+} heap pages), [-but-]{+at the cost of a+} larger index.
</para>

  <para>
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index 72b4b23c15..7956a746a6 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -265,7 +265,7 @@
  </para>

<para>
Two [-parametrized-]{+parameterized+} GiST index operator classes are provided:
<literal>gist__int_ops</literal> (used by default) is suitable for
small- to medium-size data sets, while
<literal>gist__intbig_ops</literal> uses a larger signature and is more
@@ -276,22 +276,23 @@
</para>

<para>
<literal>gist__int_ops</literal> approximates {+an+} integer set as an array of
integer ranges. [-Optional-]{+Its optional+} integer parameter <literal>numranges</literal>[-of-]
[- <literal>gist__int_ops</literal>-]
determines {+the+} maximum number of ranges in
one index key. [-Default-]{+The default+} value of <literal>numranges</literal> is 100.
Valid values are between 1 and 253. Using larger arrays as GiST index
keys leads to {+a+} more precise search [-(scan less-]{+(scaning a smaller+} fraction of [-index, scan less-]{+the index and+}
{+ fewer+} heap pages), [-but-]{+at the cost of a+} larger index.
</para>

<para>
<literal>gist__intbig_ops</literal> approximates {+an+} integer set as a bitmap
[-signature. Optional-]{+signature XXX. Its optional+} integer parameter <literal>siglen</literal>[-of-]
[- <literal>gist__intbig_ops</literal>-]
determines {+the+} signature length in bytes.
[-Default-]{+The default+} signature length is 16 bytes. Valid values of signature length
are between 1 and 2024 bytes. Longer signatures [-leads-]{+lead+} to {+a+} more precise
search [-(scan less-]{+(scanning a smaller+} fraction of [-index, scan less-]{+the index and fewer+} heap pages), [-but-]{+at+}
{+ the cost of a+} larger index.
</para>

  <para>
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index ae4b33ec85..4971b71524 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -506,16 +506,16 @@ Europe &amp; Russia*@ &amp; !Transportation
     <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
    </para>
    <para>
     <literal>gist_ltree_ops</literal> GiST opclass approximates {+a+} set of
     path labels as a bitmap signature.  [-Optional-]{+Its optional+} integer parameter
     <literal>siglen</literal>[-of <literal>gist_ltree_ops</literal>-] determines {+the+}
     signature length in bytes.  [-Default-]{+The default+} signature length is 8 bytes.
     Valid values of signature length are between 1 and 2024 bytes.  Longer
     signatures [-leads-]{+lead+} to {+a+} more precise search [-(scan less-]{+(scanning a smaller+} fraction of [-index, scan-]
[-     less-]{+the index and+}
{+     fewer+} heap pages), [-but-]{+at the cost of a+} larger index.
    </para>
    <para>
     Example of creating such an index with [-a-]{+the+} default signature length of 8 bytes:
    </para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (path);
@@ -535,13 +535,13 @@ CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
     <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
    </para>
    <para>
     <literal>gist__ltree_ops</literal> GiST opclass works [-similar-]{+similarly+} to
     <literal>gist_ltree_ops</literal> and also takes signature length as
     a parameter.  [-Default-]{+The default+} value of <literal>siglen</literal> in
      <literal>gist__ltree_ops</literal> is 28 bytes.
    </para>
    <para>
     Example of creating such an index with [-a-]{+the+} default signature length of 28 bytes:
    </para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
diff --git a/doc/src/sgml/pgtrgm.sgml b/doc/src/sgml/pgtrgm.sgml
index dde02634ae..97b3d13a88 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -391,13 +391,13 @@ CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
  </para>

<para>
<literal>gist_trgm_ops</literal> GiST opclass approximates {+a+} set of
trigrams as a bitmap signature. [-Optional-]{+Its optional+} integer parameter
<literal>siglen</literal>[-of <literal>gist_trgm_ops</literal>-] determines {+the+}
signature length in bytes. [-Default signature-]{+The default+} length is 12 bytes.
Valid values of signature length are between 1 and 2024 bytes. Longer
signatures [-leads-]{+lead+} to {+a+} more precise search [-(scan less-]{+(scanning a smaller+} fraction of [-index, scan-]
[- less-]{+the index and+}
{+ fewer+} heap pages), [-but-]{+at the cost of a+} larger index.
</para>

  <para>
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 2217fcd6c2..0dc427289d 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3670,17 +3670,17 @@ SELECT plainto_tsquery('supernovae stars');
   to check the actual table row to eliminate such false matches.
   (<productname>PostgreSQL</productname> does this automatically when needed.)
   GiST indexes are lossy because each document is represented in the
   index by a fixed-length signature.  [-Signature-]{+The signature+} length in bytes is determined
   by the value of the optional integer parameter <literal>siglen</literal>.
   [-Default-]{+The default+} signature length (when <literal>siglen</literal> is not [-specied)-]{+specified)+} is
   124 bytes, [-maximal-]{+the maximum signature+} length is 2024 bytes. The signature is generated by hashing
   each word into a single bit in an n-bit string, with all these bits OR-ed
   together to produce an n-bit document signature.  When two words hash to
   the same bit position there will be a false match.  If all words in
   the query have matches (real or false) then the table row must be
   retrieved to see if the match is correct.  Longer signatures [-leads-]{+lead+} to {+a+} more
   precise search [-(scan less-]{+(scanning a smaller+} fraction of [-index, scan less-]{+the index and fewer+} heap
   pages), [-but-]{+at the cost of a+} larger index.
  </para>

<para>

Attachments:

v1-0001-Doc-review-Implement-operator-class-parameters.patchtext/x-diff; charset=us-asciiDownload
From d3f077b813efa90b25a162bf8d227f3e4218c248 Mon Sep 17 00:00:00 2001
From: Justin Pryzby <pryzbyj@telsasoft.com>
Date: Mon, 30 Mar 2020 20:55:06 -0500
Subject: [PATCH v1] Doc review: Implement operator class parameters

commit 911e70207703799605f5a0e8aad9f06cff067c63
Author: Alexander Korotkov <akorotkov@postgresql.org>
---
 doc/src/sgml/hstore.sgml     | 12 ++++++------
 doc/src/sgml/intarray.sgml   | 27 ++++++++++++++-------------
 doc/src/sgml/ltree.sgml      | 20 ++++++++++----------
 doc/src/sgml/pgtrgm.sgml     | 12 ++++++------
 doc/src/sgml/textsearch.sgml | 12 ++++++------
 5 files changed, 42 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index f1f2b08cd7..b2e04d0815 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -468,13 +468,13 @@ CREATE INDEX hidx ON testhstore USING GIN (h);
 </programlisting>
 
   <para>
-   <literal>gist_hstore_ops</literal> GiST opclass approximates set of
-   key/value pairs as a bitmap signature.  Optional integer parameter
-   <literal>siglen</literal> of <literal>gist_hstore_ops</literal> determines
-   signature length in bytes.  Default signature length is 16 bytes.
+   <literal>gist_hstore_ops</literal> GiST opclass approximates a set of
+   key/value pairs as a bitmap signature.  Its optional integer parameter
+   <literal>siglen</literal> determines the
+   signature length in bytes.  The default length is 16 bytes.
    Valid values of signature length are between 1 and 2024 bytes.  Longer
-   signatures leads to more precise search (scan less fraction of index, scan
-   less heap pages), but larger index.
+   signatures lead to a more precise search (scanning a smaller fraction of the index and
+   fewer heap pages), at the cost of a larger index.
   </para>
 
   <para>
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index 72b4b23c15..7956a746a6 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -265,7 +265,7 @@
   </para>
 
   <para>
-   Two parametrized GiST index operator classes are provided:
+   Two parameterized GiST index operator classes are provided:
    <literal>gist__int_ops</literal> (used by default) is suitable for
    small- to medium-size data sets, while
    <literal>gist__intbig_ops</literal> uses a larger signature and is more
@@ -276,22 +276,23 @@
   </para>
    
   <para>
-   <literal>gist__int_ops</literal> approximates integer set as an array of
-   integer ranges.  Optional integer parameter <literal>numranges</literal> of
-   <literal>gist__int_ops</literal> determines maximum number of ranges in
-   one index key.  Default value of <literal>numranges</literal> is 100.
+   <literal>gist__int_ops</literal> approximates an integer set as an array of
+   integer ranges.  Its optional integer parameter <literal>numranges</literal>
+   determines the maximum number of ranges in
+   one index key.  The default value of <literal>numranges</literal> is 100.
    Valid values are between 1 and 253.  Using larger arrays as GiST index
-   keys leads to more precise search (scan less fraction of index, scan less
-   heap pages), but larger index.
+   keys leads to a more precise search (scaning a smaller fraction of the index and
+   fewer heap pages), at the cost of a larger index.
   </para>
    
   <para>
-   <literal>gist__intbig_ops</literal> approximates integer set as a bitmap
-   signature.  Optional integer parameter <literal>siglen</literal> of
-   <literal>gist__intbig_ops</literal> determines signature length in bytes.
-   Default signature length is 16 bytes.  Valid values of signature length
-   are between 1 and 2024 bytes.  Longer signatures leads to more precise
-   search (scan less fraction of index, scan less heap pages), but larger index.
+   <literal>gist__intbig_ops</literal> approximates an integer set as a bitmap
+   signature XXX.  Its optional integer parameter <literal>siglen</literal>
+   determines the signature length in bytes.
+   The default signature length is 16 bytes.  Valid values of signature length
+   are between 1 and 2024 bytes.  Longer signatures lead to a more precise
+   search (scanning a smaller fraction of the index and fewer heap pages), at
+   the cost of a larger index.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index ae4b33ec85..4971b71524 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -506,16 +506,16 @@ Europe &amp; Russia*@ &amp; !Transportation
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     <literal>gist_ltree_ops</literal> GiST opclass approximates set of
-     path labels as a bitmap signature.  Optional integer parameter
-     <literal>siglen</literal> of <literal>gist_ltree_ops</literal> determines
-     signature length in bytes.  Default signature length is 8 bytes.
+     <literal>gist_ltree_ops</literal> GiST opclass approximates a set of
+     path labels as a bitmap signature.  Its optional integer parameter
+     <literal>siglen</literal> determines the
+     signature length in bytes.  The default signature length is 8 bytes.
      Valid values of signature length are between 1 and 2024 bytes.  Longer
-     signatures leads to more precise search (scan less fraction of index, scan
-     less heap pages), but larger index.
+     signatures lead to a more precise search (scanning a smaller fraction of the index and
+     fewer heap pages), at the cost of a larger index.
     </para>
     <para>
-     Example of creating such an index with a default signature length of 8 bytes:
+     Example of creating such an index with the default signature length of 8 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (path);
@@ -535,13 +535,13 @@ CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
      <literal>@</literal>, <literal>~</literal>, <literal>?</literal>
     </para>
     <para>
-     <literal>gist__ltree_ops</literal> GiST opclass works similar to
+     <literal>gist__ltree_ops</literal> GiST opclass works similarly to
      <literal>gist_ltree_ops</literal> and also takes signature length as
-     a parameter.  Default value of <literal>siglen</literal> in
+     a parameter.  The default value of <literal>siglen</literal> in
       <literal>gist__ltree_ops</literal> is 28 bytes.
     </para>
     <para>
-     Example of creating such an index with a default signature length of 28 bytes:
+     Example of creating such an index with the default signature length of 28 bytes:
     </para>
 <programlisting>
 CREATE INDEX path_gist_idx ON test USING GIST (array_path);
diff --git a/doc/src/sgml/pgtrgm.sgml b/doc/src/sgml/pgtrgm.sgml
index dde02634ae..97b3d13a88 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -391,13 +391,13 @@ CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
   </para>
 
   <para>
-   <literal>gist_trgm_ops</literal> GiST opclass approximates set of
-   trigrams as a bitmap signature.  Optional integer parameter
-   <literal>siglen</literal> of <literal>gist_trgm_ops</literal> determines
-   signature length in bytes.  Default signature length is 12 bytes.
+   <literal>gist_trgm_ops</literal> GiST opclass approximates a set of
+   trigrams as a bitmap signature.  Its optional integer parameter
+   <literal>siglen</literal> determines the
+   signature length in bytes.  The default length is 12 bytes.
    Valid values of signature length are between 1 and 2024 bytes.  Longer
-   signatures leads to more precise search (scan less fraction of index, scan
-   less heap pages), but larger index.
+   signatures lead to a more precise search (scanning a smaller fraction of the index and
+   fewer heap pages), at the cost of a larger index.
   </para>
 
   <para>
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 2217fcd6c2..0dc427289d 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3670,17 +3670,17 @@ SELECT plainto_tsquery('supernovae stars');
    to check the actual table row to eliminate such false matches.
    (<productname>PostgreSQL</productname> does this automatically when needed.)
    GiST indexes are lossy because each document is represented in the
-   index by a fixed-length signature.  Signature length in bytes is determined
+   index by a fixed-length signature.  The signature length in bytes is determined
    by the value of the optional integer parameter <literal>siglen</literal>.
-   Default signature length (when <literal>siglen</literal> is not specied) is
-   124 bytes, maximal length is 2024 bytes. The signature is generated by hashing
+   The default signature length (when <literal>siglen</literal> is not specified) is
+   124 bytes, the maximum signature length is 2024 bytes. The signature is generated by hashing
    each word into a single bit in an n-bit string, with all these bits OR-ed
    together to produce an n-bit document signature.  When two words hash to
    the same bit position there will be a false match.  If all words in
    the query have matches (real or false) then the table row must be
-   retrieved to see if the match is correct.  Longer signatures leads to more
-   precise search (scan less fraction of index, scan less heap pages), but
-   larger index.
+   retrieved to see if the match is correct.  Longer signatures lead to a more
+   precise search (scanning a smaller fraction of the index and fewer heap
+   pages), at the cost of a larger index.
   </para>
 
   <para>
-- 
2.17.0

#31Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Justin Pryzby (#30)
Re: [PATCH] Opclass parameters

On Tue, Mar 31, 2020 at 5:44 AM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Sat, Mar 28, 2020 at 06:05:51PM +0300, Alexander Korotkov wrote:

On Wed, Mar 18, 2020 at 3:28 AM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:

Attached new version of reordered patches.

I'm going to push this if no objections.

Find attached patch with editorial corrections to docs for this commit.

Cool, thank you!

[-signature. Optional-]{+signature XXX. Its optional+} integer parameter <literal>siglen</literal>[-of-]

What is XXX supposed to be?

The rest of patch looks good to me.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#32Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Alexander Korotkov (#31)
Re: [PATCH] Opclass parameters

On Tue, Mar 31, 2020 at 12:15 PM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What is XXX supposed to be?

The rest of patch looks good to me.

I've pushed the patch excepts XXX. Thank you.
You're welcome to clarify XXX and/or do additional corrections.

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

#33Justin Pryzby
pryzby@telsasoft.com
In reply to: Alexander Korotkov (#32)
Re: [PATCH] Opclass parameters

On Wed, Apr 01, 2020 at 02:53:41PM +0300, Alexander Korotkov wrote:

On Tue, Mar 31, 2020 at 12:15 PM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What is XXX supposed to be?

The rest of patch looks good to me.

I've pushed the patch excepts XXX. Thank you.
You're welcome to clarify XXX and/or do additional corrections.

XXX was a reminder to myself to check the accuracy of that description, rather
than just its language. As you can see, I didn't manage to do that, but the
language is at least consistent.

Thanks for pushing the update.

--
Justin

#34Alexander Korotkov
a.korotkov@postgrespro.ru
In reply to: Justin Pryzby (#33)
Re: [PATCH] Opclass parameters

On Wed, Apr 1, 2020 at 2:59 PM Justin Pryzby <pryzby@telsasoft.com> wrote:

On Wed, Apr 01, 2020 at 02:53:41PM +0300, Alexander Korotkov wrote:

On Tue, Mar 31, 2020 at 12:15 PM Alexander Korotkov
<a.korotkov@postgrespro.ru> wrote:

What is XXX supposed to be?

The rest of patch looks good to me.

I've pushed the patch excepts XXX. Thank you.
You're welcome to clarify XXX and/or do additional corrections.

XXX was a reminder to myself to check the accuracy of that description, rather
than just its language. As you can see, I didn't manage to do that, but the
language is at least consistent.

Thanks for pushing the update.

Thank you!

------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company