/* 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
amroutine->amstrategies = BLOOM_NSTRATEGIES;
amroutine->amsupport = BLOOM_NPROC;
+ amroutine->amoptsprocnum = BLOOM_OPTIONS_PROC;
amroutine->amcanorder = false;
amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
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),
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",
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 \
42
(1 row)
+drop index hidx;
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=2025));
+ERROR: value 2025 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+create index hidx on testhstore using gist(h gist_hstore_ops(siglen=2024));
+set enable_seqscan=off;
+select count(*) from testhstore where h @> 'wait=>NULL';
+ count
+-------
+ 1
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC';
+ count
+-------
+ 15
+(1 row)
+
+select count(*) from testhstore where h @> 'wait=>CC, public=>t';
+ count
+-------
+ 2
+(1 row)
+
+select count(*) from testhstore where h ? 'public';
+ count
+-------
+ 194
+(1 row)
+
+select count(*) from testhstore where h ?| ARRAY['public','disabled'];
+ count
+-------
+ 337
+(1 row)
+
+select count(*) from testhstore where h ?& ARRAY['public','disabled'];
+ count
+-------
+ 42
+(1 row)
+
drop index hidx;
create index hidx on testhstore using gin (h);
set enable_seqscan=off;
--- /dev/null
+/* 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);
# 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
#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 ) ) )
#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
{
#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 ) )
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);
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);
}
}
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),
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;
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
}
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);
}
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++;
}
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;
}
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);
}
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);
}
OffsetNumber maxoff = entryvec->n - 2;
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = GET_SIGLEN();
OffsetNumber k,
j;
GISTTYPE *datum_l,
_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;
}
/* 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 ... */
{
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);
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;
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;
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ int siglen = GET_SIGLEN();
bool res = true;
BITVECP sign;
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;
}
}
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)
{
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;
}
}
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;
}
}
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();
+}
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;
_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
#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) )
/* 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 ) ) )
#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
#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 ) )
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);
/*****************************************************************************
#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);
* 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;
}
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);
}
}
* 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);
}
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);
}
} GinChkVal;
static bool
-checkcondition_gin(void *checkval, ITEM *item)
+checkcondition_gin(void *checkval, ITEM *item, void *options)
{
GinChkVal *gcv = (GinChkVal *) checkval;
}
return execute(GETQUERY(query) + query->size - 1,
- (void *) &gcv, true,
+ (void *) &gcv, NULL, true,
checkcondition_gin);
}
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);
#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))
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);
/*
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
ArrayType *r;
+ int num_ranges = G_INT_GET_NUMRANGES();
int len,
lenr;
int *dr;
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),
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);
* "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
*/
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)
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
ArrayType *r;
+ int num_ranges = G_INT_GET_NUMRANGES();
int *dr,
lenr;
ArrayType *in;
lenin = ARRNELEMS(in);
- if (lenin < 2 * MAXNUMRANGE)
+ if (lenin < 2 * num_ranges)
{ /* not compressed value */
if (in != (ArrayType *) DatumGetPointer(entry->key))
{
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();
+}
}
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++;
}
}
#include "_int.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "port/pg_bitutils.h"
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);
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);
while (num--)
{
- if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return true;
ptr++;
}
}
static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglen)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
while (num--)
{
- if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (!GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return false;
ptr++;
}
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;
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
g_intbig_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = GET_SIGLEN();
if (entry->leafkey)
{
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))
ptr = ARRPTR(in);
num = ARRNELEMS(in);
}
- SET_VARSIZE(res, CALCGTSIZE(0));
while (num--)
{
- HASH(GETSIGN(res), *ptr);
+ HASH(GETSIGN(res), *ptr, siglen);
ptr++;
}
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,
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 */
}
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
}
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;
}
{
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);
}
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);
}
{
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,
_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;
}
/* 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 ... */
{
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);
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;
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;
/* 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 */
{
retval = signconsistent((QUERYTYPE *) query,
GETSIGN(DatumGetPointer(entry->key)),
+ siglen,
false);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
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))
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])
{
}
}
+ 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:
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])
{
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();
+}
6343
(1 row)
+DROP INDEX text_idx;
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 0));
+ERROR: value 0 out of bounds for option "numranges"
+DETAIL: Valid values are between "1" and "252".
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 253));
+ERROR: value 253 out of bounds for option "numranges"
+DETAIL: Valid values are between "1" and "252".
+CREATE INDEX text_idx on test__int using gist (a gist__int_ops(numranges = 252));
+SELECT count(*) from test__int WHERE a && '{23,50}';
+ count
+-------
+ 403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23|50';
+ count
+-------
+ 403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{23,50}';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23&50';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from test__int WHERE a <@ '{73,23,20}';
+ count
+-------
+ 10
+(1 row)
+
+SELECT count(*) from test__int WHERE a = '{73,23,20}';
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '50&68';
+ count
+-------
+ 9
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
+ count
+-------
+ 21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
+ count
+-------
+ 21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '20 | !21';
+ count
+-------
+ 6566
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '!20 & !21';
+ count
+-------
+ 6343
+(1 row)
+
+DROP INDEX text_idx;
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 2025));
+ERROR: value 2025 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 2024));
+SELECT count(*) from test__int WHERE a && '{23,50}';
+ count
+-------
+ 403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23|50';
+ count
+-------
+ 403
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{23,50}';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '23&50';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}';
+ count
+-------
+ 12
+(1 row)
+
+SELECT count(*) from test__int WHERE a <@ '{73,23,20}';
+ count
+-------
+ 10
+(1 row)
+
+SELECT count(*) from test__int WHERE a = '{73,23,20}';
+ count
+-------
+ 1
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '50&68';
+ count
+-------
+ 9
+(1 row)
+
+SELECT count(*) from test__int WHERE a @> '{20,23}' or a @> '{50,68}';
+ count
+-------
+ 21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '(20&23)|(50&68)';
+ count
+-------
+ 21
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '20 | !21';
+ count
+-------
+ 6566
+(1 row)
+
+SELECT count(*) from test__int WHERE a @@ '!20 & !21';
+ count
+-------
+ 6343
+(1 row)
+
DROP INDEX text_idx;
CREATE INDEX text_idx on test__int using gist ( a gist__intbig_ops );
SELECT count(*) from test__int WHERE a && '{23,50}';
--- /dev/null
+/* 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);
# 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
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 );
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
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"
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) ) ) )
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- AHASH(sign, hash);
+ AHASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
{
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);
(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);
}
}
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,
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;
sb = LTG_SIGN(b);
*result = true;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
}
static int32
-unionkey(BITVECP sbase, ltree_gist *add)
+unionkey(BITVECP sbase, ltree_gist *add, int siglen)
{
int32 i;
BITVECP sadd = LTG_SIGN(add);
if (LTG_ISALLTRUE(add))
return 1;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
{
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 */
}
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);
}
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);
}
{
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,
_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;
}
/* 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 ... */
{
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);
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;
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;
}
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);
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--;
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);
while (vlen > 0)
{
- if (GETBIT(sign, AHASHVAL(curv->val)))
+ if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
{
isexist = true;
break;
}
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));
while (num > 0)
{
- if (gist_qe(key, query))
+ if (gist_qe(key, query, siglen))
return true;
num--;
query = (lquery *) NEXTVAL(query);
/* 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;
{
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 */
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();
+}
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' ;
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)
+
--- /dev/null
+/* 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);
+
# 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
/* 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 )
#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
#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 */
#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);
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);
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
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),
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))
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])
{
}
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- HASH(sign, hash);
+ HASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
{
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,
*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)
{
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;
}
if (isalltrue == false)
{
isalltrue = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) base)[i] != 0xff)
{
}
}
- 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);
}
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);
{
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);
{
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,
{
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))
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) ls)[i] |= sc[i];
}
}
{
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))
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) rs)[i] |= sc[i];
}
}
if (lisat == false)
{
lisat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) ls)[i] != 0xff)
{
if (risat == false)
{
risat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) rs)[i] != 0xff)
{
}
}
- 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);
}
static bool
-gist_isparent(ltree_gist *key, ltree *query)
+gist_isparent(ltree_gist *key, ltree *query, int siglen)
{
int32 numlevel = query->numlevel;
int i;
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;
}
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)
}
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);
while (vlen > 0)
{
- if (GETBIT(sign, HASHVAL(curv->val)))
+ if (GETBIT(sign, HASHVAL(curv->val, siglen)))
{
isexist = true;
break;
}
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));
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);
/* 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;
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:
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:
PointerGetDatum((ltxtquery *) query)
));
else
- res = gist_qtxt(key, (ltxtquery *) query);
+ res = gist_qtxt(key, (ltxtquery *) query, siglen);
break;
case 16:
case 17:
PointerGetDatum((ArrayType *) query)
));
else
- res = arrq_cons(key, (ArrayType *) query);
+ res = arrq_cons(key, (ArrayType *) query, siglen);
break;
default:
/* internal error */
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();
+}
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'
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}' ;
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"
1000
(1 row)
+drop index trgm_idx;
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2025));
+ERROR: value 2025 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2024));
+set enable_seqscan=off;
+select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t;
+ t | sml
+-------------+----------
+ qwertyu0988 | 1
+ qwertyu0980 | 0.714286
+ qwertyu0981 | 0.714286
+ qwertyu0982 | 0.714286
+ qwertyu0983 | 0.714286
+ qwertyu0984 | 0.714286
+ qwertyu0985 | 0.714286
+ qwertyu0986 | 0.714286
+ qwertyu0987 | 0.714286
+ qwertyu0989 | 0.714286
+ qwertyu0088 | 0.6
+ qwertyu0098 | 0.6
+ qwertyu0188 | 0.6
+ qwertyu0288 | 0.6
+ qwertyu0388 | 0.6
+ qwertyu0488 | 0.6
+ qwertyu0588 | 0.6
+ qwertyu0688 | 0.6
+ qwertyu0788 | 0.6
+ qwertyu0888 | 0.6
+ qwertyu0900 | 0.6
+ qwertyu0901 | 0.6
+ qwertyu0902 | 0.6
+ qwertyu0903 | 0.6
+ qwertyu0904 | 0.6
+ qwertyu0905 | 0.6
+ qwertyu0906 | 0.6
+ qwertyu0907 | 0.6
+ qwertyu0908 | 0.6
+ qwertyu0909 | 0.6
+ qwertyu0910 | 0.6
+ qwertyu0911 | 0.6
+ qwertyu0912 | 0.6
+ qwertyu0913 | 0.6
+ qwertyu0914 | 0.6
+ qwertyu0915 | 0.6
+ qwertyu0916 | 0.6
+ qwertyu0917 | 0.6
+ qwertyu0918 | 0.6
+ qwertyu0919 | 0.6
+ qwertyu0920 | 0.6
+ qwertyu0921 | 0.6
+ qwertyu0922 | 0.6
+ qwertyu0923 | 0.6
+ qwertyu0924 | 0.6
+ qwertyu0925 | 0.6
+ qwertyu0926 | 0.6
+ qwertyu0927 | 0.6
+ qwertyu0928 | 0.6
+ qwertyu0929 | 0.6
+ qwertyu0930 | 0.6
+ qwertyu0931 | 0.6
+ qwertyu0932 | 0.6
+ qwertyu0933 | 0.6
+ qwertyu0934 | 0.6
+ qwertyu0935 | 0.6
+ qwertyu0936 | 0.6
+ qwertyu0937 | 0.6
+ qwertyu0938 | 0.6
+ qwertyu0939 | 0.6
+ qwertyu0940 | 0.6
+ qwertyu0941 | 0.6
+ qwertyu0942 | 0.6
+ qwertyu0943 | 0.6
+ qwertyu0944 | 0.6
+ qwertyu0945 | 0.6
+ qwertyu0946 | 0.6
+ qwertyu0947 | 0.6
+ qwertyu0948 | 0.6
+ qwertyu0949 | 0.6
+ qwertyu0950 | 0.6
+ qwertyu0951 | 0.6
+ qwertyu0952 | 0.6
+ qwertyu0953 | 0.6
+ qwertyu0954 | 0.6
+ qwertyu0955 | 0.6
+ qwertyu0956 | 0.6
+ qwertyu0957 | 0.6
+ qwertyu0958 | 0.6
+ qwertyu0959 | 0.6
+ qwertyu0960 | 0.6
+ qwertyu0961 | 0.6
+ qwertyu0962 | 0.6
+ qwertyu0963 | 0.6
+ qwertyu0964 | 0.6
+ qwertyu0965 | 0.6
+ qwertyu0966 | 0.6
+ qwertyu0967 | 0.6
+ qwertyu0968 | 0.6
+ qwertyu0969 | 0.6
+ qwertyu0970 | 0.6
+ qwertyu0971 | 0.6
+ qwertyu0972 | 0.6
+ qwertyu0973 | 0.6
+ qwertyu0974 | 0.6
+ qwertyu0975 | 0.6
+ qwertyu0976 | 0.6
+ qwertyu0977 | 0.6
+ qwertyu0978 | 0.6
+ qwertyu0979 | 0.6
+ qwertyu0990 | 0.6
+ qwertyu0991 | 0.6
+ qwertyu0992 | 0.6
+ qwertyu0993 | 0.6
+ qwertyu0994 | 0.6
+ qwertyu0995 | 0.6
+ qwertyu0996 | 0.6
+ qwertyu0997 | 0.6
+ qwertyu0998 | 0.6
+ qwertyu0999 | 0.6
+ qwertyu0001 | 0.5
+ qwertyu0002 | 0.5
+ qwertyu0003 | 0.5
+ qwertyu0004 | 0.5
+ qwertyu0005 | 0.5
+ qwertyu0006 | 0.5
+ qwertyu0007 | 0.5
+ qwertyu0008 | 0.5
+ qwertyu0009 | 0.5
+ qwertyu0010 | 0.5
+ qwertyu0011 | 0.5
+ qwertyu0012 | 0.5
+ qwertyu0013 | 0.5
+ qwertyu0014 | 0.5
+ qwertyu0015 | 0.5
+ qwertyu0016 | 0.5
+ qwertyu0017 | 0.5
+ qwertyu0018 | 0.5
+ qwertyu0019 | 0.5
+ qwertyu0020 | 0.5
+ qwertyu0021 | 0.5
+ qwertyu0022 | 0.5
+ qwertyu0023 | 0.5
+ qwertyu0024 | 0.5
+ qwertyu0025 | 0.5
+ qwertyu0026 | 0.5
+ qwertyu0027 | 0.5
+ qwertyu0028 | 0.5
+ qwertyu0029 | 0.5
+ qwertyu0030 | 0.5
+ qwertyu0031 | 0.5
+ qwertyu0032 | 0.5
+ qwertyu0033 | 0.5
+ qwertyu0034 | 0.5
+ qwertyu0035 | 0.5
+ qwertyu0036 | 0.5
+ qwertyu0037 | 0.5
+ qwertyu0038 | 0.5
+ qwertyu0039 | 0.5
+ qwertyu0040 | 0.5
+ qwertyu0041 | 0.5
+ qwertyu0042 | 0.5
+ qwertyu0043 | 0.5
+ qwertyu0044 | 0.5
+ qwertyu0045 | 0.5
+ qwertyu0046 | 0.5
+ qwertyu0047 | 0.5
+ qwertyu0048 | 0.5
+ qwertyu0049 | 0.5
+ qwertyu0050 | 0.5
+ qwertyu0051 | 0.5
+ qwertyu0052 | 0.5
+ qwertyu0053 | 0.5
+ qwertyu0054 | 0.5
+ qwertyu0055 | 0.5
+ qwertyu0056 | 0.5
+ qwertyu0057 | 0.5
+ qwertyu0058 | 0.5
+ qwertyu0059 | 0.5
+ qwertyu0060 | 0.5
+ qwertyu0061 | 0.5
+ qwertyu0062 | 0.5
+ qwertyu0063 | 0.5
+ qwertyu0064 | 0.5
+ qwertyu0065 | 0.5
+ qwertyu0066 | 0.5
+ qwertyu0067 | 0.5
+ qwertyu0068 | 0.5
+ qwertyu0069 | 0.5
+ qwertyu0070 | 0.5
+ qwertyu0071 | 0.5
+ qwertyu0072 | 0.5
+ qwertyu0073 | 0.5
+ qwertyu0074 | 0.5
+ qwertyu0075 | 0.5
+ qwertyu0076 | 0.5
+ qwertyu0077 | 0.5
+ qwertyu0078 | 0.5
+ qwertyu0079 | 0.5
+ qwertyu0080 | 0.5
+ qwertyu0081 | 0.5
+ qwertyu0082 | 0.5
+ qwertyu0083 | 0.5
+ qwertyu0084 | 0.5
+ qwertyu0085 | 0.5
+ qwertyu0086 | 0.5
+ qwertyu0087 | 0.5
+ qwertyu0089 | 0.5
+ qwertyu0090 | 0.5
+ qwertyu0091 | 0.5
+ qwertyu0092 | 0.5
+ qwertyu0093 | 0.5
+ qwertyu0094 | 0.5
+ qwertyu0095 | 0.5
+ qwertyu0096 | 0.5
+ qwertyu0097 | 0.5
+ qwertyu0099 | 0.5
+ qwertyu0100 | 0.5
+ qwertyu0101 | 0.5
+ qwertyu0102 | 0.5
+ qwertyu0103 | 0.5
+ qwertyu0104 | 0.5
+ qwertyu0105 | 0.5
+ qwertyu0106 | 0.5
+ qwertyu0107 | 0.5
+ qwertyu0108 | 0.5
+ qwertyu0109 | 0.5
+ qwertyu0110 | 0.5
+ qwertyu0111 | 0.5
+ qwertyu0112 | 0.5
+ qwertyu0113 | 0.5
+ qwertyu0114 | 0.5
+ qwertyu0115 | 0.5
+ qwertyu0116 | 0.5
+ qwertyu0117 | 0.5
+ qwertyu0118 | 0.5
+ qwertyu0119 | 0.5
+ qwertyu0120 | 0.5
+ qwertyu0121 | 0.5
+ qwertyu0122 | 0.5
+ qwertyu0123 | 0.5
+ qwertyu0124 | 0.5
+ qwertyu0125 | 0.5
+ qwertyu0126 | 0.5
+ qwertyu0127 | 0.5
+ qwertyu0128 | 0.5
+ qwertyu0129 | 0.5
+ qwertyu0130 | 0.5
+ qwertyu0131 | 0.5
+ qwertyu0132 | 0.5
+ qwertyu0133 | 0.5
+ qwertyu0134 | 0.5
+ qwertyu0135 | 0.5
+ qwertyu0136 | 0.5
+ qwertyu0137 | 0.5
+ qwertyu0138 | 0.5
+ qwertyu0139 | 0.5
+ qwertyu0140 | 0.5
+ qwertyu0141 | 0.5
+ qwertyu0142 | 0.5
+ qwertyu0143 | 0.5
+ qwertyu0144 | 0.5
+ qwertyu0145 | 0.5
+ qwertyu0146 | 0.5
+ qwertyu0147 | 0.5
+ qwertyu0148 | 0.5
+ qwertyu0149 | 0.5
+ qwertyu0150 | 0.5
+ qwertyu0151 | 0.5
+ qwertyu0152 | 0.5
+ qwertyu0153 | 0.5
+ qwertyu0154 | 0.5
+ qwertyu0155 | 0.5
+ qwertyu0156 | 0.5
+ qwertyu0157 | 0.5
+ qwertyu0158 | 0.5
+ qwertyu0159 | 0.5
+ qwertyu0160 | 0.5
+ qwertyu0161 | 0.5
+ qwertyu0162 | 0.5
+ qwertyu0163 | 0.5
+ qwertyu0164 | 0.5
+ qwertyu0165 | 0.5
+ qwertyu0166 | 0.5
+ qwertyu0167 | 0.5
+ qwertyu0168 | 0.5
+ qwertyu0169 | 0.5
+ qwertyu0170 | 0.5
+ qwertyu0171 | 0.5
+ qwertyu0172 | 0.5
+ qwertyu0173 | 0.5
+ qwertyu0174 | 0.5
+ qwertyu0175 | 0.5
+ qwertyu0176 | 0.5
+ qwertyu0177 | 0.5
+ qwertyu0178 | 0.5
+ qwertyu0179 | 0.5
+ qwertyu0180 | 0.5
+ qwertyu0181 | 0.5
+ qwertyu0182 | 0.5
+ qwertyu0183 | 0.5
+ qwertyu0184 | 0.5
+ qwertyu0185 | 0.5
+ qwertyu0186 | 0.5
+ qwertyu0187 | 0.5
+ qwertyu0189 | 0.5
+ qwertyu0190 | 0.5
+ qwertyu0191 | 0.5
+ qwertyu0192 | 0.5
+ qwertyu0193 | 0.5
+ qwertyu0194 | 0.5
+ qwertyu0195 | 0.5
+ qwertyu0196 | 0.5
+ qwertyu0197 | 0.5
+ qwertyu0198 | 0.5
+ qwertyu0199 | 0.5
+ qwertyu0200 | 0.5
+ qwertyu0201 | 0.5
+ qwertyu0202 | 0.5
+ qwertyu0203 | 0.5
+ qwertyu0204 | 0.5
+ qwertyu0205 | 0.5
+ qwertyu0206 | 0.5
+ qwertyu0207 | 0.5
+ qwertyu0208 | 0.5
+ qwertyu0209 | 0.5
+ qwertyu0210 | 0.5
+ qwertyu0211 | 0.5
+ qwertyu0212 | 0.5
+ qwertyu0213 | 0.5
+ qwertyu0214 | 0.5
+ qwertyu0215 | 0.5
+ qwertyu0216 | 0.5
+ qwertyu0217 | 0.5
+ qwertyu0218 | 0.5
+ qwertyu0219 | 0.5
+ qwertyu0220 | 0.5
+ qwertyu0221 | 0.5
+ qwertyu0222 | 0.5
+ qwertyu0223 | 0.5
+ qwertyu0224 | 0.5
+ qwertyu0225 | 0.5
+ qwertyu0226 | 0.5
+ qwertyu0227 | 0.5
+ qwertyu0228 | 0.5
+ qwertyu0229 | 0.5
+ qwertyu0230 | 0.5
+ qwertyu0231 | 0.5
+ qwertyu0232 | 0.5
+ qwertyu0233 | 0.5
+ qwertyu0234 | 0.5
+ qwertyu0235 | 0.5
+ qwertyu0236 | 0.5
+ qwertyu0237 | 0.5
+ qwertyu0238 | 0.5
+ qwertyu0239 | 0.5
+ qwertyu0240 | 0.5
+ qwertyu0241 | 0.5
+ qwertyu0242 | 0.5
+ qwertyu0243 | 0.5
+ qwertyu0244 | 0.5
+ qwertyu0245 | 0.5
+ qwertyu0246 | 0.5
+ qwertyu0247 | 0.5
+ qwertyu0248 | 0.5
+ qwertyu0249 | 0.5
+ qwertyu0250 | 0.5
+ qwertyu0251 | 0.5
+ qwertyu0252 | 0.5
+ qwertyu0253 | 0.5
+ qwertyu0254 | 0.5
+ qwertyu0255 | 0.5
+ qwertyu0256 | 0.5
+ qwertyu0257 | 0.5
+ qwertyu0258 | 0.5
+ qwertyu0259 | 0.5
+ qwertyu0260 | 0.5
+ qwertyu0261 | 0.5
+ qwertyu0262 | 0.5
+ qwertyu0263 | 0.5
+ qwertyu0264 | 0.5
+ qwertyu0265 | 0.5
+ qwertyu0266 | 0.5
+ qwertyu0267 | 0.5
+ qwertyu0268 | 0.5
+ qwertyu0269 | 0.5
+ qwertyu0270 | 0.5
+ qwertyu0271 | 0.5
+ qwertyu0272 | 0.5
+ qwertyu0273 | 0.5
+ qwertyu0274 | 0.5
+ qwertyu0275 | 0.5
+ qwertyu0276 | 0.5
+ qwertyu0277 | 0.5
+ qwertyu0278 | 0.5
+ qwertyu0279 | 0.5
+ qwertyu0280 | 0.5
+ qwertyu0281 | 0.5
+ qwertyu0282 | 0.5
+ qwertyu0283 | 0.5
+ qwertyu0284 | 0.5
+ qwertyu0285 | 0.5
+ qwertyu0286 | 0.5
+ qwertyu0287 | 0.5
+ qwertyu0289 | 0.5
+ qwertyu0290 | 0.5
+ qwertyu0291 | 0.5
+ qwertyu0292 | 0.5
+ qwertyu0293 | 0.5
+ qwertyu0294 | 0.5
+ qwertyu0295 | 0.5
+ qwertyu0296 | 0.5
+ qwertyu0297 | 0.5
+ qwertyu0298 | 0.5
+ qwertyu0299 | 0.5
+ qwertyu0300 | 0.5
+ qwertyu0301 | 0.5
+ qwertyu0302 | 0.5
+ qwertyu0303 | 0.5
+ qwertyu0304 | 0.5
+ qwertyu0305 | 0.5
+ qwertyu0306 | 0.5
+ qwertyu0307 | 0.5
+ qwertyu0308 | 0.5
+ qwertyu0309 | 0.5
+ qwertyu0310 | 0.5
+ qwertyu0311 | 0.5
+ qwertyu0312 | 0.5
+ qwertyu0313 | 0.5
+ qwertyu0314 | 0.5
+ qwertyu0315 | 0.5
+ qwertyu0316 | 0.5
+ qwertyu0317 | 0.5
+ qwertyu0318 | 0.5
+ qwertyu0319 | 0.5
+ qwertyu0320 | 0.5
+ qwertyu0321 | 0.5
+ qwertyu0322 | 0.5
+ qwertyu0323 | 0.5
+ qwertyu0324 | 0.5
+ qwertyu0325 | 0.5
+ qwertyu0326 | 0.5
+ qwertyu0327 | 0.5
+ qwertyu0328 | 0.5
+ qwertyu0329 | 0.5
+ qwertyu0330 | 0.5
+ qwertyu0331 | 0.5
+ qwertyu0332 | 0.5
+ qwertyu0333 | 0.5
+ qwertyu0334 | 0.5
+ qwertyu0335 | 0.5
+ qwertyu0336 | 0.5
+ qwertyu0337 | 0.5
+ qwertyu0338 | 0.5
+ qwertyu0339 | 0.5
+ qwertyu0340 | 0.5
+ qwertyu0341 | 0.5
+ qwertyu0342 | 0.5
+ qwertyu0343 | 0.5
+ qwertyu0344 | 0.5
+ qwertyu0345 | 0.5
+ qwertyu0346 | 0.5
+ qwertyu0347 | 0.5
+ qwertyu0348 | 0.5
+ qwertyu0349 | 0.5
+ qwertyu0350 | 0.5
+ qwertyu0351 | 0.5
+ qwertyu0352 | 0.5
+ qwertyu0353 | 0.5
+ qwertyu0354 | 0.5
+ qwertyu0355 | 0.5
+ qwertyu0356 | 0.5
+ qwertyu0357 | 0.5
+ qwertyu0358 | 0.5
+ qwertyu0359 | 0.5
+ qwertyu0360 | 0.5
+ qwertyu0361 | 0.5
+ qwertyu0362 | 0.5
+ qwertyu0363 | 0.5
+ qwertyu0364 | 0.5
+ qwertyu0365 | 0.5
+ qwertyu0366 | 0.5
+ qwertyu0367 | 0.5
+ qwertyu0368 | 0.5
+ qwertyu0369 | 0.5
+ qwertyu0370 | 0.5
+ qwertyu0371 | 0.5
+ qwertyu0372 | 0.5
+ qwertyu0373 | 0.5
+ qwertyu0374 | 0.5
+ qwertyu0375 | 0.5
+ qwertyu0376 | 0.5
+ qwertyu0377 | 0.5
+ qwertyu0378 | 0.5
+ qwertyu0379 | 0.5
+ qwertyu0380 | 0.5
+ qwertyu0381 | 0.5
+ qwertyu0382 | 0.5
+ qwertyu0383 | 0.5
+ qwertyu0384 | 0.5
+ qwertyu0385 | 0.5
+ qwertyu0386 | 0.5
+ qwertyu0387 | 0.5
+ qwertyu0389 | 0.5
+ qwertyu0390 | 0.5
+ qwertyu0391 | 0.5
+ qwertyu0392 | 0.5
+ qwertyu0393 | 0.5
+ qwertyu0394 | 0.5
+ qwertyu0395 | 0.5
+ qwertyu0396 | 0.5
+ qwertyu0397 | 0.5
+ qwertyu0398 | 0.5
+ qwertyu0399 | 0.5
+ qwertyu0400 | 0.5
+ qwertyu0401 | 0.5
+ qwertyu0402 | 0.5
+ qwertyu0403 | 0.5
+ qwertyu0404 | 0.5
+ qwertyu0405 | 0.5
+ qwertyu0406 | 0.5
+ qwertyu0407 | 0.5
+ qwertyu0408 | 0.5
+ qwertyu0409 | 0.5
+ qwertyu0410 | 0.5
+ qwertyu0411 | 0.5
+ qwertyu0412 | 0.5
+ qwertyu0413 | 0.5
+ qwertyu0414 | 0.5
+ qwertyu0415 | 0.5
+ qwertyu0416 | 0.5
+ qwertyu0417 | 0.5
+ qwertyu0418 | 0.5
+ qwertyu0419 | 0.5
+ qwertyu0420 | 0.5
+ qwertyu0421 | 0.5
+ qwertyu0422 | 0.5
+ qwertyu0423 | 0.5
+ qwertyu0424 | 0.5
+ qwertyu0425 | 0.5
+ qwertyu0426 | 0.5
+ qwertyu0427 | 0.5
+ qwertyu0428 | 0.5
+ qwertyu0429 | 0.5
+ qwertyu0430 | 0.5
+ qwertyu0431 | 0.5
+ qwertyu0432 | 0.5
+ qwertyu0433 | 0.5
+ qwertyu0434 | 0.5
+ qwertyu0435 | 0.5
+ qwertyu0436 | 0.5
+ qwertyu0437 | 0.5
+ qwertyu0438 | 0.5
+ qwertyu0439 | 0.5
+ qwertyu0440 | 0.5
+ qwertyu0441 | 0.5
+ qwertyu0442 | 0.5
+ qwertyu0443 | 0.5
+ qwertyu0444 | 0.5
+ qwertyu0445 | 0.5
+ qwertyu0446 | 0.5
+ qwertyu0447 | 0.5
+ qwertyu0448 | 0.5
+ qwertyu0449 | 0.5
+ qwertyu0450 | 0.5
+ qwertyu0451 | 0.5
+ qwertyu0452 | 0.5
+ qwertyu0453 | 0.5
+ qwertyu0454 | 0.5
+ qwertyu0455 | 0.5
+ qwertyu0456 | 0.5
+ qwertyu0457 | 0.5
+ qwertyu0458 | 0.5
+ qwertyu0459 | 0.5
+ qwertyu0460 | 0.5
+ qwertyu0461 | 0.5
+ qwertyu0462 | 0.5
+ qwertyu0463 | 0.5
+ qwertyu0464 | 0.5
+ qwertyu0465 | 0.5
+ qwertyu0466 | 0.5
+ qwertyu0467 | 0.5
+ qwertyu0468 | 0.5
+ qwertyu0469 | 0.5
+ qwertyu0470 | 0.5
+ qwertyu0471 | 0.5
+ qwertyu0472 | 0.5
+ qwertyu0473 | 0.5
+ qwertyu0474 | 0.5
+ qwertyu0475 | 0.5
+ qwertyu0476 | 0.5
+ qwertyu0477 | 0.5
+ qwertyu0478 | 0.5
+ qwertyu0479 | 0.5
+ qwertyu0480 | 0.5
+ qwertyu0481 | 0.5
+ qwertyu0482 | 0.5
+ qwertyu0483 | 0.5
+ qwertyu0484 | 0.5
+ qwertyu0485 | 0.5
+ qwertyu0486 | 0.5
+ qwertyu0487 | 0.5
+ qwertyu0489 | 0.5
+ qwertyu0490 | 0.5
+ qwertyu0491 | 0.5
+ qwertyu0492 | 0.5
+ qwertyu0493 | 0.5
+ qwertyu0494 | 0.5
+ qwertyu0495 | 0.5
+ qwertyu0496 | 0.5
+ qwertyu0497 | 0.5
+ qwertyu0498 | 0.5
+ qwertyu0499 | 0.5
+ qwertyu0500 | 0.5
+ qwertyu0501 | 0.5
+ qwertyu0502 | 0.5
+ qwertyu0503 | 0.5
+ qwertyu0504 | 0.5
+ qwertyu0505 | 0.5
+ qwertyu0506 | 0.5
+ qwertyu0507 | 0.5
+ qwertyu0508 | 0.5
+ qwertyu0509 | 0.5
+ qwertyu0510 | 0.5
+ qwertyu0511 | 0.5
+ qwertyu0512 | 0.5
+ qwertyu0513 | 0.5
+ qwertyu0514 | 0.5
+ qwertyu0515 | 0.5
+ qwertyu0516 | 0.5
+ qwertyu0517 | 0.5
+ qwertyu0518 | 0.5
+ qwertyu0519 | 0.5
+ qwertyu0520 | 0.5
+ qwertyu0521 | 0.5
+ qwertyu0522 | 0.5
+ qwertyu0523 | 0.5
+ qwertyu0524 | 0.5
+ qwertyu0525 | 0.5
+ qwertyu0526 | 0.5
+ qwertyu0527 | 0.5
+ qwertyu0528 | 0.5
+ qwertyu0529 | 0.5
+ qwertyu0530 | 0.5
+ qwertyu0531 | 0.5
+ qwertyu0532 | 0.5
+ qwertyu0533 | 0.5
+ qwertyu0534 | 0.5
+ qwertyu0535 | 0.5
+ qwertyu0536 | 0.5
+ qwertyu0537 | 0.5
+ qwertyu0538 | 0.5
+ qwertyu0539 | 0.5
+ qwertyu0540 | 0.5
+ qwertyu0541 | 0.5
+ qwertyu0542 | 0.5
+ qwertyu0543 | 0.5
+ qwertyu0544 | 0.5
+ qwertyu0545 | 0.5
+ qwertyu0546 | 0.5
+ qwertyu0547 | 0.5
+ qwertyu0548 | 0.5
+ qwertyu0549 | 0.5
+ qwertyu0550 | 0.5
+ qwertyu0551 | 0.5
+ qwertyu0552 | 0.5
+ qwertyu0553 | 0.5
+ qwertyu0554 | 0.5
+ qwertyu0555 | 0.5
+ qwertyu0556 | 0.5
+ qwertyu0557 | 0.5
+ qwertyu0558 | 0.5
+ qwertyu0559 | 0.5
+ qwertyu0560 | 0.5
+ qwertyu0561 | 0.5
+ qwertyu0562 | 0.5
+ qwertyu0563 | 0.5
+ qwertyu0564 | 0.5
+ qwertyu0565 | 0.5
+ qwertyu0566 | 0.5
+ qwertyu0567 | 0.5
+ qwertyu0568 | 0.5
+ qwertyu0569 | 0.5
+ qwertyu0570 | 0.5
+ qwertyu0571 | 0.5
+ qwertyu0572 | 0.5
+ qwertyu0573 | 0.5
+ qwertyu0574 | 0.5
+ qwertyu0575 | 0.5
+ qwertyu0576 | 0.5
+ qwertyu0577 | 0.5
+ qwertyu0578 | 0.5
+ qwertyu0579 | 0.5
+ qwertyu0580 | 0.5
+ qwertyu0581 | 0.5
+ qwertyu0582 | 0.5
+ qwertyu0583 | 0.5
+ qwertyu0584 | 0.5
+ qwertyu0585 | 0.5
+ qwertyu0586 | 0.5
+ qwertyu0587 | 0.5
+ qwertyu0589 | 0.5
+ qwertyu0590 | 0.5
+ qwertyu0591 | 0.5
+ qwertyu0592 | 0.5
+ qwertyu0593 | 0.5
+ qwertyu0594 | 0.5
+ qwertyu0595 | 0.5
+ qwertyu0596 | 0.5
+ qwertyu0597 | 0.5
+ qwertyu0598 | 0.5
+ qwertyu0599 | 0.5
+ qwertyu0600 | 0.5
+ qwertyu0601 | 0.5
+ qwertyu0602 | 0.5
+ qwertyu0603 | 0.5
+ qwertyu0604 | 0.5
+ qwertyu0605 | 0.5
+ qwertyu0606 | 0.5
+ qwertyu0607 | 0.5
+ qwertyu0608 | 0.5
+ qwertyu0609 | 0.5
+ qwertyu0610 | 0.5
+ qwertyu0611 | 0.5
+ qwertyu0612 | 0.5
+ qwertyu0613 | 0.5
+ qwertyu0614 | 0.5
+ qwertyu0615 | 0.5
+ qwertyu0616 | 0.5
+ qwertyu0617 | 0.5
+ qwertyu0618 | 0.5
+ qwertyu0619 | 0.5
+ qwertyu0620 | 0.5
+ qwertyu0621 | 0.5
+ qwertyu0622 | 0.5
+ qwertyu0623 | 0.5
+ qwertyu0624 | 0.5
+ qwertyu0625 | 0.5
+ qwertyu0626 | 0.5
+ qwertyu0627 | 0.5
+ qwertyu0628 | 0.5
+ qwertyu0629 | 0.5
+ qwertyu0630 | 0.5
+ qwertyu0631 | 0.5
+ qwertyu0632 | 0.5
+ qwertyu0633 | 0.5
+ qwertyu0634 | 0.5
+ qwertyu0635 | 0.5
+ qwertyu0636 | 0.5
+ qwertyu0637 | 0.5
+ qwertyu0638 | 0.5
+ qwertyu0639 | 0.5
+ qwertyu0640 | 0.5
+ qwertyu0641 | 0.5
+ qwertyu0642 | 0.5
+ qwertyu0643 | 0.5
+ qwertyu0644 | 0.5
+ qwertyu0645 | 0.5
+ qwertyu0646 | 0.5
+ qwertyu0647 | 0.5
+ qwertyu0648 | 0.5
+ qwertyu0649 | 0.5
+ qwertyu0650 | 0.5
+ qwertyu0651 | 0.5
+ qwertyu0652 | 0.5
+ qwertyu0653 | 0.5
+ qwertyu0654 | 0.5
+ qwertyu0655 | 0.5
+ qwertyu0656 | 0.5
+ qwertyu0657 | 0.5
+ qwertyu0658 | 0.5
+ qwertyu0659 | 0.5
+ qwertyu0660 | 0.5
+ qwertyu0661 | 0.5
+ qwertyu0662 | 0.5
+ qwertyu0663 | 0.5
+ qwertyu0664 | 0.5
+ qwertyu0665 | 0.5
+ qwertyu0666 | 0.5
+ qwertyu0667 | 0.5
+ qwertyu0668 | 0.5
+ qwertyu0669 | 0.5
+ qwertyu0670 | 0.5
+ qwertyu0671 | 0.5
+ qwertyu0672 | 0.5
+ qwertyu0673 | 0.5
+ qwertyu0674 | 0.5
+ qwertyu0675 | 0.5
+ qwertyu0676 | 0.5
+ qwertyu0677 | 0.5
+ qwertyu0678 | 0.5
+ qwertyu0679 | 0.5
+ qwertyu0680 | 0.5
+ qwertyu0681 | 0.5
+ qwertyu0682 | 0.5
+ qwertyu0683 | 0.5
+ qwertyu0684 | 0.5
+ qwertyu0685 | 0.5
+ qwertyu0686 | 0.5
+ qwertyu0687 | 0.5
+ qwertyu0689 | 0.5
+ qwertyu0690 | 0.5
+ qwertyu0691 | 0.5
+ qwertyu0692 | 0.5
+ qwertyu0693 | 0.5
+ qwertyu0694 | 0.5
+ qwertyu0695 | 0.5
+ qwertyu0696 | 0.5
+ qwertyu0697 | 0.5
+ qwertyu0698 | 0.5
+ qwertyu0699 | 0.5
+ qwertyu0700 | 0.5
+ qwertyu0701 | 0.5
+ qwertyu0702 | 0.5
+ qwertyu0703 | 0.5
+ qwertyu0704 | 0.5
+ qwertyu0705 | 0.5
+ qwertyu0706 | 0.5
+ qwertyu0707 | 0.5
+ qwertyu0708 | 0.5
+ qwertyu0709 | 0.5
+ qwertyu0710 | 0.5
+ qwertyu0711 | 0.5
+ qwertyu0712 | 0.5
+ qwertyu0713 | 0.5
+ qwertyu0714 | 0.5
+ qwertyu0715 | 0.5
+ qwertyu0716 | 0.5
+ qwertyu0717 | 0.5
+ qwertyu0718 | 0.5
+ qwertyu0719 | 0.5
+ qwertyu0720 | 0.5
+ qwertyu0721 | 0.5
+ qwertyu0722 | 0.5
+ qwertyu0723 | 0.5
+ qwertyu0724 | 0.5
+ qwertyu0725 | 0.5
+ qwertyu0726 | 0.5
+ qwertyu0727 | 0.5
+ qwertyu0728 | 0.5
+ qwertyu0729 | 0.5
+ qwertyu0730 | 0.5
+ qwertyu0731 | 0.5
+ qwertyu0732 | 0.5
+ qwertyu0733 | 0.5
+ qwertyu0734 | 0.5
+ qwertyu0735 | 0.5
+ qwertyu0736 | 0.5
+ qwertyu0737 | 0.5
+ qwertyu0738 | 0.5
+ qwertyu0739 | 0.5
+ qwertyu0740 | 0.5
+ qwertyu0741 | 0.5
+ qwertyu0742 | 0.5
+ qwertyu0743 | 0.5
+ qwertyu0744 | 0.5
+ qwertyu0745 | 0.5
+ qwertyu0746 | 0.5
+ qwertyu0747 | 0.5
+ qwertyu0748 | 0.5
+ qwertyu0749 | 0.5
+ qwertyu0750 | 0.5
+ qwertyu0751 | 0.5
+ qwertyu0752 | 0.5
+ qwertyu0753 | 0.5
+ qwertyu0754 | 0.5
+ qwertyu0755 | 0.5
+ qwertyu0756 | 0.5
+ qwertyu0757 | 0.5
+ qwertyu0758 | 0.5
+ qwertyu0759 | 0.5
+ qwertyu0760 | 0.5
+ qwertyu0761 | 0.5
+ qwertyu0762 | 0.5
+ qwertyu0763 | 0.5
+ qwertyu0764 | 0.5
+ qwertyu0765 | 0.5
+ qwertyu0766 | 0.5
+ qwertyu0767 | 0.5
+ qwertyu0768 | 0.5
+ qwertyu0769 | 0.5
+ qwertyu0770 | 0.5
+ qwertyu0771 | 0.5
+ qwertyu0772 | 0.5
+ qwertyu0773 | 0.5
+ qwertyu0774 | 0.5
+ qwertyu0775 | 0.5
+ qwertyu0776 | 0.5
+ qwertyu0777 | 0.5
+ qwertyu0778 | 0.5
+ qwertyu0779 | 0.5
+ qwertyu0780 | 0.5
+ qwertyu0781 | 0.5
+ qwertyu0782 | 0.5
+ qwertyu0783 | 0.5
+ qwertyu0784 | 0.5
+ qwertyu0785 | 0.5
+ qwertyu0786 | 0.5
+ qwertyu0787 | 0.5
+ qwertyu0789 | 0.5
+ qwertyu0790 | 0.5
+ qwertyu0791 | 0.5
+ qwertyu0792 | 0.5
+ qwertyu0793 | 0.5
+ qwertyu0794 | 0.5
+ qwertyu0795 | 0.5
+ qwertyu0796 | 0.5
+ qwertyu0797 | 0.5
+ qwertyu0798 | 0.5
+ qwertyu0799 | 0.5
+ qwertyu0800 | 0.5
+ qwertyu0801 | 0.5
+ qwertyu0802 | 0.5
+ qwertyu0803 | 0.5
+ qwertyu0804 | 0.5
+ qwertyu0805 | 0.5
+ qwertyu0806 | 0.5
+ qwertyu0807 | 0.5
+ qwertyu0808 | 0.5
+ qwertyu0809 | 0.5
+ qwertyu0810 | 0.5
+ qwertyu0811 | 0.5
+ qwertyu0812 | 0.5
+ qwertyu0813 | 0.5
+ qwertyu0814 | 0.5
+ qwertyu0815 | 0.5
+ qwertyu0816 | 0.5
+ qwertyu0817 | 0.5
+ qwertyu0818 | 0.5
+ qwertyu0819 | 0.5
+ qwertyu0820 | 0.5
+ qwertyu0821 | 0.5
+ qwertyu0822 | 0.5
+ qwertyu0823 | 0.5
+ qwertyu0824 | 0.5
+ qwertyu0825 | 0.5
+ qwertyu0826 | 0.5
+ qwertyu0827 | 0.5
+ qwertyu0828 | 0.5
+ qwertyu0829 | 0.5
+ qwertyu0830 | 0.5
+ qwertyu0831 | 0.5
+ qwertyu0832 | 0.5
+ qwertyu0833 | 0.5
+ qwertyu0834 | 0.5
+ qwertyu0835 | 0.5
+ qwertyu0836 | 0.5
+ qwertyu0837 | 0.5
+ qwertyu0838 | 0.5
+ qwertyu0839 | 0.5
+ qwertyu0840 | 0.5
+ qwertyu0841 | 0.5
+ qwertyu0842 | 0.5
+ qwertyu0843 | 0.5
+ qwertyu0844 | 0.5
+ qwertyu0845 | 0.5
+ qwertyu0846 | 0.5
+ qwertyu0847 | 0.5
+ qwertyu0848 | 0.5
+ qwertyu0849 | 0.5
+ qwertyu0850 | 0.5
+ qwertyu0851 | 0.5
+ qwertyu0852 | 0.5
+ qwertyu0853 | 0.5
+ qwertyu0854 | 0.5
+ qwertyu0855 | 0.5
+ qwertyu0856 | 0.5
+ qwertyu0857 | 0.5
+ qwertyu0858 | 0.5
+ qwertyu0859 | 0.5
+ qwertyu0860 | 0.5
+ qwertyu0861 | 0.5
+ qwertyu0862 | 0.5
+ qwertyu0863 | 0.5
+ qwertyu0864 | 0.5
+ qwertyu0865 | 0.5
+ qwertyu0866 | 0.5
+ qwertyu0867 | 0.5
+ qwertyu0868 | 0.5
+ qwertyu0869 | 0.5
+ qwertyu0870 | 0.5
+ qwertyu0871 | 0.5
+ qwertyu0872 | 0.5
+ qwertyu0873 | 0.5
+ qwertyu0874 | 0.5
+ qwertyu0875 | 0.5
+ qwertyu0876 | 0.5
+ qwertyu0877 | 0.5
+ qwertyu0878 | 0.5
+ qwertyu0879 | 0.5
+ qwertyu0880 | 0.5
+ qwertyu0881 | 0.5
+ qwertyu0882 | 0.5
+ qwertyu0883 | 0.5
+ qwertyu0884 | 0.5
+ qwertyu0885 | 0.5
+ qwertyu0886 | 0.5
+ qwertyu0887 | 0.5
+ qwertyu0889 | 0.5
+ qwertyu0890 | 0.5
+ qwertyu0891 | 0.5
+ qwertyu0892 | 0.5
+ qwertyu0893 | 0.5
+ qwertyu0894 | 0.5
+ qwertyu0895 | 0.5
+ qwertyu0896 | 0.5
+ qwertyu0897 | 0.5
+ qwertyu0898 | 0.5
+ qwertyu0899 | 0.5
+ qwertyu1000 | 0.411765
+(1000 rows)
+
+select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t;
+ t | sml
+-------------+----------
+ qwertyu0988 | 0.6
+ qwertyu0980 | 0.411765
+ qwertyu0981 | 0.411765
+ qwertyu0982 | 0.411765
+ qwertyu0983 | 0.411765
+ qwertyu0984 | 0.411765
+ qwertyu0985 | 0.411765
+ qwertyu0986 | 0.411765
+ qwertyu0987 | 0.411765
+ qwertyu0989 | 0.411765
+ qwertyu0088 | 0.333333
+ qwertyu0098 | 0.333333
+ qwertyu0188 | 0.333333
+ qwertyu0288 | 0.333333
+ qwertyu0388 | 0.333333
+ qwertyu0488 | 0.333333
+ qwertyu0588 | 0.333333
+ qwertyu0688 | 0.333333
+ qwertyu0788 | 0.333333
+ qwertyu0888 | 0.333333
+ qwertyu0900 | 0.333333
+ qwertyu0901 | 0.333333
+ qwertyu0902 | 0.333333
+ qwertyu0903 | 0.333333
+ qwertyu0904 | 0.333333
+ qwertyu0905 | 0.333333
+ qwertyu0906 | 0.333333
+ qwertyu0907 | 0.333333
+ qwertyu0908 | 0.333333
+ qwertyu0909 | 0.333333
+ qwertyu0910 | 0.333333
+ qwertyu0911 | 0.333333
+ qwertyu0912 | 0.333333
+ qwertyu0913 | 0.333333
+ qwertyu0914 | 0.333333
+ qwertyu0915 | 0.333333
+ qwertyu0916 | 0.333333
+ qwertyu0917 | 0.333333
+ qwertyu0918 | 0.333333
+ qwertyu0919 | 0.333333
+ qwertyu0920 | 0.333333
+ qwertyu0921 | 0.333333
+ qwertyu0922 | 0.333333
+ qwertyu0923 | 0.333333
+ qwertyu0924 | 0.333333
+ qwertyu0925 | 0.333333
+ qwertyu0926 | 0.333333
+ qwertyu0927 | 0.333333
+ qwertyu0928 | 0.333333
+ qwertyu0929 | 0.333333
+ qwertyu0930 | 0.333333
+ qwertyu0931 | 0.333333
+ qwertyu0932 | 0.333333
+ qwertyu0933 | 0.333333
+ qwertyu0934 | 0.333333
+ qwertyu0935 | 0.333333
+ qwertyu0936 | 0.333333
+ qwertyu0937 | 0.333333
+ qwertyu0938 | 0.333333
+ qwertyu0939 | 0.333333
+ qwertyu0940 | 0.333333
+ qwertyu0941 | 0.333333
+ qwertyu0942 | 0.333333
+ qwertyu0943 | 0.333333
+ qwertyu0944 | 0.333333
+ qwertyu0945 | 0.333333
+ qwertyu0946 | 0.333333
+ qwertyu0947 | 0.333333
+ qwertyu0948 | 0.333333
+ qwertyu0949 | 0.333333
+ qwertyu0950 | 0.333333
+ qwertyu0951 | 0.333333
+ qwertyu0952 | 0.333333
+ qwertyu0953 | 0.333333
+ qwertyu0954 | 0.333333
+ qwertyu0955 | 0.333333
+ qwertyu0956 | 0.333333
+ qwertyu0957 | 0.333333
+ qwertyu0958 | 0.333333
+ qwertyu0959 | 0.333333
+ qwertyu0960 | 0.333333
+ qwertyu0961 | 0.333333
+ qwertyu0962 | 0.333333
+ qwertyu0963 | 0.333333
+ qwertyu0964 | 0.333333
+ qwertyu0965 | 0.333333
+ qwertyu0966 | 0.333333
+ qwertyu0967 | 0.333333
+ qwertyu0968 | 0.333333
+ qwertyu0969 | 0.333333
+ qwertyu0970 | 0.333333
+ qwertyu0971 | 0.333333
+ qwertyu0972 | 0.333333
+ qwertyu0973 | 0.333333
+ qwertyu0974 | 0.333333
+ qwertyu0975 | 0.333333
+ qwertyu0976 | 0.333333
+ qwertyu0977 | 0.333333
+ qwertyu0978 | 0.333333
+ qwertyu0979 | 0.333333
+ qwertyu0990 | 0.333333
+ qwertyu0991 | 0.333333
+ qwertyu0992 | 0.333333
+ qwertyu0993 | 0.333333
+ qwertyu0994 | 0.333333
+ qwertyu0995 | 0.333333
+ qwertyu0996 | 0.333333
+ qwertyu0997 | 0.333333
+ qwertyu0998 | 0.333333
+ qwertyu0999 | 0.333333
+(110 rows)
+
+select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t;
+ t | sml
+-------------+----------
+ qwertyu0988 | 0.333333
+(1 row)
+
+explain (costs off)
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+ QUERY PLAN
+---------------------------------------------------
+ Limit
+ -> Index Scan using trgm_idx on test_trgm
+ Order By: (t <-> 'q0987wertyu0988'::text)
+(3 rows)
+
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+ ?column? | t
+----------+-------------
+ 0.411765 | qwertyu0988
+ 0.5 | qwertyu0987
+(2 rows)
+
+select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
+ count
+-------
+ 1000
+(1 row)
+
drop index trgm_idx;
create index trgm_idx on test_trgm using gin (t gin_trgm_ops);
set enable_seqscan=off;
--- /dev/null
+/* 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);
# 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
select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
+drop index trgm_idx;
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=0));
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2025));
+create index trgm_idx on test_trgm using gist (t gist_trgm_ops(siglen=2024));
+set enable_seqscan=off;
+
+select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t;
+select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t;
+select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t;
+explain (costs off)
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2;
+select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}';
+
drop index trgm_idx;
create index trgm_idx on test_trgm using gin (t gin_trgm_ops);
set enable_seqscan=off;
#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 )
#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
#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) )
*/
#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 */
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
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);
}
}
gtrgm_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = LTREE_GET_ASIGLEN();
GISTENTRY *retval = entry;
if (entry->leafkey)
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,
}
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,
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- count += GETBIT(sign, HASHVAL(tmp));
+ count += GETBIT(sign, HASHVAL(tmp, siglen));
}
return count;
/* 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;
}
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)
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;
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);
/* 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;
}
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);
}
static int32
-unionkey(BITVECP sbase, TRGM *add)
+unionkey(BITVECP sbase, TRGM *add, int siglen)
{
int32 i;
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
for (i = 0; i < ARRNELEM(add); i++)
{
CPTRGM(((char *) &tmp), ptr + i);
- HASH(sbase, tmp);
+ HASH(sbase, tmp, siglen);
}
}
return 0;
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);
}
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 */
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
}
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 */
}
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
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);
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;
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)
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) )
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
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,
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;
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);
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;
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;
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();
+}
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
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>
</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
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
-- 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 && '{1,2}';
</listitem>
<listitem>
<para>
- GiST index over <type>ltree</type>:
+ GiST index over <type>ltree</type> (<literal>gist_ltree_ops</literal>
+ opclass):
<literal><</literal>, <literal><=</literal>, <literal>=</literal>,
<literal>>=</literal>, <literal>></literal>,
<literal>@></literal>, <literal><@</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[] <@ ltree</literal>, <literal>ltree @> 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.
</programlisting>
</para>
+ <para>
+ <literal>gist_trgm_ops</literal> GiST opclass approximates set of
+ trigrams as a bitmap signature. Optional integer parameter
+ <literal>siglen</literal> of <literal>gist_trgm_ops</literal> determines
+ signature length in bytes. Default signature length is 12 bytes.
+ Valid values of signature length are between 1 and 2024 bytes. Longer
+ signatures leads to more precise search (scan less fraction of index, scan
+ less heap pages), but larger index.
+ </para>
+
+ <para>
+ Example of creating such an index with a signature length of 32 bytes:
+ </para>
+<programlisting>
+CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));
+</programlisting>
+
<para>
At this point, you will have an index on the <structfield>t</structfield> column that
you can use for similarity searching. A typical query is
<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> ]
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of an operator class parameter. See below for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ASC</literal></term>
<listitem>
</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
<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>
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>
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>
amroutine->amstrategies = 0;
amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
+ amroutine->amoptsprocnum = BRIN_PROCNUM_OPTIONS;
amroutine->amcanorder = false;
amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
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 ||
need_initialization = true;
}
+/*
+ * init_local_reloptions
+ * Initialize local reloptions that will parsed into bytea structure of
+ * 'relopt_struct_size'.
+ */
+void
+init_local_reloptions(local_relopts *opts, Size relopt_struct_size)
+{
+ opts->options = NIL;
+ opts->validators = NIL;
+ opts->relopt_struct_size = relopt_struct_size;
+}
+
+/*
+ * register_reloptions_validator
+ * Register custom validation callback that will be called at the end of
+ * build_local_reloptions().
+ */
+void
+register_reloptions_validator(local_relopts *opts, relopts_validator validator)
+{
+ opts->validators = lappend(opts->validators, validator);
+}
+
+/*
+ * add_local_reloption
+ * Add an already-created custom reloption to the local list.
+ */
+static void
+add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
+{
+ local_relopt *opt = palloc(sizeof(*opt));
+
+ Assert(offset < relopts->relopt_struct_size);
+
+ opt->option = newoption;
+ opt->offset = offset;
+
+ relopts->options = lappend(relopts->options, opt);
+}
+
/*
* allocate_reloption
* Allocate a new reloption and initialize the type-agnostic fields
size_t size;
relopt_gen *newoption;
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ if (kinds != RELOPT_KIND_LOCAL)
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ else
+ oldcxt = NULL;
switch (type)
{
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;
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;
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;
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
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;
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;
}
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
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.
*
/* 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;
}
/*
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);
}
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
{
return rdopts;
}
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
+{
+ int noptions = list_length(relopts->options);
+ relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+ relopt_value *vals;
+ void *opts;
+ int i = 0;
+ ListCell *lc;
+
+ foreach(lc, relopts->options)
+ {
+ local_relopt *opt = lfirst(lc);
+
+ elems[i].optname = opt->option->name;
+ elems[i].opttype = opt->option->type;
+ elems[i].offset = opt->offset;
+
+ i++;
+ }
+
+ vals = parseLocalRelOptions(relopts, options, validate);
+ opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
+ fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
+ elems, noptions);
+
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
+}
+
/*
* Option parser for partitioned tables
*/
amroutine->amstrategies = 0;
amroutine->amsupport = GINNProcs;
+ amroutine->amoptsprocnum = GIN_OPTIONS_PROC;
amroutine->amcanorder = false;
amroutine->amcanorderbyop = false;
amroutine->amcanbackward = false;
INTERNALOID, INTERNALOID,
INTERNALOID);
break;
+ case GIN_OPTIONS_PROC:
+ ok = check_amoptsproc_signature(procform->amproc);
+ break;
default:
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
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 */
amroutine->amstrategies = 0;
amroutine->amsupport = GISTNProcs;
+ amroutine->amoptsprocnum = GIST_OPTIONS_PROC;
amroutine->amcanorder = false;
amroutine->amcanorderbyop = true;
amroutine->amcanbackward = false;
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),
(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),
amroutine->amstrategies = HTMaxStrategyNumber;
amroutine->amsupport = HASHNProcs;
+ amroutine->amoptsprocnum = HASHOPTIONS_PROC;
amroutine->amcanorder = false;
amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
procform->amproclefttype);
}
break;
+ case HASHOPTIONS_PROC:
+ if (!check_amoptsproc_signature(procform->amproc))
+ result = false;
+ break;
default:
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
#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"
return result;
}
+/*
+ * Validate the signature of an opclass options support function, that should
+ * be 'void(internal)'.
+ */
+bool
+check_amoptsproc_signature(Oid funcid)
+{
+ return check_amproc_signature(funcid, VOIDOID, true, 1, 1, INTERNALOID);
+}
+
/*
* Validate the signature (argument and result types) of an opclass operator.
* Return true if OK, false if not.
#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"
/* ----------------------------------------------------------------
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;
{
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;
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;
}
}
}
+
+/* ----------------
+ * 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);
+}
amroutine->amstrategies = BTMaxStrategyNumber;
amroutine->amsupport = BTNProcs;
+ amroutine->amoptsprocnum = BTOPTIONS_PROC;
amroutine->amcanorder = true;
amroutine->amcanorderbyop = false;
amroutine->amcanbackward = true;
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),