/* 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),
configOut.leafType, true,
1, 1, procform->amproclefttype);
break;
+ case SPGIST_OPTIONS_PROC:
+ ok = check_amoptsproc_signature(procform->amproc);
+ break;
default:
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
{
if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
+ if (i == SPGIST_OPTIONS_PROC)
+ continue; /* optional method */
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("operator family \"%s\" of access method %s is missing support function %d for type %s",
void
InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
+ Datum attoptions,
CatalogIndexState indstate)
{
Datum values[Natts_pg_attribute];
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+ values[Anum_pg_attribute_attoptions - 1] = attoptions;
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
- nulls[Anum_pg_attribute_attoptions - 1] = true;
+ nulls[Anum_pg_attribute_attoptions - 1] = attoptions == (Datum) 0;
nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
nulls[Anum_pg_attribute_attmissingval - 1] = true;
/* Make sure this is OK, too */
attr->attstattarget = -1;
- InsertPgAttributeTuple(rel, attr, indstate);
+ InsertPgAttributeTuple(rel, attr, (Datum) 0, indstate);
/* Add dependency info */
myself.classId = RelationRelationId;
/* Fill in the correct relation OID in the copied tuple */
attStruct.attrelid = new_rel_oid;
- InsertPgAttributeTuple(rel, &attStruct, indstate);
+ InsertPgAttributeTuple(rel, &attStruct, (Datum) 0, indstate);
}
}
#include "access/amapi.h"
#include "access/heapam.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/tableam.h"
Oid *classObjectId);
static void InitializeAttributeOids(Relation indexRelation,
int numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, int numatts);
+static void AppendAttributeTuples(Relation indexRelation, int numatts,
+ Datum *attopts);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
Oid parentIndexId,
IndexInfo *indexInfo,
* ----------------------------------------------------------------
*/
static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
{
Relation pg_attribute;
CatalogIndexState indstate;
for (i = 0; i < numatts; i++)
{
Form_pg_attribute attr = TupleDescAttr(indexTupDesc, i);
+ Datum attoptions = attopts ? attopts[i] : (Datum) 0;
Assert(attr->attnum == i + 1);
- InsertPgAttributeTuple(pg_attribute, attr, indstate);
+ InsertPgAttributeTuple(pg_attribute, attr, attoptions, indstate);
}
CatalogCloseIndexes(indstate);
else
predDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
/*
* append ATTRIBUTE tuples for the index
*/
- AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+ AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs,
+ indexInfo->ii_OpclassOptions);
/* ----------------
* update pg_index
indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
+ /* Validate opclass-specific options */
+ if (indexInfo->ii_OpclassOptions)
+ for (i = 0; i < indexInfo->ii_NumIndexKeyAttrs; i++)
+ (void) index_opclass_options(indexRelation, i + 1,
+ indexInfo->ii_OpclassOptions[i],
+ true);
+
/*
* If this is bootstrap (initdb) time, then we don't actually fill in the
* index yet. We'll be creating more indexes and classes later, so we
&ii->ii_ExclusionStrats);
}
+ ii->ii_OpclassOptions = RelationGetIndexRawAttOptions(index);
+
return ii;
}
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL;
indexInfo->ii_Unique = true;
indexInfo->ii_ReadyForInserts = true;
indexInfo->ii_Concurrent = false;
static bool ReindexRelationConcurrently(Oid relationOid, int options);
static void ReindexPartitionedIndex(Relation parentIdx);
static void update_relispartition(Oid relationId, bool newval);
+static bool CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts);
/*
* callback argument type for RangeVarCallbackForReindexIndex()
}
}
+ /* Any change in opclass options break compatibility. */
+ if (ret)
+ {
+ Datum *opclassOptions = RelationGetIndexRawAttOptions(irel);
+
+ ret = CompareOpclassOptions(opclassOptions,
+ indexInfo->ii_OpclassOptions, old_natts);
+
+ if (opclassOptions)
+ pfree(opclassOptions);
+ }
+
/* Any change in exclusion operator selections breaks compatibility. */
if (ret && indexInfo->ii_ExclusionOps != NULL)
{
return ret;
}
+/*
+ * CompareOpclassOptions
+ *
+ * Compare per-column opclass options which are represented by arrays of text[]
+ * datums. Both elements of arrays and array themselves can be NULL.
+ */
+static bool
+CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts)
+{
+ int i;
+
+ if (!opts1 && !opts2)
+ return true;
+
+ for (i = 0; i < natts; i++)
+ {
+ Datum opt1 = opts1 ? opts1[i] : (Datum) 0;
+ Datum opt2 = opts2 ? opts2[i] : (Datum) 0;
+
+ if (opt1 == (Datum) 0)
+ {
+ if (opt2 == (Datum) 0)
+ continue;
+ else
+ return false;
+ }
+ else if (opt2 == (Datum) 0)
+ return false;
+
+ /* Compare non-NULL text[] datums. */
+ if (!DatumGetBool(DirectFunctionCall2(array_eq, opt1, opt2)))
+ return false;
+ }
+
+ return true;
+}
/*
* WaitForOlderSnapshots
/*
* Compute per-index-column information, including indexed column numbers
- * or index expressions, opclasses, and indoptions. Note, all output vectors
+ * or index expressions, opclasses and their options. Note, all output vectors
* should be allocated for all columns, including "including" ones.
*/
static void
accessMethodName)));
}
+ /* Set up the per-column opclass options (attoptions field). */
+ if (attribute->opclassopts)
+ {
+ Assert(attn < nkeycols);
+
+ if (!indexInfo->ii_OpclassOptions)
+ indexInfo->ii_OpclassOptions =
+ palloc0(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+
+ indexInfo->ii_OpclassOptions[attn] =
+ transformRelOptions((Datum) 0, attribute->opclassopts,
+ NULL, NULL, false, false);
+ }
+
attn++;
}
}
static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt,
Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber,
- List *items);
+ int opclassOptsProcNumber, List *items);
static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt,
Oid amoid, Oid opfamilyoid,
int maxOpNumber, int maxProcNumber,
List *items);
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
-static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
+static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
+ int opclassOptsProcNum);
static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
static void storeOperators(List *opfamilyname, Oid amoid,
Oid opfamilyoid, Oid opclassoid,
opfamilyoid, /* oid of containing opfamily */
opclassoid; /* oid of opclass we create */
int maxOpNumber, /* amstrategies value */
+ optsProcNumber, /* amoptsprocnum value */
maxProcNumber; /* amsupport value */
bool amstorage; /* amstorage flag */
List *operators; /* OpFamilyMember list for operators */
if (maxOpNumber <= 0)
maxOpNumber = SHRT_MAX;
maxProcNumber = amroutine->amsupport;
+ optsProcNumber = amroutine->amoptsprocnum;
amstorage = amroutine->amstorage;
/* XXX Should we make any privilege check against the AM? */
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
get_func_name(funcOid));
#endif
-
/* Save the info */
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
member->object = funcOid;
processTypesSpec(item->class_args,
&member->lefttype, &member->righttype);
- assignProcTypes(member, amoid, typeoid);
+ assignProcTypes(member, amoid, typeoid, optsProcNumber);
addFamilyMember(&procedures, member, true);
break;
case OPCLASS_ITEM_STORAGETYPE:
Oid amoid, /* our AM's oid */
opfamilyoid; /* oid of opfamily */
int maxOpNumber, /* amstrategies value */
+ optsProcNumber, /* amopclassopts value */
maxProcNumber; /* amsupport value */
HeapTuple tup;
Form_pg_am amform;
if (maxOpNumber <= 0)
maxOpNumber = SHRT_MAX;
maxProcNumber = amroutine->amsupport;
+ optsProcNumber = amroutine->amoptsprocnum;
/* XXX Should we make any privilege check against the AM? */
maxOpNumber, maxProcNumber, stmt->items);
else
AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
- maxOpNumber, maxProcNumber, stmt->items);
+ maxOpNumber, maxProcNumber, optsProcNumber,
+ stmt->items);
return opfamilyoid;
}
*/
static void
AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
- int maxOpNumber, int maxProcNumber, List *items)
+ int maxOpNumber, int maxProcNumber, int optsProcNumber,
+ List *items)
{
List *operators; /* OpFamilyMember list for operators */
List *procedures; /* OpFamilyMember list for support procs */
processTypesSpec(item->class_args,
&member->lefttype, &member->righttype);
- assignProcTypes(member, amoid, InvalidOid);
+ assignProcTypes(member, amoid, InvalidOid, optsProcNumber);
addFamilyMember(&procedures, member, true);
break;
case OPCLASS_ITEM_STORAGETYPE:
* and do any validity checking we can manage.
*/
static void
-assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
+ int opclassOptsProcNum)
{
HeapTuple proctup;
Form_pg_proc procform;
elog(ERROR, "cache lookup failed for function %u", member->object);
procform = (Form_pg_proc) GETSTRUCT(proctup);
+ /* Check the signature of the opclass options parsing function */
+ if (member->number == opclassOptsProcNum)
+ {
+ if (OidIsValid(typeoid))
+ {
+ if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) ||
+ (OidIsValid(member->righttype) && member->righttype != typeoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("associated data types for opclass options "
+ "parsing functions must match opclass input type")));
+ }
+ else
+ {
+ if (member->lefttype != member->righttype)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("left and right associated data types for "
+ "opclass options parsing functions must match")));
+ }
+
+ if (procform->prorettype != VOIDOID ||
+ procform->pronargs != 1 ||
+ procform->proargtypes.values[0] != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("invalid opclass options parsing function"),
+ errhint("opclass options parsing function must have signature '%s'",
+ "(internal) RETURNS void")));
+ }
/*
* btree comparison procs must be 2-arg procs returning int4. btree
* sortsupport procs must take internal and return void. btree in_range
* returning int4, while proc 2 must be a 2-arg proc returning int8.
* Otherwise we don't know.
*/
- if (amoid == BTREE_AM_OID)
+ else if (amoid == BTREE_AM_OID)
{
if (member->number == BTORDER_PROC)
{
ReleaseSysCache(typeTuple);
- InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+ InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
table_close(attrdesc, RowExclusiveLock);
COPY_STRING_FIELD(indexcolname);
COPY_NODE_FIELD(collation);
COPY_NODE_FIELD(opclass);
+ COPY_NODE_FIELD(opclassopts);
COPY_SCALAR_FIELD(ordering);
COPY_SCALAR_FIELD(nulls_ordering);
COMPARE_STRING_FIELD(indexcolname);
COMPARE_NODE_FIELD(collation);
COMPARE_NODE_FIELD(opclass);
+ COMPARE_NODE_FIELD(opclassopts);
COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering);
n->ii_ExclusionProcs = NULL;
n->ii_ExclusionStrats = NULL;
+ /* opclass options */
+ n->ii_OpclassOptions = NULL;
+
/* speculative inserts */
n->ii_UniqueOps = NULL;
n->ii_UniqueProcs = NULL;
WRITE_STRING_FIELD(indexcolname);
WRITE_NODE_FIELD(collation);
WRITE_NODE_FIELD(opclass);
+ WRITE_NODE_FIELD(opclassopts);
WRITE_ENUM_FIELD(ordering, SortByDir);
WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
}
info->amcostestimate = amroutine->amcostestimate;
Assert(info->amcostestimate != NULL);
+ /* Fetch index opclass options */
+ info->opclassoptions = RelationGetIndexAttOptions(indexRelation, true);
+
/*
* Fetch the ordering information for the index, if any.
*/
%type <alias> alias_clause opt_alias_clause
%type <list> func_alias_clause
%type <sortby> sortby
-%type <ielem> index_elem
+%type <ielem> index_elem index_elem_options
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
+
+index_elem_options:
+ opt_collate opt_class opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = NIL;
+ $$->ordering = $3;
+ $$->nulls_ordering = $4;
+ }
+ | opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ ;
+
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
{
- $$ = makeNode(IndexElem);
+ $$ = $2;
$$->name = $1;
- $$->expr = NULL;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $2;
$$->expr = $1;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $4;
$$->expr = $2;
- $$->indexcolname = NULL;
- $$->collation = $4;
- $$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
}
;
/* Add the operator class name, if non-default */
iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
+ iparam->opclassopts =
+ untransformRelOptions(get_attoptions(source_relid, keyno + 1));
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
* constraint; and there's also the dump/reload problem
* mentioned above.
*/
+ Datum attoptions =
+ get_attoptions(RelationGetRelid(index_rel), i + 1);
+
defopclass = GetDefaultOpClass(attform->atttypid,
index_rel->rd_rel->relam);
if (indclass->values[i] != defopclass ||
attform->attcollation != index_rel->rd_indcollation[i] ||
+ attoptions != (Datum) 0 ||
index_rel->rd_indoption[i] != 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
iparam->indexcolname = NULL;
iparam->collation = NIL;
iparam->opclass = NIL;
+ iparam->opclassopts = NIL;
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
index->indexParams = lappend(index->indexParams, iparam);
iparam->indexcolname = NULL;
iparam->collation = NIL;
iparam->opclass = NIL;
+ iparam->opclassopts = NIL;
index->indexIncludingParams = lappend(index->indexIncludingParams, iparam);
}
static char *generate_qualified_type_name(Oid typid);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
{
int16 opt = indoption->values[keyno];
Oid indcoll = indcollation->values[keyno];
+ Datum attoptions = get_attoptions(indexrelid, keyno + 1);
+ bool has_options = attoptions != (Datum) 0;
/* Add collation, if not default for column */
if (OidIsValid(indcoll) && indcoll != keycolcollation)
generate_collation_name((indcoll)));
/* Add the operator class name, if not default */
- get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+ get_opclass_name(indclass->values[keyno],
+ has_options ? InvalidOid : keycoltype, &buf);
+
+ if (has_options)
+ {
+ appendStringInfoString(&buf, " (");
+ get_reloptions(&buf, attoptions);
+ appendStringInfoChar(&buf, ')');
+ }
/* Add options if relevant */
if (amroutine->amcanorder)
ReleaseSysCache(ht_opc);
}
+/*
+ * generate_opclass_name
+ * Compute the name to display for a opclass specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+char *
+generate_opclass_name(Oid opclass)
+{
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+ get_opclass_name(opclass, InvalidOid, &buf);
+
+ return &buf.data[1]; /* get_opclass_name() prepends space */
+}
+
/*
* processIndirection - take care of array and subfield assignment
*
return result;
}
+/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+ Datum *options;
+ int noptions;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(reloptions),
+ TEXTOID, -1, false, TYPALIGN_INT,
+ &options, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *option = TextDatumGetCString(options[i]);
+ char *name;
+ char *separator;
+ char *value;
+
+ /*
+ * Each array element should have the form name=value. If the "="
+ * is missing for some reason, treat it like an empty value.
+ */
+ name = option;
+ separator = strchr(option, '=');
+ if (separator)
+ {
+ *separator = '\0';
+ value = separator + 1;
+ }
+ else
+ value = "";
+
+ if (i > 0)
+ appendStringInfoString(buf, ", ");
+ appendStringInfo(buf, "%s=", quote_identifier(name));
+
+ /*
+ * In general we need to quote the value; but to avoid unnecessary
+ * clutter, do not quote if it is an identifier that would not
+ * need quoting. (We could also allow numbers, but that is a bit
+ * trickier than it looks --- for example, are leading zeroes
+ * significant? We don't want to assume very much here about what
+ * custom reloptions might mean.)
+ */
+ if (quote_identifier(value) == value)
+ appendStringInfoString(buf, value);
+ else
+ simple_quote_literal(buf, value);
+
+ pfree(option);
+ }
+}
+
/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
if (!isnull)
{
StringInfoData buf;
- Datum *options;
- int noptions;
- int i;
initStringInfo(&buf);
-
- deconstruct_array(DatumGetArrayTypeP(reloptions),
- TEXTOID, -1, false, TYPALIGN_INT,
- &options, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *option = TextDatumGetCString(options[i]);
- char *name;
- char *separator;
- char *value;
-
- /*
- * Each array element should have the form name=value. If the "="
- * is missing for some reason, treat it like an empty value.
- */
- name = option;
- separator = strchr(option, '=');
- if (separator)
- {
- *separator = '\0';
- value = separator + 1;
- }
- else
- value = "";
-
- if (i > 0)
- appendStringInfoString(&buf, ", ");
- appendStringInfo(&buf, "%s=", quote_identifier(name));
-
- /*
- * In general we need to quote the value; but to avoid unnecessary
- * clutter, do not quote if it is an identifier that would not
- * need quoting. (We could also allow numbers, but that is a bit
- * trickier than it looks --- for example, are leading zeroes
- * significant? We don't want to assume very much here about what
- * custom reloptions might mean.)
- */
- if (quote_identifier(value) == value)
- appendStringInfoString(&buf, value);
- else
- simple_quote_literal(&buf, value);
-
- pfree(option);
- }
+ get_reloptions(&buf, reloptions);
result = buf.data;
}
Oid clause_op, Datum query,
GinQualCounts *counts)
{
+ FmgrInfo flinfo;
Oid extractProcOid;
Oid collation;
int strategy_op;
else
collation = DEFAULT_COLLATION_OID;
- OidFunctionCall7Coll(extractProcOid,
- collation,
- query,
- PointerGetDatum(&nentries),
- UInt16GetDatum(strategy_op),
- PointerGetDatum(&partial_matches),
- PointerGetDatum(&extra_data),
- PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode));
+ fmgr_info(extractProcOid, &flinfo);
+
+ set_fn_opclass_options(&flinfo, index->opclassoptions[indexcol]);
+
+ FunctionCall7Coll(&flinfo,
+ collation,
+ query,
+ PointerGetDatum(&nentries),
+ UInt16GetDatum(strategy_op),
+ PointerGetDatum(&partial_matches),
+ PointerGetDatum(&extra_data),
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(&searchMode));
if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
{
#include "access/gist.h"
#include "access/heaptoast.h"
+#include "access/reloptions.h"
#include "lib/qunique.h"
#include "port/pg_bitutils.h"
#include "tsearch/ts_utils.h"
#include "utils/pg_crc.h"
-#define SIGLENINT 31 /* >121 => key will toast, so it will not work
- * !!! */
+/* tsvector_ops opclass options */
+typedef struct
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length */
+} GistTsVectorOptions;
-#define SIGLEN ( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLEN_DEFAULT (31 * 4)
+#define SIGLEN_MAX GISTMaxIndexKeySize
+#define GET_SIGLEN() (PG_HAS_OPCLASS_OPTIONS() ? \
+ ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen : \
+ SIGLEN_DEFAULT)
+
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
#define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
#define GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
#define ISALLTRUE(x) ( ((SignTSVector*)(x))->flag & ALLISTRUE )
#define GTHDRSIZE ( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
#define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
#define GETARR(x) ( (int32*)( (char*)(x)+GTHDRSIZE ) )
#define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
Datum
gtsvectorin(PG_FUNCTION_ARGS)
sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
else
{
- int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+ int siglen = GETSIGLEN(key);
+ int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
- sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+ sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
}
PG_FREE_IF_COPY(key, 0);
}
static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
int32 *ptr = GETARR(a);
- MemSet((void *) sign, 0, sizeof(BITVEC));
+ MemSet((void *) sign, 0, siglen);
for (k = 0; k < len; k++)
- HASH(sign, ptr[k]);
+ HASH(sign, ptr[k], siglen);
}
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+ int size = CALCGTSIZE(flag, len);
+ SignTSVector *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+ memcpy(GETSIGN(res), sign, len);
+
+ return res;
+}
+
+
Datum
gtsvector_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = GET_SIGLEN();
GISTENTRY *retval = entry;
if (entry->leafkey)
{ /* tsvector */
- SignTSVector *res;
TSVector val = DatumGetTSVector(entry->key);
+ SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
int32 len;
int32 *arr;
WordEntry *ptr = ARRPTR(val);
char *words = STRPTR(val);
- len = CALCGTSIZE(ARRKEY, val->size);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = ARRKEY;
arr = GETARR(res);
len = val->size;
while (len--)
/* make signature, if array is too long */
if (VARSIZE(res) > TOAST_INDEX_TARGET)
{
- SignTSVector *ressign;
+ SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
- len = CALCGTSIZE(SIGNKEY, 0);
- ressign = (SignTSVector *) palloc(len);
- SET_VARSIZE(ressign, len);
- ressign->flag = SIGNKEY;
- makesign(GETSIGN(ressign), res);
+ makesign(GETSIGN(ressign), res, siglen);
res = ressign;
}
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
SignTSVector *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
static bool
checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
{
+ void *key = (SignTSVector *) checkval;
+
/*
* we are not able to find a prefix in signature tree
*/
if (val->prefix)
return true;
- return GETBIT(checkval, HASHVAL(val->valcrc));
+ return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
}
Datum
/* since signature is lossy, cannot specify CALC_NOT here */
PG_RETURN_BOOL(TS_execute(GETQUERY(query),
- (void *) GETSIGN(key),
+ key,
TS_EXEC_PHRASE_NO_POS,
checkcondition_bit));
}
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
int32 *ptr = GETARR(add);
for (i = 0; i < ARRNELEM(add); i++)
- HASH(sbase, ptr[i]);
+ HASH(sbase, ptr[i], siglen);
}
return 0;
}
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- SignTSVector *result;
+ int siglen = GET_SIGLEN();
+ SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+ BITVECP base = GETSIGN(result);
+ int32 i;
+
+ memset(base, 0, siglen);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (SignTSVector *) palloc(len);
- *size = len;
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ int siglen = GET_SIGLEN();
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
BITVECP sa = GETSIGN(a),
sb = GETSIGN(b);
+ Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
*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(SignTSVector *a, SignTSVector *b)
{
+ int siglena = GETSIGLEN(a);
+ int siglenb = GETSIGLEN(b);
+
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
+
+ Assert(siglena == siglenb);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
}
Datum
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ int siglen = GET_SIGLEN();
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
if (ISARRKEY(newval))
{
- BITVEC sign;
+ BITVECP sign = palloc(siglen);
- makesign(sign, newval);
+ makesign(sign, newval, siglen);
if (ISALLTRUE(origval))
- *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+ {
+ int siglenbit = SIGLENBIT(siglen);
+
+ *penalty =
+ (float) (siglenbit - sizebitvec(sign, siglen)) /
+ (float) (siglenbit + 1);
+ }
else
- *penalty = hemdistsign(sign, orig);
+ *penalty = hemdistsign(sign, orig, siglen);
+
+ pfree(sign);
}
else
*penalty = hemdist(origval, newval);
typedef struct
{
bool allistrue;
- BITVEC sign;
+ BITVECP sign;
} CACHESIGN;
static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
{
item->allistrue = false;
if (ISARRKEY(key))
- makesign(item->sign, key);
+ makesign(item->sign, key, siglen);
else if (ISALLTRUE(key))
item->allistrue = true;
else
- memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC));
+ memcpy((void *) item->sign, (void *) GETSIGN(key), siglen);
}
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
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);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = GET_SIGLEN();
OffsetNumber k,
j;
SignTSVector *datum_l,
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
v->spl_right = (OffsetNumber *) palloc(nbytes);
cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
- fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+ cache_sign = palloc(siglen * (maxoff + 2));
+
+ for (j = 0; j < maxoff + 2; j++)
+ cache[j].sign = &cache_sign[siglen * j];
+
+ fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+ siglen);
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
{
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
if (k == FirstOffsetNumber)
- fillcache(&cache[j], GETENTRY(entryvec, j));
+ fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
- size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+ size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
if (size_waste > waste)
{
waste = size_waste;
}
/* form initial .. */
- if (cache[seed_1].allistrue)
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_l->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0));
- datum_l->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC));
- }
- if (cache[seed_2].allistrue)
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_r->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0));
- datum_r->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC));
- }
-
+ datum_l = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_1].sign);
+ datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_2].sign);
union_l = GETSIGN(datum_l);
union_r = GETSIGN(datum_r);
maxoff = OffsetNumberNext(maxoff);
- fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff));
+ fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen);
/* sort before ... */
costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
{
costvector[j - 1].pos = j;
- size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]));
- size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]));
+ size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
+ size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec((cache[j].allistrue) ?
- GETSIGN(datum_l) :
- GETSIGN(cache[j].sign));
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ?
+ GETSIGN(datum_l) :
+ GETSIGN(cache[j].sign),
+ siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec((cache[j].allistrue) ?
- GETSIGN(datum_r) :
- GETSIGN(cache[j].sign));
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ?
+ GETSIGN(datum_r) :
+ GETSIGN(cache[j].sign),
+ siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
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;
{
return gtsvector_consistent(fcinfo);
}
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+ local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
+
+ init_local_reloptions(relopts, sizeof(GistTsVectorOptions));
+ add_local_int_reloption(relopts, "siglen", "signature length",
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+ offsetof(GistTsVectorOptions, siglen));
+
+ PG_RETURN_VOID();
+}
ReleaseSysCache(tp);
}
+/*
+ * get_attoptions
+ *
+ * Given the relation id and the attribute number,
+ * return the attribute options text[] datum, if any.
+ */
+Datum
+get_attoptions(Oid relid, int16 attnum)
+{
+ HeapTuple tuple;
+ Datum attopts;
+ Datum result;
+ bool isnull;
+
+ tuple = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, relid);
+
+ attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+ &isnull);
+
+ if (isnull)
+ result = (Datum) 0;
+ else
+ result = datumCopy(attopts, false, -1); /* text[] */
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
/* ---------- PG_CAST CACHE ---------- */
/*
amsupport = relation->rd_indam->amsupport;
if (amsupport > 0)
{
- int nsupport = indnatts * amsupport;
+ int nsupport = indnatts * (amsupport + 1);
relation->rd_support = (RegProcedure *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(RegProcedure));
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
+ (void) RelationGetIndexAttOptions(relation, false);
+
/*
* expressions, predicate, exclusion caches will be filled later
*/
opFamily[attIndex] = opcentry->opcfamily;
opcInType[attIndex] = opcentry->opcintype;
if (maxSupportNumber > 0)
- memcpy(&indexSupport[attIndex * maxSupportNumber],
+ memcpy(&indexSupport[attIndex * (maxSupportNumber + 1)],
opcentry->supportProcs,
- maxSupportNumber * sizeof(RegProcedure));
+ (maxSupportNumber + 1) * sizeof(RegProcedure));
}
}
if (numSupport > 0)
opcentry->supportProcs = (RegProcedure *)
MemoryContextAllocZero(CacheMemoryContext,
- numSupport * sizeof(RegProcedure));
+ (numSupport + 1) * sizeof(RegProcedure));
else
opcentry->supportProcs = NULL;
}
{
Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(htup);
- if (amprocform->amprocnum <= 0 ||
+ if (amprocform->amprocnum < 0 ||
(StrategyNumber) amprocform->amprocnum > numSupport)
elog(ERROR, "invalid amproc number %d for opclass %u",
amprocform->amprocnum, operatorClassOid);
- opcentry->supportProcs[amprocform->amprocnum - 1] =
- amprocform->amproc;
+ opcentry->supportProcs[amprocform->amprocnum] = amprocform->amproc;
}
systable_endscan(scan);
ird->rd_refcnt = 1;
UnlockRelationOid(indexoid, AccessShareLock);
UnlockRelationOid(heapoid, AccessShareLock);
+
+ (void) RelationGetIndexAttOptions(ird, false);
}
/*
return pubactions;
}
+/*
+ * RelationGetIndexRawAttOptions -- get AM/opclass-specific options for the index
+ */
+Datum *
+RelationGetIndexRawAttOptions(Relation indexrel)
+{
+ Oid indexrelid = RelationGetRelid(indexrel);
+ int16 natts = RelationGetNumberOfAttributes(indexrel);
+ Datum *options = NULL;
+ int16 attnum;
+
+ for (attnum = 1; attnum <= natts; attnum++)
+ {
+ if (!OidIsValid(index_getprocid(indexrel, attnum,
+ indexrel->rd_indam->amoptsprocnum)))
+ continue;
+
+ if (!options)
+ options = palloc0(sizeof(Datum) * natts);
+
+ options[attnum - 1] = get_attoptions(indexrelid, attnum);
+ }
+
+ return options;
+}
+
+static bytea **
+CopyIndexAttOptions(bytea **srcopts, int natts)
+{
+ bytea **opts = palloc(sizeof(*opts) * natts);
+
+ for (int i = 0; i < natts; i++)
+ {
+ bytea *opt = srcopts[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+}
+
+/*
+ * RelationGetIndexAttOptions
+ * get AM/opclass-specific options for an index parsed into a binary form
+ */
+bytea **
+RelationGetIndexAttOptions(Relation relation, bool copy)
+{
+ MemoryContext oldcxt;
+ bytea **opts = relation->rd_opcoptions;
+ Oid relid = RelationGetRelid(relation);
+ int natts = RelationGetNumberOfAttributes(relation); /* XXX IndexRelationGetNumberOfKeyAttributes */
+ int i;
+
+ /* Try to copy cached options. */
+ if (opts)
+ return copy ? CopyIndexAttOptions(opts, natts) : opts;
+
+ /* Get and parse opclass options. */
+ opts = palloc0(sizeof(*opts) * natts);
+
+ for (i = 0; i < natts; i++)
+ {
+ if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId)
+ {
+ Datum attoptions = get_attoptions(relid, i + 1);
+
+ opts[i] = index_opclass_options(relation, i + 1, attoptions, false);
+
+ if (attoptions != (Datum) 0)
+ pfree(DatumGetPointer(attoptions));
+ }
+ }
+
+ /* Copy parsed options to the cache. */
+ oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
+ relation->rd_opcoptions = CopyIndexAttOptions(opts, natts);
+ MemoryContextSwitchTo(oldcxt);
+
+ if (copy)
+ return opts;
+
+ for (i = 0; i < natts; i++)
+ {
+ if (opts[i])
+ pfree(opts[i]);
+ }
+
+ pfree(opts);
+
+ return relation->rd_opcoptions;
+}
+
/*
* Routines to support ereport() reports of relation-related errors
*
rel->rd_indoption = indoption;
+ /* finally, read the vector of opcoptions values */
+ rel->rd_opcoptions = (bytea **)
+ MemoryContextAllocZero(indexcxt, sizeof(*rel->rd_opcoptions) * relform->relnatts);
+
+ for (i = 0; i < relform->relnatts; i++)
+ {
+ if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
+ goto read_failed;
+
+ if (len > 0)
+ {
+ rel->rd_opcoptions[i] = (bytea *) MemoryContextAlloc(indexcxt, len);
+ if (fread(rel->rd_opcoptions[i], 1, len, fp) != len)
+ goto read_failed;
+ }
+ }
+
/* set up zeroed fmgr-info vector */
- nsupport = relform->relnatts * rel->rd_indam->amsupport;
+ nsupport = relform->relnatts * (rel->rd_indam->amsupport + 1);
rel->rd_supportinfo = (FmgrInfo *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
}
Assert(rel->rd_supportinfo == NULL);
Assert(rel->rd_indoption == NULL);
Assert(rel->rd_indcollation == NULL);
+ Assert(rel->rd_opcoptions == NULL);
}
/*
/* next, write the vector of support procedure OIDs */
write_item(rel->rd_support,
- relform->relnatts * (rel->rd_indam->amsupport * sizeof(RegProcedure)),
+ relform->relnatts * ((rel->rd_indam->amsupport + 1) * sizeof(RegProcedure)),
fp);
/* next, write the vector of collation OIDs */
write_item(rel->rd_indoption,
relform->relnatts * sizeof(int16),
fp);
+
+ Assert(rel->rd_opcoptions);
+
+ /* finally, write the vector of opcoptions values */
+ for (i = 0; i < relform->relnatts; i++)
+ {
+ bytea *opt = rel->rd_opcoptions[i];
+
+ write_item(opt, opt ? VARSIZE(opt) : 0, fp);
+ }
}
}
#include "access/detoast.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
#include "executor/functions.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
+#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "pgstat.h"
#include "utils/acl.h"
return false;
}
+/*
+ * Set options to FmgrInfo of opclass support function.
+ *
+ * Opclass support functions are called outside of expressions. Thanks to that
+ * we can use fn_expr to store opclass options as bytea constant.
+ */
+void
+set_fn_opclass_options(FmgrInfo *flinfo, bytea *options)
+{
+ flinfo->fn_expr = (Node *) makeConst(BYTEAOID, -1, InvalidOid, -1,
+ PointerGetDatum(options),
+ options == NULL, false);
+}
+
+/*
+ * Check if options are defined for opclass support function.
+ */
+bool
+has_fn_opclass_options(FmgrInfo *flinfo)
+{
+ if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const))
+ {
+ Const *expr = (Const *) flinfo->fn_expr;
+
+ if (expr->consttype == BYTEAOID)
+ return !expr->constisnull;
+ }
+ return false;
+}
+
+/*
+ * Get options for opclass support function.
+ */
+bytea *
+get_fn_opclass_options(FmgrInfo *flinfo)
+{
+ if (flinfo && flinfo->fn_expr && IsA(flinfo->fn_expr, Const))
+ {
+ Const *expr = (Const *) flinfo->fn_expr;
+
+ if (expr->consttype == BYTEAOID)
+ return expr->constisnull ? NULL : DatumGetByteaP(expr->constvalue);
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("opclass options info is absent in function call context")));
+
+ return NULL;
+}
+
/*-------------------------------------------------------------------------
* Support routines for procedural language implementations
*-------------------------------------------------------------------------
uint16 amstrategies;
/* total number of support functions that this AM uses */
uint16 amsupport;
+ /* opclass options support function number or 0 */
+ uint16 amoptsprocnum;
/* does AM support ORDER BY indexed column's value? */
bool amcanorder;
/* does AM support ORDER BY result of an operator on indexed column? */
extern List *identify_opfamily_groups(CatCList *oprlist, CatCList *proclist);
extern bool check_amproc_signature(Oid funcid, Oid restype, bool exact,
int minargs, int maxargs,...);
+extern bool check_amoptsproc_signature(Oid funcid);
extern bool check_amop_signature(Oid opno, Oid restype,
Oid lefttype, Oid righttype);
extern bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid);
#define BRIN_PROCNUM_CONSISTENT 3
#define BRIN_PROCNUM_UNION 4
#define BRIN_MANDATORY_NPROCS 4
+#define BRIN_PROCNUM_OPTIONS 5 /* optional */
/* procedure numbers up to 10 are reserved for BRIN future expansion */
#define BRIN_FIRST_OPTIONAL_PROCNUM 11
#define BRIN_LAST_OPTIONAL_PROCNUM 15
Oid *orderByTypes,
IndexOrderByDistance *distances,
bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+ Datum attoptions, bool validate);
+
/*
* index access method support routines (in genam.c)
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
#define GIN_TRICONSISTENT_PROC 6
-#define GINNProcs 6
+#define GIN_OPTIONS_PROC 7
+#define GINNProcs 7
/*
* searchMode settings for extractQueryFn.
#ifndef GIST_H
#define GIST_H
+#include "access/itup.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "access/xlogdefs.h"
#define GIST_EQUAL_PROC 7
#define GIST_DISTANCE_PROC 8
#define GIST_FETCH_PROC 9
-#define GISTNProcs 9
+#define GIST_OPTIONS_PROC 10
+#define GISTNProcs 10
/*
* Page opaque data in a GiST index page.
typedef GISTPageOpaqueData *GISTPageOpaque;
+/*
+ * Maximum possible sizes for GiST index tuple and index key. Calculation is
+ * based on assumption that GiST page should fit at least 4 tuples. In theory,
+ * GiST index can be functional when page can fit 3 tuples. But that seems
+ * rather inefficent, so we use a bit conservative estimate.
+ *
+ * The maximum size of index key is true for unicolumn index. Therefore, this
+ * estimation should be used to figure out which maximum size of GiST index key
+ * makes sense at all. For multicolumn indexes, user might be able to tune
+ * key size using opclass parameters.
+ */
+#define GISTMaxIndexTupleSize \
+ MAXALIGN_DOWN((BLCKSZ - SizeOfPageHeaderData - sizeof(GISTPageOpaqueData)) / \
+ 4 - sizeof(ItemIdData))
+
+#define GISTMaxIndexKeySize \
+ (GISTMaxIndexTupleSize - MAXALIGN(sizeof(IndexTupleData)))
+
/*
* The page ID is for the convenience of pg_filedump and similar utilities,
* which otherwise would have a hard time telling pages of different index
*/
#define HASHSTANDARD_PROC 1
#define HASHEXTENDED_PROC 2
-#define HASHNProcs 2
+#define HASHOPTIONS_PROC 3
+#define HASHNProcs 3
/* public routines */
#define BTSORTSUPPORT_PROC 2
#define BTINRANGE_PROC 3
#define BTEQUALIMAGE_PROC 4
-#define BTNProcs 4
+#define BTOPTIONS_PROC 5
+#define BTNProcs 5
/*
* We need to be able to tell the difference between read and write
/* kinds supported by reloptions */
typedef enum relopt_kind
{
+ RELOPT_KIND_LOCAL = 0,
RELOPT_KIND_HEAP = (1 << 0),
RELOPT_KIND_TOAST = (1 << 1),
RELOPT_KIND_BTREE = (1 << 2),
/* validation routines for strings */
typedef void (*validate_string_relopt) (const char *value);
+typedef Size (*fill_string_relopt) (const char *value, void *ptr);
+
+/* validation routine for the whole option set */
+typedef void (*relopts_validator) (void *parsed_options, relopt_value *vals, int nvals);
typedef struct relopt_string
{
int default_len;
bool default_isnull;
validate_string_relopt validate_cb;
+ fill_string_relopt fill_cb;
char *default_val;
} relopt_string;
int offset; /* offset of field in result struct */
} relopt_parse_elt;
+/* Local reloption definition */
+typedef struct local_relopt
+{
+ relopt_gen *option; /* option definition */
+ int offset; /* offset of parsed value in bytea structure */
+} local_relopt;
+
+/* Structure to hold local reloption data for build_local_reloptions() */
+typedef struct local_relopts
+{
+ List *options; /* list of local_relopt definitions */
+ List *validators; /* list of relopts_validator callbacks */
+ Size relopt_struct_size; /* size of parsed bytea structure */
+} local_relopts;
+
/*
* Utility macro to get a value for a string reloption once the options
* are parsed. This gets a pointer to the string value itself. "optstruct"
const char *default_val, validate_string_relopt validator,
LOCKMODE lockmode);
+extern void init_local_reloptions(local_relopts *opts, Size relopt_struct_size);
+extern void register_reloptions_validator(local_relopts *opts,
+ relopts_validator validator);
+extern void add_local_bool_reloption(local_relopts *opts, const char *name,
+ const char *desc, bool default_val,
+ int offset);
+extern void add_local_int_reloption(local_relopts *opts, const char *name,
+ const char *desc, int default_val,
+ int min_val, int max_val, int offset);
+extern void add_local_real_reloption(local_relopts *opts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val,
+ int offset);
+extern void add_local_enum_reloption(local_relopts *relopts,
+ const char *name, const char *desc,
+ relopt_enum_elt_def *members,
+ int default_val, const char *detailmsg,
+ int offset);
+extern void add_local_string_reloption(local_relopts *opts, const char *name,
+ const char *desc,
+ const char *default_val,
+ validate_string_relopt validator,
+ fill_string_relopt filler, int offset);
+
extern Datum transformRelOptions(Datum oldOptions, List *defList,
const char *namspace, char *validnsps[],
bool acceptOidsOff, bool isReset);
Size relopt_struct_size,
const relopt_parse_elt *relopt_elems,
int num_relopt_elems);
+extern void *build_local_reloptions(local_relopts *relopts, Datum options,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
#define SPGIST_INNER_CONSISTENT_PROC 4
#define SPGIST_LEAF_CONSISTENT_PROC 5
#define SPGIST_COMPRESS_PROC 6
+#define SPGIST_OPTIONS_PROC 7
#define SPGISTNRequiredProc 5
-#define SPGISTNProc 6
+#define SPGISTNProc 7
/*
* Argument structs for spg_config method
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202003281
+#define CATALOG_VERSION_NO 202003301
#endif
extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
+ Datum attoptions,
CatalogIndexState indstate);
extern void InsertPgClassTuple(Relation pg_class_desc,
amproc => 'gtsvector_picksplit' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+ amprocrighttype => 'tsvector', amprocnum => '10',
+ amproc => 'gtsvector_options' },
{ amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
amprocrighttype => 'tsquery', amprocnum => '1',
amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
proname => 'gtsvector_consistent', prorettype => 'bool',
proargtypes => 'internal gtsvector int4 oid internal',
prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3434', descr => 'GiST tsvector support',
+ proname => 'gtsvector_options', prorettype => 'void', proisstrict => 'f',
+ proargtypes => 'internal', prosrc => 'gtsvector_options' },
{ oid => '3656', descr => 'GIN tsvector support',
proname => 'gin_extract_tsvector', prorettype => 'internal',
#define PG_GETARG_BPCHAR_P(n) DatumGetBpCharP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARCHAR_P(n) DatumGetVarCharP(PG_GETARG_DATUM(n))
+/* To access options from opclass support functions use this: */
+#define PG_HAS_OPCLASS_OPTIONS() has_fn_opclass_options(fcinfo->flinfo)
+#define PG_GET_OPCLASS_OPTIONS() get_fn_opclass_options(fcinfo->flinfo)
+
/* To return a NULL do this: */
#define PG_RETURN_NULL() \
do { fcinfo->isnull = true; return (Datum) 0; } while (0)
extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
extern bool get_fn_expr_variadic(FmgrInfo *flinfo);
+extern bytea *get_fn_opclass_options(FmgrInfo *flinfo);
+extern bool has_fn_opclass_options(FmgrInfo *flinfo);
+extern void set_fn_opclass_options(FmgrInfo *flinfo, bytea *options);
extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
/*
* UniqueProcs
* UniqueStrats
* Unique is it a unique index?
+ * OpclassOptions opclass-specific options, or NULL if none
* ReadyForInserts is it valid for inserts?
* Concurrent are we doing a concurrent index build?
* BrokenHotChain did we detect any broken HOT chains?
Oid *ii_UniqueOps; /* array with one entry per column */
Oid *ii_UniqueProcs; /* array with one entry per column */
uint16 *ii_UniqueStrats; /* array with one entry per column */
+ Datum *ii_OpclassOptions; /* array with one entry per column */
bool ii_Unique;
bool ii_ReadyForInserts;
bool ii_Concurrent;
char *indexcolname; /* name for index column; NULL = default */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
+ List *opclassopts; /* opclass-specific options, or NIL */
SortByDir ordering; /* ASC/DESC/default */
SortByNulls nulls_ordering; /* FIRST/LAST/default */
} IndexElem;
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
+ bytea **opclassoptions; /* opclass-specific options for columns */
bool *canreturn; /* which index cols can be returned in an
* index-only scan? */
Oid relam; /* OID of the access method (in pg_am) */
extern Oid get_atttype(Oid relid, AttrNumber attnum);
extern void get_atttypetypmodcoll(Oid relid, AttrNumber attnum,
Oid *typid, int32 *typmod, Oid *collid);
+extern Datum get_attoptions(Oid relid, int16 attnum);
extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
extern char *get_collation_name(Oid colloid);
extern bool get_collation_isdeterministic(Oid colloid);
Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
Oid *rd_indcollation; /* OIDs of index collations */
+ bytea **rd_opcoptions; /* parsed opclass-specific options */
/*
* rd_amcache is available for index and table AMs to cache private data
#ifndef RELCACHE_H
#define RELCACHE_H
+#include "postgres.h"
#include "access/tupdesc.h"
#include "nodes/bitmapset.h"
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetDummyIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern Datum *RelationGetIndexRawAttOptions(Relation relation);
+extern bytea **RelationGetIndexAttOptions(Relation relation, bool copy);
typedef enum IndexAttrBitmapKind
{
extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
+extern char *generate_opclass_name(Oid opclass);
extern char *get_range_partbound_string(List *bound_datums);
#endif /* RULEUTILS_H */
ERROR: invalid operator number 0, must be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
ERROR: operator argument types must be specified in ALTER OPERATOR FAMILY
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR: invalid function number 0, must be between 1 and 4
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
+ERROR: invalid function number 0, must be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
-ERROR: invalid function number 6, must be between 1 and 4
+ERROR: invalid function number 6, must be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
ERROR: STORAGE cannot be specified in ALTER OPERATOR FAMILY
DROP OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
ERROR: function 2(integer,integer) does not exist in operator family "alt_opf18"
DROP OPERATOR FAMILY alt_opf18 USING btree;
+-- Should fail. Invalid opclass options function (#5) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 test_opclass_options_func(internal, text[], bool);
+ERROR: function test_opclass_options_func(internal, text[], boolean) does not exist
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) btint42cmp(int4, int2);
+ERROR: invalid opclass options parsing function
+HINT: opclass options parsing function must have signature '(internal) RETURNS void'
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4, int2) btint42cmp(int4, int2);
+ERROR: left and right associated data types for opclass options parsing functions must match
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4);
+DROP OPERATOR FAMILY alt_opf19 USING btree;
--
-- Statistics
--
-- The vacuum above should've turned the leaf page into a fast root. We just
-- need to insert some rows to cause the fast root page to split.
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: operator class int4_ops has no options
SELECT p1.amprocfamily, p1.amprocnum
FROM pg_amproc as p1
WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
- OR p1.amprocnum < 1 OR p1.amproc = 0;
+ OR p1.amprocnum < 0 OR p1.amproc = 0;
amprocfamily | amprocnum
--------------+-----------
(0 rows)
508
(1 row)
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=2048));
+ERROR: value 2048 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "2024".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR: parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a)
+ "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx2
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a tsvector_ops(siglen=484));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
LANGUAGE C STRICT;
+CREATE FUNCTION test_opclass_options_func(internal)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+ LANGUAGE C;
+
-- Things that shouldn't work:
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
RETURNS internal
AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
LANGUAGE C STRICT;
+CREATE FUNCTION test_opclass_options_func(internal)
+ RETURNS void
+ AS '@libdir@/regress@DLSUFFIX@', 'test_opclass_options_func'
+ LANGUAGE C;
-- Things that shouldn't work:
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
AS 'SELECT ''not an integer'';';
PG_RETURN_POINTER(ret);
}
+
+PG_FUNCTION_INFO_V1(test_opclass_options_func);
+Datum
+test_opclass_options_func(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_NULL();
+}
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 6 < (int4, int2); -- operator number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 0 < (int4, int2); -- operator number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD OPERATOR 1 < ; -- operator without argument types
-ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- function number should be between 1 and 5
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 6 btint42cmp(int4, int2); -- function number should be between 1 and 5
ALTER OPERATOR FAMILY alt_opf4 USING btree ADD STORAGE invalid_storage; -- Ensure STORAGE is not a part of ALTER OPERATOR FAMILY
DROP OPERATOR FAMILY alt_opf4 USING btree;
ALTER OPERATOR FAMILY alt_opf18 USING btree DROP FUNCTION 2 (int4, int4);
DROP OPERATOR FAMILY alt_opf18 USING btree;
+-- Should fail. Invalid opclass options function (#5) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 test_opclass_options_func(internal, text[], bool);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4, int2) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4);
+DROP OPERATOR FAMILY alt_opf19 USING btree;
+
--
-- Statistics
--
-- The vacuum above should've turned the leaf page into a fast root. We just
-- need to insert some rows to cause the fast root page to split.
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
SELECT p1.amprocfamily, p1.amprocnum
FROM pg_amproc as p1
WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
- OR p1.amprocnum < 1 OR p1.amproc = 0;
+ OR p1.amprocnum < 0 OR p1.amproc = 0;
-- Support routines that are primary members of opfamilies must be immutable
-- (else it suggests that the index ordering isn't fixed). But cross-type
SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=2048));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a tsvector_ops(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
GISTENTRY
GISTInsertStack
GISTInsertState
+GISTIntArrayBigOptions
+GISTIntArrayOptions
GISTNodeBuffer
GISTNodeBufferPage
GISTPageOpaque
GistBufferingMode
GistBulkDeleteResult
GistEntryVector
+GistHstoreOptions
GistInetKey
GistNSN
GistOptBufferingMode
GistSplitUnion
GistSplitVector
+GistTsVectorOptions
GistVacState
GlobalTransaction
GrantRoleStmt
LogicalRewriteMappingData
LogicalTape
LogicalTapeSet
+LtreeGistOptions
+LtreeSignature
MAGIC
MBuf
MCVItem
TrgmBound
TrgmColor
TrgmColorInfo
+TrgmGistOptions
TrgmNFA
TrgmPackArcInfo
TrgmPackedArc
file_entry_t
file_type_t
filemap_t
+fill_string_relopt
finalize_primnode_context
find_dependent_phvs_context
find_expr_references_context
line_t
lineno_t
list_qsort_comparator
+local_relopt
+local_relopts
locale_t
locate_agg_of_level_context
locate_var_of_level_context
relopt_string
relopt_type
relopt_value
+relopts_validator
remoteConn
remoteConnHashEnt
remoteDep