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