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