pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
To: PostgreSQL mailing lists <pgsql-hackers@postgresql.org>
Cc: Nikolay Shaplov <dhyan@nataraj.su>
Cc: obartunov@gmail.com
Subject: Re: [PATCH] Opclass parameters
Date: Thu, 15 Nov 2018 18:26:43 +0300
Message-ID: <71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru> (raw)
In-Reply-To: <6382cb29-83d8-90f2-1dbb-b4ac606cbf1f@postgrespro.ru>
References: <d22c3a18-31c7-1879-fc11-4c1ce2f5e5af@postgrespro.ru>
	<15077864.qMPJiTemUy@x200m>
	<CAF4Au4xedPHJQyN60x+6giMpEyLdKn7jFR85_kzaApy9BptRUw@mail.gmail.com>
	<2803786.3sFNWAn8Pn@x200m>
	<6382cb29-83d8-90f2-1dbb-b4ac606cbf1f@postgrespro.ru>


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:

  [text/x-patch] 0001-Add-opclass-parameters-v02.patch (42.1K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/2-0001-Add-opclass-parameters-v02.patch)
  download | inline diff:
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

  [text/x-patch] 0002-Add-opclass-parameters-to-GiST-v02.patch (13.7K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/3-0002-Add-opclass-parameters-to-GiST-v02.patch)
  download | inline diff:
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

  [text/x-patch] 0003-Add-opclass-parameters-to-GIN-v02.patch (15.1K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/4-0003-Add-opclass-parameters-to-GIN-v02.patch)
  download | inline diff:
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

  [text/x-patch] 0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patch (31.8K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/5-0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patch)
  download | inline diff:
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

  [text/x-patch] 0005-Add-opclass-parameters-to-contrib-intarray-v02.patch (55.2K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/6-0005-Add-opclass-parameters-to-contrib-intarray-v02.patch)
  download | inline diff:
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

  [text/x-patch] 0006-Add-opclass-parameters-to-contrib-ltree-v02.patch (41.3K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/7-0006-Add-opclass-parameters-to-contrib-ltree-v02.patch)
  download | inline diff:
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

  [text/x-patch] 0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patch (23.0K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/8-0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patch)
  download | inline diff:
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

  [text/x-patch] 0008-Add-opclass-parameters-to-contrib-hstore-v02.patch (17.6K, ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/9-0008-Add-opclass-parameters-to-contrib-hstore-v02.patch)
  download | inline diff:
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

view thread (34+ messages)  latest in thread

Message-ID: <71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru>
Permalink:  ../71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru/
Also on:    postgresql.org/message-id/71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: n.gluhov@postgrespro.ru, dhyan@nataraj.su, obartunov@gmail.com
  Subject: Re: [PATCH] Opclass parameters
  In-Reply-To: <71c6164e-59ad-e0c4-76c5-b1dfdd68eee3@postgrespro.ru>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox