[PATCH] Opclass parameters
Hi hackers.
I would like to present patch set implementing opclass parameters.
This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdf
A analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.
Opclass parameters can give user ability to:
* Define the values of the constants that are hardcoded now in the opclasses
depending on the indexed data.
* Specify what to index for non-atomic data types (arrays, json[b], tsvector).
Partial index can only filter whole rows.
* Specify what indexing algorithm to use depending on the indexed data.
Description of patches:
1. Infrastructure for opclass parameters.
SQL grammar is changed only for CREATE INDEX statement: parenthesized parameters
in reloptions format are added after column's opclass name. Default opclass can
be specified with DEFAULT keyword:
CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);
Example for contrib/intarray:
CREATE INDEX ON arrays USING gist (
arr gist__intbig_ops (siglen = 32),
arr DEFAULT (numranges = 100)
);
\d arrays
Table "public.arrays"
Column | Type | Collation | Nullable | Default
--------+-----------+-----------+----------+---------
arr | integer[] | | |
Indexes:
"arrays_arr_arr1_idx" gist (arr gist__intbig_ops (siglen='32'), arr gist__int_ops (numranges='100'))
I decided to store parameters in text[] column pg_index.indoptions near to
existing columns like indkey, indcollation, indclass, indoption. I-th element of
indoptions[] is a text array of parameters of i-th index column serialized into
a string. Each parameter is stored as 'name=value' text string like ordinal
reloptions. There is another way to store opclass parameters: store them in
the existing column pg_attribute.attoptions (as it was done by Nikolay Shaplov)
and there will be no need to serialize reloptions to a text array element.
Example query showing how parameters are stored:
SELECT ARRAY(
SELECT (pg_identify_object('pg_opclass'::regclass, opcid, 0)).name
FROM unnest(indclass::int[]) opcid
) indclass, indoptions
FROM pg_index
WHERE indoptions IS NOT NULL;
indclass | indoptions
----------------------------------+------------------------------------
{gist__intbig_ops,gist__int_ops} | {"{siglen=32}","{numranges=100}"}
{jsonb_path_ops} | {"{projection=$.tags[*].term}"}
(2 rows)
Each access method supporting opclass parameters specifies amopclassoptions
routine for transformation of text[] parameters datum into a binary bytea
structure which will be cached in RelationData and IndexOptInfo structures:
typedef bytea *(*amopclassoptions_function) (
Relation index, AttrNumber colnum, Datum indoptions, bool validate
);
If access method wants simply to delegate parameters processing to one of
column opclass's support functions, then it can use
index_opclass_options_generic() subroutine in its amopclassoptions
implementation:
bytea *index_opclass_options_generic(
Relation relation, AttrNumber attnum, uint16 procnum,
Datum indoptions, bool validate
);
This support functions must have the following signature:
internal (options internal, validate bool).
Opclass parameters are passed as a text[] reloptions datum, returned pointer to
a bytea structure with parsed parameter values.
Opclass can use new functions parseLocalRelOptions(),
parseAndFillLocalRelOptions() for reloptions parsing. This functions differ
from the standard parseRelOptions() in that a local array of reloptions
descriptions is passed here, not a global relopt_kind. But it seems that
reloptions processing still needs deeper refactoring like the one already done
by Nikolay Shaplov (/messages/by-id/2146419.veIEZdk4E4@x200m
2. Opclass parameters support in GiST indices.
Parametrized GiST opclass specifies optional 10th (GIST_OPCLASSOPT_PROC)
support function with the following signature:
internal (options internal, validate bool)
Returned parsed bytea pointer with parameters will be passed to all support
functions in the last argument.
3. Opclass parameters support in GIN indices.
Everything is the same as for GiST, except for the optional support
function number which is 7 (GIN_OPCLASSOPTIONS_PROC) here.
4. Opclass parameters for GiST tsvector_ops
5. Opclass parameters for contrib/intarray
6. Opclass parameters for contrib/ltree
7. Opclass parameters for contrib/pg_trgm
8. Opclass parameters for contrib/hstore
This 5 patches for GiST opclasses are very similar: added optional 'siglen'
parameter for specifying signature length. Default signature length is left
equal to the hardcoded value that was here before. Also added 'numranges'
parameter for gist__int_ops.
We also have two more complex unfinished patches for GIN opclasses which
should be posted in separate threads:
* tsvector_ops: added parameter 'weights' for specification of indexed
lexeme's weight groups. This parameter can reduce index size and its
build/update time and can also eliminate recheck. By default, all weights
are indexed within the same group.
* jsonb_ops: added jsonpath parameter 'projection' for specification of
indexed paths in jsonb (this patch depends on SQL/JSON jsonpath patch).
Analogically to tsvector_ops, this parameter can reduce index size and its
build/update time, but can not eliminate recheck.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0005-opclass-parameters-contrib_intarray-v01.patchtext/x-patch; name=0005-opclass-parameters-contrib_intarray-v01.patchDownload
diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile
index 2505294..d99d977 100644
--- a/contrib/intarray/Makefile
+++ b/contrib/intarray/Makefile
@@ -5,8 +5,8 @@ OBJS = _int_bool.o _int_gist.o _int_op.o _int_tool.o \
_intbig_gist.o _int_gin.o _int_selfuncs.o $(WIN32RES)
EXTENSION = intarray
-DATA = intarray--1.2.sql intarray--1.1--1.2.sql intarray--1.0--1.1.sql \
- intarray--unpackaged--1.0.sql
+DATA = intarray--1.3.sql intarray--1.2-1.3.sql intarray--1.1--1.2.sql \
+ intarray--1.0--1.1.sql intarray--unpackaged--1.0.sql
PGFILEDESC = "intarray - functions and operators for arrays of integers"
REGRESS = _int
diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index b689eb7..87d21e9 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -8,7 +8,15 @@
#include "utils/memutils.h"
/* number ranges for compression */
-#define MAXNUMRANGE 100
+#define G_INT_NUMRANGES_DEFAULT 100
+#define G_INT_NUMRANGES_MAX 1000
+
+/* gist_int_ops opclass options */
+typedef struct IntArrayOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int num_ranges; /* number of ranges */
+} IntArrayOptions;
/* useful macros for accessing int4 arrays */
#define ARRPTR(x) ( (int32 *) ARR_DATA_PTR(x) )
@@ -47,15 +55,14 @@
/* bigint defines */
-#define SIGLENINT 63 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
+#define SIGLEN_DEFAULT (63 * 4)
+#define SIGLEN_MAX (122 * 4) /* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
/* beware of multiple evaluation of arguments to these macros! */
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
@@ -63,8 +70,15 @@ typedef char *BITVECP;
#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
+
+/* gist_intbig_ops opclass options */
+typedef struct IntArrayBigOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} IntArrayBigOptions;
/*
* type of index key
@@ -81,7 +95,7 @@ typedef struct
#define ISALLTRUE(x) ( ((GISTTYPE*)x)->flag & ALLISTRUE )
#define GTHDRSIZE (VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
@@ -109,7 +123,7 @@ bool inner_int_contains(ArrayType *a, ArrayType *b);
ArrayType *inner_int_union(ArrayType *a, ArrayType *b);
ArrayType *inner_int_inter(ArrayType *a, ArrayType *b);
void rt__int_size(ArrayType *a, float *size);
-void gensign(BITVEC sign, int *a, int len);
+void gensign(BITVECP sign, int *a, int len, int siglen);
/*****************************************************************************
@@ -155,7 +169,7 @@ typedef struct QUERYTYPE
#define PG_GETARG_QUERYTYPE_P(n) DatumGetQueryTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_QUERYTYPE_P_COPY(n) DatumGetQueryTypePCopy(PG_GETARG_DATUM(n))
-bool signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot);
+bool signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot);
bool execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
bool gin_bool_consistent(QUERYTYPE *query, bool *check);
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index 91e2a80..4e7d55a 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -233,7 +233,7 @@ typedef struct
* is there value 'val' in (sorted) array or not ?
*/
static bool
-checkcondition_arr(void *checkval, ITEM *item)
+checkcondition_arr(void *checkval, ITEM *item, void *options)
{
int32 *StopLow = ((CHKVAL *) checkval)->arrb;
int32 *StopHigh = ((CHKVAL *) checkval)->arre;
@@ -255,42 +255,42 @@ checkcondition_arr(void *checkval, ITEM *item)
}
static bool
-checkcondition_bit(void *checkval, ITEM *item)
+checkcondition_bit(void *checkval, ITEM *item, void *siglen)
{
- return GETBIT(checkval, HASHVAL(item->val));
+ return GETBIT(checkval, HASHVAL(item->val, (int)(intptr_t) siglen));
}
/*
* evaluate boolean expression, using chkcond() to test the primitive cases
*/
static bool
-execute(ITEM *curitem, void *checkval, bool calcnot,
- bool (*chkcond) (void *checkval, ITEM *item))
+execute(ITEM *curitem, void *checkval, void *options, bool calcnot,
+ bool (*chkcond) (void *checkval, ITEM *item, void *options))
{
/* since this function recurses, it could be driven to stack overflow */
check_stack_depth();
if (curitem->type == VAL)
- return (*chkcond) (checkval, curitem);
+ return (*chkcond) (checkval, curitem, options);
else if (curitem->val == (int32) '!')
{
return calcnot ?
- ((execute(curitem - 1, checkval, calcnot, chkcond)) ? false : true)
+ ((execute(curitem - 1, checkval, options, calcnot, chkcond)) ? false : true)
: true;
}
else if (curitem->val == (int32) '&')
{
- if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
- return execute(curitem - 1, checkval, calcnot, chkcond);
+ if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
+ return execute(curitem - 1, checkval, options, calcnot, chkcond);
else
return false;
}
else
{ /* |-operator */
- if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
+ if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
return true;
else
- return execute(curitem - 1, checkval, calcnot, chkcond);
+ return execute(curitem - 1, checkval, options, calcnot, chkcond);
}
}
@@ -298,10 +298,10 @@ execute(ITEM *curitem, void *checkval, bool calcnot,
* signconsistent & execconsistent called by *_consistent
*/
bool
-signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot)
+signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot)
{
return execute(GETQUERY(query) + query->size - 1,
- (void *) sign, calcnot,
+ (void *) sign, (void *)(intptr_t) siglen, calcnot,
checkcondition_bit);
}
@@ -315,7 +315,7 @@ execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot)
chkval.arrb = ARRPTR(array);
chkval.arre = chkval.arrb + ARRNELEMS(array);
return execute(GETQUERY(query) + query->size - 1,
- (void *) &chkval, calcnot,
+ (void *) &chkval, NULL, calcnot,
checkcondition_arr);
}
@@ -326,7 +326,7 @@ typedef struct
} GinChkVal;
static bool
-checkcondition_gin(void *checkval, ITEM *item)
+checkcondition_gin(void *checkval, ITEM *item, void *options)
{
GinChkVal *gcv = (GinChkVal *) checkval;
@@ -357,7 +357,7 @@ gin_bool_consistent(QUERYTYPE *query, bool *check)
}
return execute(GETQUERY(query) + query->size - 1,
- (void *) &gcv, true,
+ (void *) &gcv, NULL, true,
checkcondition_gin);
}
@@ -429,7 +429,7 @@ boolop(PG_FUNCTION_ARGS)
chkval.arrb = ARRPTR(val);
chkval.arre = chkval.arrb + ARRNELEMS(val);
result = execute(GETQUERY(query) + query->size - 1,
- &chkval, true,
+ &chkval, NULL, true,
checkcondition_arr);
pfree(val);
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index 911d180..9de7f55 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -6,6 +6,7 @@
#include <limits.h>
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "_int.h"
@@ -22,6 +23,7 @@ PG_FUNCTION_INFO_V1(g_int_penalty);
PG_FUNCTION_INFO_V1(g_int_picksplit);
PG_FUNCTION_INFO_V1(g_int_union);
PG_FUNCTION_INFO_V1(g_int_same);
+PG_FUNCTION_INFO_V1(g_int_options);
/*
@@ -139,6 +141,7 @@ Datum
g_int_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
GISTENTRY *retval;
ArrayType *r;
int len;
@@ -153,9 +156,9 @@ g_int_compress(PG_FUNCTION_ARGS)
CHECKARRVALID(r);
PREPAREARR(r);
- if (ARRNELEMS(r) >= 2 * MAXNUMRANGE)
+ if (ARRNELEMS(r) >= 2 * opts->num_ranges)
elog(NOTICE, "input array is too big (%d maximum allowed, %d current), use gist__intbig_ops opclass instead",
- 2 * MAXNUMRANGE - 1, ARRNELEMS(r));
+ 2 * opts->num_ranges - 1, ARRNELEMS(r));
retval = palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(r),
@@ -178,7 +181,7 @@ g_int_compress(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(entry);
}
- if ((len = ARRNELEMS(r)) >= 2 * MAXNUMRANGE)
+ if ((len = ARRNELEMS(r)) >= 2 * opts->num_ranges)
{ /* compress */
if (r == (ArrayType *) DatumGetPointer(entry->key))
r = DatumGetArrayTypePCopy(entry->key);
@@ -191,7 +194,7 @@ g_int_compress(PG_FUNCTION_ARGS)
len *= 2;
cand = 1;
- while (len > MAXNUMRANGE * 2)
+ while (len > opts->num_ranges * 2)
{
min = INT_MAX;
for (i = 2; i < len; i += 2)
@@ -217,6 +220,7 @@ Datum
g_int_decompress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
GISTENTRY *retval;
ArrayType *r;
int *dr,
@@ -245,7 +249,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
lenin = ARRNELEMS(in);
- if (lenin < 2 * MAXNUMRANGE)
+ if (lenin < 2 * opts->num_ranges)
{ /* not compressed value */
if (in != (ArrayType *) DatumGetPointer(entry->key))
{
@@ -542,3 +546,20 @@ g_int_picksplit(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(v);
}
+
+Datum
+g_int_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"numranges", "number of ranges for compression", 0, 0, 9, RELOPT_TYPE_INT },
+ G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(IntArrayOptions, num_ranges) };
+ IntArrayOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(IntArrayOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index ee8fb64..d7c5317 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -313,14 +313,14 @@ _int_unique(ArrayType *r)
}
void
-gensign(BITVEC sign, int *a, int len)
+gensign(BITVECP sign, int *a, int len, int siglen)
{
int i;
/* we assume that the sign vector is previously zeroed */
for (i = 0; i < len; i++)
{
- HASH(sign, *a);
+ HASH(sign, *a, siglen);
a++;
}
}
diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c
index de7bc82..4d2b3a2 100644
--- a/contrib/intarray/_intbig_gist.c
+++ b/contrib/intarray/_intbig_gist.c
@@ -4,6 +4,7 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "_int.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(g_intbig_penalty);
PG_FUNCTION_INFO_V1(g_intbig_picksplit);
PG_FUNCTION_INFO_V1(g_intbig_union);
PG_FUNCTION_INFO_V1(g_intbig_same);
+PG_FUNCTION_INFO_V1(g_intbig_options);
/* Number of one-bits in an unsigned byte */
static const uint8 number_of_ones[256] = {
@@ -61,12 +63,33 @@ _intbig_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static GISTTYPE *
+_intbig_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+ int flag = allistrue ? ALLISTRUE : 0;
+ int size = CALCGTSIZE(flag, siglen);
+ GISTTYPE *res = (GISTTYPE *) palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!allistrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
/*********************************************************************
** intbig functions
*********************************************************************/
static bool
-_intbig_overlap(GISTTYPE *a, ArrayType *b)
+_intbig_overlap(GISTTYPE *a, ArrayType *b, int siglen)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -75,7 +98,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return true;
ptr++;
}
@@ -84,7 +107,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
}
static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglen)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -93,7 +116,7 @@ _intbig_contains(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (!GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return false;
ptr++;
}
@@ -107,6 +130,8 @@ g_intbig_same(PG_FUNCTION_ARGS)
GISTTYPE *a = (GISTTYPE *) PG_GETARG_POINTER(0);
GISTTYPE *b = (GISTTYPE *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+ int siglen = opts->siglen;
if (ISALLTRUE(a) && ISALLTRUE(b))
*result = true;
@@ -121,7 +146,7 @@ g_intbig_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -137,6 +162,8 @@ Datum
g_intbig_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(1);
+ int siglen = opts->siglen;
if (entry->leafkey)
{
@@ -144,7 +171,7 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ArrayType *in = DatumGetArrayTypeP(entry->key);
int32 *ptr;
int num;
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+ GISTTYPE *res = _intbig_alloc(false, siglen, NULL);
CHECKARRVALID(in);
if (ARRISEMPTY(in))
@@ -157,11 +184,10 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ptr = ARRPTR(in);
num = ARRNELEMS(in);
}
- SET_VARSIZE(res, CALCGTSIZE(0));
while (num--)
{
- HASH(GETSIGN(res), *ptr);
+ HASH(GETSIGN(res), *ptr, siglen);
ptr++;
}
@@ -182,16 +208,13 @@ g_intbig_compress(PG_FUNCTION_ARGS)
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
GISTTYPE *res;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(entry);
}
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
- res->flag = ALLISTRUE;
-
+ res = _intbig_alloc(true, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -205,24 +228,24 @@ g_intbig_compress(PG_FUNCTION_ARGS)
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -231,19 +254,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
Datum
@@ -253,14 +276,14 @@ g_intbig_decompress(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
{
int32 i;
BITVECP sadd = GETSIGN(add);
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -270,29 +293,23 @@ g_intbig_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- GISTTYPE *result;
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+ int siglen = opts->siglen;
+ int32 i;
+ GISTTYPE *result = _intbig_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- len = CALCGTSIZE(flag);
- result = (GISTTYPE *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -305,8 +322,10 @@ g_intbig_penalty(PG_FUNCTION_ARGS)
float *penalty = (float *) PG_GETARG_POINTER(2);
GISTTYPE *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
GISTTYPE *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+ int siglen = opts->siglen;
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -329,6 +348,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+ int siglen = opts->siglen;
OffsetNumber k,
j;
GISTTYPE *datum_l,
@@ -361,7 +382,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -383,32 +404,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_l, GTHDRSIZE);
- datum_l->flag = ALLISTRUE;
- }
- else
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
- datum_l->flag = 0;
- memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC));
- }
- if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_r, GTHDRSIZE);
- datum_r->flag = ALLISTRUE;
- }
- else
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
- datum_r->flag = 0;
- memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
- }
+ datum_l = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_1)));
+ datum_r = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_2)));
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -417,8 +416,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -442,20 +441,20 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
{
if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -466,12 +465,12 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -497,6 +496,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(5);
+ int siglen = opts->siglen;
bool retval;
/* All cases served by this function are inexact */
@@ -509,6 +510,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
{
retval = signconsistent((QUERYTYPE *) query,
GETSIGN(DatumGetPointer(entry->key)),
+ siglen,
false);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
@@ -519,7 +521,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
switch (strategy)
{
case RTOverlapStrategyNumber:
- retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTSameStrategyNumber:
if (GIST_LEAF(entry))
@@ -527,22 +530,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
- BITVECP dq,
+ BITVECP dq = palloc0(siglen),
de;
- memset(qp, 0, sizeof(BITVEC));
-
while (num--)
{
- HASH(qp, *ptr);
+ HASH(dq, *ptr, siglen);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
- dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (de[i] != dq[i])
{
@@ -551,13 +550,16 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
}
+ pfree(dq);
}
else
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTContainsStrategyNumber:
case RTOldContainsStrategyNumber:
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTContainedByStrategyNumber:
case RTOldContainedByStrategyNumber:
@@ -566,22 +568,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
- BITVECP dq,
+ BITVECP dq = palloc0(siglen),
de;
- memset(qp, 0, sizeof(BITVEC));
-
while (num--)
{
- HASH(qp, *ptr);
+ HASH(dq, *ptr, siglen);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
- dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (de[i] & ~dq[i])
{
@@ -591,7 +589,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
}
else
- retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
default:
retval = false;
@@ -599,3 +598,20 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
}
+
+Datum
+g_intbig_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, NoLock, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(IntArrayBigOptions, siglen) };
+ IntArrayBigOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(IntArrayBigOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/intarray--1.2-1.3.sql b/contrib/intarray/intarray--1.2-1.3.sql
new file mode 100644
index 0000000..c9b83ef
--- /dev/null
+++ b/contrib/intarray/intarray--1.2-1.3.sql
@@ -0,0 +1,34 @@
+/* contrib/intarray/intarray--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.3'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('g_int_compress(internal)', '{internal,internal}'),
+('g_int_decompress(internal)', '{internal,internal}'),
+('g_intbig_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('g_intbig_compress(internal)', '{internal,internal}'),
+('g_intbig_decompress(internal)', '{internal,internal}'),
+('g_intbig_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('g_intbig_picksplit(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_union(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_same(intbig_gkey,intbig_gkey,internal)', '{intbig_gkey,intbig_gkey,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist__intbig_ops USING gist
+ADD FUNCTION 10 (_int4) g_intbig_options (internal, boolean);
diff --git a/contrib/intarray/intarray--1.2.sql b/contrib/intarray/intarray--1.2.sql
deleted file mode 100644
index f10b53d..0000000
--- a/contrib/intarray/intarray--1.2.sql
+++ /dev/null
@@ -1,520 +0,0 @@
-/* contrib/intarray/intarray--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION intarray" to load this file. \quit
-
---
--- Create the user-defined type for the 1-D integer arrays (_int4)
---
-
--- Query type
-CREATE FUNCTION bqarr_in(cstring)
-RETURNS query_int
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION bqarr_out(query_int)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE query_int (
- INTERNALLENGTH = -1,
- INPUT = bqarr_in,
- OUTPUT = bqarr_out
-);
-
---only for debug
-CREATE FUNCTION querytree(query_int)
-RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-
-CREATE FUNCTION boolop(_int4, query_int)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
-
-CREATE FUNCTION rboolop(query_int, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
-
-CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE OPERATOR @@ (
- LEFTARG = _int4,
- RIGHTARG = query_int,
- PROCEDURE = boolop,
- COMMUTATOR = '~~',
- RESTRICT = _int_matchsel,
- JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~~ (
- LEFTARG = query_int,
- RIGHTARG = _int4,
- PROCEDURE = rboolop,
- COMMUTATOR = '@@',
- RESTRICT = _int_matchsel,
- JOIN = contjoinsel
-);
-
-
---
--- External C-functions for R-tree methods
---
-
--- Comparison methods
-
-CREATE FUNCTION _int_contains(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
-
-CREATE FUNCTION _int_contained(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
-
-CREATE FUNCTION _int_overlap(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
-
-CREATE FUNCTION _int_same(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
-
-CREATE FUNCTION _int_different(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
-
--- support routines for indexing
-
-CREATE FUNCTION _int_union(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_inter(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
---
--- OPERATORS
---
-
-CREATE OPERATOR && (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_overlap,
- COMMUTATOR = '&&',
- RESTRICT = _int_overlap_sel,
- JOIN = _int_overlap_joinsel
-);
-
---CREATE OPERATOR = (
--- LEFTARG = _int4,
--- RIGHTARG = _int4,
--- PROCEDURE = _int_same,
--- COMMUTATOR = '=',
--- NEGATOR = '<>',
--- RESTRICT = eqsel,
--- JOIN = eqjoinsel,
--- SORT1 = '<',
--- SORT2 = '<'
---);
-
---CREATE OPERATOR <> (
--- LEFTARG = _int4,
--- RIGHTARG = _int4,
--- PROCEDURE = _int_different,
--- COMMUTATOR = '<>',
--- NEGATOR = '=',
--- RESTRICT = neqsel,
--- JOIN = neqjoinsel
---);
-
-CREATE OPERATOR @> (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contains,
- COMMUTATOR = '<@',
- RESTRICT = _int_contains_sel,
- JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR <@ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contained,
- COMMUTATOR = '@>',
- RESTRICT = _int_contained_sel,
- JOIN = _int_contained_joinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contains,
- COMMUTATOR = '~',
- RESTRICT = _int_contains_sel,
- JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR ~ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contained,
- COMMUTATOR = '@',
- RESTRICT = _int_contained_sel,
- JOIN = _int_contained_joinsel
-);
-
---------------
-CREATE FUNCTION intset(int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION icount(_int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
- RIGHTARG = _int4,
- PROCEDURE = icount
-);
-
-CREATE FUNCTION sort(_int4, text)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_asc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_desc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION uniq(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION idx(_int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = idx
-);
-
-CREATE FUNCTION subarray(_int4, int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION subarray(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION intarray_push_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intarray_push_elem
-);
-
-CREATE FUNCTION intarray_push_array(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = +,
- PROCEDURE = intarray_push_array
-);
-
-CREATE FUNCTION intarray_del_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intarray_del_elem
-);
-
-CREATE FUNCTION intset_union_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR | (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intset_union_elem
-);
-
-CREATE OPERATOR | (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = |,
- PROCEDURE = _int_union
-);
-
-CREATE FUNCTION intset_subtract(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = intset_subtract
-);
-
-CREATE OPERATOR & (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = &,
- PROCEDURE = _int_inter
-);
---------------
-
--- define the GiST support methods
-CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_union(internal, internal)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_same(_int4, _int4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-
--- Create the operator class for indexing
-
-CREATE OPERATOR CLASS gist__int_ops
-DEFAULT FOR TYPE _int4 USING gist AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal),
- FUNCTION 2 g_int_union (internal, internal),
- FUNCTION 3 g_int_compress (internal),
- FUNCTION 4 g_int_decompress (internal),
- FUNCTION 5 g_int_penalty (internal, internal, internal),
- FUNCTION 6 g_int_picksplit (internal, internal),
- FUNCTION 7 g_int_same (_int4, _int4, internal);
-
-
----------------------------------------------
--- intbig
----------------------------------------------
--- define the GiST support methods
-
-CREATE FUNCTION _intbig_in(cstring)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _intbig_out(intbig_gkey)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE intbig_gkey (
- INTERNALLENGTH = -1,
- INPUT = _intbig_in,
- OUTPUT = _intbig_out
-);
-
-CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_union(internal, internal)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
--- register the opclass for indexing (not as default)
-
-CREATE OPERATOR CLASS gist__intbig_ops
-FOR TYPE _int4 USING gist
-AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 g_intbig_consistent (internal, _int4, smallint, oid, internal),
- FUNCTION 2 g_intbig_union (internal, internal),
- FUNCTION 3 g_intbig_compress (internal),
- FUNCTION 4 g_intbig_decompress (internal),
- FUNCTION 5 g_intbig_penalty (internal, internal, internal),
- FUNCTION 6 g_intbig_picksplit (internal, internal),
- FUNCTION 7 g_intbig_same (intbig_gkey, intbig_gkey, internal),
- STORAGE intbig_gkey;
-
---GIN
-
-CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE OPERATOR CLASS gin__int_ops
-FOR TYPE _int4 USING gin
-AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 btint4cmp (int4, int4),
- FUNCTION 2 ginarrayextract (anyarray, internal, internal),
- FUNCTION 3 ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
- FUNCTION 4 ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
- STORAGE int4;
diff --git a/contrib/intarray/intarray--1.3.sql b/contrib/intarray/intarray--1.3.sql
new file mode 100644
index 0000000..6c91c75
--- /dev/null
+++ b/contrib/intarray/intarray--1.3.sql
@@ -0,0 +1,531 @@
+/* contrib/intarray/intarray--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION intarray" to load this file. \quit
+
+--
+-- Create the user-defined type for the 1-D integer arrays (_int4)
+--
+
+-- Query type
+CREATE FUNCTION bqarr_in(cstring)
+RETURNS query_int
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION bqarr_out(query_int)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE query_int (
+ INTERNALLENGTH = -1,
+ INPUT = bqarr_in,
+ OUTPUT = bqarr_out
+);
+
+--only for debug
+CREATE FUNCTION querytree(query_int)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE FUNCTION boolop(_int4, query_int)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
+
+CREATE FUNCTION rboolop(query_int, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
+
+CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE OPERATOR @@ (
+ LEFTARG = _int4,
+ RIGHTARG = query_int,
+ PROCEDURE = boolop,
+ COMMUTATOR = '~~',
+ RESTRICT = _int_matchsel,
+ JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~~ (
+ LEFTARG = query_int,
+ RIGHTARG = _int4,
+ PROCEDURE = rboolop,
+ COMMUTATOR = '@@',
+ RESTRICT = _int_matchsel,
+ JOIN = contjoinsel
+);
+
+
+--
+-- External C-functions for R-tree methods
+--
+
+-- Comparison methods
+
+CREATE FUNCTION _int_contains(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
+
+CREATE FUNCTION _int_contained(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
+
+CREATE FUNCTION _int_overlap(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
+
+CREATE FUNCTION _int_same(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
+
+CREATE FUNCTION _int_different(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
+
+-- support routines for indexing
+
+CREATE FUNCTION _int_union(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_inter(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+--
+-- OPERATORS
+--
+
+CREATE OPERATOR && (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = _int_overlap_sel,
+ JOIN = _int_overlap_joinsel
+);
+
+--CREATE OPERATOR = (
+-- LEFTARG = _int4,
+-- RIGHTARG = _int4,
+-- PROCEDURE = _int_same,
+-- COMMUTATOR = '=',
+-- NEGATOR = '<>',
+-- RESTRICT = eqsel,
+-- JOIN = eqjoinsel,
+-- SORT1 = '<',
+-- SORT2 = '<'
+--);
+
+--CREATE OPERATOR <> (
+-- LEFTARG = _int4,
+-- RIGHTARG = _int4,
+-- PROCEDURE = _int_different,
+-- COMMUTATOR = '<>',
+-- NEGATOR = '=',
+-- RESTRICT = neqsel,
+-- JOIN = neqjoinsel
+--);
+
+CREATE OPERATOR @> (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = _int_contains_sel,
+ JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR <@ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = _int_contained_sel,
+ JOIN = _int_contained_joinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contains,
+ COMMUTATOR = '~',
+ RESTRICT = _int_contains_sel,
+ JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR ~ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contained,
+ COMMUTATOR = '@',
+ RESTRICT = _int_contained_sel,
+ JOIN = _int_contained_joinsel
+);
+
+--------------
+CREATE FUNCTION intset(int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION icount(_int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+ RIGHTARG = _int4,
+ PROCEDURE = icount
+);
+
+CREATE FUNCTION sort(_int4, text)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_asc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_desc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION uniq(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION idx(_int4, int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = idx
+);
+
+CREATE FUNCTION subarray(_int4, int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subarray(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION intarray_push_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intarray_push_elem
+);
+
+CREATE FUNCTION intarray_push_array(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = +,
+ PROCEDURE = intarray_push_array
+);
+
+CREATE FUNCTION intarray_del_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intarray_del_elem
+);
+
+CREATE FUNCTION intset_union_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR | (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intset_union_elem
+);
+
+CREATE OPERATOR | (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = |,
+ PROCEDURE = _int_union
+);
+
+CREATE FUNCTION intset_subtract(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = intset_subtract
+);
+
+CREATE OPERATOR & (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = &,
+ PROCEDURE = _int_inter
+);
+--------------
+
+-- define the GiST support methods
+CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_compress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_decompress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_union(internal, internal)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_same(_int4, _int4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_int_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- Create the operator class for indexing
+
+CREATE OPERATOR CLASS gist__int_ops
+DEFAULT FOR TYPE _int4 USING gist AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal),
+ FUNCTION 2 g_int_union (internal, internal),
+ FUNCTION 3 g_int_compress (internal, internal),
+ FUNCTION 4 g_int_decompress (internal, internal),
+ FUNCTION 5 g_int_penalty (internal, internal, internal),
+ FUNCTION 6 g_int_picksplit (internal, internal),
+ FUNCTION 7 g_int_same (_int4, _int4, internal),
+ FUNCTION 10 g_int_options (internal, boolean);
+
+
+---------------------------------------------
+-- intbig
+---------------------------------------------
+-- define the GiST support methods
+
+CREATE FUNCTION _intbig_in(cstring)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _intbig_out(intbig_gkey)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE intbig_gkey (
+ INTERNALLENGTH = -1,
+ INPUT = _intbig_in,
+ OUTPUT = _intbig_out
+);
+
+CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_compress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_decompress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_penalty(internal,internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_picksplit(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_union(internal, internal, internal)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- register the opclass for indexing (not as default)
+
+CREATE OPERATOR CLASS gist__intbig_ops
+FOR TYPE _int4 USING gist
+AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 g_intbig_consistent (internal, _int4, smallint, oid, internal, internal),
+ FUNCTION 2 g_intbig_union (internal, internal, internal),
+ FUNCTION 3 g_intbig_compress (internal, internal),
+ FUNCTION 4 g_intbig_decompress (internal, internal),
+ FUNCTION 5 g_intbig_penalty (internal, internal, internal, internal),
+ FUNCTION 6 g_intbig_picksplit (internal, internal, internal),
+ FUNCTION 7 g_intbig_same (intbig_gkey, intbig_gkey, internal, internal),
+ FUNCTION 10 g_intbig_options (internal, boolean),
+ STORAGE intbig_gkey;
+
+--GIN
+
+CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gin__int_ops
+FOR TYPE _int4 USING gin
+AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 btint4cmp (int4, int4),
+ FUNCTION 2 ginarrayextract (anyarray, internal, internal),
+ FUNCTION 3 ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
+ FUNCTION 4 ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
+ STORAGE int4;
diff --git a/contrib/intarray/intarray.control b/contrib/intarray/intarray.control
index 7e50cc3..72952f7 100644
--- a/contrib/intarray/intarray.control
+++ b/contrib/intarray/intarray.control
@@ -1,5 +1,5 @@
# intarray extension
comment = 'functions, operators, and index support for 1-D arrays of integers'
-default_version = '1.2'
+default_version = '1.3'
module_pathname = '$libdir/_int'
relocatable = true
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index b633cf3..2540cc6 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -259,14 +259,22 @@
</para>
<para>
- Two GiST index operator classes are provided:
- <literal>gist__int_ops</literal> (used by default) is suitable for
+ Two parametrized GiST index operator classes are provided:
+ <literal>gist__int_ops(numranges)</literal> (used by default) is suitable for
small- to medium-size data sets, while
- <literal>gist__intbig_ops</literal> uses a larger signature and is more
+ <literal>gist__intbig_ops(siglen)</literal> uses a larger signature and is more
suitable for indexing large data sets (i.e., columns containing
a large number of distinct array values).
The implementation uses an RD-tree data structure with
built-in lossy compression.
+
+ Optional integer parameter <literal>numranges</literal> of
+ <literal>gist__int_ops</literal> determines
+ number of ranges in compressed array (100 by default).
+
+ Optional integer parameter <literal>siglen</literal> of
+ <literal>gist__intbig_ops</literal> determines
+ signature length in bytes (252 by default, 488 maximum).
</para>
<para>
0006-opclass-parameters-contrib_ltree-v01.patchtext/x-patch; name=0006-opclass-parameters-contrib_ltree-v01.patchDownload
diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index c101603..b0e90a3 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -6,7 +6,7 @@ OBJS = ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
PG_CPPFLAGS = -DLOWER_NODE
EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.1--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
PGFILEDESC = "ltree - hierarchical label data type"
REGRESS = ltree
diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c
index 28bf7ad..394f7ad 100644
--- a/contrib/ltree/_ltree_gist.c
+++ b/contrib/ltree/_ltree_gist.c
@@ -8,6 +8,7 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(_ltree_union);
PG_FUNCTION_INFO_V1(_ltree_penalty);
PG_FUNCTION_INFO_V1(_ltree_picksplit);
PG_FUNCTION_INFO_V1(_ltree_consistent);
+PG_FUNCTION_INFO_V1(_ltree_gist_options);
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
@@ -47,7 +49,7 @@ static const uint8 number_of_ones[256] = {
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
@@ -56,7 +58,7 @@ hashing(BITVECP sign, ltree *t)
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- AHASH(sign, hash);
+ AHASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
@@ -67,12 +69,13 @@ _ltree_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval = entry;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
if (entry->leafkey)
{ /* ltree */
ltree_gist *key;
ArrayType *val = DatumGetArrayTypeP(entry->key);
- int32 len = LTG_HDRSIZE + ASIGLEN;
int num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
ltree *item = (ltree *) ARR_DATA_PTR(val);
@@ -85,14 +88,11 @@ _ltree_compress(PG_FUNCTION_ARGS)
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("array must not contain nulls")));
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = 0;
+ key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
- MemSet(LTG_SIGN(key), 0, ASIGLEN);
while (num > 0)
{
- hashing(LTG_SIGN(key), item);
+ hashing(LTG_SIGN(key), item, siglen);
num--;
item = NEXTVAL(item);
}
@@ -104,22 +104,17 @@ _ltree_compress(PG_FUNCTION_ARGS)
}
else if (!LTG_ISALLTRUE(entry->key))
{
- int32 i,
- len;
+ int32 i;
ltree_gist *key;
-
BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key));
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = LTG_HDRSIZE;
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = LTG_ALLTRUE;
+ key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(key),
entry->rel, entry->page,
@@ -134,6 +129,8 @@ _ltree_same(PG_FUNCTION_ARGS)
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
*result = true;
@@ -148,7 +145,7 @@ _ltree_same(PG_FUNCTION_ARGS)
sb = LTG_SIGN(b);
*result = true;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -161,7 +158,7 @@ _ltree_same(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, ltree_gist *add)
+unionkey(BITVECP sbase, ltree_gist *add, int siglen)
{
int32 i;
BITVECP sadd = LTG_SIGN(add);
@@ -169,7 +166,7 @@ unionkey(BITVECP sbase, ltree_gist *add)
if (LTG_ISALLTRUE(add))
return 1;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -179,52 +176,46 @@ _ltree_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- ABITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- ltree_gist *result;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
+ int32 i;
+ ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
+ BITVECP base = LTG_SIGN(result);
- MemSet((void *) base, 0, sizeof(ABITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = LTG_ALLTRUE;
+ result->flag |= LTG_ALLTRUE;
+ SET_VARSIZE(result, LTG_HDRSIZE);
break;
}
}
- len = LTG_HDRSIZE + ((flag & LTG_ALLTRUE) ? 0 : ASIGLEN);
- result = (ltree_gist *) palloc0(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!LTG_ISALLTRUE(result))
- memcpy((void *) LTG_SIGN(result), (void *) base, sizeof(ABITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -233,19 +224,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(ltree_gist *a, ltree_gist *b)
+hemdist(ltree_gist *a, ltree_gist *b, int siglen)
{
if (LTG_ISALLTRUE(a))
{
if (LTG_ISALLTRUE(b))
return 0;
else
- return ASIGLENBIT - sizebitvec(LTG_SIGN(b));
+ return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
}
else if (LTG_ISALLTRUE(b))
- return ASIGLENBIT - sizebitvec(LTG_SIGN(a));
+ return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
- return hemdistsign(LTG_SIGN(a), LTG_SIGN(b));
+ return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
}
@@ -255,8 +246,10 @@ _ltree_penalty(PG_FUNCTION_ARGS)
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -277,6 +270,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
ltree_gist *datum_l,
@@ -309,7 +304,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -331,32 +326,13 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE);
- SET_VARSIZE(datum_l, LTG_HDRSIZE);
- datum_l->flag = LTG_ALLTRUE;
- }
- else
- {
- datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
- SET_VARSIZE(datum_l, LTG_HDRSIZE + ASIGLEN);
- datum_l->flag = 0;
- memcpy((void *) LTG_SIGN(datum_l), (void *) LTG_SIGN(GETENTRY(entryvec, seed_1)), sizeof(ABITVEC));
- }
- if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE);
- SET_VARSIZE(datum_r, LTG_HDRSIZE);
- datum_r->flag = LTG_ALLTRUE;
- }
- else
- {
- datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
- SET_VARSIZE(datum_r, LTG_HDRSIZE + ASIGLEN);
- datum_r->flag = 0;
- memcpy((void *) LTG_SIGN(datum_r), (void *) LTG_SIGN(GETENTRY(entryvec, seed_2)), sizeof(ABITVEC));
- }
+ datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
+ LTG_SIGN(GETENTRY(entryvec, seed_1)),
+ siglen, NULL, NULL);
+
+ datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
+ LTG_SIGN(GETENTRY(entryvec, seed_2)),
+ siglen, NULL, NULL);
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -365,8 +341,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -390,20 +366,20 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
{
if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
{
if (!LTG_ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(ABITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = LTG_SIGN(_j);
- ALOOPBYTE
+ ALOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -414,12 +390,12 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
{
if (!LTG_ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(ABITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = LTG_SIGN(_j);
- ALOOPBYTE
+ ALOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -436,7 +412,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
}
static bool
-gist_te(ltree_gist *key, ltree *query)
+gist_te(ltree_gist *key, ltree *query, int siglen)
{
ltree_level *curq = LTREE_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -449,7 +425,7 @@ gist_te(ltree_gist *key, ltree *query)
while (qlen > 0)
{
hv = ltree_crc32_sz(curq->name, curq->len);
- if (!GETBIT(sign, AHASHVAL(hv)))
+ if (!GETBIT(sign, AHASHVAL(hv, siglen)))
return false;
curq = LEVEL_NEXT(curq);
qlen--;
@@ -458,27 +434,40 @@ gist_te(ltree_gist *key, ltree *query)
return true;
}
+typedef struct LtreeSignature
+{
+ BITVECP sign;
+ int siglen;
+} LtreeSignature;
+
static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
{
- return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, AHASHVAL(val->val)) : true;
+ LtreeSignature *sig = cxt;
+
+ return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
}
static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
{
+ LtreeSignature sig;
+
if (LTG_ISALLTRUE(key))
return true;
+ sig.sign = LTG_SIGN(key);
+ sig.siglen = siglen;
+
return ltree_execute(
GETQUERY(query),
- (void *) LTG_SIGN(key), false,
+ &sig, false,
checkcondition_bit
);
}
static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
{
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -497,7 +486,7 @@ gist_qe(ltree_gist *key, lquery *query)
while (vlen > 0)
{
- if (GETBIT(sign, AHASHVAL(curv->val)))
+ if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
{
isexist = true;
break;
@@ -517,7 +506,7 @@ gist_qe(ltree_gist *key, lquery *query)
}
static bool
-_arrq_cons(ltree_gist *key, ArrayType *_query)
+_arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
{
lquery *query = (lquery *) ARR_DATA_PTR(_query);
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -533,7 +522,7 @@ _arrq_cons(ltree_gist *key, ArrayType *_query)
while (num > 0)
{
- if (gist_qe(key, query))
+ if (gist_qe(key, query, siglen))
return true;
num--;
query = (lquery *) NEXTVAL(query);
@@ -550,6 +539,8 @@ _ltree_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
bool res = false;
@@ -560,19 +551,19 @@ _ltree_consistent(PG_FUNCTION_ARGS)
{
case 10:
case 11:
- res = gist_te(key, (ltree *) query);
+ res = gist_te(key, (ltree *) query, siglen);
break;
case 12:
case 13:
- res = gist_qe(key, (lquery *) query);
+ res = gist_qe(key, (lquery *) query, siglen);
break;
case 14:
case 15:
- res = gist_qtxt(key, (ltxtquery *) query);
+ res = gist_qtxt(key, (ltxtquery *) query, siglen);
break;
case 16:
case 17:
- res = _arrq_cons(key, (ArrayType *) query);
+ res = _arrq_cons(key, (ArrayType *) query, siglen);
break;
default:
/* internal error */
@@ -581,3 +572,20 @@ _ltree_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
+
+Datum
+_ltree_gist_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(LtreeGistOptions, siglen) };
+ LtreeGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(LtreeGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/ltree/ltree--1.1--1.2.sql b/contrib/ltree/ltree--1.1--1.2.sql
new file mode 100644
index 0000000..d002e2b
--- /dev/null
+++ b/contrib/ltree/ltree--1.1--1.2.sql
@@ -0,0 +1,47 @@
+/* contrib/ltree/ltree--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION ltree UPDATE TO '1.2'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('ltree_compress(internal)', '{internal,internal}'),
+('ltree_decompress(internal)', '{internal,internal}'),
+('ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('ltree_union(internal,internal)', '{internal,internal,internal}'),
+('ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('_ltree_compress(internal)', '{internal,internal}'),
+('_ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('_ltree_union(internal,internal)', '{internal,internal,internal}'),
+('_ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('_ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('_ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', '_ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_ltree_ops USING gist
+ADD FUNCTION 10 (ltree) ltree_gist_options (internal, boolean);
+
+ALTER OPERATOR FAMILY gist__ltree_ops USING gist
+ADD FUNCTION 10 (_ltree) _ltree_gist_options (internal, boolean);
+
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1..61c8cdf 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
# ltree extension
comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/ltree'
relocatable = true
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index e4b8c84..6247564 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -183,15 +183,16 @@ int ltree_strncasecmp(const char *a, const char *b, size_t s);
/* GiST support for ltree */
+#define SIGLEN_MAX (122 * sizeof(int32))
+#define SIGLEN_DEFAULT (2 * sizeof(int32))
#define BITBYTE 8
-#define SIGLENINT 2
-#define SIGLEN ( sizeof(int32)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
-typedef unsigned char BITVEC[SIGLEN];
+#define SIGLEN (sizeof(int32) * SIGLENINT)
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
+
typedef unsigned char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for(i = 0; i < (siglen); i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
#define GETBITBYTE(x,i) ( ((unsigned char)(x)) >> i & 0x01 )
@@ -199,8 +200,8 @@ typedef unsigned char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
/*
* type of index key for ltree. Tree are combined B-Tree and R-Tree
@@ -230,26 +231,34 @@ typedef struct
#define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
#define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
#define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
-#define LTG_LNODE(x) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
-#define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + VARSIZE(LTG_LNODE(x))) )
-#define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
+#define LTG_LNODE(x, siglen) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : (siglen) ) ) )
+#define LTG_RENODE(x, siglen) ( (ltree*)( ((char*)LTG_LNODE(x, siglen)) + VARSIZE(LTG_LNODE(x, siglen))) )
+#define LTG_RNODE(x, siglen) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x, siglen) : LTG_RENODE(x, siglen) )
-#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
-#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
+#define LTG_GETLNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x, siglen) )
+#define LTG_GETRNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x, siglen) )
+extern ltree_gist *ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+ ltree *left, ltree *right);
/* GiST support for ltree[] */
-#define ASIGLENINT (7)
-#define ASIGLEN (sizeof(int32)*ASIGLENINT)
-#define ASIGLENBIT (ASIGLEN*BITBYTE)
-typedef unsigned char ABITVEC[ASIGLEN];
+#define LTREE_ASIGLEN_DEFAULT (7 * sizeof(int32))
+#define LTREE_ASIGLEN_MAX (122 * sizeof(int32))
+#define ASIGLENBIT(siglen) ((siglen) * BITBYTE)
+
+#define ALOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
-#define ALOOPBYTE \
- for(i=0;i<ASIGLEN;i++)
+#define AHASHVAL(val, siglen) (((unsigned int)(val)) % ASIGLENBIT(siglen))
+#define AHASH(sign, val, siglen) SETBIT((sign), AHASHVAL(val, siglen))
-#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
-#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
+/* gist_ltree_ops and gist__ltree_ops opclass options */
+typedef struct LtreeGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} LtreeGistOptions;
/* type of key is the same to ltree_gist */
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 12aa8ff..9847b83 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -6,11 +6,13 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"
#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
PG_FUNCTION_INFO_V1(ltree_gist_in);
PG_FUNCTION_INFO_V1(ltree_gist_out);
@@ -33,6 +35,47 @@ ltree_gist_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+ltree_gist *
+ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+ ltree *left, ltree *right)
+{
+ int32 size = LTG_HDRSIZE + (isalltrue ? 0 : siglen) +
+ (left ? VARSIZE(left) + (right ? VARSIZE(right) : 0) : 0);
+ ltree_gist *result = palloc(size);
+
+ SET_VARSIZE(result, size);
+
+ if (siglen)
+ {
+ result->flag = 0;
+
+ if (isalltrue)
+ result->flag |= LTG_ALLTRUE;
+ else if (sign)
+ memcpy(LTG_SIGN(result), sign, siglen);
+ else
+ memset(LTG_SIGN(result), 0, siglen);
+
+ if (left)
+ {
+ memcpy(LTG_LNODE(result, siglen), left, VARSIZE(left));
+
+ if (!right || left == right || ISEQ(left, right))
+ result->flag |= LTG_NORIGHT;
+ else
+ memcpy(LTG_RNODE(result, siglen), right, VARSIZE(right));
+ }
+ }
+ else
+ {
+ Assert(left);
+ result->flag = LTG_ONENODE;
+ memcpy(LTG_NODE(result), left, VARSIZE(left));
+ }
+
+ return result;
+}
+
PG_FUNCTION_INFO_V1(ltree_compress);
PG_FUNCTION_INFO_V1(ltree_decompress);
PG_FUNCTION_INFO_V1(ltree_same);
@@ -40,8 +83,8 @@ PG_FUNCTION_INFO_V1(ltree_union);
PG_FUNCTION_INFO_V1(ltree_penalty);
PG_FUNCTION_INFO_V1(ltree_picksplit);
PG_FUNCTION_INFO_V1(ltree_consistent);
+PG_FUNCTION_INFO_V1(ltree_gist_options);
-#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
Datum
@@ -52,14 +95,8 @@ ltree_compress(PG_FUNCTION_ARGS)
if (entry->leafkey)
{ /* ltree */
- ltree_gist *key;
ltree *val = DatumGetLtreeP(entry->key);
- int32 len = LTG_HDRSIZE + VARSIZE(val);
-
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = LTG_ONENODE;
- memcpy((void *) LTG_NODE(key), (void *) val, VARSIZE(val));
+ ltree_gist *key = ltree_gist_alloc(false, NULL, 0, val, 0);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(key),
@@ -93,6 +130,8 @@ ltree_same(PG_FUNCTION_ARGS)
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
*result = false;
if (LTG_ISONENODE(a) != LTG_ISONENODE(b))
@@ -109,15 +148,15 @@ ltree_same(PG_FUNCTION_ARGS)
if (LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b))
PG_RETURN_POINTER(result);
- if (!ISEQ(LTG_LNODE(a), LTG_LNODE(b)))
+ if (!ISEQ(LTG_LNODE(a, siglen), LTG_LNODE(b, siglen)))
PG_RETURN_POINTER(result);
- if (!ISEQ(LTG_RNODE(a), LTG_RNODE(b)))
+ if (!ISEQ(LTG_RNODE(a, siglen), LTG_RNODE(b, siglen)))
PG_RETURN_POINTER(result);
*result = true;
if (!LTG_ISALLTRUE(a))
{
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -132,7 +171,7 @@ ltree_same(PG_FUNCTION_ARGS)
}
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
@@ -141,7 +180,7 @@ hashing(BITVECP sign, ltree *t)
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- HASH(sign, hash);
+ HASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
@@ -152,7 +191,9 @@ ltree_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
+ BITVECP base = palloc0(siglen);
int32 i,
j;
ltree_gist *result,
@@ -161,16 +202,14 @@ ltree_union(PG_FUNCTION_ARGS)
*right = NULL,
*curtree;
bool isalltrue = false;
- bool isleqr;
- MemSet((void *) base, 0, sizeof(BITVEC));
for (j = 0; j < entryvec->n; j++)
{
cur = GETENTRY(entryvec, j);
if (LTG_ISONENODE(cur))
{
curtree = LTG_NODE(cur);
- hashing(base, curtree);
+ hashing(base, curtree, siglen);
if (!left || ltree_compare(left, curtree) > 0)
left = curtree;
if (!right || ltree_compare(right, curtree) < 0)
@@ -184,14 +223,14 @@ ltree_union(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(cur);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) base)[i] |= sc[i];
}
- curtree = LTG_LNODE(cur);
+ curtree = LTG_LNODE(cur, siglen);
if (!left || ltree_compare(left, curtree) > 0)
left = curtree;
- curtree = LTG_RNODE(cur);
+ curtree = LTG_RNODE(cur, siglen);
if (!right || ltree_compare(right, curtree) < 0)
right = curtree;
}
@@ -200,7 +239,7 @@ ltree_union(PG_FUNCTION_ARGS)
if (isalltrue == false)
{
isalltrue = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) base)[i] != 0xff)
{
@@ -210,23 +249,9 @@ ltree_union(PG_FUNCTION_ARGS)
}
}
- isleqr = (left == right || ISEQ(left, right)) ? true : false;
- *size = LTG_HDRSIZE + ((isalltrue) ? 0 : SIGLEN) + VARSIZE(left) + ((isleqr) ? 0 : VARSIZE(right));
-
- result = (ltree_gist *) palloc0(*size);
- SET_VARSIZE(result, *size);
- result->flag = 0;
+ result = ltree_gist_alloc(isalltrue, base, siglen, left, right);
- if (isalltrue)
- result->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(result), base, SIGLEN);
-
- memcpy((void *) LTG_LNODE(result), (void *) left, VARSIZE(left));
- if (isleqr)
- result->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(result), (void *) right, VARSIZE(right));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -237,11 +262,13 @@ ltree_penalty(PG_FUNCTION_ARGS)
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
int32 cmpr,
cmpl;
- cmpl = ltree_compare(LTG_GETLNODE(origval), LTG_GETLNODE(newval));
- cmpr = ltree_compare(LTG_GETRNODE(newval), LTG_GETRNODE(origval));
+ cmpl = ltree_compare(LTG_GETLNODE(origval, siglen), LTG_GETLNODE(newval, siglen));
+ cmpr = ltree_compare(LTG_GETRNODE(newval, siglen), LTG_GETRNODE(origval, siglen));
*penalty = Max(cmpl, 0) + Max(cmpr, 0);
@@ -270,26 +297,24 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber j;
int32 i;
RIX *array;
OffsetNumber maxoff;
int nbytes;
- int size;
ltree *lu_l,
*lu_r,
*ru_l,
*ru_r;
ltree_gist *lu,
*ru;
- BITVEC ls,
- rs;
+ BITVECP ls = palloc0(siglen),
+ rs = palloc0(siglen);
bool lisat = false,
- risat = false,
- isleqr;
+ risat = false;
- memset((void *) ls, 0, sizeof(BITVEC));
- memset((void *) rs, 0, sizeof(BITVEC));
maxoff = entryvec->n - 1;
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
v->spl_left = (OffsetNumber *) palloc(nbytes);
@@ -303,7 +328,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
array[j].index = j;
lu = GETENTRY(entryvec, j); /* use as tmp val */
- array[j].r = LTG_GETLNODE(lu);
+ array[j].r = LTG_GETLNODE(lu, siglen);
}
qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
@@ -317,10 +342,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
v->spl_left[v->spl_nleft] = array[j].index;
v->spl_nleft++;
- if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu), lu_r) > 0)
- lu_r = LTG_GETRNODE(lu);
+ if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), lu_r) > 0)
+ lu_r = LTG_GETRNODE(lu, siglen);
if (LTG_ISONENODE(lu))
- hashing(ls, LTG_NODE(lu));
+ hashing(ls, LTG_NODE(lu), siglen);
else
{
if (lisat || LTG_ISALLTRUE(lu))
@@ -329,7 +354,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) ls)[i] |= sc[i];
}
}
@@ -338,10 +363,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
v->spl_right[v->spl_nright] = array[j].index;
v->spl_nright++;
- if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu), ru_r) > 0)
- ru_r = LTG_GETRNODE(lu);
+ if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), ru_r) > 0)
+ ru_r = LTG_GETRNODE(lu, siglen);
if (LTG_ISONENODE(lu))
- hashing(rs, LTG_NODE(lu));
+ hashing(rs, LTG_NODE(lu), siglen);
else
{
if (risat || LTG_ISALLTRUE(lu))
@@ -350,7 +375,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) rs)[i] |= sc[i];
}
}
@@ -360,7 +385,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
if (lisat == false)
{
lisat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) ls)[i] != 0xff)
{
@@ -373,7 +398,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
if (risat == false)
{
risat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) rs)[i] != 0xff)
{
@@ -383,38 +408,14 @@ ltree_picksplit(PG_FUNCTION_ARGS)
}
}
- lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index));
- isleqr = (lu_l == lu_r || ISEQ(lu_l, lu_r)) ? true : false;
- size = LTG_HDRSIZE + ((lisat) ? 0 : SIGLEN) + VARSIZE(lu_l) + ((isleqr) ? 0 : VARSIZE(lu_r));
- lu = (ltree_gist *) palloc0(size);
- SET_VARSIZE(lu, size);
- lu->flag = 0;
- if (lisat)
- lu->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(lu), ls, SIGLEN);
- memcpy((void *) LTG_LNODE(lu), (void *) lu_l, VARSIZE(lu_l));
- if (isleqr)
- lu->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(lu), (void *) lu_r, VARSIZE(lu_r));
+ lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index), siglen);
+ lu = ltree_gist_alloc(lisat, ls, siglen, lu_l, lu_r);
+ ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index), siglen);
+ ru = ltree_gist_alloc(risat, rs, siglen, ru_l, ru_r);
- ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index));
- isleqr = (ru_l == ru_r || ISEQ(ru_l, ru_r)) ? true : false;
- size = LTG_HDRSIZE + ((risat) ? 0 : SIGLEN) + VARSIZE(ru_l) + ((isleqr) ? 0 : VARSIZE(ru_r));
- ru = (ltree_gist *) palloc0(size);
- SET_VARSIZE(ru, size);
- ru->flag = 0;
- if (risat)
- ru->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(ru), rs, SIGLEN);
- memcpy((void *) LTG_LNODE(ru), (void *) ru_l, VARSIZE(ru_l));
- if (isleqr)
- ru->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(ru), (void *) ru_r, VARSIZE(ru_r));
+ pfree(ls);
+ pfree(rs);
v->spl_ldatum = PointerGetDatum(lu);
v->spl_rdatum = PointerGetDatum(ru);
@@ -423,7 +424,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
}
static bool
-gist_isparent(ltree_gist *key, ltree *query)
+gist_isparent(ltree_gist *key, ltree *query, int siglen)
{
int32 numlevel = query->numlevel;
int i;
@@ -431,7 +432,8 @@ gist_isparent(ltree_gist *key, ltree *query)
for (i = query->numlevel; i >= 0; i--)
{
query->numlevel = i;
- if (ltree_compare(query, LTG_GETLNODE(key)) >= 0 && ltree_compare(query, LTG_GETRNODE(key)) <= 0)
+ if (ltree_compare(query, LTG_GETLNODE(key, siglen)) >= 0 &&
+ ltree_compare(query, LTG_GETRNODE(key, siglen)) <= 0)
{
query->numlevel = numlevel;
return true;
@@ -452,10 +454,10 @@ copy_ltree(ltree *src)
}
static bool
-gist_ischild(ltree_gist *key, ltree *query)
+gist_ischild(ltree_gist *key, ltree *query, int siglen)
{
- ltree *left = copy_ltree(LTG_GETLNODE(key));
- ltree *right = copy_ltree(LTG_GETRNODE(key));
+ ltree *left = copy_ltree(LTG_GETLNODE(key, siglen));
+ ltree *right = copy_ltree(LTG_GETRNODE(key, siglen));
bool res = true;
if (left->numlevel > query->numlevel)
@@ -477,7 +479,7 @@ gist_ischild(ltree_gist *key, ltree *query)
}
static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
{
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -496,7 +498,7 @@ gist_qe(ltree_gist *key, lquery *query)
while (vlen > 0)
{
- if (GETBIT(sign, HASHVAL(curv->val)))
+ if (GETBIT(sign, HASHVAL(curv->val, siglen)))
{
isexist = true;
break;
@@ -545,41 +547,54 @@ gist_tqcmp(ltree *t, lquery *q)
}
static bool
-gist_between(ltree_gist *key, lquery *query)
+gist_between(ltree_gist *key, lquery *query, int siglen)
{
if (query->firstgood == 0)
return true;
- if (gist_tqcmp(LTG_GETLNODE(key), query) > 0)
+ if (gist_tqcmp(LTG_GETLNODE(key, siglen), query) > 0)
return false;
- if (gist_tqcmp(LTG_GETRNODE(key), query) < 0)
+ if (gist_tqcmp(LTG_GETRNODE(key, siglen), query) < 0)
return false;
return true;
}
+typedef struct LtreeSignature
+{
+ BITVECP sign;
+ int siglen;
+} LtreeSignature;
+
static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
{
- return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, HASHVAL(val->val)) : true;
+ LtreeSignature *sig = cxt;
+
+ return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, HASHVAL(val->val, sig->siglen)) : true;
}
static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
{
+ LtreeSignature sig;
+
if (LTG_ISALLTRUE(key))
return true;
+ sig.sign = LTG_SIGN(key);
+ sig.siglen = siglen;
+
return ltree_execute(
GETQUERY(query),
- (void *) LTG_SIGN(key), false,
+ &sig, false,
checkcondition_bit
);
}
static bool
-arrq_cons(ltree_gist *key, ArrayType *_query)
+arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
{
lquery *query = (lquery *) ARR_DATA_PTR(_query);
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -595,7 +610,7 @@ arrq_cons(ltree_gist *key, ArrayType *_query)
while (num > 0)
{
- if (gist_qe(key, query) && gist_between(key, query))
+ if (gist_qe(key, query, siglen) && gist_between(key, query, siglen))
return true;
num--;
query = NEXTVAL(query);
@@ -611,6 +626,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
void *query = NULL;
bool res = false;
@@ -625,11 +642,11 @@ ltree_consistent(PG_FUNCTION_ARGS)
res = (GIST_LEAF(entry)) ?
(ltree_compare((ltree *) query, LTG_NODE(key)) > 0)
:
- (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+ (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
break;
case BTLessEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
- res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+ res = (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
break;
case BTEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
@@ -637,35 +654,35 @@ ltree_consistent(PG_FUNCTION_ARGS)
res = (ltree_compare((ltree *) query, LTG_NODE(key)) == 0);
else
res = (
- ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0
+ ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0
&&
- ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0
+ ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0
);
break;
case BTGreaterEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
- res = (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+ res = (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
break;
case BTGreaterStrategyNumber:
query = PG_GETARG_LTREE_P(1);
res = (GIST_LEAF(entry)) ?
- (ltree_compare((ltree *) query, LTG_GETRNODE(key)) < 0)
+ (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) < 0)
:
- (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+ (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
break;
case 10:
query = PG_GETARG_LTREE_P_COPY(1);
res = (GIST_LEAF(entry)) ?
inner_isparent((ltree *) query, LTG_NODE(key))
:
- gist_isparent(key, (ltree *) query);
+ gist_isparent(key, (ltree *) query, siglen);
break;
case 11:
query = PG_GETARG_LTREE_P(1);
res = (GIST_LEAF(entry)) ?
inner_isparent(LTG_NODE(key), (ltree *) query)
:
- gist_ischild(key, (ltree *) query);
+ gist_ischild(key, (ltree *) query, siglen);
break;
case 12:
case 13:
@@ -676,7 +693,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((lquery *) query)
));
else
- res = (gist_qe(key, (lquery *) query) && gist_between(key, (lquery *) query));
+ res = (gist_qe(key, (lquery *) query, siglen) &&
+ gist_between(key, (lquery *) query, siglen));
break;
case 14:
case 15:
@@ -687,7 +705,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((ltxtquery *) query)
));
else
- res = gist_qtxt(key, (ltxtquery *) query);
+ res = gist_qtxt(key, (ltxtquery *) query, siglen);
break;
case 16:
case 17:
@@ -698,7 +716,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((ArrayType *) query)
));
else
- res = arrq_cons(key, (ArrayType *) query);
+ res = arrq_cons(key, (ArrayType *) query, siglen);
break;
default:
/* internal error */
@@ -708,3 +726,20 @@ ltree_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
+
+Datum
+ltree_gist_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(LtreeGistOptions, siglen) };
+ LtreeGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(LtreeGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index ea362f8..dd4e269 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -502,11 +502,17 @@ Europe & Russia*@ & !Transportation
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
</para>
<para>
- Example of creating such an index:
+ Example of creating such an index with a default signature length of 8 bytes:
</para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (path);
</programlisting>
+ <para>
+ Example of creating such an index with a signature length of 100 bytes:
+ </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
+</programlisting>
</listitem>
<listitem>
<para>
@@ -515,12 +521,18 @@ CREATE INDEX path_gist_idx ON test USING GIST (path);
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
</para>
<para>
- Example of creating such an index:
+ Example of creating such an index with a default signature length of 28 bytes:
</para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
</programlisting>
<para>
+ Example of creating such an index with a signature length of 100 bytes:
+ </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
+</programlisting>
+ <para>
Note: This index type is lossy.
</para>
</listitem>
0007-opclass-parameters-contrib_pg_trgm-v01.patchtext/x-patch; name=0007-opclass-parameters-contrib_pg_trgm-v01.patchDownload
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index 212a890..eb6b594 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -4,8 +4,8 @@ MODULE_big = pg_trgm
OBJS = trgm_op.o trgm_gist.o trgm_gin.o trgm_regexp.o $(WIN32RES)
EXTENSION = pg_trgm
-DATA = pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
- pg_trgm--1.0--1.1.sql pg_trgm--unpackaged--1.0.sql
+DATA = pg_trgm--1.3--1.4.sql pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql \
+ pg_trgm--1.1--1.2.sql pg_trgm--1.0--1.1.sql pg_trgm--unpackaged--1.0.sql
PGFILEDESC = "pg_trgm - trigram matching"
REGRESS = pg_trgm pg_word_trgm
diff --git a/contrib/pg_trgm/pg_trgm--1.3--1.4.sql b/contrib/pg_trgm/pg_trgm--1.3--1.4.sql
new file mode 100644
index 0000000..318671a
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.3--1.4.sql
@@ -0,0 +1,33 @@
+/* contrib/pg_trgm/pg_trgm--1.3--1.4.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.4'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('gtrgm_compress(internal)', '{internal,internal}'),
+('gtrgm_decompress(internal)', '{internal,internal}'),
+('gtrgm_same(gtrgm,gtrgm,internal)', '{gtrgm,gtrgm,internal,internal}'),
+('gtrgm_union(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('gtrgm_picksplit(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_consistent(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}'),
+('gtrgm_distance(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION gtrgm_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'gtrgm_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_trgm_ops USING gist
+ADD FUNCTION 10 (text) gtrgm_options (internal, boolean);
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 06f274f..3e325dd 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,5 +1,5 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.3'
+default_version = '1.4'
module_pathname = '$libdir/pg_trgm'
relocatable = true
diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h
index 45df918..ba15cc9 100644
--- a/contrib/pg_trgm/trgm.h
+++ b/contrib/pg_trgm/trgm.h
@@ -70,17 +70,16 @@ typedef struct
#define TRGMHDRSIZE (VARHDRSZ + sizeof(uint8))
/* gist */
+#define SIGLEN_DEFAULT (sizeof(int) * 3)
+#define SIGLEN_MAX (sizeof(int) * 122) /* key will toast, so very slow!!! */
#define BITBYTE 8
-#define SIGLENINT 3 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE - 1) /* see makesign */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE - 1) /* see makesign */
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
#define GETBITBYTE(x,i) ( (((char)(x)) >> (i)) & 0x01 )
@@ -88,8 +87,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
@@ -99,7 +98,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 e55dc19..b877b96 100644
--- a/contrib/pg_trgm/trgm_gist.c
+++ b/contrib/pg_trgm/trgm_gist.c
@@ -5,9 +5,16 @@
#include "trgm.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "fmgr.h"
+/* gist_trgm_ops opclass options */
+typedef struct TrgmGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} TrgmGistOptions;
typedef struct
{
@@ -38,6 +45,7 @@ PG_FUNCTION_INFO_V1(gtrgm_union);
PG_FUNCTION_INFO_V1(gtrgm_same);
PG_FUNCTION_INFO_V1(gtrgm_penalty);
PG_FUNCTION_INFO_V1(gtrgm_picksplit);
+PG_FUNCTION_INFO_V1(gtrgm_options);
/* Number of one-bits in an unsigned byte */
static const uint8 number_of_ones[256] = {
@@ -74,20 +82,41 @@ gtrgm_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static TRGM *
+gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
+{
+ int flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
+ int size = CALCGTSIZE(flag, siglen);
+ TRGM *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!isalltrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
static void
-makesign(BITVECP sign, TRGM *a)
+makesign(BITVECP sign, TRGM *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
trgm *ptr = GETARR(a);
int32 tmp = 0;
- MemSet((void *) sign, 0, sizeof(BITVEC));
- SETBIT(sign, SIGLENBIT); /* set last unused bit */
+ MemSet((void *) sign, 0, siglen);
+ SETBIT(sign, SIGLENBIT(siglen)); /* set last unused bit */
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- HASH(sign, tmp);
+ HASH(sign, tmp, siglen);
}
}
@@ -95,6 +124,8 @@ Datum
gtrgm_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
@@ -111,22 +142,17 @@ gtrgm_compress(PG_FUNCTION_ARGS)
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
TRGM *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
- res = (TRGM *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtrgm_alloc(true, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -160,7 +186,7 @@ gtrgm_decompress(PG_FUNCTION_ARGS)
}
static int32
-cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
+cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen)
{
int32 count = 0;
int32 k,
@@ -171,7 +197,7 @@ cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- count += GETBIT(sign, HASHVAL(tmp));
+ count += GETBIT(sign, HASHVAL(tmp, siglen));
}
return count;
@@ -186,6 +212,8 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
TRGM *key = (TRGM *) DatumGetPointer(entry->key);
TRGM *qtrg;
bool res;
@@ -307,7 +335,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)
@@ -349,7 +377,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;
@@ -402,7 +430,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);
@@ -432,6 +460,8 @@ gtrgm_distance(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
TRGM *key = (TRGM *) DatumGetPointer(entry->key);
TRGM *qtrg;
float8 res;
@@ -487,7 +517,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);
@@ -503,7 +533,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, TRGM *add)
+unionkey(BITVECP sbase, TRGM *add, int siglen)
{
int32 i;
@@ -514,7 +544,7 @@ unionkey(BITVECP sbase, TRGM *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -525,7 +555,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;
@@ -538,29 +568,23 @@ gtrgm_union(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int32 len = entryvec->n;
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
int32 i;
- int32 flag = 0;
- TRGM *result;
+ TRGM *result = gtrgm_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < len; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag = ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (TRGM *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -571,6 +595,8 @@ gtrgm_same(PG_FUNCTION_ARGS)
TRGM *a = (TRGM *) PG_GETARG_POINTER(0);
TRGM *b = (TRGM *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -587,7 +613,7 @@ gtrgm_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -624,24 +650,24 @@ gtrgm_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -650,19 +676,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
@@ -671,6 +697,8 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
TRGM *origval = (TRGM *) DatumGetPointer(origentry->key);
TRGM *newval = (TRGM *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -680,7 +708,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;
@@ -694,12 +722,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)
@@ -711,31 +739,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) )
@@ -756,19 +785,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
@@ -777,6 +806,8 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
OffsetNumber maxoff = entryvec->n - 2;
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
TRGM *datum_l,
@@ -795,19 +826,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;
@@ -832,44 +867,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);
@@ -895,36 +908,36 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign),
+ siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign),
+ siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -935,12 +948,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;
@@ -954,3 +967,20 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(v);
}
+
+Datum
+gtrgm_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(TrgmGistOptions, siglen) };
+ TrgmGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(TrgmGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/pgtrgm.sgml b/doc/src/sgml/pgtrgm.sgml
index 338ef30..82e25b9 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -267,6 +267,15 @@ CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
</para>
<para>
+ GiST opclass <literal>gist_trgm_ops</literal> has one optional parameter
+ <literal>siglen</literal> for specifying signature length in bytes
+ (default signature length is 12 bytes, maximal length is 488 bytes):
+<programlisting>
+CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));
+</programlisting>
+ </para>
+
+ <para>
At this point, you will have an index on the <structfield>t</structfield> column that
you can use for similarity searching. A typical query is
<programlisting>
0008-opclass-parameters-contrib_hstore-v01.patchtext/x-patch; name=0008-opclass-parameters-contrib_hstore-v01.patchDownload
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index ab7fef3..6782f33 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -5,7 +5,7 @@ OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
$(WIN32RES)
EXTENSION = hstore
-DATA = hstore--1.4.sql hstore--1.4--1.5.sql \
+DATA = hstore--1.4.sql hstore--1.4--1.5.sql hstore--1.5--1.6.sql \
hstore--1.3--1.4.sql hstore--1.2--1.3.sql \
hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
hstore--unpackaged--1.0.sql
diff --git a/contrib/hstore/hstore--1.5--1.6.sql b/contrib/hstore/hstore--1.5--1.6.sql
new file mode 100644
index 0000000..08c85ee
--- /dev/null
+++ b/contrib/hstore/hstore--1.5--1.6.sql
@@ -0,0 +1,32 @@
+/* contrib/hstore/hstore--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.6'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('ghstore_compress(internal)', '{internal,internal}'),
+('ghstore_decompress(internal)', '{internal,internal}'),
+('ghstore_consistent(internal,hstore,smallint,oid,internal)', '{internal,hstore,smallint,oid,internal,internal}'),
+('ghstore_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ghstore_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ghstore_union(internal,internal)', '{internal,internal,internal}'),
+('ghstore_same(ghstore,ghstore,internal)', '{ghstore,ghstore,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ghstore_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ghstore_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_hstore_ops USING gist
+ADD FUNCTION 10 (hstore) ghstore_options (internal, boolean);
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 8a71947..93688cd 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
# hstore extension
comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/hstore'
relocatable = true
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 6d24d2f..13be2c0 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -4,29 +4,33 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/pg_crc.h"
#include "hstore.h"
+/* gist_hstore_ops opclass options */
+typedef struct HstoreGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} GistHstoreOptions;
+
/* bigint defines */
#define BITBYTE 8
-#define SIGLENINT 4 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
+#define SIGLEN_DEFAULT (sizeof(int32) * 4)
+#define SIGLEN_MAX (sizeof(int32) * 122) /* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define SIGPTR(x) ( (BITVECP) ARR_DATA_PTR(x) )
-
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
-
-#define LOOPBIT \
- for(i=0;i<SIGLENBIT;i++)
+#define LOOPBIT(siglen) \
+ for (i = 0; i < SIGLENBIT(siglen); i++)
/* beware of multiple evaluation of arguments to these macros! */
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
@@ -34,8 +38,8 @@ typedef char *BITVECP;
#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) )
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
typedef struct
{
@@ -49,7 +53,7 @@ typedef struct
#define ISALLTRUE(x) ( ((GISTTYPE*)x)->flag & ALLISTRUE )
#define GTHDRSIZE (VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
@@ -100,6 +104,27 @@ ghstore_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static GISTTYPE *
+ghstore_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+ int flag = allistrue ? ALLISTRUE : 0;
+ int size = CALCGTSIZE(flag, siglen);
+ GISTTYPE *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!allistrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
PG_FUNCTION_INFO_V1(ghstore_consistent);
PG_FUNCTION_INFO_V1(ghstore_compress);
PG_FUNCTION_INFO_V1(ghstore_decompress);
@@ -107,36 +132,37 @@ PG_FUNCTION_INFO_V1(ghstore_penalty);
PG_FUNCTION_INFO_V1(ghstore_picksplit);
PG_FUNCTION_INFO_V1(ghstore_union);
PG_FUNCTION_INFO_V1(ghstore_same);
+PG_FUNCTION_INFO_V1(ghstore_options);
Datum
ghstore_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+ GISTTYPE *res = ghstore_alloc(false, siglen, NULL);
HStore *val = DatumGetHStoreP(entry->key);
HEntry *hsent = ARRPTR(val);
char *ptr = STRPTR(val);
int count = HS_COUNT(val);
int i;
- SET_VARSIZE(res, CALCGTSIZE(0));
-
for (i = 0; i < count; ++i)
{
int h;
h = crc32_sz((char *) HSTORE_KEY(hsent, ptr, i),
HSTORE_KEYLEN(hsent, i));
- HASH(GETSIGN(res), h);
+ HASH(GETSIGN(res), h, siglen);
if (!HSTORE_VALISNULL(hsent, i))
{
h = crc32_sz((char *) HSTORE_VAL(hsent, ptr, i),
HSTORE_VALLEN(hsent, i));
- HASH(GETSIGN(res), h);
+ HASH(GETSIGN(res), h, siglen);
}
}
@@ -152,15 +178,13 @@ ghstore_compress(PG_FUNCTION_ARGS)
GISTTYPE *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
- res->flag = ALLISTRUE;
+ res = ghstore_alloc(true, siglen, NULL);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
@@ -188,6 +212,9 @@ ghstore_same(PG_FUNCTION_ARGS)
GISTTYPE *a = (GISTTYPE *) PG_GETARG_POINTER(0);
GISTTYPE *b = (GISTTYPE *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
+
if (ISALLTRUE(a) && ISALLTRUE(b))
*result = true;
@@ -202,7 +229,7 @@ ghstore_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -215,12 +242,12 @@ ghstore_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
size += SUMBIT(sign);
sign = (BITVECP) (((char *) sign) + 1);
@@ -229,12 +256,12 @@ sizebitvec(BITVECP sign)
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
dist = 0;
- LOOPBIT
+ LOOPBIT(siglen)
{
if (GETBIT(a, i) != GETBIT(b, i))
dist++;
@@ -243,30 +270,30 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
{
int32 i;
BITVECP sadd = GETSIGN(add);
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -278,28 +305,23 @@ ghstore_union(PG_FUNCTION_ARGS)
int32 len = entryvec->n;
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
int32 i;
- int32 flag = 0;
- GISTTYPE *result;
+ GISTTYPE *result = ghstore_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < len; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- len = CALCGTSIZE(flag);
- result = (GISTTYPE *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -310,10 +332,12 @@ ghstore_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
GISTTYPE *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
GISTTYPE *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -338,6 +362,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
OffsetNumber maxoff = entryvec->n - 2;
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
GISTTYPE *datum_l,
@@ -368,7 +394,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -390,33 +416,10 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_l, GTHDRSIZE);
- datum_l->flag = ALLISTRUE;
- }
- else
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
- datum_l->flag = 0;
- memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
- ;
- }
- if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_r, GTHDRSIZE);
- datum_r->flag = ALLISTRUE;
- }
- else
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
- datum_r->flag = 0;
- memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
- }
+ datum_l = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_1)));
+ datum_r = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_2)));
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -425,8 +428,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -450,20 +453,20 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.0001))
{
if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -474,12 +477,12 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -504,6 +507,8 @@ ghstore_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
bool res = true;
BITVECP sign;
@@ -529,13 +534,13 @@ ghstore_consistent(PG_FUNCTION_ARGS)
int crc = crc32_sz((char *) HSTORE_KEY(qe, qv, i),
HSTORE_KEYLEN(qe, i));
- if (GETBIT(sign, HASHVAL(crc)))
+ if (GETBIT(sign, HASHVAL(crc, siglen)))
{
if (!HSTORE_VALISNULL(qe, i))
{
crc = crc32_sz((char *) HSTORE_VAL(qe, qv, i),
HSTORE_VALLEN(qe, i));
- if (!GETBIT(sign, HASHVAL(crc)))
+ if (!GETBIT(sign, HASHVAL(crc, siglen)))
res = false;
}
}
@@ -548,7 +553,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
text *query = PG_GETARG_TEXT_PP(1);
int crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
- res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
+ res = (GETBIT(sign, HASHVAL(crc, siglen))) ? true : false;
}
else if (strategy == HStoreExistsAllStrategyNumber)
{
@@ -569,7 +574,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
if (key_nulls[i])
continue;
crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
- if (!(GETBIT(sign, HASHVAL(crc))))
+ if (!(GETBIT(sign, HASHVAL(crc, siglen))))
res = false;
}
}
@@ -594,7 +599,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
if (key_nulls[i])
continue;
crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
- if (GETBIT(sign, HASHVAL(crc)))
+ if (GETBIT(sign, HASHVAL(crc, siglen)))
res = true;
}
}
@@ -603,3 +608,20 @@ ghstore_consistent(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+
+Datum
+ghstore_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistHstoreOptions, siglen) };
+ GistHstoreOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistHstoreOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 94ccd12..45a811f 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -462,6 +462,15 @@ CREATE INDEX hidx ON testhstore USING GIN (h);
</programlisting>
<para>
+ GiST opclass <literal>gist_hstore_ops</literal> has one optional parameter
+ <literal>siglen</literal> for specifying signature length in bytes
+ (default signature length is 16 bytes, maximal length is 488 bytes):
+<programlisting>
+ CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32));
+</programlisting>
+ </para>
+
+ <para>
<type>hstore</type> also supports <type>btree</type> or <type>hash</type> indexes for
the <literal>=</literal> operator. This allows <type>hstore</type> columns to be
declared <literal>UNIQUE</literal>, or to be used in <literal>GROUP BY</literal>,
0001-opclass-parameters-v01.patchtext/x-patch; name=0001-opclass-parameters-v01.patchDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 71e20f2..4cfd759 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3874,6 +3874,17 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
partial index.
</entry>
</row>
+
+ <row>
+ <entry><structfield>indoptions</structfield></entry>
+ <entry><type>text[]</type></entry>
+ <entry></entry>
+ <entry>
+ This is an array of <structfield>indnatts</structfield> strings that
+ store per-column opclass options or null. Each array element is a
+ serialized array of column's opclass options in reloptions format or null.
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 0818196..e6c967b 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -983,7 +983,7 @@ CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
An index definition can specify an <firstterm>operator
class</firstterm> for each column of an index.
<synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
</synopsis>
The operator class identifies the operators to be used by the index
for that column. For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 1fd21e1..907b436 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
- ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+ ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
[ WHERE <replaceable class="parameter">predicate</replaceable> ]
@@ -232,6 +232,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</varlistentry>
<varlistentry>
+ <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of an operator class parameter. See below for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>ASC</literal></term>
<listitem>
<para>
@@ -544,8 +553,9 @@ Indexes:
</para>
<para>
- An <firstterm>operator class</firstterm> can be specified for each
- column of an index. The operator class identifies the operators to be
+ An <firstterm>operator class</firstterm> with its optional parameters
+ can be specified for each column of an index.
+ The operator class identifies the operators to be
used by the index for that column. For example, a B-tree index on
four-byte integers would use the <literal>int4_ops</literal> class;
this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 46276ce..4c72986 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1007,6 +1007,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
return options;
}
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+ relopt_value *reloptions, int numoptions)
+{
+ ArrayType *array = DatumGetArrayTypeP(options);
+ Datum *optiondatums;
+ int noptions;
+ int i;
+
+ deconstruct_array(array, TEXTOID, -1, false, 'i',
+ &optiondatums, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *text_str = VARDATA(optiondatums[i]);
+ int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+ int j;
+
+ /* Search for a match in reloptions */
+ for (j = 0; j < numoptions; j++)
+ {
+ int kw_len = reloptions[j].gen->namelen;
+
+ if (text_len > kw_len && text_str[kw_len] == '=' &&
+ strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+ {
+ parse_one_reloption(&reloptions[j], text_str, text_len,
+ validate);
+ break;
+ }
+ }
+
+ if (j >= numoptions && validate)
+ {
+ char *s;
+ char *p;
+
+ s = TextDatumGetCString(optiondatums[i]);
+ p = strchr(s, '=');
+ if (p)
+ *p = '\0';
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s\"", s)));
+ }
+ }
+
+ /* It's worth avoiding memory leaks in this function */
+ pfree(optiondatums);
+
+ if (((void *) array) != DatumGetPointer(options))
+ pfree(array);
+}
+
/*
* Interpret reloptions that are given in text-array format.
*
@@ -1061,57 +1115,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
/* Done if no options */
if (PointerIsValid(DatumGetPointer(options)))
- {
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
+ parseRelOptionsInternal(options, validate, reloptions, numoptions);
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
+ *numrelopts = numoptions;
+ return reloptions;
+}
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen *optgen[], int nopts)
+{
+ relopt_value *values = palloc(sizeof(*values) * nopts);
+ int i;
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
+ for (i = 0; i < nopts; i++)
+ {
+ values[i].gen = optgen[i];
+ values[i].isset = false;
+ }
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
- {
- parse_one_reloption(&reloptions[j], text_str, text_len,
- validate);
- break;
- }
- }
+ if (options != (Datum) 0)
+ parseRelOptionsInternal(options, validate, values, nopts);
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
+ return values;
+}
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+ int noptions, size_t base_size, bool validate)
+{
+ relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+ relopt_value *vals;
+ void *opts;
+ int i;
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
+ for (i = 0; i < noptions; i++)
+ {
+ elems[i].optname = optgen[i]->name;
+ elems[i].opttype = optgen[i]->type;
+ elems[i].offset = offsets[i];
}
- *numrelopts = numoptions;
- return reloptions;
+ vals = parseLocalRelOptions(options, validate, optgen, noptions);
+ opts = allocateReloptStruct(base_size, vals, noptions);
+ fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
}
/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 91247f0..9f8aad2 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -75,11 +75,14 @@
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
+#include "commands/defrem.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
+#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
@@ -898,3 +901,72 @@ index_getprocinfo(Relation irel,
return locinfo;
}
+
+/*
+ * Parse opclass-specific options for index column.
+ *
+ * amoptions index relation
+ * attnum column number
+ * indoptions options as text[] datum
+ * validate error flag
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum indoptions,
+ bool validate)
+{
+ amopclassoptions_function amopclassoptions =
+ relation->rd_amroutine->amopclassoptions;
+
+ if (!amopclassoptions)
+ {
+ if (validate && PointerIsValid(DatumGetPointer(indoptions)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("access method \"%s\" does not support opclass options ",
+ get_am_name(relation->rd_rel->relam))));
+
+ return NULL;
+ }
+
+ return amopclassoptions(relation, attnum, indoptions, validate);
+}
+
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+ uint16 procnum, Datum reloptions, bool validate)
+{
+ Oid procid = index_getprocid(indrel, attnum, procnum);
+ FmgrInfo *procinfo;
+
+ if (!OidIsValid(procid))
+ {
+ StringInfoData opclassname;
+ Oid opclass;
+ Datum indclassDatum;
+ oidvector *indclass;
+ bool isnull;
+
+ if (!DatumGetPointer(reloptions))
+ return NULL;
+
+ indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+ indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+ opclass = indclass->values[attnum - 1];
+
+ initStringInfo(&opclassname);
+ get_opclass_name(opclass, InvalidOid, &opclassname);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("operator class%s has no options ", opclassname.data)));
+ }
+
+ procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+ return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+ reloptions,
+ BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 564f206..f74cac1 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -25,6 +25,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/transam.h"
@@ -586,6 +587,7 @@ UpdateIndexRelation(Oid indexoid,
int2vector *indoption;
Datum exprsDatum;
Datum predDatum;
+ Datum indoptionsDatum;
Datum values[Natts_pg_index];
bool nulls[Natts_pg_index];
Relation pg_index;
@@ -632,6 +634,53 @@ UpdateIndexRelation(Oid indexoid,
else
predDatum = (Datum) 0;
+ if (indexInfo->ii_OpclassOptions != NULL)
+ {
+ Datum *vals = palloc(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+ bool *nulls = palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
+ int dims[1];
+ int lbs[1];
+
+ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+ {
+ Datum options = indexInfo->ii_OpclassOptions[i];
+
+ nulls[i] = options == (Datum) 0;
+
+ if (!nulls[i])
+ {
+ FunctionCallInfoData fcinfo;
+ Datum result;
+ FmgrInfo flinfo = { 0 };
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 1, InvalidOid, NULL,
+ NULL);
+
+ fcinfo.arg[0] = options;
+ fcinfo.argnull[0] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ result = array_out(&fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_out);
+
+ vals[i] = CStringGetTextDatum(DatumGetCString(result));
+ }
+ }
+
+ dims[0] = indexInfo->ii_NumIndexAttrs;
+ lbs[0] = 1;
+
+ indoptionsDatum = PointerGetDatum(construct_md_array(vals, nulls, 1,
+ dims, lbs, TEXTOID,
+ -1, false, 'i'));
+ }
+ else
+ indoptionsDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
@@ -665,6 +714,9 @@ UpdateIndexRelation(Oid indexoid,
values[Anum_pg_index_indpred - 1] = predDatum;
if (predDatum == (Datum) 0)
nulls[Anum_pg_index_indpred - 1] = true;
+ values[Anum_pg_index_indoptions - 1] = indoptionsDatum;
+ if (indoptionsDatum == (Datum) 0)
+ nulls[Anum_pg_index_indoptions - 1] = true;
tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls);
@@ -1140,6 +1192,12 @@ index_create(Relation heapRelation,
else
Assert(indexRelation->rd_indexcxt != NULL);
+ /* Validate opclass-specific options */
+ if (indexInfo->ii_OpclassOptions)
+ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+ (void) index_opclass_options(indexRelation, i + 1,
+ indexInfo->ii_OpclassOptions[i], true);
+
/*
* If this is bootstrap (initdb) time, then we don't actually fill in the
* index yet. We'll be creating more indexes and classes later, so we
@@ -1777,6 +1835,8 @@ BuildIndexInfo(Relation index)
ii->ii_ExclusionStrats = NULL;
}
+ ii->ii_OpclassOptions = RelationGetRawOpclassOptions(index);
+
/* other info */
ii->ii_Unique = indexStruct->indisunique;
ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 8bf2698..099fb7d 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -311,6 +311,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 3e48a58..97cf5d9 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -612,6 +612,7 @@ DefineIndex(Oid relationId,
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL; /* for now */
indexInfo->ii_Unique = stmt->unique;
/* In a concurrent build, mark it not-ready-for-inserts */
indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1348,12 +1349,11 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
ListCell *nextExclOp;
ListCell *lc;
int attn;
+ int ncols = list_length(attList);
/* Allocate space for exclusion operator info, if needed */
if (exclusionOpNames)
{
- int ncols = list_length(attList);
-
Assert(list_length(exclusionOpNames) == ncols);
indexInfo->ii_ExclusionOps = (Oid *) palloc(sizeof(Oid) * ncols);
indexInfo->ii_ExclusionProcs = (Oid *) palloc(sizeof(Oid) * ncols);
@@ -1594,6 +1594,17 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName)));
}
+ /* Set up the per-column opclass options (indoptions field). */
+ if (attribute->opclassopts)
+ {
+ if (!indexInfo->ii_OpclassOptions)
+ indexInfo->ii_OpclassOptions = palloc0(sizeof(Datum) * ncols);
+
+ indexInfo->ii_OpclassOptions[attn] =
+ transformRelOptions((Datum) 0, attribute->opclassopts,
+ NULL, NULL, false, false);
+ }
+
attn++;
}
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 266a3ef..e089a6a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2807,6 +2807,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 bbffc87..3c484f4 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2545,6 +2545,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
COMPARE_STRING_FIELD(indexcolname);
COMPARE_NODE_FIELD(collation);
COMPARE_NODE_FIELD(opclass);
+ COMPARE_NODE_FIELD(opclassopts);
COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 011d2a3..c4c2e2e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2872,6 +2872,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 60f2171..5e8f1dd 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -353,6 +353,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->nulls_first = NULL;
}
+ /* Fetch index opclass options */
+ info->opclassoptions =
+ RelationGetParsedOpclassOptions(indexRelation);
+
/*
* Fetch the index expressions and predicate, if any. We must
* modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d99f2be..eb780ce 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -483,7 +483,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <alias> alias_clause opt_alias_clause
%type <list> func_alias_clause
%type <sortby> sortby
-%type <ielem> index_elem
+%type <ielem> index_elem index_elem_options
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
@@ -7438,43 +7438,65 @@ index_params: index_elem { $$ = list_make1($1); }
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
+
+index_elem_options:
+ opt_collate opt_class opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = NIL;
+ $$->ordering = $3;
+ $$->nulls_ordering = $4;
+ }
+ | opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ | opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = NIL;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ ;
+
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
{
- $$ = makeNode(IndexElem);
+ $$ = $2;
$$->name = $1;
- $$->expr = NULL;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $2;
$$->expr = $1;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $4;
$$->expr = $2;
- $$->indexcolname = NULL;
- $$->collation = $4;
- $$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
}
;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3697466..7e8bd56 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -450,8 +450,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
deparse_context *context);
static void get_tablesample_def(TableSampleClause *tablesample,
deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
- StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
@@ -465,6 +463,7 @@ static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
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 ")
@@ -1180,6 +1179,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
oidvector *indcollation;
oidvector *indclass;
int2vector *indoption;
+ Datum *indoptions = NULL;
StringInfoData buf;
char *str;
char *sep;
@@ -1215,6 +1215,16 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Assert(!isnull);
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+ if (!attrsOnly)
+ {
+ Datum indoptionsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+ Anum_pg_index_indoptions,
+ &isnull);
+ if (!isnull)
+ indoptions = ExtractRawOpclassOptions(indoptionsDatum,
+ idxrec->indnatts);
+ }
+
/*
* Fetch the pg_class tuple of the index relation
*/
@@ -1338,6 +1348,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
if (!attrsOnly && (!colno || colno == keyno + 1))
{
Oid indcoll;
+ bool has_options =
+ indoptions && indoptions[keyno] != (Datum) 0;
/* Add collation, if not default for column */
indcoll = indcollation->values[keyno];
@@ -1346,7 +1358,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
generate_collation_name((indcoll)));
/* Add the operator class name, if not default */
- get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+ get_opclass_name(indclass->values[keyno],
+ has_options ? InvalidOid : keycoltype, &buf);
+
+ if (has_options)
+ {
+ appendStringInfoString(&buf, " (");
+ get_reloptions(&buf, indoptions[keyno]);
+ appendStringInfoChar(&buf, ')');
+ }
/* Add options if relevant */
if (amroutine->amcanorder)
@@ -10297,7 +10317,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
* actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.)
*/
-static void
+void
get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf)
{
@@ -10934,6 +10954,62 @@ string_to_text(char *str)
}
/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+ Datum *options;
+ int noptions;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(reloptions),
+ TEXTOID, -1, false, 'i',
+ &options, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *option = TextDatumGetCString(options[i]);
+ char *name;
+ char *separator;
+ char *value;
+
+ /*
+ * Each array element should have the form name=value. If the "="
+ * is missing for some reason, treat it like an empty value.
+ */
+ name = option;
+ separator = strchr(option, '=');
+ if (separator)
+ {
+ *separator = '\0';
+ value = separator + 1;
+ }
+ else
+ value = "";
+
+ if (i > 0)
+ appendStringInfoString(buf, ", ");
+ appendStringInfo(buf, "%s=", quote_identifier(name));
+
+ /*
+ * In general we need to quote the value; but to avoid unnecessary
+ * clutter, do not quote if it is an identifier that would not
+ * need quoting. (We could also allow numbers, but that is a bit
+ * trickier than it looks --- for example, are leading zeroes
+ * significant? We don't want to assume very much here about what
+ * custom reloptions might mean.)
+ */
+ if (quote_identifier(value) == value)
+ appendStringInfoString(buf, value);
+ else
+ simple_quote_literal(buf, value);
+
+ pfree(option);
+ }
+}
+
+/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
static char *
@@ -10953,56 +11029,9 @@ flatten_reloptions(Oid relid)
if (!isnull)
{
StringInfoData buf;
- Datum *options;
- int noptions;
- int i;
initStringInfo(&buf);
-
- deconstruct_array(DatumGetArrayTypeP(reloptions),
- TEXTOID, -1, false, 'i',
- &options, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *option = TextDatumGetCString(options[i]);
- char *name;
- char *separator;
- char *value;
-
- /*
- * Each array element should have the form name=value. If the "="
- * is missing for some reason, treat it like an empty value.
- */
- name = option;
- separator = strchr(option, '=');
- if (separator)
- {
- *separator = '\0';
- value = separator + 1;
- }
- else
- value = "";
-
- if (i > 0)
- appendStringInfoString(&buf, ", ");
- appendStringInfo(&buf, "%s=", quote_identifier(name));
-
- /*
- * In general we need to quote the value; but to avoid unnecessary
- * clutter, do not quote if it is an identifier that would not
- * need quoting. (We could also allow numbers, but that is a bit
- * trickier than it looks --- for example, are leading zeroes
- * significant? We don't want to assume very much here about what
- * custom reloptions might mean.)
- */
- if (quote_identifier(value) == value)
- appendStringInfoString(&buf, value);
- else
- simple_quote_literal(&buf, value);
-
- pfree(option);
- }
+ get_reloptions(&buf, reloptions);
result = buf.data;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1ebf9c4..1556927 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -77,6 +77,7 @@
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
@@ -5212,6 +5213,185 @@ GetRelationPublicationActions(Relation relation)
}
/*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+ MemoryContext oldcxt;
+ bytea **opts;
+ Datum indoptionsDatum;
+ Datum *indoptions;
+ bool *indoptionsnulls;
+ int indoptionsnum;
+ int i;
+ int ncols = relation->rd_rel->relnatts;
+ bool isnull;
+
+ if (relation->rd_indoptions)
+ {
+ opts = palloc(sizeof(*opts) * ncols);
+
+ for (i = 0; i < ncols; i++)
+ {
+ bytea *opt = relation->rd_indoptions[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+ }
+
+ opts = palloc0(sizeof(*opts) * ncols);
+
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions))
+ {
+ indoptions = NULL;
+ indoptionsnulls = NULL;
+ indoptionsnum = 0;
+ }
+ else
+ {
+ indoptionsDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indoptions,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+
+ deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+ TEXTOID, -1, false, 'i',
+ &indoptions, &indoptionsnulls, &indoptionsnum);
+
+ Assert(indoptionsnum == ncols);
+ }
+
+ for (i = 0; i < ncols; i++)
+ {
+ Datum options;
+
+ if (i < indoptionsnum && !indoptionsnulls[i])
+ {
+ FunctionCallInfoData fcinfo;
+ FmgrInfo flinfo = { 0 };
+ char *optionsstr = TextDatumGetCString(indoptions[i]);
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL, NULL);
+
+ fcinfo.arg[0] = CStringGetDatum(optionsstr);
+ fcinfo.argnull[0] = false;
+ fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+ fcinfo.argnull[1] = false;
+ fcinfo.arg[2] = Int32GetDatum(-1);
+ fcinfo.argnull[2] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ options = array_in(&fcinfo);
+
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_in);
+
+ pfree(optionsstr);
+ }
+ else
+ {
+ options = PointerGetDatum(NULL);
+ }
+
+ opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+ if (options != PointerGetDatum(NULL))
+ pfree(DatumGetPointer(options));
+ }
+
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ relation->rd_indoptions = palloc(sizeof(*opts) * ncols);
+
+ for (i = 0; i < ncols; i++)
+ relation->rd_indoptions[i] = !opts[i] ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return opts;
+}
+
+Datum *
+ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols)
+{
+ Datum *indoptions;
+ bool *indoptionsnulls;
+ int indoptionsnum;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+ TEXTOID, -1, false, 'i',
+ &indoptions, &indoptionsnulls, &indoptionsnum);
+
+ Assert(indoptionsnum == ncols);
+
+ for (i = 0; i < indoptionsnum; i++)
+ {
+ if (indoptionsnulls[i])
+ indoptions[i] = PointerGetDatum(NULL);
+ else
+ {
+ FunctionCallInfoData fcinfo;
+ FmgrInfo flinfo = { 0 };
+ char *optionsstr = TextDatumGetCString(indoptions[i]);
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL,
+ NULL);
+
+ fcinfo.arg[0] = CStringGetDatum(optionsstr);
+ fcinfo.argnull[0] = false;
+ fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+ fcinfo.argnull[1] = false;
+ fcinfo.arg[2] = Int32GetDatum(-1);
+ fcinfo.argnull[2] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ indoptions[i] = array_in(&fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_in);
+ }
+ }
+
+ return indoptions;
+}
+
+/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for an index
+ */
+Datum *
+RelationGetRawOpclassOptions(Relation relation)
+{
+ Datum indoptionsDatum;
+ bool isnull;
+
+ /* Quick exit if there is nothing to do. */
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions))
+ return NULL;
+
+ indoptionsDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indoptions,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+
+ return ExtractRawOpclassOptions(indoptionsDatum,
+ relation->rd_rel->relnatts);
+}
+
+
+/*
* Routines to support ereport() reports of relation-related errors
*
* These could have been put into elog.c, but it seems like a module layering
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 8d7bc24..bde93c3 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -102,6 +102,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
typedef bytea *(*amoptions_function) (Datum reloptions,
bool validate);
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+ AttrNumber colno,
+ Datum indoptions,
+ bool validate);
+
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -203,6 +209,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
+ amopclassoptions_function amopclassoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 24c720b..0d490d6 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -174,6 +174,13 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
uint16 procnum);
extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
uint16 procnum);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+ Datum indoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+ AttrNumber attnum, uint16 procnum,
+ Datum indoptions, bool validate);
+
+
/*
* index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index b32c1e9..9bb82d2 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -263,12 +263,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen **gen, int nelems);
extern void *allocateReloptStruct(Size base, relopt_value *options,
int numoptions);
extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+ int offsets[], int noptions, size_t base_size,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 057a9f7..c40d009 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -56,6 +56,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
* each zero entry in indkey[] */
pg_node_tree indpred; /* expression tree for predicate, if a partial
* index; else NULL */
+ text indoptions[1]; /* opclass options */
#endif
} FormData_pg_index;
@@ -70,7 +71,7 @@ typedef FormData_pg_index *Form_pg_index;
* compiler constants for pg_index
* ----------------
*/
-#define Natts_pg_index 19
+#define Natts_pg_index 20
#define Anum_pg_index_indexrelid 1
#define Anum_pg_index_indrelid 2
#define Anum_pg_index_indnatts 3
@@ -90,6 +91,7 @@ typedef FormData_pg_index *Form_pg_index;
#define Anum_pg_index_indoption 17
#define Anum_pg_index_indexprs 18
#define Anum_pg_index_indpred 19
+#define Anum_pg_index_indoptions 20
/*
* Index AMs that support ordered scans must support these two indoption
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a953820..9e65097 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -129,6 +129,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?
@@ -155,6 +156,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 ac292bc..5a359f4 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -698,6 +698,7 @@ typedef struct IndexElem
char *indexcolname; /* name for index column; NULL = default */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
+ List *opclassopts; /* opclass-specific options, or NIL */
SortByDir ordering; /* ASC/DESC/default */
SortByNulls nulls_ordering; /* FIRST/LAST/default */
} IndexElem;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index db8de2d..795f7f6 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -740,6 +740,7 @@ typedef struct IndexOptInfo
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
+ bytea **opclassoptions; /* opclass-specific options for columns */
bool *canreturn; /* which index cols can be returned in an
* index-only scan? */
Oid relam; /* OID of the access method (in pg_am) */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index aa8add5..739478f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -191,6 +191,7 @@ typedef struct RelationData
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
void *rd_amcache; /* available for use by index AM */
Oid *rd_indcollation; /* OIDs of index collations */
+ bytea **rd_indoptions; /* parsed opclass-specific options */
/*
* foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 8a546ab..8ef5795 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
#ifndef RELCACHE_H
#define RELCACHE_H
+#include "postgres.h"
#include "access/tupdesc.h"
#include "nodes/bitmapset.h"
@@ -50,6 +51,9 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Relation relation);
+extern Datum *ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols);
typedef enum IndexAttrBitmapKind
{
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9f9b029..86e6953 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,5 +34,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 755cd17..5504dd9 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,8 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: access method "btree" does not support opclass options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR: access method "btree" does not support opclass options
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 65b08c8..b7a4890 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -92,3 +92,7 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
0002-opclass-parameters-GiST-v01.patchtext/x-patch; name=0002-opclass-parameters-GiST-v01.patchDownload
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9f5c0c3..2f00930 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
index-only scans (optional)</entry>
<entry>9</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>parse opclass-specific options (optional)</entry>
+ <entry>10</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 51c32e4..e260e2e 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -94,6 +94,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amopclassoptions = gistopclassoptions;
PG_RETURN_POINTER(amroutine);
}
@@ -1445,6 +1446,7 @@ initGISTstate(Relation index)
giststate->scanCxt = scanCxt;
giststate->tempCxt = scanCxt; /* caller must change this if needed */
giststate->tupdesc = index->rd_att;
+ giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < index->rd_att->natts; i++)
{
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index b30b931..fe8d75e 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -194,6 +194,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum test;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -214,13 +215,14 @@ gistindex_keytest(IndexScanDesc scan,
*/
recheck = true;
- test = FunctionCall5Coll(&key->sk_func,
+ test = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
if (!DatumGetBool(test))
return false;
@@ -255,6 +257,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum dist;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -277,13 +280,14 @@ gistindex_keytest(IndexScanDesc scan,
* about the flag, but are expected to never be lossy.
*/
recheck = false;
- dist = FunctionCall5Coll(&key->sk_func,
+ dist = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
*recheck_distances_p |= recheck;
*distance_p = DatumGetFloat8(dist);
}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index a7038cc..96a9290 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
evec->n = v->spl_nleft;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
sizeof(GISTENTRY) * evec->n);
- v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
evec->n = v->spl_nright;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
sizeof(GISTENTRY) * evec->n);
- v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
/*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
* Let the opclass-specific PickSplit method do its thing. Note that at
* this point we know there are no null keys in the entryvec.
*/
- FunctionCall2Coll(&giststate->picksplitFn[attno],
+ FunctionCall3Coll(&giststate->picksplitFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(entryvec),
- PointerGetDatum(sv));
+ PointerGetDatum(sv),
+ PointerGetDatum(giststate->opclassoptions[attno]));
if (sv->spl_nleft == 0 || sv->spl_nright == 0)
{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 55cccd2..bab02f1 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -23,6 +23,7 @@
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
+#include "utils/ruleutils.h"
#include "utils/syscache.h"
@@ -199,10 +200,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
}
/* Make union and store in attr array */
- attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+ attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
giststate->supportCollation[i],
PointerGetDatum(evec),
- PointerGetDatum(&attrsize));
+ PointerGetDatum(&attrsize),
+ PointerGetDatum(giststate->opclassoptions[i]));
isnull[i] = false;
}
@@ -268,10 +270,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
}
*dstisnull = false;
- *dst = FunctionCall2Coll(&giststate->unionFn[attno],
+ *dst = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&dstsize));
+ PointerGetDatum(&dstsize),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
}
@@ -280,10 +283,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
bool result;
- FunctionCall3Coll(&giststate->equalFn[attno],
+ FunctionCall4Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
a, b,
- PointerGetDatum(&result));
+ PointerGetDatum(&result),
+ PointerGetDatum(giststate->opclassoptions[attno]));
return result;
}
@@ -556,9 +560,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
return;
dep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(e)));
+ PointerGetDatum(e),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* decompressFn may just return the given pointer */
if (dep != e)
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -593,9 +598,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
/* there may not be a compress function in opclass */
if (OidIsValid(giststate->compressFn[i].fn_oid))
cep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+ DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
giststate->supportCollation[i],
- PointerGetDatum(¢ry)));
+ PointerGetDatum(¢ry),
+ PointerGetDatum(giststate->opclassoptions[i])));
else
cep = ¢ry;
compatt[i] = cep->key;
@@ -624,9 +630,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
fep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(&fentry)));
+ PointerGetDatum(&fentry),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* fetchFn set 'key', return it to the caller */
return fep->key;
@@ -694,11 +701,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
if (giststate->penaltyFn[attno].fn_strict == false ||
(isNullOrig == false && isNullAdd == false))
{
- FunctionCall3Coll(&giststate->penaltyFn[attno],
+ FunctionCall4Coll(&giststate->penaltyFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(orig),
PointerGetDatum(add),
- PointerGetDatum(&penalty));
+ PointerGetDatum(&penalty),
+ PointerGetDatum(giststate->opclassoptions[attno]));
/* disallow negative or NaN penalty */
if (isnan(penalty) || penalty < 0.0)
penalty = 0.0;
@@ -860,6 +868,14 @@ gistoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum options, bool validate)
+{
+ return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+ options, validate);
+}
+
+
/*
* gistproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index dd87dad..0fa5646 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
{
case GIST_CONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_UNION_PROC:
ok = check_amproc_signature(procform->amproc, opckeytype, false,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_COMPRESS_PROC:
case GIST_DECOMPRESS_PROC:
case GIST_FETCH_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 1, 1, INTERNALOID);
+ 1, 2, INTERNALOID, INTERNALOID);
break;
case GIST_PENALTY_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 3, 3, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ 3, 4, INTERNALOID,
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_PICKSPLIT_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_EQUAL_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 3, 3, opckeytype, opckeytype,
- INTERNALOID);
+ 3, 4, opckeytype, opckeytype,
+ INTERNALOID, INTERNALOID);
break;
case GIST_DISTANCE_PROC:
ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
+ break;
+ case GIST_OPCLASSOPT_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+ 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
- i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+ i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+ i == GIST_OPCLASSOPT_PROC)
continue; /* optional methods */
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 827566d..08e7ada 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
#define GIST_EQUAL_PROC 7
#define GIST_DISTANCE_PROC 8
#define GIST_FETCH_PROC 9
-#define GISTNProcs 9
+#define GIST_OPCLASSOPT_PROC 10
+#define GISTNProcs 10
/*
* Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 36ed724..eedd896 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -94,6 +94,8 @@ typedef struct GISTSTATE
/* Collations to pass to the support functions */
Oid supportCollation[INDEX_MAX_KEYS];
+
+ bytea **opclassoptions; /* parsed opclass-specific options */
} GISTSTATE;
@@ -436,6 +438,8 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_DEFAULT_FILLFACTOR 90
extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+ Datum options, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
0003-opclass-parameters-GIN-v01.patchtext/x-patch; name=0003-opclass-parameters-GIN-v01.patchDownload
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 2f00930..10937bc 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
</entry>
<entry>6</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>
+ parse opclass-specific options (optional)
+ </entry>
+ <entry>7</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 6fe67f3..eb6b778 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -178,13 +178,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
btree->ginstate->supportCollation[attnum - 1],
scanEntry->queryKey,
idatum,
UInt16GetDatum(scanEntry->strategy),
- PointerGetDatum(scanEntry->extra_data)));
-
+ PointerGetDatum(scanEntry->extra_data),
+ PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
if (cmp > 0)
return true;
else if (cmp < 0)
@@ -1460,12 +1460,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
ginstate->supportCollation[entry->attnum - 1],
entry->queryKey,
datum[off - 1],
UInt16GetDatum(entry->strategy),
- PointerGetDatum(entry->extra_data)));
+ PointerGetDatum(entry->extra_data),
+ PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
if (cmp == 0)
return true;
else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
*/
key->recheckCurItem = true;
- return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+ return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
PointerGetDatum(key->extra_data),
PointerGetDatum(&key->recheckCurItem),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
static GinTernaryValue
directTriConsistentFn(GinScanKey key)
{
- return DatumGetGinTernaryValue(FunctionCall7Coll(
+ return DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
{
GinTernaryValue result;
- result = DatumGetGinTernaryValue(FunctionCall7Coll(
+ result = DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
if (result == GIN_MAYBE)
{
key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
key->strategy = strategy;
key->searchMode = searchMode;
key->attnum = attnum;
+ key->opclassOptions = ginstate->opclassOptions[attnum - 1];
ItemPointerSetMin(&key->curItem);
key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
/* OK to call the extractQueryFn */
queryValues = (Datum *)
- DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+ DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
so->ginstate.supportCollation[skey->sk_attno - 1],
skey->sk_argument,
PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode)));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
/*
* If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 7bac7a1..c34b1bc 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -61,6 +61,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amoptions = ginoptions;
+ amroutine->amopclassoptions = ginopclassoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -93,6 +94,7 @@ initGinState(GinState *state, Relation index)
state->index = index;
state->oneCol = (origTupdesc->natts == 1) ? true : false;
state->origTupdesc = origTupdesc;
+ state->opclassOptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < origTupdesc->natts; i++)
{
@@ -401,9 +403,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
return 0;
/* both not null, so safe to call the compareFn */
- return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+ return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
- a, b));
+ a, b,
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
}
/*
@@ -439,6 +442,7 @@ typedef struct
{
FmgrInfo *cmpDatumFunc;
Oid collation;
+ Datum options;
bool haveDups;
} cmpEntriesArg;
@@ -460,9 +464,10 @@ cmpEntries(const void *a, const void *b, void *arg)
else if (bb->isnull)
res = -1; /* not-NULL "<" NULL */
else
- res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+ res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
data->collation,
- aa->datum, bb->datum));
+ aa->datum, bb->datum,
+ data->options));
/*
* Detect if we have any duplicates. If there are equal keys, qsort must
@@ -508,11 +513,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
/* OK, call the opclass's extractValueFn */
nullFlags = NULL; /* in case extractValue doesn't set it */
entries = (Datum *)
- DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+ DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
value,
PointerGetDatum(nentries),
- PointerGetDatum(&nullFlags)));
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
/*
* Generate a placeholder if the item contained no keys.
@@ -555,6 +561,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.collation = ginstate->supportCollation[attnum - 1];
+ arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
arg.haveDups = false;
qsort_arg(keydata, *nentries, sizeof(keyEntryData),
cmpEntries, (void *) &arg);
@@ -630,6 +637,14 @@ ginoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum indoptions,
+ bool validate)
+{
+ return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+ indoptions, validate);
+}
+
/*
* Fetch index's statistical data into *stats
*
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1035be4..3035c2c 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
{
case GIN_COMPARE_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 2, 2, opckeytype, opckeytype);
+ 2, 3, opckeytype, opckeytype,
+ INTERNALOID);
break;
case GIN_EXTRACTVALUE_PROC:
/* Some opclasses omit nullFlags */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 2, 3, opcintype, INTERNALOID,
- INTERNALOID);
+ 2, 4, opcintype, INTERNALOID,
+ INTERNALOID, INTERNALOID);
break;
case GIN_EXTRACTQUERY_PROC:
/* Some opclasses omit nullFlags and searchMode */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 5, 7, opcintype, INTERNALOID,
+ 5, 8, opcintype, INTERNALOID,
INT2OID, INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_CONSISTENT_PROC:
/* Some opclasses omit queryKeys and nullFlags */
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 6, 8, INTERNALOID, INT2OID,
+ 6, 9, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_COMPARE_PARTIAL_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 4, 4, opckeytype, opckeytype,
- INT2OID, INTERNALOID);
+ 4, 5, opckeytype, opckeytype,
+ INT2OID, INTERNALOID, INTERNALOID);
break;
case GIN_TRICONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, CHAROID, false,
- 7, 7, INTERNALOID, INT2OID,
+ 7, 8, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID);
+ INTERNALOID, INTERNALOID);
+ break;
+ case GIN_OPCLASSOPTIONS_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID,
+ false, 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
if (opclassgroup &&
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
- if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+ if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+ i == GIN_OPCLASSOPTIONS_PROC)
continue; /* optional method */
if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
continue; /* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index fcc8323..65ecf28 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -141,6 +141,7 @@
#include "utils/nabstime.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
+#include "utils/relcache.h"
#include "utils/selfuncs.h"
#include "utils/snapmgr.h"
#include "utils/spccache.h"
@@ -7410,7 +7411,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
else
collation = DEFAULT_COLLATION_OID;
- OidFunctionCall7Coll(extractProcOid,
+ OidFunctionCall8Coll(extractProcOid,
collation,
query,
PointerGetDatum(&nentries),
@@ -7418,7 +7419,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(index->opclassoptions[indexcol]));
if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 0acdb88..6240aab 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
#define GIN_TRICONSISTENT_PROC 6
-#define GINNProcs 6
+#define GIN_OPCLASSOPTIONS_PROC 7
+#define GINNProcs 7
/*
* searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index a709596..d371799 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
TupleDesc origTupdesc;
TupleDesc tupdesc[INDEX_MAX_KEYS];
+ bytea **opclassOptions; /* per-index-column opclass options */
+
/*
* Per-index-column opclass support functions
*/
@@ -85,6 +87,8 @@ typedef struct GinState
/* ginutil.c */
extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+ Datum indoptions, bool validate);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -297,6 +301,7 @@ typedef struct GinScanKeyData
StrategyNumber strategy;
int32 searchMode;
OffsetNumber attnum;
+ bytea *opclassOptions;
/*
* Match status data. curItem is the TID most recently tested (could be a
0004-opclass-parameters-GiST-tsvector_ops-v01.patchtext/x-patch; name=0004-opclass-parameters-GiST-tsvector_ops-v01.patchDownload
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 610b7bf..3fead3a 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3555,7 +3555,7 @@ SELECT plainto_tsquery('supernovae stars');
<tertiary>text search</tertiary>
</indexterm>
- <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+ <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
</term>
<listitem>
@@ -3563,6 +3563,8 @@ SELECT plainto_tsquery('supernovae stars');
Creates a GiST (Generalized Search Tree)-based index.
The <replaceable>column</replaceable> can be of <type>tsvector</type> or
<type>tsquery</type> type.
+ Optional integer parameter <literal>siglen</literal> determines
+ signature length in bytes (see below for details).
</para>
</listitem>
</varlistentry>
@@ -3586,7 +3588,10 @@ SELECT plainto_tsquery('supernovae stars');
to check the actual table row to eliminate such false matches.
(<productname>PostgreSQL</productname> does this automatically when needed.)
GiST indexes are lossy because each document is represented in the
- index by a fixed-length signature. The signature is generated by hashing
+ index by a fixed-length signature. Signature length in bytes is determined
+ by the value of the optional integer parameter <literal>siglen</literal>.
+ Default signature length (when <literal>siglen</literal> is not specied) is
+ 124 bytes, maximal length is 484 bytes. The signature is generated by hashing
each word into a single bit in an n-bit string, with all these bits OR-ed
together to produce an n-bit document signature. When two words hash to
the same bit position there will be a false match. If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 2d9ecc4..462791a 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,23 +15,29 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/pg_crc.h"
-#define SIGLENINT 31 /* >121 => key will toast, so it will not work
- * !!! */
+#define SIGLEN_DEFAULT (31 * 4)
+#define SIGLEN_MAX (121 * 4) /* key will toast, so it will not work !!! */
-#define SIGLEN ( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length */
+} GistTsVectorOptions;
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
#define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -39,8 +45,8 @@ typedef char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
#define GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
@@ -64,9 +70,10 @@ typedef struct
#define ISALLTRUE(x) ( ((SignTSVector*)(x))->flag & ALLISTRUE )
#define GTHDRSIZE ( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
#define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
#define GETARR(x) ( (int32*)( (char*)(x)+GTHDRSIZE ) )
#define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
@@ -90,7 +97,7 @@ static const uint8 number_of_ones[256] = {
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
Datum
gtsvectorin(PG_FUNCTION_ARGS)
@@ -121,9 +128,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
else
{
- int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+ int siglen = GETSIGLEN(key);
+ int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
- sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+ sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
}
PG_FREE_IF_COPY(key, 0);
@@ -167,36 +175,49 @@ uniqueint(int32 *a, int32 l)
}
static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
int32 *ptr = GETARR(a);
- MemSet((void *) sign, 0, sizeof(BITVEC));
+ MemSet((void *) sign, 0, siglen);
for (k = 0; k < len; k++)
- HASH(sign, ptr[k]);
+ HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+ int size = CALCGTSIZE(flag, len);
+ SignTSVector *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+ memcpy(GETSIGN(res), sign, len);
+
+ return res;
}
+
Datum
gtsvector_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{ /* tsvector */
- SignTSVector *res;
TSVector val = DatumGetTSVector(entry->key);
+ SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
int32 len;
int32 *arr;
WordEntry *ptr = ARRPTR(val);
char *words = STRPTR(val);
- len = CALCGTSIZE(ARRKEY, val->size);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = ARRKEY;
arr = GETARR(res);
len = val->size;
while (len--)
@@ -227,13 +248,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
/* make signature, if array is too long */
if (VARSIZE(res) > TOAST_INDEX_TARGET)
{
- SignTSVector *ressign;
+ SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
- len = CALCGTSIZE(SIGNKEY, 0);
- ressign = (SignTSVector *) palloc(len);
- SET_VARSIZE(ressign, len);
- ressign->flag = SIGNKEY;
- makesign(GETSIGN(ressign), res);
+ makesign(GETSIGN(ressign), res, siglen);
res = ressign;
}
@@ -245,22 +262,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
SignTSVector *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -334,12 +346,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
static bool
checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
{
+ void *key = (SignTSVector *) checkval;
+
/*
* we are not able to find a prefix in signature tree
*/
if (val->prefix)
return true;
- return GETBIT(checkval, HASHVAL(val->valcrc));
+ return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
}
Datum
@@ -366,7 +380,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
/* since signature is lossy, cannot specify CALC_NOT here */
PG_RETURN_BOOL(TS_execute(GETQUERY(query),
- (void *) GETSIGN(key),
+ key,
TS_EXEC_PHRASE_NO_POS,
checkcondition_bit));
}
@@ -384,7 +398,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
@@ -395,7 +409,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -403,7 +419,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
int32 *ptr = GETARR(add);
for (i = 0; i < ARRNELEM(add); i++)
- HASH(sbase, ptr[i]);
+ HASH(sbase, ptr[i], siglen);
}
return 0;
}
@@ -414,30 +430,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- SignTSVector *result;
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+ SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+ BITVECP base = GETSIGN(result);
+ int32 i;
+
+ memset(base, 0, siglen);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (SignTSVector *) palloc(len);
- *size = len;
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -448,6 +458,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -463,8 +474,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
BITVECP sa = GETSIGN(a),
sb = GETSIGN(b);
+ Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -501,24 +514,24 @@ gtsvector_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -529,17 +542,22 @@ hemdistsign(BITVECP a, BITVECP b)
static int
hemdist(SignTSVector *a, SignTSVector *b)
{
+ int siglena = GETSIGLEN(a);
+ int siglenb = GETSIGLEN(b);
+
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ Assert(siglena == siglenb);
+
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
}
Datum
@@ -548,6 +566,7 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -556,14 +575,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
if (ISARRKEY(newval))
{
- BITVEC sign;
+ BITVECP sign = palloc(siglen);
- makesign(sign, newval);
+ makesign(sign, newval, siglen);
if (ISALLTRUE(origval))
- *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+ {
+ int siglenbit = SIGLENBIT(siglen);
+
+ *penalty =
+ (float) (siglenbit - sizebitvec(sign, siglen)) /
+ (float) (siglenbit + 1);
+ }
else
- *penalty = hemdistsign(sign, orig);
+ *penalty = hemdistsign(sign, orig, siglen);
+
+ pfree(sign);
}
else
*penalty = hemdist(origval, newval);
@@ -573,19 +600,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
typedef struct
{
bool allistrue;
- BITVEC sign;
+ BITVECP sign;
} CACHESIGN;
static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
{
item->allistrue = false;
if (ISARRKEY(key))
- makesign(item->sign, key);
+ makesign(item->sign, key, siglen);
else if (ISALLTRUE(key))
item->allistrue = true;
else
- memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC));
+ memcpy((void *) item->sign, (void *) GETSIGN(key), siglen);
}
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
@@ -609,19 +636,19 @@ comparecost(const void *va, const void *vb)
static int
-hemdistcache(CACHESIGN *a, CACHESIGN *b)
+hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
{
if (a->allistrue)
{
if (b->allistrue)
return 0;
else
- return SIGLENBIT - sizebitvec(b->sign);
+ return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
}
else if (b->allistrue)
- return SIGLENBIT - sizebitvec(a->sign);
+ return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
- return hemdistsign(a->sign, b->sign);
+ return hemdistsign(a->sign, b->sign, siglen);
}
Datum
@@ -629,6 +656,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
OffsetNumber k,
j;
SignTSVector *datum_l,
@@ -648,6 +676,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
@@ -656,16 +685,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
v->spl_right = (OffsetNumber *) palloc(nbytes);
cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
- fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+ cache_sign = palloc(siglen * (maxoff + 2));
+
+ for (j = 0; j < maxoff + 2; j++)
+ cache[j].sign = &cache_sign[siglen * j];
+
+ fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+ siglen);
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
{
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
if (k == FirstOffsetNumber)
- fillcache(&cache[j], GETENTRY(entryvec, j));
+ fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
- size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+ size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -687,44 +722,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (cache[seed_1].allistrue)
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_l->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0));
- datum_l->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC));
- }
- if (cache[seed_2].allistrue)
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_r->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0));
- datum_r->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC));
- }
-
+ datum_l = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_1].sign);
+ datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_2].sign);
union_l = GETSIGN(datum_l);
union_r = GETSIGN(datum_r);
maxoff = OffsetNumberNext(maxoff);
- fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff));
+ fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen);
/* sort before ... */
costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
{
costvector[j - 1].pos = j;
- size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]));
- size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]));
+ size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
+ size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -750,36 +762,34 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign), siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign), siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -790,12 +800,12 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_r), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -822,3 +832,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
{
return gtsvector_consistent(fcinfo);
}
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistTsVectorOptions, siglen) };
+ GistTsVectorOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistTsVectorOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index eb595e8..b49a332 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -277,6 +277,7 @@ DATA(insert ( 3655 3614 3614 4 3649 ));
DATA(insert ( 3655 3614 3614 5 3653 ));
DATA(insert ( 3655 3614 3614 6 3650 ));
DATA(insert ( 3655 3614 3614 7 3652 ));
+DATA(insert ( 3655 3614 3614 10 3696 ));
DATA(insert ( 3702 3615 3615 1 3701 ));
DATA(insert ( 3702 3615 3615 2 3698 ));
DATA(insert ( 3702 3615 3615 3 3695 ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c00d055..91a5c48 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4756,22 +4756,24 @@ DATA(insert OID = 3635 ( ts_match_qv PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2
DATA(insert OID = 3760 ( ts_match_tt PGNSP PGUID 12 100 0 0 0 f f f f t f s s 2 0 16 "25 25" _null_ _null_ _null_ _null_ _null_ ts_match_tt _null_ _null_ _null_ ));
DATA(insert OID = 3761 ( ts_match_tq PGNSP PGUID 12 100 0 0 0 f f f f t f s s 2 0 16 "25 3615" _null_ _null_ _null_ _null_ _null_ ts_match_tq _null_ _null_ _null_ ));
-DATA(insert OID = 3648 ( gtsvector_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ gtsvector_compress _null_ _null_ _null_ ));
+DATA(insert OID = 3648 ( gtsvector_compress PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_compress _null_ _null_ _null_ ));
DESCR("GiST tsvector support");
-DATA(insert OID = 3649 ( gtsvector_decompress PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ gtsvector_decompress _null_ _null_ _null_ ));
+DATA(insert OID = 3649 ( gtsvector_decompress PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_decompress _null_ _null_ _null_ ));
DESCR("GiST tsvector support");
-DATA(insert OID = 3650 ( gtsvector_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_picksplit _null_ _null_ _null_ ));
+DATA(insert OID = 3650 ( gtsvector_picksplit PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_picksplit _null_ _null_ _null_ ));
DESCR("GiST tsvector support");
-DATA(insert OID = 3651 ( gtsvector_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 3642 "2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_union _null_ _null_ _null_ ));
+DATA(insert OID = 3651 ( gtsvector_union PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 3642 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_union _null_ _null_ _null_ ));
DESCR("GiST tsvector support");
-DATA(insert OID = 3652 ( gtsvector_same PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "3642 3642 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_same _null_ _null_ _null_ ));
+DATA(insert OID = 3652 ( gtsvector_same PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 2281 "3642 3642 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_same _null_ _null_ _null_ ));
DESCR("GiST tsvector support");
-DATA(insert OID = 3653 ( gtsvector_penalty PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_penalty _null_ _null_ _null_ ));
+DATA(insert OID = 3653 ( gtsvector_penalty PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_penalty _null_ _null_ _null_ ));
DESCR("GiST tsvector support");
-DATA(insert OID = 3654 ( gtsvector_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 5 0 16 "2281 3614 21 26 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_consistent _null_ _null_ _null_ ));
+DATA(insert OID = 3654 ( gtsvector_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 6 0 16 "2281 3614 21 26 2281 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_consistent _null_ _null_ _null_ ));
DESCR("GiST tsvector support");
DATA(insert OID = 3790 ( gtsvector_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i s 5 0 16 "2281 3642 23 26 2281" _null_ _null_ _null_ _null_ _null_ gtsvector_consistent_oldsig _null_ _null_ _null_ ));
DESCR("GiST tsvector support (obsolete)");
+DATA(insert OID = 3696 ( gtsvector_options PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ _null_ gtsvector_options _null_ _null_ _null_ ));
+DESCR("GiST tsvector support");
DATA(insert OID = 3656 ( gin_extract_tsvector PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "3614 2281 2281" _null_ _null_ _null_ _null_ _null_ gin_extract_tsvector _null_ _null_ _null_ ));
DESCR("GIN tsvector support");
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index d63fb12..c8353ab 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
508
(1 row)
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR: value 485 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR: parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a)
+ "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx2
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a tsvector_ops (siglen='484'))
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 1c8520b..396958d 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
В письме от 28 февраля 2018 00:46:36 пользователь Nikita Glukhov написал:
I would like to present patch set implementing opclass parameters.
This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdfA analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.
Hi!
You know, I am still working on this issue.
When I started to work on it, I found out that option code is not flexible at
all, and you can' reuse it for options that are not relOptions.
I gave your patch just a short glance for now, but as far as I can you start
deviding options into global and local one. I am afraid it will create grater
mess than it is now.
What I am doing right now, I am creating a new reloption internal API, that
will allow to create any option in any place using the very same code.
I think it should be done first, and then use it for opclass parameters and
any kind of options you like.
The big patch is here https://commitfest.postgresql.org/14/992/ (I am afraid
it will not apply to current master as it is, It is quite old. But you can get
the idea)
The smaller parts of the patch that in current commitfest are
https://commitfest.postgresql.org/17/1536/
https://commitfest.postgresql.org/17/1489/
You can help reviewing them. Then the whole thing will go faster. The second
patch is quite trivial. The first will also need attention of someone who is
really good in understanding postgres internals.
-----------
Concerning the patch that you've provided. I've just have a short look. But I
already have some question.
1. I've seen you've added a new attribute into pg_index. Why??!!
As far as I can get, if have index built on several columns (A1, A2, A3) you
can set, own opclass for each column. And set individual options for each
opclass if we are speaking about options. So I would expect to have these
options not in pg_index, but in pg_attribute. And we already have one there:
attoptions.I just do not get how you have come to per-index options. May be I
should read code more attentively...
2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for intarray.
May be you should do the same. (Use my example it will be more easy then do it
from scratch). It will give more understanding of how this improvement can be
used.
3.
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -150,3 +150,8 @@ vacuum btree_tall_tbl;
-- need to insert some rows to cause the fast root page to split.
insert into btree_tall_tbl (id, t)
select g, repeat('x', 100) from generate_series(1, 500) g;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: access method "btree" does not support opclass options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR: access method "btree" does not support opclass options
Are you sure we really need these in postgres???
---------------------------------
So my proposal is the following:
let's commit
https://commitfest.postgresql.org/17/1536/
https://commitfest.postgresql.org/17/1489/
I will provide a final patch for option engine refactoring in commit fest, and
you will write you implementation of opclass options on top of it. We can to
it simultaneously. Or I will write opclass options myself... I do not care who
will do oplcass options as long it is done using refactored options engine. If
you do it, I can review it.
Opclass parameters can give user ability to:
* Define the values of the constants that are hardcoded now in the
opclasses depending on the indexed data.
* Specify what to index for non-atomic data types (arrays, json[b],
tsvector). Partial index can only filter whole rows.
* Specify what indexing algorithm to use depending on the indexed data.Description of patches:
1. Infrastructure for opclass parameters.
SQL grammar is changed only for CREATE INDEX statement: parenthesized
parameters in reloptions format are added after column's opclass name.
Default opclass can be specified with DEFAULT keyword:CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);Example for contrib/intarray:
CREATE INDEX ON arrays USING gist (
arr gist__intbig_ops (siglen = 32),
arr DEFAULT (numranges = 100)
);\d arrays
Table "public.arrays"
Column | Type | Collation | Nullable | Default
--------+-----------+-----------+----------+---------
arr | integer[] | | |
Indexes:
"arrays_arr_arr1_idx" gist (arr gist__intbig_ops (siglen='32'), arr
gist__int_ops (numranges='100'))I decided to store parameters in text[] column pg_index.indoptions near to
existing columns like indkey, indcollation, indclass, indoption. I-th
element of indoptions[] is a text array of parameters of i-th index column
serialized into a string. Each parameter is stored as 'name=value' text
string like ordinal reloptions. There is another way to store opclass
parameters: store them in the existing column pg_attribute.attoptions (as
it was done by Nikolay Shaplov) and there will be no need to serialize
reloptions to a text array element.Example query showing how parameters are stored:
SELECT ARRAY(
SELECT (pg_identify_object('pg_opclass'::regclass, opcid, 0)).name
FROM unnest(indclass::int[]) opcid
) indclass, indoptions
FROM pg_index
WHERE indoptions IS NOT NULL;indclass | indoptions
----------------------------------+------------------------------------
{gist__intbig_ops,gist__int_ops} | {"{siglen=32}","{numranges=100}"}
{jsonb_path_ops} | {"{projection=$.tags[*].term}"}
(2 rows)Each access method supporting opclass parameters specifies amopclassoptions
routine for transformation of text[] parameters datum into a binary bytea
structure which will be cached in RelationData and IndexOptInfo structures:typedef bytea *(*amopclassoptions_function) (
Relation index, AttrNumber colnum, Datum indoptions, bool validate
);If access method wants simply to delegate parameters processing to one of
column opclass's support functions, then it can use
index_opclass_options_generic() subroutine in its amopclassoptions
implementation:bytea *index_opclass_options_generic(
Relation relation, AttrNumber attnum, uint16 procnum,
Datum indoptions, bool validate
);This support functions must have the following signature:
internal (options internal, validate bool).
Opclass parameters are passed as a text[] reloptions datum, returned pointer
to a bytea structure with parsed parameter values.Opclass can use new functions parseLocalRelOptions(),
parseAndFillLocalRelOptions() for reloptions parsing. This functions differ
from the standard parseRelOptions() in that a local array of reloptions
descriptions is passed here, not a global relopt_kind. But it seems that
reloptions processing still needs deeper refactoring like the one already
done by Nikolay Shaplov
(/messages/by-id/2146419.veIEZdk4E4@x200m
419.veIEZdk4E4@x200m).2. Opclass parameters support in GiST indices.
Parametrized GiST opclass specifies optional 10th (GIST_OPCLASSOPT_PROC)
support function with the following signature:
internal (options internal, validate bool)Returned parsed bytea pointer with parameters will be passed to all support
functions in the last argument.3. Opclass parameters support in GIN indices.
Everything is the same as for GiST, except for the optional support
function number which is 7 (GIN_OPCLASSOPTIONS_PROC) here.4. Opclass parameters for GiST tsvector_ops
5. Opclass parameters for contrib/intarray
6. Opclass parameters for contrib/ltree
7. Opclass parameters for contrib/pg_trgm
8. Opclass parameters for contrib/hstoreThis 5 patches for GiST opclasses are very similar: added optional 'siglen'
parameter for specifying signature length. Default signature length is left
equal to the hardcoded value that was here before. Also added 'numranges'
parameter for gist__int_ops.We also have two more complex unfinished patches for GIN opclasses which
should be posted in separate threads:* tsvector_ops: added parameter 'weights' for specification of indexed
lexeme's weight groups. This parameter can reduce index size and its
build/update time and can also eliminate recheck. By default, all
weights are indexed within the same group.* jsonb_ops: added jsonpath parameter 'projection' for specification of
indexed paths in jsonb (this patch depends on SQL/JSON jsonpath patch).
Analogically to tsvector_ops, this parameter can reduce index size and
its build/update time, but can not eliminate recheck.
--
Do code for fun.
Hi Nikita,
On 2/28/18 9:46 AM, Nikolay Shaplov wrote:
В письме от 28 февраля 2018 00:46:36 пользователь Nikita Glukhov написал:
I would like to present patch set implementing opclass parameters.
This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdfA analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.You know, I am still working on this issue.
This patch was submitted to the 2018-03 CF at the last moment with no
prior discussion or review as far as I can tell. It appears to be
non-trivial and therefore not a good fit for the last CF for PG11.
In addition, based on Nikolay's response, I think the patch should be
marked Returned with Feedback until it is reconciled with the existing
patches.
Any objects to marking this Returned with Feedback? Or, I can move it
to the next CF as is.
Regards,
--
-David
david@pgmasters.net
On Wed, Feb 28, 2018 at 5:46 PM, Nikolay Shaplov <dhyan@nataraj.su> wrote:
Concerning the patch that you've provided. I've just have a short look. But I
already have some question.1. I've seen you've added a new attribute into pg_index. Why??!!
As far as I can get, if have index built on several columns (A1, A2, A3) you
can set, own opclass for each column. And set individual options for each
opclass if we are speaking about options. So I would expect to have these
options not in pg_index, but in pg_attribute. And we already have one there:
attoptions.I just do not get how you have come to per-index options. May be I
should read code more attentively...
this is what we want to discuss.
2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for intarray.
May be you should do the same. (Use my example it will be more easy then do it
from scratch). It will give more understanding of how this improvement can be
used.
Hey, look on patches, there are many examples !
3.
--- a/src/test/regress/expected/btree_index.out +++ b/src/test/regress/expected/btree_index.out @@ -150,3 +150,8 @@ vacuum btree_tall_tbl; -- need to insert some rows to cause the fast root page to split. insert into btree_tall_tbl (id, t) select g, repeat('x', 100) from generate_series(1, 500) g; +-- Test unsupported btree opclass parameters +create index on btree_tall_tbl (id int4_ops(foo=1)); +ERROR: access method "btree" does not support opclass options +create index on btree_tall_tbl (id default(foo=1)); +ERROR: access method "btree" does not support opclass optionsAre you sure we really need these in postgres???
Hey, this is a just a test to check unsupported opclass options !
On Thu, Mar 1, 2018 at 7:02 PM, David Steele <david@pgmasters.net> wrote:
Hi Nikita,
On 2/28/18 9:46 AM, Nikolay Shaplov wrote:
В письме от 28 февраля 2018 00:46:36 пользователь Nikita Glukhov написал:
I would like to present patch set implementing opclass parameters.
This feature was recently presented at pgconf.ru:
http://www.sai.msu.su/~megera/postgres/talks/opclass_pgconf.ru-2018.pdfA analogous work was already done by Nikolay Shaplov two years ago:
/messages/by-id/5213596.TqFRiqmCTe@nataraj-amd64
But this patches are not based on it, although they are very similar.You know, I am still working on this issue.
This patch was submitted to the 2018-03 CF at the last moment with no
prior discussion or review as far as I can tell. It appears to be
non-trivial and therefore not a good fit for the last CF for PG11.
the idea is simple, the main problem is where to store parameters.
We hoped that we get a bright idea from developers.
In addition, based on Nikolay's response, I think the patch should be
marked Returned with Feedback until it is reconciled with the existing
patches.
We proposed something that works and could be useful for people,
especially for people, who use complex json documents. It would
require minimal changes if Nikolay's patch, which is quite invasive,
will be committed in future.
What we need to discuss is the user-visible features - the syntax changes.
Any objects to marking this Returned with Feedback? Or, I can move it
to the next CF as is.
I think that Returned with Feedback would be good. We will continue
discussion in -hackers.
Show quoted text
Regards,
--
-David
david@pgmasters.net
On 3/1/18 3:50 PM, Oleg Bartunov wrote:
On Thu, Mar 1, 2018 at 7:02 PM, David Steele <david@pgmasters.net> wrote:
Any objections to marking this Returned with Feedback? Or, I can move it
to the next CF as is.I think that Returned with Feedback would be good. We will continue
discussion in -hackers.
Marked as Returned with Feedback. Hopefully we'll see this patch in the
2018-09 CF.
--
-David
david@pgmasters.net
В письме от 1 марта 2018 23:02:20 пользователь Oleg Bartunov написал:
2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for
intarray. May be you should do the same. (Use my example it will be more
easy then do it from scratch). It will give more understanding of how
this improvement can be used.Hey, look on patches, there are many examples !
Oups...Sorry. I've looked at the patch from commitfest
https://commitfest.postgresql.org/17/1559/ and it have shown only the first
file. And When I read the letter I did not pay attention to attachments at
all. So I was sure there is only one there.
Yes. Now I see seven examples. But I think seven it is too many.
For each case a reviewer should make consideration if this parameter worth
moving to opclass options, or fixed definition in the C code is quite ok.
Doing it for whole bunch, may make it messy. I think, it would be good to
commit an implementation of opclass options, with a good example of usage. And
then commit patches for all cases where these options can be used.
But since it is now "Rejected with feedback", let's wait till autumn.
--
Do code for fun.
On 02.03.2018 19:12, Nikolay Shaplov wrote:
В письме от 1 марта 2018 23:02:20 пользователь Oleg Bartunov написал:
2. Your patch does not provide any example of your new tool usage. In my
prototype patch I've shown the implementation of opclass options for
intarray. May be you should do the same. (Use my example it will be more
easy then do it from scratch). It will give more understanding of how
this improvement can be used.Hey, look on patches, there are many examples !
Oups...Sorry. I've looked at the patch from commitfest
https://commitfest.postgresql.org/17/1559/ and it have shown only the first
file. And When I read the letter I did not pay attention to attachments at
all. So I was sure there is only one there.Yes. Now I see seven examples. But I think seven it is too many.
For each case a reviewer should make consideration if this parameter worth
moving to opclass options, or fixed definition in the C code is quite ok.
Doing it for whole bunch, may make it messy. I think, it would be good to
commit an implementation of opclass options, with a good example of usage. And
then commit patches for all cases where these options can be used.
There are 5 examples for GiST opclasses, not 7, and they are almost
identical -- in all of them added 'siglen' parameter for signature length
specification.
But since it is now "Rejected with feedback", let's wait till autumn.
We don't want to wait that long. But now we only need to сome to an agreement
about CREATE INDEX syntax and where to store the opclass parameters.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On 03.03.2018 1:32, Nikita Glukhov wrote:
On 02.03.2018 19:12, Nikolay Shaplov wrote:
But since it is now "Rejected with feedback", let's wait till autumn.
We don't want to wait that long. But now we only need to сome to an
agreement
about CREATE INDEX syntax and where to store the opclass parameters.
Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.
CREATE INDEX syntax and parameters storage method still need discussion.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Add-opclass-parameters-v02.patchtext/x-patch; name=0001-Add-opclass-parameters-v02.patchDownload
From 76513025cf34b2a72c8396a3d868db286fe54dbb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Wed, 10 Jan 2018 00:46:28 +0300
Subject: [PATCH 1/9] Add opclass parameters
---
doc/src/sgml/catalogs.sgml | 11 ++
doc/src/sgml/indices.sgml | 2 +-
doc/src/sgml/ref/create_index.sgml | 16 ++-
src/backend/access/common/reloptions.c | 142 +++++++++++++++++-------
src/backend/access/index/indexam.c | 72 ++++++++++++
src/backend/catalog/index.c | 60 ++++++++++
src/backend/catalog/toasting.c | 1 +
src/backend/commands/indexcmds.c | 12 ++
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/plancat.c | 4 +
src/backend/parser/gram.y | 72 +++++++-----
src/backend/utils/adt/ruleutils.c | 133 +++++++++++++---------
src/backend/utils/cache/relcache.c | 179 ++++++++++++++++++++++++++++++
src/include/access/amapi.h | 7 ++
src/include/access/genam.h | 6 +
src/include/access/reloptions.h | 5 +
src/include/catalog/pg_index.h | 1 +
src/include/nodes/execnodes.h | 2 +
src/include/nodes/parsenodes.h | 1 +
src/include/nodes/relation.h | 1 +
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 4 +
src/include/utils/ruleutils.h | 2 +
src/test/regress/expected/btree_index.out | 5 +
src/test/regress/expected/misc_sanity.out | 3 +-
src/test/regress/sql/btree_index.sql | 4 +
28 files changed, 625 insertions(+), 124 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 8b7f169..bdba62b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3901,6 +3901,17 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
partial index.
</entry>
</row>
+
+ <row>
+ <entry><structfield>indoptions</structfield></entry>
+ <entry><type>text[]</type></entry>
+ <entry></entry>
+ <entry>
+ This is an array of <structfield>indnatts</structfield> strings that
+ store per-column opclass options or null. Each array element is a
+ serialized array of column's opclass options in reloptions format or null.
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index df7d16f..8afc93e 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -993,7 +993,7 @@ CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
An index definition can specify an <firstterm>operator
class</firstterm> for each column of an index.
<synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
</synopsis>
The operator class identifies the operators to be used by the index
for that column. For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index ad619cd..1946d3b 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
- ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+ ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</varlistentry>
<varlistentry>
+ <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of an operator class parameter. See below for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>ASC</literal></term>
<listitem>
<para>
@@ -607,8 +616,9 @@ Indexes:
</para>
<para>
- An <firstterm>operator class</firstterm> can be specified for each
- column of an index. The operator class identifies the operators to be
+ An <firstterm>operator class</firstterm> with its optional parameters
+ can be specified for each column of an index.
+ The operator class identifies the operators to be
used by the index for that column. For example, a B-tree index on
four-byte integers would use the <literal>int4_ops</literal> class;
this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index db84da0..4820a14 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1025,6 +1025,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
return options;
}
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+ relopt_value *reloptions, int numoptions)
+{
+ ArrayType *array = DatumGetArrayTypeP(options);
+ Datum *optiondatums;
+ int noptions;
+ int i;
+
+ deconstruct_array(array, TEXTOID, -1, false, 'i',
+ &optiondatums, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *text_str = VARDATA(optiondatums[i]);
+ int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+ int j;
+
+ /* Search for a match in reloptions */
+ for (j = 0; j < numoptions; j++)
+ {
+ int kw_len = reloptions[j].gen->namelen;
+
+ if (text_len > kw_len && text_str[kw_len] == '=' &&
+ strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+ {
+ parse_one_reloption(&reloptions[j], text_str, text_len,
+ validate);
+ break;
+ }
+ }
+
+ if (j >= numoptions && validate)
+ {
+ char *s;
+ char *p;
+
+ s = TextDatumGetCString(optiondatums[i]);
+ p = strchr(s, '=');
+ if (p)
+ *p = '\0';
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s\"", s)));
+ }
+ }
+
+ /* It's worth avoiding memory leaks in this function */
+ pfree(optiondatums);
+
+ if (((void *) array) != DatumGetPointer(options))
+ pfree(array);
+}
+
/*
* Interpret reloptions that are given in text-array format.
*
@@ -1079,57 +1133,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
/* Done if no options */
if (PointerIsValid(DatumGetPointer(options)))
- {
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
+ parseRelOptionsInternal(options, validate, reloptions, numoptions);
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
+ *numrelopts = numoptions;
+ return reloptions;
+}
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen *optgen[], int nopts)
+{
+ relopt_value *values = palloc(sizeof(*values) * nopts);
+ int i;
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
+ for (i = 0; i < nopts; i++)
+ {
+ values[i].gen = optgen[i];
+ values[i].isset = false;
+ }
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
- {
- parse_one_reloption(&reloptions[j], text_str, text_len,
- validate);
- break;
- }
- }
+ if (options != (Datum) 0)
+ parseRelOptionsInternal(options, validate, values, nopts);
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
+ return values;
+}
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+ int noptions, size_t base_size, bool validate)
+{
+ relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+ relopt_value *vals;
+ void *opts;
+ int i;
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
+ for (i = 0; i < noptions; i++)
+ {
+ elems[i].optname = optgen[i]->name;
+ elems[i].opttype = optgen[i]->type;
+ elems[i].offset = offsets[i];
}
- *numrelopts = numoptions;
- return reloptions;
+ vals = parseLocalRelOptions(options, validate, optgen, noptions);
+ opts = allocateReloptStruct(base_size, vals, noptions);
+ fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
}
/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index eade540..c03ad0c 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -75,11 +75,14 @@
#include "access/xlog.h"
#include "catalog/index.h"
#include "catalog/pg_type.h"
+#include "commands/defrem.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
+#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
@@ -967,3 +970,72 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
}
}
}
+
+/*
+ * Parse opclass-specific options for index column.
+ *
+ * amoptions index relation
+ * attnum column number
+ * indoptions options as text[] datum
+ * validate error flag
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum indoptions,
+ bool validate)
+{
+ amopclassoptions_function amopclassoptions =
+ relation->rd_amroutine->amopclassoptions;
+
+ if (!amopclassoptions)
+ {
+ if (validate && PointerIsValid(DatumGetPointer(indoptions)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("access method \"%s\" does not support opclass options ",
+ get_am_name(relation->rd_rel->relam))));
+
+ return NULL;
+ }
+
+ return amopclassoptions(relation, attnum, indoptions, validate);
+}
+
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+ uint16 procnum, Datum reloptions, bool validate)
+{
+ Oid procid = index_getprocid(indrel, attnum, procnum);
+ FmgrInfo *procinfo;
+
+ if (!OidIsValid(procid))
+ {
+ StringInfoData opclassname;
+ Oid opclass;
+ Datum indclassDatum;
+ oidvector *indclass;
+ bool isnull;
+
+ if (!DatumGetPointer(reloptions))
+ return NULL;
+
+ indclassDatum = SysCacheGetAttr(INDEXRELID, indrel->rd_indextuple,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+ indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+ opclass = indclass->values[attnum - 1];
+
+ initStringInfo(&opclassname);
+ get_opclass_name(opclass, InvalidOid, &opclassname);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("operator class%s has no options ", opclassname.data)));
+ }
+
+ procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+ return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+ reloptions,
+ BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 4088286..e186dc6 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -25,6 +25,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
@@ -588,6 +589,7 @@ UpdateIndexRelation(Oid indexoid,
int2vector *indoption;
Datum exprsDatum;
Datum predDatum;
+ Datum indoptionsDatum;
Datum values[Natts_pg_index];
bool nulls[Natts_pg_index];
Relation pg_index;
@@ -634,6 +636,53 @@ UpdateIndexRelation(Oid indexoid,
else
predDatum = (Datum) 0;
+ if (indexInfo->ii_OpclassOptions != NULL)
+ {
+ Datum *vals = palloc(sizeof(Datum) * indexInfo->ii_NumIndexAttrs);
+ bool *nulls = palloc(sizeof(bool) * indexInfo->ii_NumIndexAttrs);
+ int dims[1];
+ int lbs[1];
+
+ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+ {
+ Datum options = indexInfo->ii_OpclassOptions[i];
+
+ nulls[i] = options == (Datum) 0;
+
+ if (!nulls[i])
+ {
+ FunctionCallInfoData fcinfo;
+ Datum result;
+ FmgrInfo flinfo = { 0 };
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 1, InvalidOid, NULL,
+ NULL);
+
+ fcinfo.arg[0] = options;
+ fcinfo.argnull[0] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ result = array_out(&fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_out);
+
+ vals[i] = CStringGetTextDatum(DatumGetCString(result));
+ }
+ }
+
+ dims[0] = indexInfo->ii_NumIndexAttrs;
+ lbs[0] = 1;
+
+ indoptionsDatum = PointerGetDatum(construct_md_array(vals, nulls, 1,
+ dims, lbs, TEXTOID,
+ -1, false, 'i'));
+ }
+ else
+ indoptionsDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
@@ -668,6 +717,9 @@ UpdateIndexRelation(Oid indexoid,
values[Anum_pg_index_indpred - 1] = predDatum;
if (predDatum == (Datum) 0)
nulls[Anum_pg_index_indpred - 1] = true;
+ values[Anum_pg_index_indoptions - 1] = indoptionsDatum;
+ if (indoptionsDatum == (Datum) 0)
+ nulls[Anum_pg_index_indoptions - 1] = true;
tuple = heap_form_tuple(RelationGetDescr(pg_index), values, nulls);
@@ -1175,6 +1227,12 @@ index_create(Relation heapRelation,
indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
+ /* Validate opclass-specific options */
+ if (indexInfo->ii_OpclassOptions)
+ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+ (void) index_opclass_options(indexRelation, i + 1,
+ indexInfo->ii_OpclassOptions[i], true);
+
/*
* If this is bootstrap (initdb) time, then we don't actually fill in the
* index yet. We'll be creating more indexes and classes later, so we
@@ -1803,6 +1861,8 @@ BuildIndexInfo(Relation index)
ii->ii_ExclusionStrats = NULL;
}
+ ii->ii_OpclassOptions = RelationGetRawOpclassOptions(index);
+
/* other info */
ii->ii_Unique = indexStruct->indisunique;
ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3baaa08..8d08b6a 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -313,6 +313,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL;
indexInfo->ii_Unique = true;
indexInfo->ii_ReadyForInserts = true;
indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 906d711..75c6013 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -642,6 +642,7 @@ DefineIndex(Oid relationId,
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL; /* for now */
indexInfo->ii_Unique = stmt->unique;
/* In a concurrent build, mark it not-ready-for-inserts */
indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1695,6 +1696,17 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName)));
}
+ /* Set up the per-column opclass options (indoptions field). */
+ if (attribute->opclassopts)
+ {
+ if (!indexInfo->ii_OpclassOptions)
+ indexInfo->ii_OpclassOptions = palloc0(sizeof(Datum) * nkeycols);
+
+ indexInfo->ii_OpclassOptions[attn] =
+ transformRelOptions((Datum) 0, attribute->opclassopts,
+ NULL, NULL, false, false);
+ }
+
attn++;
}
}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index db49968..ad3ff3c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2856,6 +2856,7 @@ _copyIndexElem(const IndexElem *from)
COPY_STRING_FIELD(indexcolname);
COPY_NODE_FIELD(collation);
COPY_NODE_FIELD(opclass);
+ COPY_NODE_FIELD(opclassopts);
COPY_SCALAR_FIELD(ordering);
COPY_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a084b4..e3dfe6e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2538,6 +2538,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
COMPARE_STRING_FIELD(indexcolname);
COMPARE_NODE_FIELD(collation);
COMPARE_NODE_FIELD(opclass);
+ COMPARE_NODE_FIELD(opclassopts);
COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f0c3965..a23c508 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2931,6 +2931,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
WRITE_STRING_FIELD(indexcolname);
WRITE_NODE_FIELD(collation);
WRITE_NODE_FIELD(opclass);
+ WRITE_NODE_FIELD(opclassopts);
WRITE_ENUM_FIELD(ordering, SortByDir);
WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 0c88c90..2af7c41 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -361,6 +361,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->nulls_first = NULL;
}
+ /* Fetch index opclass options */
+ info->opclassoptions =
+ RelationGetParsedOpclassOptions(indexRelation);
+
/*
* Fetch the index expressions and predicate, if any. We must
* modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2effd51..9584262 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -485,7 +485,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <alias> alias_clause opt_alias_clause
%type <list> func_alias_clause
%type <sortby> sortby
-%type <ielem> index_elem
+%type <ielem> index_elem index_elem_options
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
@@ -7433,43 +7433,65 @@ index_params: index_elem { $$ = list_make1($1); }
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
+
+index_elem_options:
+ opt_collate opt_class opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = NIL;
+ $$->ordering = $3;
+ $$->nulls_ordering = $4;
+ }
+ | opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ | opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = NIL;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ ;
+
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
{
- $$ = makeNode(IndexElem);
+ $$ = $2;
$$->name = $1;
- $$->expr = NULL;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $2;
$$->expr = $1;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $4;
$$->expr = $2;
- $$->indexcolname = NULL;
- $$->collation = $4;
- $$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
}
;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 29884f1..64a4990 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -452,8 +452,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
deparse_context *context);
static void get_tablesample_def(TableSampleClause *tablesample,
deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
- StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
@@ -468,6 +466,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
static char *generate_qualified_type_name(Oid typid);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -1197,6 +1196,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
oidvector *indcollation;
oidvector *indclass;
int2vector *indoption;
+ Datum *indoptions = NULL;
StringInfoData buf;
char *str;
char *sep;
@@ -1232,6 +1232,16 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Assert(!isnull);
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+ if (!attrsOnly)
+ {
+ Datum indoptionsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+ Anum_pg_index_indoptions,
+ &isnull);
+ if (!isnull)
+ indoptions = ExtractRawOpclassOptions(indoptionsDatum,
+ idxrec->indnatts);
+ }
+
/*
* Fetch the pg_class tuple of the index relation
*/
@@ -1370,6 +1380,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
(!colno || colno == keyno + 1))
{
Oid indcoll;
+ bool has_options =
+ indoptions && indoptions[keyno] != (Datum) 0;
/* Add collation, if not default for column */
indcoll = indcollation->values[keyno];
@@ -1378,7 +1390,15 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
generate_collation_name((indcoll)));
/* Add the operator class name, if not default */
- get_opclass_name(indclass->values[keyno], keycoltype, &buf);
+ get_opclass_name(indclass->values[keyno],
+ has_options ? InvalidOid : keycoltype, &buf);
+
+ if (has_options)
+ {
+ appendStringInfoString(&buf, " (");
+ get_reloptions(&buf, indoptions[keyno]);
+ appendStringInfoChar(&buf, ')');
+ }
/* Add options if relevant */
if (amroutine->amcanorder)
@@ -10401,7 +10421,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
* actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.)
*/
-static void
+void
get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf)
{
@@ -11112,6 +11132,62 @@ string_to_text(char *str)
}
/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+ Datum *options;
+ int noptions;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(reloptions),
+ TEXTOID, -1, false, 'i',
+ &options, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *option = TextDatumGetCString(options[i]);
+ char *name;
+ char *separator;
+ char *value;
+
+ /*
+ * Each array element should have the form name=value. If the "="
+ * is missing for some reason, treat it like an empty value.
+ */
+ name = option;
+ separator = strchr(option, '=');
+ if (separator)
+ {
+ *separator = '\0';
+ value = separator + 1;
+ }
+ else
+ value = "";
+
+ if (i > 0)
+ appendStringInfoString(buf, ", ");
+ appendStringInfo(buf, "%s=", quote_identifier(name));
+
+ /*
+ * In general we need to quote the value; but to avoid unnecessary
+ * clutter, do not quote if it is an identifier that would not
+ * need quoting. (We could also allow numbers, but that is a bit
+ * trickier than it looks --- for example, are leading zeroes
+ * significant? We don't want to assume very much here about what
+ * custom reloptions might mean.)
+ */
+ if (quote_identifier(value) == value)
+ appendStringInfoString(buf, value);
+ else
+ simple_quote_literal(buf, value);
+
+ pfree(option);
+ }
+}
+
+/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
static char *
@@ -11131,56 +11207,9 @@ flatten_reloptions(Oid relid)
if (!isnull)
{
StringInfoData buf;
- Datum *options;
- int noptions;
- int i;
initStringInfo(&buf);
-
- deconstruct_array(DatumGetArrayTypeP(reloptions),
- TEXTOID, -1, false, 'i',
- &options, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *option = TextDatumGetCString(options[i]);
- char *name;
- char *separator;
- char *value;
-
- /*
- * Each array element should have the form name=value. If the "="
- * is missing for some reason, treat it like an empty value.
- */
- name = option;
- separator = strchr(option, '=');
- if (separator)
- {
- *separator = '\0';
- value = separator + 1;
- }
- else
- value = "";
-
- if (i > 0)
- appendStringInfoString(&buf, ", ");
- appendStringInfo(&buf, "%s=", quote_identifier(name));
-
- /*
- * In general we need to quote the value; but to avoid unnecessary
- * clutter, do not quote if it is an identifier that would not
- * need quoting. (We could also allow numbers, but that is a bit
- * trickier than it looks --- for example, are leading zeroes
- * significant? We don't want to assume very much here about what
- * custom reloptions might mean.)
- */
- if (quote_identifier(value) == value)
- appendStringInfoString(&buf, value);
- else
- simple_quote_literal(&buf, value);
-
- pfree(option);
- }
+ get_reloptions(&buf, reloptions);
result = buf.data;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index aecbd4a..0fd7dbe 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5257,6 +5257,185 @@ GetRelationPublicationActions(Relation relation)
}
/*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+ MemoryContext oldcxt;
+ bytea **opts;
+ Datum indoptionsDatum;
+ Datum *indoptions;
+ bool *indoptionsnulls;
+ int indoptionsnum;
+ int i;
+ int ncols = relation->rd_rel->relnatts;
+ bool isnull;
+
+ if (relation->rd_indoptions)
+ {
+ opts = palloc(sizeof(*opts) * ncols);
+
+ for (i = 0; i < ncols; i++)
+ {
+ bytea *opt = relation->rd_indoptions[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+ }
+
+ opts = palloc0(sizeof(*opts) * ncols);
+
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions, NULL))
+ {
+ indoptions = NULL;
+ indoptionsnulls = NULL;
+ indoptionsnum = 0;
+ }
+ else
+ {
+ indoptionsDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indoptions,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+
+ deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+ TEXTOID, -1, false, 'i',
+ &indoptions, &indoptionsnulls, &indoptionsnum);
+
+ Assert(indoptionsnum == ncols);
+ }
+
+ for (i = 0; i < ncols; i++)
+ {
+ Datum options;
+
+ if (i < indoptionsnum && !indoptionsnulls[i])
+ {
+ FunctionCallInfoData fcinfo;
+ FmgrInfo flinfo = { 0 };
+ char *optionsstr = TextDatumGetCString(indoptions[i]);
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL, NULL);
+
+ fcinfo.arg[0] = CStringGetDatum(optionsstr);
+ fcinfo.argnull[0] = false;
+ fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+ fcinfo.argnull[1] = false;
+ fcinfo.arg[2] = Int32GetDatum(-1);
+ fcinfo.argnull[2] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ options = array_in(&fcinfo);
+
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_in);
+
+ pfree(optionsstr);
+ }
+ else
+ {
+ options = PointerGetDatum(NULL);
+ }
+
+ opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+ if (options != PointerGetDatum(NULL))
+ pfree(DatumGetPointer(options));
+ }
+
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ relation->rd_indoptions = palloc(sizeof(*opts) * ncols);
+
+ for (i = 0; i < ncols; i++)
+ relation->rd_indoptions[i] = !opts[i] ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return opts;
+}
+
+Datum *
+ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols)
+{
+ Datum *indoptions;
+ bool *indoptionsnulls;
+ int indoptionsnum;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(indoptionsDatum),
+ TEXTOID, -1, false, 'i',
+ &indoptions, &indoptionsnulls, &indoptionsnum);
+
+ Assert(indoptionsnum == ncols);
+
+ for (i = 0; i < indoptionsnum; i++)
+ {
+ if (indoptionsnulls[i])
+ indoptions[i] = PointerGetDatum(NULL);
+ else
+ {
+ FunctionCallInfoData fcinfo;
+ FmgrInfo flinfo = { 0 };
+ char *optionsstr = TextDatumGetCString(indoptions[i]);
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 3, InvalidOid, NULL,
+ NULL);
+
+ fcinfo.arg[0] = CStringGetDatum(optionsstr);
+ fcinfo.argnull[0] = false;
+ fcinfo.arg[1] = Int32GetDatum(TEXTOID);
+ fcinfo.argnull[1] = false;
+ fcinfo.arg[2] = Int32GetDatum(-1);
+ fcinfo.argnull[2] = false;
+
+ flinfo.fn_mcxt = CurrentMemoryContext;
+
+ indoptions[i] = array_in(&fcinfo);
+
+ /* Check for null result, since caller is clearly not expecting one */
+ if (fcinfo.isnull)
+ elog(ERROR, "function %p returned NULL", (void *) array_in);
+ }
+ }
+
+ return indoptions;
+}
+
+/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for an index
+ */
+Datum *
+RelationGetRawOpclassOptions(Relation relation)
+{
+ Datum indoptionsDatum;
+ bool isnull;
+
+ /* Quick exit if there is nothing to do. */
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indoptions, NULL))
+ return NULL;
+
+ indoptionsDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indoptions,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+
+ return ExtractRawOpclassOptions(indoptionsDatum,
+ relation->rd_rel->relnatts);
+}
+
+
+/*
* Routines to support ereport() reports of relation-related errors
*
* These could have been put into elog.c, but it seems like a module layering
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 14526a6..c1da8e9 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -103,6 +103,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
typedef bytea *(*amoptions_function) (Datum reloptions,
bool validate);
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+ AttrNumber colno,
+ Datum indoptions,
+ bool validate);
+
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -212,6 +218,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
+ amopclassoptions_function amopclassoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 534fac7..e558ba3 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -177,6 +177,12 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
extern void index_store_float8_orderby_distances(IndexScanDesc scan,
Oid *orderByTypes, double *distances,
bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+ Datum indoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+ AttrNumber attnum, uint16 procnum,
+ Datum indoptions, bool validate);
+
/*
* index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 4022c14..f6d6b43 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -264,12 +264,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen **gen, int nelems);
extern void *allocateReloptStruct(Size base, relopt_value *options,
int numoptions);
extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+ int offsets[], int noptions, size_t base_size,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 5f72a55..04a5c4c 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -55,6 +55,7 @@ CATALOG(pg_index,2610,IndexRelationId) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO
* each zero entry in indkey[] */
pg_node_tree indpred; /* expression tree for predicate, if a partial
* index; else NULL */
+ text indoptions[1]; /* opclass options */
#endif
} FormData_pg_index;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1854456..fde9153 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -136,6 +136,7 @@ typedef struct ExprState
* UniqueProcs
* UniqueStrats
* Unique is it a unique index?
+ * OpclassOptions opclass-specific options, or NULL if none
* ReadyForInserts is it valid for inserts?
* Concurrent are we doing a concurrent index build?
* BrokenHotChain did we detect any broken HOT chains?
@@ -164,6 +165,7 @@ typedef struct IndexInfo
Oid *ii_UniqueOps; /* array with one entry per column */
Oid *ii_UniqueProcs; /* array with one entry per column */
uint16 *ii_UniqueStrats; /* array with one entry per column */
+ Datum *ii_OpclassOptions; /* array with one entry per column */
bool ii_Unique;
bool ii_ReadyForInserts;
bool ii_Concurrent;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9da8bf2..c715a02 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -699,6 +699,7 @@ typedef struct IndexElem
char *indexcolname; /* name for index column; NULL = default */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
+ List *opclassopts; /* opclass-specific options, or NIL */
SortByDir ordering; /* ASC/DESC/default */
SortByNulls nulls_ordering; /* FIRST/LAST/default */
} IndexElem;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6fd2420..fba493e 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -783,6 +783,7 @@ typedef struct IndexOptInfo
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
+ bytea **opclassoptions; /* opclass-specific options for columns */
bool *canreturn; /* which index cols can be returned in an
* index-only scan? */
Oid relam; /* OID of the access method (in pg_am) */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 84469f5..2826e5d 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -163,6 +163,7 @@ typedef struct RelationData
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
void *rd_amcache; /* available for use by index AM */
Oid *rd_indcollation; /* OIDs of index collations */
+ bytea **rd_indoptions; /* parsed opclass-specific options */
/*
* foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index dbbf41b..7af7bb6 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -14,6 +14,7 @@
#ifndef RELCACHE_H
#define RELCACHE_H
+#include "postgres.h"
#include "access/tupdesc.h"
#include "nodes/bitmapset.h"
@@ -50,6 +51,9 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Relation relation);
+extern Datum *ExtractRawOpclassOptions(Datum indoptionsDatum, int ncols);
typedef enum IndexAttrBitmapKind
{
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9f9b029..86e6953 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,5 +34,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..e420f10 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,8 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: access method "btree" does not support opclass options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR: access method "btree" does not support opclass options
diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index 2d3522b..43a9962 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -102,8 +102,9 @@ ORDER BY 1, 2;
pg_class | reloptions | text[]
pg_class | relpartbound | pg_node_tree
pg_index | indexprs | pg_node_tree
+ pg_index | indoptions | text[]
pg_index | indpred | pg_node_tree
pg_largeobject | data | bytea
pg_largeobject_metadata | lomacl | aclitem[]
-(11 rows)
+(12 rows)
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..a3f7d9b 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,7 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
--
2.7.4
0002-Add-opclass-parameters-to-GiST-v02.patchtext/x-patch; name=0002-Add-opclass-parameters-to-GiST-v02.patchDownload
From 8618654d93554f0684b0cf3c89de049d6d29435c Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 18:58:39 +0300
Subject: [PATCH 2/9] Add opclass parameters to GiST
---
doc/src/sgml/xindex.sgml | 5 ++++
src/backend/access/gist/gist.c | 2 ++
src/backend/access/gist/gistget.c | 12 ++++++----
src/backend/access/gist/gistsplit.c | 15 +++++++-----
src/backend/access/gist/gistutil.c | 43 +++++++++++++++++++++++-----------
src/backend/access/gist/gistvalidate.c | 34 +++++++++++++++++----------
src/include/access/gist.h | 3 ++-
src/include/access/gist_private.h | 4 ++++
8 files changed, 81 insertions(+), 37 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..8c5b528 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
index-only scans (optional)</entry>
<entry>9</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>parse opclass-specific options (optional)</entry>
+ <entry>10</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..905cbbf 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -97,6 +97,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amopclassoptions = gistopclassoptions;
PG_RETURN_POINTER(amroutine);
}
@@ -1457,6 +1458,7 @@ initGISTstate(Relation index)
giststate->scanCxt = scanCxt;
giststate->tempCxt = scanCxt; /* caller must change this if needed */
giststate->tupdesc = index->rd_att;
+ giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < index->rd_att->natts; i++)
{
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index e4a3786..9302e68 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum test;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan,
*/
recheck = true;
- test = FunctionCall5Coll(&key->sk_func,
+ test = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
if (!DatumGetBool(test))
return false;
@@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum dist;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan,
* about the flag, but are expected to never be lossy.
*/
recheck = false;
- dist = FunctionCall5Coll(&key->sk_func,
+ dist = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
*recheck_distances_p |= recheck;
*distance_p = DatumGetFloat8(dist);
}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index a7038cc..96a9290 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
evec->n = v->spl_nleft;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
sizeof(GISTENTRY) * evec->n);
- v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
evec->n = v->spl_nright;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
sizeof(GISTENTRY) * evec->n);
- v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
/*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
* Let the opclass-specific PickSplit method do its thing. Note that at
* this point we know there are no null keys in the entryvec.
*/
- FunctionCall2Coll(&giststate->picksplitFn[attno],
+ FunctionCall3Coll(&giststate->picksplitFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(entryvec),
- PointerGetDatum(sv));
+ PointerGetDatum(sv),
+ PointerGetDatum(giststate->opclassoptions[attno]));
if (sv->spl_nleft == 0 || sv->spl_nright == 0)
{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 70627e5..f9367f9 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -199,10 +199,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
}
/* Make union and store in attr array */
- attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+ attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
giststate->supportCollation[i],
PointerGetDatum(evec),
- PointerGetDatum(&attrsize));
+ PointerGetDatum(&attrsize),
+ PointerGetDatum(giststate->opclassoptions[i]));
isnull[i] = false;
}
@@ -268,10 +269,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
}
*dstisnull = false;
- *dst = FunctionCall2Coll(&giststate->unionFn[attno],
+ *dst = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&dstsize));
+ PointerGetDatum(&dstsize),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
}
@@ -280,10 +282,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
bool result;
- FunctionCall3Coll(&giststate->equalFn[attno],
+ FunctionCall4Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
a, b,
- PointerGetDatum(&result));
+ PointerGetDatum(&result),
+ PointerGetDatum(giststate->opclassoptions[attno]));
return result;
}
@@ -556,9 +559,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
return;
dep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(e)));
+ PointerGetDatum(e),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* decompressFn may just return the given pointer */
if (dep != e)
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -593,9 +597,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
/* there may not be a compress function in opclass */
if (OidIsValid(giststate->compressFn[i].fn_oid))
cep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+ DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
giststate->supportCollation[i],
- PointerGetDatum(¢ry)));
+ PointerGetDatum(¢ry),
+ PointerGetDatum(giststate->opclassoptions[i])));
else
cep = ¢ry;
compatt[i] = cep->key;
@@ -624,9 +629,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
fep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(&fentry)));
+ PointerGetDatum(&fentry),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* fetchFn set 'key', return it to the caller */
return fep->key;
@@ -694,11 +700,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
if (giststate->penaltyFn[attno].fn_strict == false ||
(isNullOrig == false && isNullAdd == false))
{
- FunctionCall3Coll(&giststate->penaltyFn[attno],
+ FunctionCall4Coll(&giststate->penaltyFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(orig),
PointerGetDatum(add),
- PointerGetDatum(&penalty));
+ PointerGetDatum(&penalty),
+ PointerGetDatum(giststate->opclassoptions[attno]));
/* disallow negative or NaN penalty */
if (isnan(penalty) || penalty < 0.0)
penalty = 0.0;
@@ -860,6 +867,14 @@ gistoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum options, bool validate)
+{
+ return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+ options, validate);
+}
+
+
/*
* gistproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index c300e52..c5e1b23 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
{
case GIST_CONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_UNION_PROC:
ok = check_amproc_signature(procform->amproc, opckeytype, false,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_COMPRESS_PROC:
case GIST_DECOMPRESS_PROC:
case GIST_FETCH_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 1, 1, INTERNALOID);
+ 1, 2, INTERNALOID, INTERNALOID);
break;
case GIST_PENALTY_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 3, 3, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ 3, 4, INTERNALOID,
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_PICKSPLIT_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_EQUAL_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 3, 3, opckeytype, opckeytype,
- INTERNALOID);
+ 3, 4, opckeytype, opckeytype,
+ INTERNALOID, INTERNALOID);
break;
case GIST_DISTANCE_PROC:
ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
+ break;
+ case GIST_OPCLASSOPT_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+ 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
- i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+ i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+ i == GIST_OPCLASSOPT_PROC)
continue; /* optional methods */
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 827566d..08e7ada 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
#define GIST_EQUAL_PROC 7
#define GIST_DISTANCE_PROC 8
#define GIST_FETCH_PROC 9
-#define GISTNProcs 9
+#define GIST_OPCLASSOPT_PROC 10
+#define GISTNProcs 10
/*
* Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 36ed724..eedd896 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -94,6 +94,8 @@ typedef struct GISTSTATE
/* Collations to pass to the support functions */
Oid supportCollation[INDEX_MAX_KEYS];
+
+ bytea **opclassoptions; /* parsed opclass-specific options */
} GISTSTATE;
@@ -436,6 +438,8 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_DEFAULT_FILLFACTOR 90
extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+ Datum options, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
--
2.7.4
0003-Add-opclass-parameters-to-GIN-v02.patchtext/x-patch; name=0003-Add-opclass-parameters-to-GIN-v02.patchDownload
From 8a015b2803852a03da2d1bde42951473cca1d3c7 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 01:50:09 +0300
Subject: [PATCH 3/9] Add opclass parameters to GIN
---
doc/src/sgml/xindex.sgml | 7 +++++++
src/backend/access/gin/ginget.c | 11 ++++++-----
src/backend/access/gin/ginlogic.c | 15 +++++++++------
src/backend/access/gin/ginscan.c | 6 ++++--
src/backend/access/gin/ginutil.c | 27 +++++++++++++++++++++------
src/backend/access/gin/ginvalidate.c | 32 ++++++++++++++++++++------------
src/backend/utils/adt/selfuncs.c | 6 ++++--
src/include/access/gin.h | 3 ++-
src/include/access/gin_private.h | 5 +++++
9 files changed, 78 insertions(+), 34 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b528..658ec9b 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
</entry>
<entry>6</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>
+ parse opclass-specific options (optional)
+ </entry>
+ <entry>7</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 8466d94..5eb47e0 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
btree->ginstate->supportCollation[attnum - 1],
scanEntry->queryKey,
idatum,
UInt16GetDatum(scanEntry->strategy),
- PointerGetDatum(scanEntry->extra_data)));
-
+ PointerGetDatum(scanEntry->extra_data),
+ PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
if (cmp > 0)
return true;
else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
ginstate->supportCollation[entry->attnum - 1],
entry->queryKey,
datum[off - 1],
UInt16GetDatum(entry->strategy),
- PointerGetDatum(entry->extra_data)));
+ PointerGetDatum(entry->extra_data),
+ PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
if (cmp == 0)
return true;
else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
*/
key->recheckCurItem = true;
- return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+ return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
PointerGetDatum(key->extra_data),
PointerGetDatum(&key->recheckCurItem),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
static GinTernaryValue
directTriConsistentFn(GinScanKey key)
{
- return DatumGetGinTernaryValue(FunctionCall7Coll(
+ return DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
{
GinTernaryValue result;
- result = DatumGetGinTernaryValue(FunctionCall7Coll(
+ result = DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
if (result == GIN_MAYBE)
{
key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
key->strategy = strategy;
key->searchMode = searchMode;
key->attnum = attnum;
+ key->opclassOptions = ginstate->opclassOptions[attnum - 1];
ItemPointerSetMin(&key->curItem);
key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
/* OK to call the extractQueryFn */
queryValues = (Datum *)
- DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+ DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
so->ginstate.supportCollation[skey->sk_attno - 1],
skey->sk_argument,
PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode)));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
/*
* If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 0a32182..817bbce 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amoptions = ginoptions;
+ amroutine->amopclassoptions = ginopclassoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -95,6 +96,7 @@ initGinState(GinState *state, Relation index)
state->index = index;
state->oneCol = (origTupdesc->natts == 1) ? true : false;
state->origTupdesc = origTupdesc;
+ state->opclassOptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < origTupdesc->natts; i++)
{
@@ -403,9 +405,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
return 0;
/* both not null, so safe to call the compareFn */
- return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+ return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
- a, b));
+ a, b,
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
}
/*
@@ -441,6 +444,7 @@ typedef struct
{
FmgrInfo *cmpDatumFunc;
Oid collation;
+ Datum options;
bool haveDups;
} cmpEntriesArg;
@@ -462,9 +466,10 @@ cmpEntries(const void *a, const void *b, void *arg)
else if (bb->isnull)
res = -1; /* not-NULL "<" NULL */
else
- res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+ res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
data->collation,
- aa->datum, bb->datum));
+ aa->datum, bb->datum,
+ data->options));
/*
* Detect if we have any duplicates. If there are equal keys, qsort must
@@ -510,11 +515,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
/* OK, call the opclass's extractValueFn */
nullFlags = NULL; /* in case extractValue doesn't set it */
entries = (Datum *)
- DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+ DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
value,
PointerGetDatum(nentries),
- PointerGetDatum(&nullFlags)));
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
/*
* Generate a placeholder if the item contained no keys.
@@ -557,6 +563,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.collation = ginstate->supportCollation[attnum - 1];
+ arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
arg.haveDups = false;
qsort_arg(keydata, *nentries, sizeof(keyEntryData),
cmpEntries, (void *) &arg);
@@ -632,6 +639,14 @@ ginoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum indoptions,
+ bool validate)
+{
+ return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+ indoptions, validate);
+}
+
/*
* Fetch index's statistical data into *stats
*
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1922260..4f87d12 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
{
case GIN_COMPARE_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 2, 2, opckeytype, opckeytype);
+ 2, 3, opckeytype, opckeytype,
+ INTERNALOID);
break;
case GIN_EXTRACTVALUE_PROC:
/* Some opclasses omit nullFlags */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 2, 3, opcintype, INTERNALOID,
- INTERNALOID);
+ 2, 4, opcintype, INTERNALOID,
+ INTERNALOID, INTERNALOID);
break;
case GIN_EXTRACTQUERY_PROC:
/* Some opclasses omit nullFlags and searchMode */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 5, 7, opcintype, INTERNALOID,
+ 5, 8, opcintype, INTERNALOID,
INT2OID, INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_CONSISTENT_PROC:
/* Some opclasses omit queryKeys and nullFlags */
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 6, 8, INTERNALOID, INT2OID,
+ 6, 9, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_COMPARE_PARTIAL_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 4, 4, opckeytype, opckeytype,
- INT2OID, INTERNALOID);
+ 4, 5, opckeytype, opckeytype,
+ INT2OID, INTERNALOID, INTERNALOID);
break;
case GIN_TRICONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, CHAROID, false,
- 7, 7, INTERNALOID, INT2OID,
+ 7, 8, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID);
+ INTERNALOID, INTERNALOID);
+ break;
+ case GIN_OPCLASSOPTIONS_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID,
+ false, 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
if (opclassgroup &&
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
- if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+ if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+ i == GIN_OPCLASSOPTIONS_PROC)
continue; /* optional method */
if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
continue; /* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index e0ece74..a53f416 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -139,6 +139,7 @@
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
+#include "utils/relcache.h"
#include "utils/selfuncs.h"
#include "utils/snapmgr.h"
#include "utils/spccache.h"
@@ -7456,7 +7457,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
else
collation = DEFAULT_COLLATION_OID;
- OidFunctionCall7Coll(extractProcOid,
+ OidFunctionCall8Coll(extractProcOid,
collation,
query,
PointerGetDatum(&nentries),
@@ -7464,7 +7465,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(index->opclassoptions[indexcol]));
if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 3d8a130..20ce792 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
#define GIN_TRICONSISTENT_PROC 6
-#define GINNProcs 6
+#define GIN_OPCLASSOPTIONS_PROC 7
+#define GINNProcs 7
/*
* searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 81bf873..6820899 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
TupleDesc origTupdesc;
TupleDesc tupdesc[INDEX_MAX_KEYS];
+ bytea **opclassOptions; /* per-index-column opclass options */
+
/*
* Per-index-column opclass support functions
*/
@@ -85,6 +87,8 @@ typedef struct GinState
/* ginutil.c */
extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+ Datum indoptions, bool validate);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -296,6 +300,7 @@ typedef struct GinScanKeyData
StrategyNumber strategy;
int32 searchMode;
OffsetNumber attnum;
+ bytea *opclassOptions;
/*
* Match status data. curItem is the TID most recently tested (could be a
--
2.7.4
0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patchtext/x-patch; name=0004-Add-opclass-parameters-to-GiST-tsvector_ops-v02.patchDownload
From af502308192d3f316e493682a36f63733881043b Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 18:31:23 +0300
Subject: [PATCH 4/9] Add opclass parameters to GiST tsvector_ops
---
doc/src/sgml/textsearch.sgml | 9 +-
src/backend/utils/adt/tsgistidx.c | 269 +++++++++++++++++++---------------
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_proc.dat | 19 ++-
src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
src/test/regress/sql/tsearch.sql | 45 ++++++
6 files changed, 392 insertions(+), 131 deletions(-)
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index ecebade..92abf6e 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3635,7 +3635,7 @@ SELECT plainto_tsquery('supernovae stars');
<tertiary>text search</tertiary>
</indexterm>
- <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+ <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
</term>
<listitem>
@@ -3643,6 +3643,8 @@ SELECT plainto_tsquery('supernovae stars');
Creates a GiST (Generalized Search Tree)-based index.
The <replaceable>column</replaceable> can be of <type>tsvector</type> or
<type>tsquery</type> type.
+ Optional integer parameter <literal>siglen</literal> determines
+ signature length in bytes (see below for details).
</para>
</listitem>
</varlistentry>
@@ -3666,7 +3668,10 @@ SELECT plainto_tsquery('supernovae stars');
to check the actual table row to eliminate such false matches.
(<productname>PostgreSQL</productname> does this automatically when needed.)
GiST indexes are lossy because each document is represented in the
- index by a fixed-length signature. The signature is generated by hashing
+ index by a fixed-length signature. Signature length in bytes is determined
+ by the value of the optional integer parameter <literal>siglen</literal>.
+ Default signature length (when <literal>siglen</literal> is not specied) is
+ 124 bytes, maximal length is 484 bytes. The signature is generated by hashing
each word into a single bit in an n-bit string, with all these bits OR-ed
together to produce an n-bit document signature. When two words hash to
the same bit position there will be a false match. If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 2d9ecc4..462791a 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,23 +15,29 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/pg_crc.h"
-#define SIGLENINT 31 /* >121 => key will toast, so it will not work
- * !!! */
+#define SIGLEN_DEFAULT (31 * 4)
+#define SIGLEN_MAX (121 * 4) /* key will toast, so it will not work !!! */
-#define SIGLEN ( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length */
+} GistTsVectorOptions;
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
#define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -39,8 +45,8 @@ typedef char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
#define GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
@@ -64,9 +70,10 @@ typedef struct
#define ISALLTRUE(x) ( ((SignTSVector*)(x))->flag & ALLISTRUE )
#define GTHDRSIZE ( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
#define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
#define GETARR(x) ( (int32*)( (char*)(x)+GTHDRSIZE ) )
#define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
@@ -90,7 +97,7 @@ static const uint8 number_of_ones[256] = {
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
Datum
gtsvectorin(PG_FUNCTION_ARGS)
@@ -121,9 +128,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
else
{
- int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+ int siglen = GETSIGLEN(key);
+ int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
- sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+ sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
}
PG_FREE_IF_COPY(key, 0);
@@ -167,36 +175,49 @@ uniqueint(int32 *a, int32 l)
}
static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
int32 *ptr = GETARR(a);
- MemSet((void *) sign, 0, sizeof(BITVEC));
+ MemSet((void *) sign, 0, siglen);
for (k = 0; k < len; k++)
- HASH(sign, ptr[k]);
+ HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+ int size = CALCGTSIZE(flag, len);
+ SignTSVector *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+ memcpy(GETSIGN(res), sign, len);
+
+ return res;
}
+
Datum
gtsvector_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{ /* tsvector */
- SignTSVector *res;
TSVector val = DatumGetTSVector(entry->key);
+ SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
int32 len;
int32 *arr;
WordEntry *ptr = ARRPTR(val);
char *words = STRPTR(val);
- len = CALCGTSIZE(ARRKEY, val->size);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = ARRKEY;
arr = GETARR(res);
len = val->size;
while (len--)
@@ -227,13 +248,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
/* make signature, if array is too long */
if (VARSIZE(res) > TOAST_INDEX_TARGET)
{
- SignTSVector *ressign;
+ SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
- len = CALCGTSIZE(SIGNKEY, 0);
- ressign = (SignTSVector *) palloc(len);
- SET_VARSIZE(ressign, len);
- ressign->flag = SIGNKEY;
- makesign(GETSIGN(ressign), res);
+ makesign(GETSIGN(ressign), res, siglen);
res = ressign;
}
@@ -245,22 +262,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
SignTSVector *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -334,12 +346,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
static bool
checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
{
+ void *key = (SignTSVector *) checkval;
+
/*
* we are not able to find a prefix in signature tree
*/
if (val->prefix)
return true;
- return GETBIT(checkval, HASHVAL(val->valcrc));
+ return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
}
Datum
@@ -366,7 +380,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
/* since signature is lossy, cannot specify CALC_NOT here */
PG_RETURN_BOOL(TS_execute(GETQUERY(query),
- (void *) GETSIGN(key),
+ key,
TS_EXEC_PHRASE_NO_POS,
checkcondition_bit));
}
@@ -384,7 +398,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
@@ -395,7 +409,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -403,7 +419,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
int32 *ptr = GETARR(add);
for (i = 0; i < ARRNELEM(add); i++)
- HASH(sbase, ptr[i]);
+ HASH(sbase, ptr[i], siglen);
}
return 0;
}
@@ -414,30 +430,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- SignTSVector *result;
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+ SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+ BITVECP base = GETSIGN(result);
+ int32 i;
+
+ memset(base, 0, siglen);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (SignTSVector *) palloc(len);
- *size = len;
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -448,6 +458,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -463,8 +474,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
BITVECP sa = GETSIGN(a),
sb = GETSIGN(b);
+ Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -501,24 +514,24 @@ gtsvector_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -529,17 +542,22 @@ hemdistsign(BITVECP a, BITVECP b)
static int
hemdist(SignTSVector *a, SignTSVector *b)
{
+ int siglena = GETSIGLEN(a);
+ int siglenb = GETSIGLEN(b);
+
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ Assert(siglena == siglenb);
+
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
}
Datum
@@ -548,6 +566,7 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -556,14 +575,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
if (ISARRKEY(newval))
{
- BITVEC sign;
+ BITVECP sign = palloc(siglen);
- makesign(sign, newval);
+ makesign(sign, newval, siglen);
if (ISALLTRUE(origval))
- *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+ {
+ int siglenbit = SIGLENBIT(siglen);
+
+ *penalty =
+ (float) (siglenbit - sizebitvec(sign, siglen)) /
+ (float) (siglenbit + 1);
+ }
else
- *penalty = hemdistsign(sign, orig);
+ *penalty = hemdistsign(sign, orig, siglen);
+
+ pfree(sign);
}
else
*penalty = hemdist(origval, newval);
@@ -573,19 +600,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
typedef struct
{
bool allistrue;
- BITVEC sign;
+ BITVECP sign;
} CACHESIGN;
static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
{
item->allistrue = false;
if (ISARRKEY(key))
- makesign(item->sign, key);
+ makesign(item->sign, key, siglen);
else if (ISALLTRUE(key))
item->allistrue = true;
else
- memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC));
+ memcpy((void *) item->sign, (void *) GETSIGN(key), siglen);
}
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
@@ -609,19 +636,19 @@ comparecost(const void *va, const void *vb)
static int
-hemdistcache(CACHESIGN *a, CACHESIGN *b)
+hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
{
if (a->allistrue)
{
if (b->allistrue)
return 0;
else
- return SIGLENBIT - sizebitvec(b->sign);
+ return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
}
else if (b->allistrue)
- return SIGLENBIT - sizebitvec(a->sign);
+ return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
- return hemdistsign(a->sign, b->sign);
+ return hemdistsign(a->sign, b->sign, siglen);
}
Datum
@@ -629,6 +656,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
OffsetNumber k,
j;
SignTSVector *datum_l,
@@ -648,6 +676,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
@@ -656,16 +685,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
v->spl_right = (OffsetNumber *) palloc(nbytes);
cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
- fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+ cache_sign = palloc(siglen * (maxoff + 2));
+
+ for (j = 0; j < maxoff + 2; j++)
+ cache[j].sign = &cache_sign[siglen * j];
+
+ fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+ siglen);
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
{
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
if (k == FirstOffsetNumber)
- fillcache(&cache[j], GETENTRY(entryvec, j));
+ fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
- size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+ size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -687,44 +722,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (cache[seed_1].allistrue)
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_l->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0));
- datum_l->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC));
- }
- if (cache[seed_2].allistrue)
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_r->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0));
- datum_r->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC));
- }
-
+ datum_l = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_1].sign);
+ datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_2].sign);
union_l = GETSIGN(datum_l);
union_r = GETSIGN(datum_r);
maxoff = OffsetNumberNext(maxoff);
- fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff));
+ fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen);
/* sort before ... */
costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
{
costvector[j - 1].pos = j;
- size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]));
- size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]));
+ size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
+ size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -750,36 +762,34 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign), siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign), siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -790,12 +800,12 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_r), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -822,3 +832,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
{
return gtsvector_consistent(fcinfo);
}
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistTsVectorOptions, siglen) };
+ GistTsVectorOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistTsVectorOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 0ef2c08..c1ee3db 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -450,7 +450,7 @@
amproc => 'gist_circle_distance' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '1',
- amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
+ amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '2',
amproc => 'gtsvector_union' },
@@ -468,6 +468,9 @@
amproc => 'gtsvector_picksplit' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+ amprocrighttype => 'tsvector', amprocnum => '10',
+ amproc => 'gtsvector_options' },
{ amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
amprocrighttype => 'tsquery', amprocnum => '1',
amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 9264a2e..c22d639 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8430,30 +8430,35 @@
{ oid => '3648', descr => 'GiST tsvector support',
proname => 'gtsvector_compress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_compress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_compress' },
{ oid => '3649', descr => 'GiST tsvector support',
proname => 'gtsvector_decompress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_decompress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' },
{ oid => '3650', descr => 'GiST tsvector support',
proname => 'gtsvector_picksplit', prorettype => 'internal',
- proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' },
{ oid => '3651', descr => 'GiST tsvector support',
proname => 'gtsvector_union', prorettype => 'gtsvector',
- proargtypes => 'internal internal', prosrc => 'gtsvector_union' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' },
{ oid => '3652', descr => 'GiST tsvector support',
proname => 'gtsvector_same', prorettype => 'internal',
- proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' },
+ proargtypes => 'gtsvector gtsvector internal internal',
+ prosrc => 'gtsvector_same' },
{ oid => '3653', descr => 'GiST tsvector support',
proname => 'gtsvector_penalty', prorettype => 'internal',
- proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' },
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'gtsvector_penalty' },
{ oid => '3654', descr => 'GiST tsvector support',
proname => 'gtsvector_consistent', prorettype => 'bool',
- proargtypes => 'internal tsvector int2 oid internal',
+ proargtypes => 'internal tsvector int2 oid internal internal',
prosrc => 'gtsvector_consistent' },
{ oid => '3790', descr => 'GiST tsvector support (obsolete)',
proname => 'gtsvector_consistent', prorettype => 'bool',
proargtypes => 'internal gtsvector int4 oid internal',
prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3996', descr => 'GiST tsvector support',
+ proname => 'gtsvector_options', prorettype => 'internal',
+ proargtypes => 'internal bool', prosrc => 'gtsvector_options' },
{ oid => '3656', descr => 'GIN tsvector support',
proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index b088ff0..9d81489 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
508
(1 row)
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR: value 485 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR: parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a)
+ "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx2
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a tsvector_ops (siglen='484'))
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 637bfb3..740d5e0 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+explain (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
--
2.7.4
0005-Add-opclass-parameters-to-contrib-intarray-v02.patchtext/x-patch; name=0005-Add-opclass-parameters-to-contrib-intarray-v02.patchDownload
From c246a65e9a613f57e8812d32c395705bb940cfef Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 17:09:22 +0300
Subject: [PATCH 5/9] Add opclass parameters to contrib/intarray
---
contrib/intarray/Makefile | 4 +-
contrib/intarray/_int.h | 38 ++-
contrib/intarray/_int_bool.c | 34 +--
contrib/intarray/_int_gist.c | 31 +-
contrib/intarray/_int_tool.c | 4 +-
contrib/intarray/_intbig_gist.c | 200 +++++++------
contrib/intarray/intarray--1.2-1.3.sql | 34 +++
contrib/intarray/intarray--1.2.sql | 520 --------------------------------
contrib/intarray/intarray--1.3.sql | 531 +++++++++++++++++++++++++++++++++
contrib/intarray/intarray.control | 2 +-
doc/src/sgml/intarray.sgml | 14 +-
11 files changed, 758 insertions(+), 654 deletions(-)
create mode 100644 contrib/intarray/intarray--1.2-1.3.sql
delete mode 100644 contrib/intarray/intarray--1.2.sql
create mode 100644 contrib/intarray/intarray--1.3.sql
diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile
index 2505294..d99d977 100644
--- a/contrib/intarray/Makefile
+++ b/contrib/intarray/Makefile
@@ -5,8 +5,8 @@ OBJS = _int_bool.o _int_gist.o _int_op.o _int_tool.o \
_intbig_gist.o _int_gin.o _int_selfuncs.o $(WIN32RES)
EXTENSION = intarray
-DATA = intarray--1.2.sql intarray--1.1--1.2.sql intarray--1.0--1.1.sql \
- intarray--unpackaged--1.0.sql
+DATA = intarray--1.3.sql intarray--1.2-1.3.sql intarray--1.1--1.2.sql \
+ intarray--1.0--1.1.sql intarray--unpackaged--1.0.sql
PGFILEDESC = "intarray - functions and operators for arrays of integers"
REGRESS = _int
diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index b689eb7..87d21e9 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -8,7 +8,15 @@
#include "utils/memutils.h"
/* number ranges for compression */
-#define MAXNUMRANGE 100
+#define G_INT_NUMRANGES_DEFAULT 100
+#define G_INT_NUMRANGES_MAX 1000
+
+/* gist_int_ops opclass options */
+typedef struct IntArrayOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int num_ranges; /* number of ranges */
+} IntArrayOptions;
/* useful macros for accessing int4 arrays */
#define ARRPTR(x) ( (int32 *) ARR_DATA_PTR(x) )
@@ -47,15 +55,14 @@
/* bigint defines */
-#define SIGLENINT 63 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
+#define SIGLEN_DEFAULT (63 * 4)
+#define SIGLEN_MAX (122 * 4) /* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
/* beware of multiple evaluation of arguments to these macros! */
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
@@ -63,8 +70,15 @@ typedef char *BITVECP;
#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
+
+/* gist_intbig_ops opclass options */
+typedef struct IntArrayBigOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} IntArrayBigOptions;
/*
* type of index key
@@ -81,7 +95,7 @@ typedef struct
#define ISALLTRUE(x) ( ((GISTTYPE*)x)->flag & ALLISTRUE )
#define GTHDRSIZE (VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
@@ -109,7 +123,7 @@ bool inner_int_contains(ArrayType *a, ArrayType *b);
ArrayType *inner_int_union(ArrayType *a, ArrayType *b);
ArrayType *inner_int_inter(ArrayType *a, ArrayType *b);
void rt__int_size(ArrayType *a, float *size);
-void gensign(BITVEC sign, int *a, int len);
+void gensign(BITVECP sign, int *a, int len, int siglen);
/*****************************************************************************
@@ -155,7 +169,7 @@ typedef struct QUERYTYPE
#define PG_GETARG_QUERYTYPE_P(n) DatumGetQueryTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_QUERYTYPE_P_COPY(n) DatumGetQueryTypePCopy(PG_GETARG_DATUM(n))
-bool signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot);
+bool signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot);
bool execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
bool gin_bool_consistent(QUERYTYPE *query, bool *check);
diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c
index 91e2a80..4e7d55a 100644
--- a/contrib/intarray/_int_bool.c
+++ b/contrib/intarray/_int_bool.c
@@ -233,7 +233,7 @@ typedef struct
* is there value 'val' in (sorted) array or not ?
*/
static bool
-checkcondition_arr(void *checkval, ITEM *item)
+checkcondition_arr(void *checkval, ITEM *item, void *options)
{
int32 *StopLow = ((CHKVAL *) checkval)->arrb;
int32 *StopHigh = ((CHKVAL *) checkval)->arre;
@@ -255,42 +255,42 @@ checkcondition_arr(void *checkval, ITEM *item)
}
static bool
-checkcondition_bit(void *checkval, ITEM *item)
+checkcondition_bit(void *checkval, ITEM *item, void *siglen)
{
- return GETBIT(checkval, HASHVAL(item->val));
+ return GETBIT(checkval, HASHVAL(item->val, (int)(intptr_t) siglen));
}
/*
* evaluate boolean expression, using chkcond() to test the primitive cases
*/
static bool
-execute(ITEM *curitem, void *checkval, bool calcnot,
- bool (*chkcond) (void *checkval, ITEM *item))
+execute(ITEM *curitem, void *checkval, void *options, bool calcnot,
+ bool (*chkcond) (void *checkval, ITEM *item, void *options))
{
/* since this function recurses, it could be driven to stack overflow */
check_stack_depth();
if (curitem->type == VAL)
- return (*chkcond) (checkval, curitem);
+ return (*chkcond) (checkval, curitem, options);
else if (curitem->val == (int32) '!')
{
return calcnot ?
- ((execute(curitem - 1, checkval, calcnot, chkcond)) ? false : true)
+ ((execute(curitem - 1, checkval, options, calcnot, chkcond)) ? false : true)
: true;
}
else if (curitem->val == (int32) '&')
{
- if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
- return execute(curitem - 1, checkval, calcnot, chkcond);
+ if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
+ return execute(curitem - 1, checkval, options, calcnot, chkcond);
else
return false;
}
else
{ /* |-operator */
- if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
+ if (execute(curitem + curitem->left, checkval, options, calcnot, chkcond))
return true;
else
- return execute(curitem - 1, checkval, calcnot, chkcond);
+ return execute(curitem - 1, checkval, options, calcnot, chkcond);
}
}
@@ -298,10 +298,10 @@ execute(ITEM *curitem, void *checkval, bool calcnot,
* signconsistent & execconsistent called by *_consistent
*/
bool
-signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot)
+signconsistent(QUERYTYPE *query, BITVECP sign, int siglen, bool calcnot)
{
return execute(GETQUERY(query) + query->size - 1,
- (void *) sign, calcnot,
+ (void *) sign, (void *)(intptr_t) siglen, calcnot,
checkcondition_bit);
}
@@ -315,7 +315,7 @@ execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot)
chkval.arrb = ARRPTR(array);
chkval.arre = chkval.arrb + ARRNELEMS(array);
return execute(GETQUERY(query) + query->size - 1,
- (void *) &chkval, calcnot,
+ (void *) &chkval, NULL, calcnot,
checkcondition_arr);
}
@@ -326,7 +326,7 @@ typedef struct
} GinChkVal;
static bool
-checkcondition_gin(void *checkval, ITEM *item)
+checkcondition_gin(void *checkval, ITEM *item, void *options)
{
GinChkVal *gcv = (GinChkVal *) checkval;
@@ -357,7 +357,7 @@ gin_bool_consistent(QUERYTYPE *query, bool *check)
}
return execute(GETQUERY(query) + query->size - 1,
- (void *) &gcv, true,
+ (void *) &gcv, NULL, true,
checkcondition_gin);
}
@@ -429,7 +429,7 @@ boolop(PG_FUNCTION_ARGS)
chkval.arrb = ARRPTR(val);
chkval.arre = chkval.arrb + ARRNELEMS(val);
result = execute(GETQUERY(query) + query->size - 1,
- &chkval, true,
+ &chkval, NULL, true,
checkcondition_arr);
pfree(val);
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index 911d180..9de7f55 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -6,6 +6,7 @@
#include <limits.h>
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "_int.h"
@@ -22,6 +23,7 @@ PG_FUNCTION_INFO_V1(g_int_penalty);
PG_FUNCTION_INFO_V1(g_int_picksplit);
PG_FUNCTION_INFO_V1(g_int_union);
PG_FUNCTION_INFO_V1(g_int_same);
+PG_FUNCTION_INFO_V1(g_int_options);
/*
@@ -139,6 +141,7 @@ Datum
g_int_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
GISTENTRY *retval;
ArrayType *r;
int len;
@@ -153,9 +156,9 @@ g_int_compress(PG_FUNCTION_ARGS)
CHECKARRVALID(r);
PREPAREARR(r);
- if (ARRNELEMS(r) >= 2 * MAXNUMRANGE)
+ if (ARRNELEMS(r) >= 2 * opts->num_ranges)
elog(NOTICE, "input array is too big (%d maximum allowed, %d current), use gist__intbig_ops opclass instead",
- 2 * MAXNUMRANGE - 1, ARRNELEMS(r));
+ 2 * opts->num_ranges - 1, ARRNELEMS(r));
retval = palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(r),
@@ -178,7 +181,7 @@ g_int_compress(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(entry);
}
- if ((len = ARRNELEMS(r)) >= 2 * MAXNUMRANGE)
+ if ((len = ARRNELEMS(r)) >= 2 * opts->num_ranges)
{ /* compress */
if (r == (ArrayType *) DatumGetPointer(entry->key))
r = DatumGetArrayTypePCopy(entry->key);
@@ -191,7 +194,7 @@ g_int_compress(PG_FUNCTION_ARGS)
len *= 2;
cand = 1;
- while (len > MAXNUMRANGE * 2)
+ while (len > opts->num_ranges * 2)
{
min = INT_MAX;
for (i = 2; i < len; i += 2)
@@ -217,6 +220,7 @@ Datum
g_int_decompress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayOptions *opts = (IntArrayOptions *) PG_GETARG_POINTER(1);
GISTENTRY *retval;
ArrayType *r;
int *dr,
@@ -245,7 +249,7 @@ g_int_decompress(PG_FUNCTION_ARGS)
lenin = ARRNELEMS(in);
- if (lenin < 2 * MAXNUMRANGE)
+ if (lenin < 2 * opts->num_ranges)
{ /* not compressed value */
if (in != (ArrayType *) DatumGetPointer(entry->key))
{
@@ -542,3 +546,20 @@ g_int_picksplit(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(v);
}
+
+Datum
+g_int_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"numranges", "number of ranges for compression", 0, 0, 9, RELOPT_TYPE_INT },
+ G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(IntArrayOptions, num_ranges) };
+ IntArrayOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(IntArrayOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index d86485d..9bec6b9 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -324,14 +324,14 @@ _int_unique(ArrayType *r)
}
void
-gensign(BITVEC sign, int *a, int len)
+gensign(BITVECP sign, int *a, int len, int siglen)
{
int i;
/* we assume that the sign vector is previously zeroed */
for (i = 0; i < len; i++)
{
- HASH(sign, *a);
+ HASH(sign, *a, siglen);
a++;
}
}
diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c
index de7bc82..4d2b3a2 100644
--- a/contrib/intarray/_intbig_gist.c
+++ b/contrib/intarray/_intbig_gist.c
@@ -4,6 +4,7 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "_int.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(g_intbig_penalty);
PG_FUNCTION_INFO_V1(g_intbig_picksplit);
PG_FUNCTION_INFO_V1(g_intbig_union);
PG_FUNCTION_INFO_V1(g_intbig_same);
+PG_FUNCTION_INFO_V1(g_intbig_options);
/* Number of one-bits in an unsigned byte */
static const uint8 number_of_ones[256] = {
@@ -61,12 +63,33 @@ _intbig_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static GISTTYPE *
+_intbig_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+ int flag = allistrue ? ALLISTRUE : 0;
+ int size = CALCGTSIZE(flag, siglen);
+ GISTTYPE *res = (GISTTYPE *) palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!allistrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
/*********************************************************************
** intbig functions
*********************************************************************/
static bool
-_intbig_overlap(GISTTYPE *a, ArrayType *b)
+_intbig_overlap(GISTTYPE *a, ArrayType *b, int siglen)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -75,7 +98,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return true;
ptr++;
}
@@ -84,7 +107,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
}
static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglen)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -93,7 +116,7 @@ _intbig_contains(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (!GETBIT(GETSIGN(a), HASHVAL(*ptr, siglen)))
return false;
ptr++;
}
@@ -107,6 +130,8 @@ g_intbig_same(PG_FUNCTION_ARGS)
GISTTYPE *a = (GISTTYPE *) PG_GETARG_POINTER(0);
GISTTYPE *b = (GISTTYPE *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+ int siglen = opts->siglen;
if (ISALLTRUE(a) && ISALLTRUE(b))
*result = true;
@@ -121,7 +146,7 @@ g_intbig_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -137,6 +162,8 @@ Datum
g_intbig_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(1);
+ int siglen = opts->siglen;
if (entry->leafkey)
{
@@ -144,7 +171,7 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ArrayType *in = DatumGetArrayTypeP(entry->key);
int32 *ptr;
int num;
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+ GISTTYPE *res = _intbig_alloc(false, siglen, NULL);
CHECKARRVALID(in);
if (ARRISEMPTY(in))
@@ -157,11 +184,10 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ptr = ARRPTR(in);
num = ARRNELEMS(in);
}
- SET_VARSIZE(res, CALCGTSIZE(0));
while (num--)
{
- HASH(GETSIGN(res), *ptr);
+ HASH(GETSIGN(res), *ptr, siglen);
ptr++;
}
@@ -182,16 +208,13 @@ g_intbig_compress(PG_FUNCTION_ARGS)
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
GISTTYPE *res;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(entry);
}
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
- res->flag = ALLISTRUE;
-
+ res = _intbig_alloc(true, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -205,24 +228,24 @@ g_intbig_compress(PG_FUNCTION_ARGS)
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -231,19 +254,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
Datum
@@ -253,14 +276,14 @@ g_intbig_decompress(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
{
int32 i;
BITVECP sadd = GETSIGN(add);
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -270,29 +293,23 @@ g_intbig_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- GISTTYPE *result;
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+ int siglen = opts->siglen;
+ int32 i;
+ GISTTYPE *result = _intbig_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- len = CALCGTSIZE(flag);
- result = (GISTTYPE *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -305,8 +322,10 @@ g_intbig_penalty(PG_FUNCTION_ARGS)
float *penalty = (float *) PG_GETARG_POINTER(2);
GISTTYPE *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
GISTTYPE *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(3);
+ int siglen = opts->siglen;
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -329,6 +348,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(2);
+ int siglen = opts->siglen;
OffsetNumber k,
j;
GISTTYPE *datum_l,
@@ -361,7 +382,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -383,32 +404,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_l, GTHDRSIZE);
- datum_l->flag = ALLISTRUE;
- }
- else
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
- datum_l->flag = 0;
- memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC));
- }
- if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_r, GTHDRSIZE);
- datum_r->flag = ALLISTRUE;
- }
- else
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
- datum_r->flag = 0;
- memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
- }
+ datum_l = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_1)));
+ datum_r = _intbig_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_2)));
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -417,8 +416,8 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -442,20 +441,20 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
{
if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -466,12 +465,12 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -497,6 +496,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ IntArrayBigOptions *opts = (IntArrayBigOptions *) PG_GETARG_POINTER(5);
+ int siglen = opts->siglen;
bool retval;
/* All cases served by this function are inexact */
@@ -509,6 +510,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
{
retval = signconsistent((QUERYTYPE *) query,
GETSIGN(DatumGetPointer(entry->key)),
+ siglen,
false);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
@@ -519,7 +521,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
switch (strategy)
{
case RTOverlapStrategyNumber:
- retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTSameStrategyNumber:
if (GIST_LEAF(entry))
@@ -527,22 +530,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
- BITVECP dq,
+ BITVECP dq = palloc0(siglen),
de;
- memset(qp, 0, sizeof(BITVEC));
-
while (num--)
{
- HASH(qp, *ptr);
+ HASH(dq, *ptr, siglen);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
- dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (de[i] != dq[i])
{
@@ -551,13 +550,16 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
}
+ pfree(dq);
}
else
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTContainsStrategyNumber:
case RTOldContainsStrategyNumber:
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
case RTContainedByStrategyNumber:
case RTOldContainedByStrategyNumber:
@@ -566,22 +568,18 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
- BITVECP dq,
+ BITVECP dq = palloc0(siglen),
de;
- memset(qp, 0, sizeof(BITVEC));
-
while (num--)
{
- HASH(qp, *ptr);
+ HASH(dq, *ptr, siglen);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
- dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (de[i] & ~dq[i])
{
@@ -591,7 +589,8 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
}
else
- retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key),
+ query, siglen);
break;
default:
retval = false;
@@ -599,3 +598,20 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
}
+
+Datum
+g_intbig_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, NoLock, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(IntArrayBigOptions, siglen) };
+ IntArrayBigOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(IntArrayBigOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/intarray/intarray--1.2-1.3.sql b/contrib/intarray/intarray--1.2-1.3.sql
new file mode 100644
index 0000000..c9b83ef
--- /dev/null
+++ b/contrib/intarray/intarray--1.2-1.3.sql
@@ -0,0 +1,34 @@
+/* contrib/intarray/intarray--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.3'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('g_int_compress(internal)', '{internal,internal}'),
+('g_int_decompress(internal)', '{internal,internal}'),
+('g_intbig_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('g_intbig_compress(internal)', '{internal,internal}'),
+('g_intbig_decompress(internal)', '{internal,internal}'),
+('g_intbig_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('g_intbig_picksplit(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_union(internal,internal)', '{internal,internal,internal}'),
+('g_intbig_same(intbig_gkey,intbig_gkey,internal)', '{intbig_gkey,intbig_gkey,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist__intbig_ops USING gist
+ADD FUNCTION 10 (_int4) g_intbig_options (internal, boolean);
diff --git a/contrib/intarray/intarray--1.2.sql b/contrib/intarray/intarray--1.2.sql
deleted file mode 100644
index f10b53d..0000000
--- a/contrib/intarray/intarray--1.2.sql
+++ /dev/null
@@ -1,520 +0,0 @@
-/* contrib/intarray/intarray--1.2.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION intarray" to load this file. \quit
-
---
--- Create the user-defined type for the 1-D integer arrays (_int4)
---
-
--- Query type
-CREATE FUNCTION bqarr_in(cstring)
-RETURNS query_int
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION bqarr_out(query_int)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE query_int (
- INTERNALLENGTH = -1,
- INPUT = bqarr_in,
- OUTPUT = bqarr_out
-);
-
---only for debug
-CREATE FUNCTION querytree(query_int)
-RETURNS text
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-
-CREATE FUNCTION boolop(_int4, query_int)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
-
-CREATE FUNCTION rboolop(query_int, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
-
-CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE OPERATOR @@ (
- LEFTARG = _int4,
- RIGHTARG = query_int,
- PROCEDURE = boolop,
- COMMUTATOR = '~~',
- RESTRICT = _int_matchsel,
- JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~~ (
- LEFTARG = query_int,
- RIGHTARG = _int4,
- PROCEDURE = rboolop,
- COMMUTATOR = '@@',
- RESTRICT = _int_matchsel,
- JOIN = contjoinsel
-);
-
-
---
--- External C-functions for R-tree methods
---
-
--- Comparison methods
-
-CREATE FUNCTION _int_contains(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
-
-CREATE FUNCTION _int_contained(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
-
-CREATE FUNCTION _int_overlap(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
-
-CREATE FUNCTION _int_same(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
-
-CREATE FUNCTION _int_different(_int4, _int4)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
-
--- support routines for indexing
-
-CREATE FUNCTION _int_union(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_inter(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
-CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT STABLE PARALLEL SAFE;
-
---
--- OPERATORS
---
-
-CREATE OPERATOR && (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_overlap,
- COMMUTATOR = '&&',
- RESTRICT = _int_overlap_sel,
- JOIN = _int_overlap_joinsel
-);
-
---CREATE OPERATOR = (
--- LEFTARG = _int4,
--- RIGHTARG = _int4,
--- PROCEDURE = _int_same,
--- COMMUTATOR = '=',
--- NEGATOR = '<>',
--- RESTRICT = eqsel,
--- JOIN = eqjoinsel,
--- SORT1 = '<',
--- SORT2 = '<'
---);
-
---CREATE OPERATOR <> (
--- LEFTARG = _int4,
--- RIGHTARG = _int4,
--- PROCEDURE = _int_different,
--- COMMUTATOR = '<>',
--- NEGATOR = '=',
--- RESTRICT = neqsel,
--- JOIN = neqjoinsel
---);
-
-CREATE OPERATOR @> (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contains,
- COMMUTATOR = '<@',
- RESTRICT = _int_contains_sel,
- JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR <@ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contained,
- COMMUTATOR = '@>',
- RESTRICT = _int_contained_sel,
- JOIN = _int_contained_joinsel
-);
-
--- obsolete:
-CREATE OPERATOR @ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contains,
- COMMUTATOR = '~',
- RESTRICT = _int_contains_sel,
- JOIN = _int_contains_joinsel
-);
-
-CREATE OPERATOR ~ (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = _int_contained,
- COMMUTATOR = '@',
- RESTRICT = _int_contained_sel,
- JOIN = _int_contained_joinsel
-);
-
---------------
-CREATE FUNCTION intset(int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION icount(_int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
- RIGHTARG = _int4,
- PROCEDURE = icount
-);
-
-CREATE FUNCTION sort(_int4, text)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_asc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION sort_desc(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION uniq(_int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION idx(_int4, int4)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR # (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = idx
-);
-
-CREATE FUNCTION subarray(_int4, int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION subarray(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION intarray_push_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intarray_push_elem
-);
-
-CREATE FUNCTION intarray_push_array(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR + (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = +,
- PROCEDURE = intarray_push_array
-);
-
-CREATE FUNCTION intarray_del_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intarray_del_elem
-);
-
-CREATE FUNCTION intset_union_elem(_int4, int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR | (
- LEFTARG = _int4,
- RIGHTARG = int4,
- PROCEDURE = intset_union_elem
-);
-
-CREATE OPERATOR | (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = |,
- PROCEDURE = _int_union
-);
-
-CREATE FUNCTION intset_subtract(_int4, _int4)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE OPERATOR - (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- PROCEDURE = intset_subtract
-);
-
-CREATE OPERATOR & (
- LEFTARG = _int4,
- RIGHTARG = _int4,
- COMMUTATOR = &,
- PROCEDURE = _int_inter
-);
---------------
-
--- define the GiST support methods
-CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_union(internal, internal)
-RETURNS _int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_int_same(_int4, _int4, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-
--- Create the operator class for indexing
-
-CREATE OPERATOR CLASS gist__int_ops
-DEFAULT FOR TYPE _int4 USING gist AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal),
- FUNCTION 2 g_int_union (internal, internal),
- FUNCTION 3 g_int_compress (internal),
- FUNCTION 4 g_int_decompress (internal),
- FUNCTION 5 g_int_penalty (internal, internal, internal),
- FUNCTION 6 g_int_picksplit (internal, internal),
- FUNCTION 7 g_int_same (_int4, _int4, internal);
-
-
----------------------------------------------
--- intbig
----------------------------------------------
--- define the GiST support methods
-
-CREATE FUNCTION _intbig_in(cstring)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE FUNCTION _intbig_out(intbig_gkey)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
-
-CREATE TYPE intbig_gkey (
- INTERNALLENGTH = -1,
- INPUT = _intbig_in,
- OUTPUT = _intbig_out
-);
-
-CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_union(internal, internal)
-RETURNS intbig_gkey
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
--- register the opclass for indexing (not as default)
-
-CREATE OPERATOR CLASS gist__intbig_ops
-FOR TYPE _int4 USING gist
-AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 g_intbig_consistent (internal, _int4, smallint, oid, internal),
- FUNCTION 2 g_intbig_union (internal, internal),
- FUNCTION 3 g_intbig_compress (internal),
- FUNCTION 4 g_intbig_decompress (internal),
- FUNCTION 5 g_intbig_penalty (internal, internal, internal),
- FUNCTION 6 g_intbig_picksplit (internal, internal),
- FUNCTION 7 g_intbig_same (intbig_gkey, intbig_gkey, internal),
- STORAGE intbig_gkey;
-
---GIN
-
-CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-
-CREATE OPERATOR CLASS gin__int_ops
-FOR TYPE _int4 USING gin
-AS
- OPERATOR 3 &&,
- OPERATOR 6 = (anyarray, anyarray),
- OPERATOR 7 @>,
- OPERATOR 8 <@,
- OPERATOR 13 @,
- OPERATOR 14 ~,
- OPERATOR 20 @@ (_int4, query_int),
- FUNCTION 1 btint4cmp (int4, int4),
- FUNCTION 2 ginarrayextract (anyarray, internal, internal),
- FUNCTION 3 ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
- FUNCTION 4 ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
- STORAGE int4;
diff --git a/contrib/intarray/intarray--1.3.sql b/contrib/intarray/intarray--1.3.sql
new file mode 100644
index 0000000..6c91c75
--- /dev/null
+++ b/contrib/intarray/intarray--1.3.sql
@@ -0,0 +1,531 @@
+/* contrib/intarray/intarray--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION intarray" to load this file. \quit
+
+--
+-- Create the user-defined type for the 1-D integer arrays (_int4)
+--
+
+-- Query type
+CREATE FUNCTION bqarr_in(cstring)
+RETURNS query_int
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION bqarr_out(query_int)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE query_int (
+ INTERNALLENGTH = -1,
+ INPUT = bqarr_in,
+ OUTPUT = bqarr_out
+);
+
+--only for debug
+CREATE FUNCTION querytree(query_int)
+RETURNS text
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+
+CREATE FUNCTION boolop(_int4, query_int)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array';
+
+CREATE FUNCTION rboolop(query_int, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION rboolop(query_int, _int4) IS 'boolean operation with array';
+
+CREATE FUNCTION _int_matchsel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE OPERATOR @@ (
+ LEFTARG = _int4,
+ RIGHTARG = query_int,
+ PROCEDURE = boolop,
+ COMMUTATOR = '~~',
+ RESTRICT = _int_matchsel,
+ JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~~ (
+ LEFTARG = query_int,
+ RIGHTARG = _int4,
+ PROCEDURE = rboolop,
+ COMMUTATOR = '@@',
+ RESTRICT = _int_matchsel,
+ JOIN = contjoinsel
+);
+
+
+--
+-- External C-functions for R-tree methods
+--
+
+-- Comparison methods
+
+CREATE FUNCTION _int_contains(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains';
+
+CREATE FUNCTION _int_contained(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in';
+
+CREATE FUNCTION _int_overlap(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps';
+
+CREATE FUNCTION _int_same(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as';
+
+CREATE FUNCTION _int_different(_int4, _int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different';
+
+-- support routines for indexing
+
+CREATE FUNCTION _int_union(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_inter(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_sel(internal, oid, internal, integer)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_overlap_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contains_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+CREATE FUNCTION _int_contained_joinsel(internal, oid, internal, smallint, internal)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT STABLE PARALLEL SAFE;
+
+--
+-- OPERATORS
+--
+
+CREATE OPERATOR && (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = _int_overlap_sel,
+ JOIN = _int_overlap_joinsel
+);
+
+--CREATE OPERATOR = (
+-- LEFTARG = _int4,
+-- RIGHTARG = _int4,
+-- PROCEDURE = _int_same,
+-- COMMUTATOR = '=',
+-- NEGATOR = '<>',
+-- RESTRICT = eqsel,
+-- JOIN = eqjoinsel,
+-- SORT1 = '<',
+-- SORT2 = '<'
+--);
+
+--CREATE OPERATOR <> (
+-- LEFTARG = _int4,
+-- RIGHTARG = _int4,
+-- PROCEDURE = _int_different,
+-- COMMUTATOR = '<>',
+-- NEGATOR = '=',
+-- RESTRICT = neqsel,
+-- JOIN = neqjoinsel
+--);
+
+CREATE OPERATOR @> (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = _int_contains_sel,
+ JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR <@ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = _int_contained_sel,
+ JOIN = _int_contained_joinsel
+);
+
+-- obsolete:
+CREATE OPERATOR @ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contains,
+ COMMUTATOR = '~',
+ RESTRICT = _int_contains_sel,
+ JOIN = _int_contains_joinsel
+);
+
+CREATE OPERATOR ~ (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = _int_contained,
+ COMMUTATOR = '@',
+ RESTRICT = _int_contained_sel,
+ JOIN = _int_contained_joinsel
+);
+
+--------------
+CREATE FUNCTION intset(int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION icount(_int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+ RIGHTARG = _int4,
+ PROCEDURE = icount
+);
+
+CREATE FUNCTION sort(_int4, text)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_asc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION sort_desc(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION uniq(_int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION idx(_int4, int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR # (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = idx
+);
+
+CREATE FUNCTION subarray(_int4, int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION subarray(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION intarray_push_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intarray_push_elem
+);
+
+CREATE FUNCTION intarray_push_array(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR + (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = +,
+ PROCEDURE = intarray_push_array
+);
+
+CREATE FUNCTION intarray_del_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intarray_del_elem
+);
+
+CREATE FUNCTION intset_union_elem(_int4, int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR | (
+ LEFTARG = _int4,
+ RIGHTARG = int4,
+ PROCEDURE = intset_union_elem
+);
+
+CREATE OPERATOR | (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = |,
+ PROCEDURE = _int_union
+);
+
+CREATE FUNCTION intset_subtract(_int4, _int4)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE OPERATOR - (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ PROCEDURE = intset_subtract
+);
+
+CREATE OPERATOR & (
+ LEFTARG = _int4,
+ RIGHTARG = _int4,
+ COMMUTATOR = &,
+ PROCEDURE = _int_inter
+);
+--------------
+
+-- define the GiST support methods
+CREATE FUNCTION g_int_consistent(internal,_int4,smallint,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_compress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_decompress(internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_union(internal, internal)
+RETURNS _int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_same(_int4, _int4, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_int_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_int_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- Create the operator class for indexing
+
+CREATE OPERATOR CLASS gist__int_ops
+DEFAULT FOR TYPE _int4 USING gist AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal),
+ FUNCTION 2 g_int_union (internal, internal),
+ FUNCTION 3 g_int_compress (internal, internal),
+ FUNCTION 4 g_int_decompress (internal, internal),
+ FUNCTION 5 g_int_penalty (internal, internal, internal),
+ FUNCTION 6 g_int_picksplit (internal, internal),
+ FUNCTION 7 g_int_same (_int4, _int4, internal),
+ FUNCTION 10 g_int_options (internal, boolean);
+
+
+---------------------------------------------
+-- intbig
+---------------------------------------------
+-- define the GiST support methods
+
+CREATE FUNCTION _intbig_in(cstring)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION _intbig_out(intbig_gkey)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT IMMUTABLE PARALLEL SAFE;
+
+CREATE TYPE intbig_gkey (
+ INTERNALLENGTH = -1,
+ INPUT = _intbig_in,
+ OUTPUT = _intbig_out
+);
+
+CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_compress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_decompress(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_penalty(internal,internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_picksplit(internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_union(internal, internal, internal)
+RETURNS intbig_gkey
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION g_intbig_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- register the opclass for indexing (not as default)
+
+CREATE OPERATOR CLASS gist__intbig_ops
+FOR TYPE _int4 USING gist
+AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 g_intbig_consistent (internal, _int4, smallint, oid, internal, internal),
+ FUNCTION 2 g_intbig_union (internal, internal, internal),
+ FUNCTION 3 g_intbig_compress (internal, internal),
+ FUNCTION 4 g_intbig_decompress (internal, internal),
+ FUNCTION 5 g_intbig_penalty (internal, internal, internal, internal),
+ FUNCTION 6 g_intbig_picksplit (internal, internal, internal),
+ FUNCTION 7 g_intbig_same (intbig_gkey, intbig_gkey, internal, internal),
+ FUNCTION 10 g_intbig_options (internal, boolean),
+ STORAGE intbig_gkey;
+
+--GIN
+
+CREATE FUNCTION ginint4_queryextract(_int4, internal, int2, internal, internal, internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION ginint4_consistent(internal, int2, _int4, int4, internal, internal, internal, internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR CLASS gin__int_ops
+FOR TYPE _int4 USING gin
+AS
+ OPERATOR 3 &&,
+ OPERATOR 6 = (anyarray, anyarray),
+ OPERATOR 7 @>,
+ OPERATOR 8 <@,
+ OPERATOR 13 @,
+ OPERATOR 14 ~,
+ OPERATOR 20 @@ (_int4, query_int),
+ FUNCTION 1 btint4cmp (int4, int4),
+ FUNCTION 2 ginarrayextract (anyarray, internal, internal),
+ FUNCTION 3 ginint4_queryextract (_int4, internal, int2, internal, internal, internal, internal),
+ FUNCTION 4 ginint4_consistent (internal, int2, _int4, int4, internal, internal, internal, internal),
+ STORAGE int4;
diff --git a/contrib/intarray/intarray.control b/contrib/intarray/intarray.control
index 7e50cc3..72952f7 100644
--- a/contrib/intarray/intarray.control
+++ b/contrib/intarray/intarray.control
@@ -1,5 +1,5 @@
# intarray extension
comment = 'functions, operators, and index support for 1-D arrays of integers'
-default_version = '1.2'
+default_version = '1.3'
module_pathname = '$libdir/_int'
relocatable = true
diff --git a/doc/src/sgml/intarray.sgml b/doc/src/sgml/intarray.sgml
index b633cf3..2540cc6 100644
--- a/doc/src/sgml/intarray.sgml
+++ b/doc/src/sgml/intarray.sgml
@@ -259,14 +259,22 @@
</para>
<para>
- Two GiST index operator classes are provided:
- <literal>gist__int_ops</literal> (used by default) is suitable for
+ Two parametrized GiST index operator classes are provided:
+ <literal>gist__int_ops(numranges)</literal> (used by default) is suitable for
small- to medium-size data sets, while
- <literal>gist__intbig_ops</literal> uses a larger signature and is more
+ <literal>gist__intbig_ops(siglen)</literal> uses a larger signature and is more
suitable for indexing large data sets (i.e., columns containing
a large number of distinct array values).
The implementation uses an RD-tree data structure with
built-in lossy compression.
+
+ Optional integer parameter <literal>numranges</literal> of
+ <literal>gist__int_ops</literal> determines
+ number of ranges in compressed array (100 by default).
+
+ Optional integer parameter <literal>siglen</literal> of
+ <literal>gist__intbig_ops</literal> determines
+ signature length in bytes (252 by default, 488 maximum).
</para>
<para>
--
2.7.4
0006-Add-opclass-parameters-to-contrib-ltree-v02.patchtext/x-patch; name=0006-Add-opclass-parameters-to-contrib-ltree-v02.patchDownload
From 5a69a859c8d066ae73394e67a13b9f535534c1dd Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 2 Feb 2018 03:36:13 +0300
Subject: [PATCH 6/9] Add opclass parameters to contrib/ltree
---
contrib/ltree/Makefile | 2 +-
contrib/ltree/_ltree_gist.c | 192 ++++++++++++++-------------
contrib/ltree/ltree--1.1--1.2.sql | 47 +++++++
contrib/ltree/ltree.control | 2 +-
contrib/ltree/ltree.h | 51 +++++---
contrib/ltree/ltree_gist.c | 265 +++++++++++++++++++++-----------------
doc/src/sgml/ltree.sgml | 16 ++-
7 files changed, 343 insertions(+), 232 deletions(-)
create mode 100644 contrib/ltree/ltree--1.1--1.2.sql
diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile
index 416c8da..af031b3 100644
--- a/contrib/ltree/Makefile
+++ b/contrib/ltree/Makefile
@@ -6,7 +6,7 @@ OBJS = ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
PG_CPPFLAGS = -DLOWER_NODE
EXTENSION = ltree
-DATA = ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
+DATA = ltree--1.1--1.2.sql ltree--1.1.sql ltree--1.0--1.1.sql ltree--unpackaged--1.0.sql
PGFILEDESC = "ltree - hierarchical label data type"
HEADERS = ltree.h
diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c
index 28bf7ad..394f7ad 100644
--- a/contrib/ltree/_ltree_gist.c
+++ b/contrib/ltree/_ltree_gist.c
@@ -8,6 +8,7 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"
@@ -19,6 +20,7 @@ PG_FUNCTION_INFO_V1(_ltree_union);
PG_FUNCTION_INFO_V1(_ltree_penalty);
PG_FUNCTION_INFO_V1(_ltree_picksplit);
PG_FUNCTION_INFO_V1(_ltree_consistent);
+PG_FUNCTION_INFO_V1(_ltree_gist_options);
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
@@ -47,7 +49,7 @@ static const uint8 number_of_ones[256] = {
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
@@ -56,7 +58,7 @@ hashing(BITVECP sign, ltree *t)
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- AHASH(sign, hash);
+ AHASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
@@ -67,12 +69,13 @@ _ltree_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval = entry;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
if (entry->leafkey)
{ /* ltree */
ltree_gist *key;
ArrayType *val = DatumGetArrayTypeP(entry->key);
- int32 len = LTG_HDRSIZE + ASIGLEN;
int num = ArrayGetNItems(ARR_NDIM(val), ARR_DIMS(val));
ltree *item = (ltree *) ARR_DATA_PTR(val);
@@ -85,14 +88,11 @@ _ltree_compress(PG_FUNCTION_ARGS)
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("array must not contain nulls")));
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = 0;
+ key = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
- MemSet(LTG_SIGN(key), 0, ASIGLEN);
while (num > 0)
{
- hashing(LTG_SIGN(key), item);
+ hashing(LTG_SIGN(key), item, siglen);
num--;
item = NEXTVAL(item);
}
@@ -104,22 +104,17 @@ _ltree_compress(PG_FUNCTION_ARGS)
}
else if (!LTG_ISALLTRUE(entry->key))
{
- int32 i,
- len;
+ int32 i;
ltree_gist *key;
-
BITVECP sign = LTG_SIGN(DatumGetPointer(entry->key));
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = LTG_HDRSIZE;
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = LTG_ALLTRUE;
+ key = ltree_gist_alloc(true, sign, siglen, NULL, NULL);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(key),
entry->rel, entry->page,
@@ -134,6 +129,8 @@ _ltree_same(PG_FUNCTION_ARGS)
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
if (LTG_ISALLTRUE(a) && LTG_ISALLTRUE(b))
*result = true;
@@ -148,7 +145,7 @@ _ltree_same(PG_FUNCTION_ARGS)
sb = LTG_SIGN(b);
*result = true;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -161,7 +158,7 @@ _ltree_same(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, ltree_gist *add)
+unionkey(BITVECP sbase, ltree_gist *add, int siglen)
{
int32 i;
BITVECP sadd = LTG_SIGN(add);
@@ -169,7 +166,7 @@ unionkey(BITVECP sbase, ltree_gist *add)
if (LTG_ISALLTRUE(add))
return 1;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -179,52 +176,46 @@ _ltree_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- ABITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- ltree_gist *result;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
+ int32 i;
+ ltree_gist *result = ltree_gist_alloc(false, NULL, siglen, NULL, NULL);
+ BITVECP base = LTG_SIGN(result);
- MemSet((void *) base, 0, sizeof(ABITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = LTG_ALLTRUE;
+ result->flag |= LTG_ALLTRUE;
+ SET_VARSIZE(result, LTG_HDRSIZE);
break;
}
}
- len = LTG_HDRSIZE + ((flag & LTG_ALLTRUE) ? 0 : ASIGLEN);
- result = (ltree_gist *) palloc0(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!LTG_ISALLTRUE(result))
- memcpy((void *) LTG_SIGN(result), (void *) base, sizeof(ABITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- ALOOPBYTE
+ ALOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -233,19 +224,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(ltree_gist *a, ltree_gist *b)
+hemdist(ltree_gist *a, ltree_gist *b, int siglen)
{
if (LTG_ISALLTRUE(a))
{
if (LTG_ISALLTRUE(b))
return 0;
else
- return ASIGLENBIT - sizebitvec(LTG_SIGN(b));
+ return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(b), siglen);
}
else if (LTG_ISALLTRUE(b))
- return ASIGLENBIT - sizebitvec(LTG_SIGN(a));
+ return ASIGLENBIT(siglen) - sizebitvec(LTG_SIGN(a), siglen);
- return hemdistsign(LTG_SIGN(a), LTG_SIGN(b));
+ return hemdistsign(LTG_SIGN(a), LTG_SIGN(b), siglen);
}
@@ -255,8 +246,10 @@ _ltree_penalty(PG_FUNCTION_ARGS)
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -277,6 +270,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
ltree_gist *datum_l,
@@ -309,7 +304,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -331,32 +326,13 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE);
- SET_VARSIZE(datum_l, LTG_HDRSIZE);
- datum_l->flag = LTG_ALLTRUE;
- }
- else
- {
- datum_l = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
- SET_VARSIZE(datum_l, LTG_HDRSIZE + ASIGLEN);
- datum_l->flag = 0;
- memcpy((void *) LTG_SIGN(datum_l), (void *) LTG_SIGN(GETENTRY(entryvec, seed_1)), sizeof(ABITVEC));
- }
- if (LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE);
- SET_VARSIZE(datum_r, LTG_HDRSIZE);
- datum_r->flag = LTG_ALLTRUE;
- }
- else
- {
- datum_r = (ltree_gist *) palloc0(LTG_HDRSIZE + ASIGLEN);
- SET_VARSIZE(datum_r, LTG_HDRSIZE + ASIGLEN);
- datum_r->flag = 0;
- memcpy((void *) LTG_SIGN(datum_r), (void *) LTG_SIGN(GETENTRY(entryvec, seed_2)), sizeof(ABITVEC));
- }
+ datum_l = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_1)),
+ LTG_SIGN(GETENTRY(entryvec, seed_1)),
+ siglen, NULL, NULL);
+
+ datum_r = ltree_gist_alloc(LTG_ISALLTRUE(GETENTRY(entryvec, seed_2)),
+ LTG_SIGN(GETENTRY(entryvec, seed_2)),
+ siglen, NULL, NULL);
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -365,8 +341,8 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -390,20 +366,20 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.00001))
{
if (LTG_ISALLTRUE(datum_l) || LTG_ISALLTRUE(_j))
{
if (!LTG_ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(ABITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = LTG_SIGN(_j);
- ALOOPBYTE
+ ALOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -414,12 +390,12 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
if (LTG_ISALLTRUE(datum_r) || LTG_ISALLTRUE(_j))
{
if (!LTG_ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(ABITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = LTG_SIGN(_j);
- ALOOPBYTE
+ ALOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -436,7 +412,7 @@ _ltree_picksplit(PG_FUNCTION_ARGS)
}
static bool
-gist_te(ltree_gist *key, ltree *query)
+gist_te(ltree_gist *key, ltree *query, int siglen)
{
ltree_level *curq = LTREE_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -449,7 +425,7 @@ gist_te(ltree_gist *key, ltree *query)
while (qlen > 0)
{
hv = ltree_crc32_sz(curq->name, curq->len);
- if (!GETBIT(sign, AHASHVAL(hv)))
+ if (!GETBIT(sign, AHASHVAL(hv, siglen)))
return false;
curq = LEVEL_NEXT(curq);
qlen--;
@@ -458,27 +434,40 @@ gist_te(ltree_gist *key, ltree *query)
return true;
}
+typedef struct LtreeSignature
+{
+ BITVECP sign;
+ int siglen;
+} LtreeSignature;
+
static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
{
- return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, AHASHVAL(val->val)) : true;
+ LtreeSignature *sig = cxt;
+
+ return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, AHASHVAL(val->val, sig->siglen)) : true;
}
static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
{
+ LtreeSignature sig;
+
if (LTG_ISALLTRUE(key))
return true;
+ sig.sign = LTG_SIGN(key);
+ sig.siglen = siglen;
+
return ltree_execute(
GETQUERY(query),
- (void *) LTG_SIGN(key), false,
+ &sig, false,
checkcondition_bit
);
}
static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
{
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -497,7 +486,7 @@ gist_qe(ltree_gist *key, lquery *query)
while (vlen > 0)
{
- if (GETBIT(sign, AHASHVAL(curv->val)))
+ if (GETBIT(sign, AHASHVAL(curv->val, siglen)))
{
isexist = true;
break;
@@ -517,7 +506,7 @@ gist_qe(ltree_gist *key, lquery *query)
}
static bool
-_arrq_cons(ltree_gist *key, ArrayType *_query)
+_arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
{
lquery *query = (lquery *) ARR_DATA_PTR(_query);
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -533,7 +522,7 @@ _arrq_cons(ltree_gist *key, ArrayType *_query)
while (num > 0)
{
- if (gist_qe(key, query))
+ if (gist_qe(key, query, siglen))
return true;
num--;
query = (lquery *) NEXTVAL(query);
@@ -550,6 +539,8 @@ _ltree_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
bool res = false;
@@ -560,19 +551,19 @@ _ltree_consistent(PG_FUNCTION_ARGS)
{
case 10:
case 11:
- res = gist_te(key, (ltree *) query);
+ res = gist_te(key, (ltree *) query, siglen);
break;
case 12:
case 13:
- res = gist_qe(key, (lquery *) query);
+ res = gist_qe(key, (lquery *) query, siglen);
break;
case 14:
case 15:
- res = gist_qtxt(key, (ltxtquery *) query);
+ res = gist_qtxt(key, (ltxtquery *) query, siglen);
break;
case 16:
case 17:
- res = _arrq_cons(key, (ArrayType *) query);
+ res = _arrq_cons(key, (ArrayType *) query, siglen);
break;
default:
/* internal error */
@@ -581,3 +572,20 @@ _ltree_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
+
+Datum
+_ltree_gist_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(LtreeGistOptions, siglen) };
+ LtreeGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(LtreeGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/contrib/ltree/ltree--1.1--1.2.sql b/contrib/ltree/ltree--1.1--1.2.sql
new file mode 100644
index 0000000..d002e2b
--- /dev/null
+++ b/contrib/ltree/ltree--1.1--1.2.sql
@@ -0,0 +1,47 @@
+/* contrib/ltree/ltree--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION ltree UPDATE TO '1.2'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('ltree_compress(internal)', '{internal,internal}'),
+('ltree_decompress(internal)', '{internal,internal}'),
+('ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('ltree_union(internal,internal)', '{internal,internal,internal}'),
+('ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}'),
+('_ltree_compress(internal)', '{internal,internal}'),
+('_ltree_same(ltree_gist,ltree_gist,internal)', '{ltree_gist,ltree_gist,internal,internal}'),
+('_ltree_union(internal,internal)', '{internal,internal,internal}'),
+('_ltree_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('_ltree_picksplit(internal,internal)', '{internal,internal,internal}'),
+('_ltree_consistent(internal,_int4,smallint,oid,internal)', '{internal,_int4,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION _ltree_gist_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', '_ltree_gist_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_ltree_ops USING gist
+ADD FUNCTION 10 (ltree) ltree_gist_options (internal, boolean);
+
+ALTER OPERATOR FAMILY gist__ltree_ops USING gist
+ADD FUNCTION 10 (_ltree) _ltree_gist_options (internal, boolean);
+
diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control
index 03c3fb1..61c8cdf 100644
--- a/contrib/ltree/ltree.control
+++ b/contrib/ltree/ltree.control
@@ -1,5 +1,5 @@
# ltree extension
comment = 'data type for hierarchical tree-like structures'
-default_version = '1.1'
+default_version = '1.2'
module_pathname = '$libdir/ltree'
relocatable = true
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index e4b8c84..6247564 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -183,15 +183,16 @@ int ltree_strncasecmp(const char *a, const char *b, size_t s);
/* GiST support for ltree */
+#define SIGLEN_MAX (122 * sizeof(int32))
+#define SIGLEN_DEFAULT (2 * sizeof(int32))
#define BITBYTE 8
-#define SIGLENINT 2
-#define SIGLEN ( sizeof(int32)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
-typedef unsigned char BITVEC[SIGLEN];
+#define SIGLEN (sizeof(int32) * SIGLENINT)
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
+
typedef unsigned char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for(i = 0; i < (siglen); i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
#define GETBITBYTE(x,i) ( ((unsigned char)(x)) >> i & 0x01 )
@@ -199,8 +200,8 @@ typedef unsigned char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
/*
* type of index key for ltree. Tree are combined B-Tree and R-Tree
@@ -230,26 +231,34 @@ typedef struct
#define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
#define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
#define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
-#define LTG_LNODE(x) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
-#define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + VARSIZE(LTG_LNODE(x))) )
-#define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
+#define LTG_LNODE(x, siglen) ( (ltree*)( ( ((char*)(x))+LTG_HDRSIZE ) + ( LTG_ISALLTRUE(x) ? 0 : (siglen) ) ) )
+#define LTG_RENODE(x, siglen) ( (ltree*)( ((char*)LTG_LNODE(x, siglen)) + VARSIZE(LTG_LNODE(x, siglen))) )
+#define LTG_RNODE(x, siglen) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x, siglen) : LTG_RENODE(x, siglen) )
-#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
-#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
+#define LTG_GETLNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x, siglen) )
+#define LTG_GETRNODE(x, siglen) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x, siglen) )
+extern ltree_gist *ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+ ltree *left, ltree *right);
/* GiST support for ltree[] */
-#define ASIGLENINT (7)
-#define ASIGLEN (sizeof(int32)*ASIGLENINT)
-#define ASIGLENBIT (ASIGLEN*BITBYTE)
-typedef unsigned char ABITVEC[ASIGLEN];
+#define LTREE_ASIGLEN_DEFAULT (7 * sizeof(int32))
+#define LTREE_ASIGLEN_MAX (122 * sizeof(int32))
+#define ASIGLENBIT(siglen) ((siglen) * BITBYTE)
+
+#define ALOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
-#define ALOOPBYTE \
- for(i=0;i<ASIGLEN;i++)
+#define AHASHVAL(val, siglen) (((unsigned int)(val)) % ASIGLENBIT(siglen))
+#define AHASH(sign, val, siglen) SETBIT((sign), AHASHVAL(val, siglen))
-#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
-#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
+/* gist_ltree_ops and gist__ltree_ops opclass options */
+typedef struct LtreeGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} LtreeGistOptions;
/* type of key is the same to ltree_gist */
diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c
index 12aa8ff..9847b83 100644
--- a/contrib/ltree/ltree_gist.c
+++ b/contrib/ltree/ltree_gist.c
@@ -6,11 +6,13 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "crc32.h"
#include "ltree.h"
#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
+#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
PG_FUNCTION_INFO_V1(ltree_gist_in);
PG_FUNCTION_INFO_V1(ltree_gist_out);
@@ -33,6 +35,47 @@ ltree_gist_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+ltree_gist *
+ltree_gist_alloc(bool isalltrue, BITVECP sign, int siglen,
+ ltree *left, ltree *right)
+{
+ int32 size = LTG_HDRSIZE + (isalltrue ? 0 : siglen) +
+ (left ? VARSIZE(left) + (right ? VARSIZE(right) : 0) : 0);
+ ltree_gist *result = palloc(size);
+
+ SET_VARSIZE(result, size);
+
+ if (siglen)
+ {
+ result->flag = 0;
+
+ if (isalltrue)
+ result->flag |= LTG_ALLTRUE;
+ else if (sign)
+ memcpy(LTG_SIGN(result), sign, siglen);
+ else
+ memset(LTG_SIGN(result), 0, siglen);
+
+ if (left)
+ {
+ memcpy(LTG_LNODE(result, siglen), left, VARSIZE(left));
+
+ if (!right || left == right || ISEQ(left, right))
+ result->flag |= LTG_NORIGHT;
+ else
+ memcpy(LTG_RNODE(result, siglen), right, VARSIZE(right));
+ }
+ }
+ else
+ {
+ Assert(left);
+ result->flag = LTG_ONENODE;
+ memcpy(LTG_NODE(result), left, VARSIZE(left));
+ }
+
+ return result;
+}
+
PG_FUNCTION_INFO_V1(ltree_compress);
PG_FUNCTION_INFO_V1(ltree_decompress);
PG_FUNCTION_INFO_V1(ltree_same);
@@ -40,8 +83,8 @@ PG_FUNCTION_INFO_V1(ltree_union);
PG_FUNCTION_INFO_V1(ltree_penalty);
PG_FUNCTION_INFO_V1(ltree_picksplit);
PG_FUNCTION_INFO_V1(ltree_consistent);
+PG_FUNCTION_INFO_V1(ltree_gist_options);
-#define ISEQ(a,b) ( (a)->numlevel == (b)->numlevel && ltree_compare(a,b)==0 )
#define GETENTRY(vec,pos) ((ltree_gist *) DatumGetPointer((vec)->vector[(pos)].key))
Datum
@@ -52,14 +95,8 @@ ltree_compress(PG_FUNCTION_ARGS)
if (entry->leafkey)
{ /* ltree */
- ltree_gist *key;
ltree *val = DatumGetLtreeP(entry->key);
- int32 len = LTG_HDRSIZE + VARSIZE(val);
-
- key = (ltree_gist *) palloc0(len);
- SET_VARSIZE(key, len);
- key->flag = LTG_ONENODE;
- memcpy((void *) LTG_NODE(key), (void *) val, VARSIZE(val));
+ ltree_gist *key = ltree_gist_alloc(false, NULL, 0, val, 0);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(key),
@@ -93,6 +130,8 @@ ltree_same(PG_FUNCTION_ARGS)
ltree_gist *a = (ltree_gist *) PG_GETARG_POINTER(0);
ltree_gist *b = (ltree_gist *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
*result = false;
if (LTG_ISONENODE(a) != LTG_ISONENODE(b))
@@ -109,15 +148,15 @@ ltree_same(PG_FUNCTION_ARGS)
if (LTG_ISALLTRUE(a) != LTG_ISALLTRUE(b))
PG_RETURN_POINTER(result);
- if (!ISEQ(LTG_LNODE(a), LTG_LNODE(b)))
+ if (!ISEQ(LTG_LNODE(a, siglen), LTG_LNODE(b, siglen)))
PG_RETURN_POINTER(result);
- if (!ISEQ(LTG_RNODE(a), LTG_RNODE(b)))
+ if (!ISEQ(LTG_RNODE(a, siglen), LTG_RNODE(b, siglen)))
PG_RETURN_POINTER(result);
*result = true;
if (!LTG_ISALLTRUE(a))
{
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -132,7 +171,7 @@ ltree_same(PG_FUNCTION_ARGS)
}
static void
-hashing(BITVECP sign, ltree *t)
+hashing(BITVECP sign, ltree *t, int siglen)
{
int tlen = t->numlevel;
ltree_level *cur = LTREE_FIRST(t);
@@ -141,7 +180,7 @@ hashing(BITVECP sign, ltree *t)
while (tlen > 0)
{
hash = ltree_crc32_sz(cur->name, cur->len);
- HASH(sign, hash);
+ HASH(sign, hash, siglen);
cur = LEVEL_NEXT(cur);
tlen--;
}
@@ -152,7 +191,9 @@ ltree_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
+ BITVECP base = palloc0(siglen);
int32 i,
j;
ltree_gist *result,
@@ -161,16 +202,14 @@ ltree_union(PG_FUNCTION_ARGS)
*right = NULL,
*curtree;
bool isalltrue = false;
- bool isleqr;
- MemSet((void *) base, 0, sizeof(BITVEC));
for (j = 0; j < entryvec->n; j++)
{
cur = GETENTRY(entryvec, j);
if (LTG_ISONENODE(cur))
{
curtree = LTG_NODE(cur);
- hashing(base, curtree);
+ hashing(base, curtree, siglen);
if (!left || ltree_compare(left, curtree) > 0)
left = curtree;
if (!right || ltree_compare(right, curtree) < 0)
@@ -184,14 +223,14 @@ ltree_union(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(cur);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) base)[i] |= sc[i];
}
- curtree = LTG_LNODE(cur);
+ curtree = LTG_LNODE(cur, siglen);
if (!left || ltree_compare(left, curtree) > 0)
left = curtree;
- curtree = LTG_RNODE(cur);
+ curtree = LTG_RNODE(cur, siglen);
if (!right || ltree_compare(right, curtree) < 0)
right = curtree;
}
@@ -200,7 +239,7 @@ ltree_union(PG_FUNCTION_ARGS)
if (isalltrue == false)
{
isalltrue = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) base)[i] != 0xff)
{
@@ -210,23 +249,9 @@ ltree_union(PG_FUNCTION_ARGS)
}
}
- isleqr = (left == right || ISEQ(left, right)) ? true : false;
- *size = LTG_HDRSIZE + ((isalltrue) ? 0 : SIGLEN) + VARSIZE(left) + ((isleqr) ? 0 : VARSIZE(right));
-
- result = (ltree_gist *) palloc0(*size);
- SET_VARSIZE(result, *size);
- result->flag = 0;
+ result = ltree_gist_alloc(isalltrue, base, siglen, left, right);
- if (isalltrue)
- result->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(result), base, SIGLEN);
-
- memcpy((void *) LTG_LNODE(result), (void *) left, VARSIZE(left));
- if (isleqr)
- result->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(result), (void *) right, VARSIZE(right));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -237,11 +262,13 @@ ltree_penalty(PG_FUNCTION_ARGS)
ltree_gist *origval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
ltree_gist *newval = (ltree_gist *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
int32 cmpr,
cmpl;
- cmpl = ltree_compare(LTG_GETLNODE(origval), LTG_GETLNODE(newval));
- cmpr = ltree_compare(LTG_GETRNODE(newval), LTG_GETRNODE(origval));
+ cmpl = ltree_compare(LTG_GETLNODE(origval, siglen), LTG_GETLNODE(newval, siglen));
+ cmpr = ltree_compare(LTG_GETRNODE(newval, siglen), LTG_GETRNODE(origval, siglen));
*penalty = Max(cmpl, 0) + Max(cmpr, 0);
@@ -270,26 +297,24 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber j;
int32 i;
RIX *array;
OffsetNumber maxoff;
int nbytes;
- int size;
ltree *lu_l,
*lu_r,
*ru_l,
*ru_r;
ltree_gist *lu,
*ru;
- BITVEC ls,
- rs;
+ BITVECP ls = palloc0(siglen),
+ rs = palloc0(siglen);
bool lisat = false,
- risat = false,
- isleqr;
+ risat = false;
- memset((void *) ls, 0, sizeof(BITVEC));
- memset((void *) rs, 0, sizeof(BITVEC));
maxoff = entryvec->n - 1;
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
v->spl_left = (OffsetNumber *) palloc(nbytes);
@@ -303,7 +328,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
array[j].index = j;
lu = GETENTRY(entryvec, j); /* use as tmp val */
- array[j].r = LTG_GETLNODE(lu);
+ array[j].r = LTG_GETLNODE(lu, siglen);
}
qsort((void *) &array[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1,
@@ -317,10 +342,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
v->spl_left[v->spl_nleft] = array[j].index;
v->spl_nleft++;
- if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu), lu_r) > 0)
- lu_r = LTG_GETRNODE(lu);
+ if (lu_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), lu_r) > 0)
+ lu_r = LTG_GETRNODE(lu, siglen);
if (LTG_ISONENODE(lu))
- hashing(ls, LTG_NODE(lu));
+ hashing(ls, LTG_NODE(lu), siglen);
else
{
if (lisat || LTG_ISALLTRUE(lu))
@@ -329,7 +354,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) ls)[i] |= sc[i];
}
}
@@ -338,10 +363,10 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
v->spl_right[v->spl_nright] = array[j].index;
v->spl_nright++;
- if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu), ru_r) > 0)
- ru_r = LTG_GETRNODE(lu);
+ if (ru_r == NULL || ltree_compare(LTG_GETRNODE(lu, siglen), ru_r) > 0)
+ ru_r = LTG_GETRNODE(lu, siglen);
if (LTG_ISONENODE(lu))
- hashing(rs, LTG_NODE(lu));
+ hashing(rs, LTG_NODE(lu), siglen);
else
{
if (risat || LTG_ISALLTRUE(lu))
@@ -350,7 +375,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
{
BITVECP sc = LTG_SIGN(lu);
- LOOPBYTE
+ LOOPBYTE(siglen)
((unsigned char *) rs)[i] |= sc[i];
}
}
@@ -360,7 +385,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
if (lisat == false)
{
lisat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) ls)[i] != 0xff)
{
@@ -373,7 +398,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
if (risat == false)
{
risat = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (((unsigned char *) rs)[i] != 0xff)
{
@@ -383,38 +408,14 @@ ltree_picksplit(PG_FUNCTION_ARGS)
}
}
- lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index));
- isleqr = (lu_l == lu_r || ISEQ(lu_l, lu_r)) ? true : false;
- size = LTG_HDRSIZE + ((lisat) ? 0 : SIGLEN) + VARSIZE(lu_l) + ((isleqr) ? 0 : VARSIZE(lu_r));
- lu = (ltree_gist *) palloc0(size);
- SET_VARSIZE(lu, size);
- lu->flag = 0;
- if (lisat)
- lu->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(lu), ls, SIGLEN);
- memcpy((void *) LTG_LNODE(lu), (void *) lu_l, VARSIZE(lu_l));
- if (isleqr)
- lu->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(lu), (void *) lu_r, VARSIZE(lu_r));
+ lu_l = LTG_GETLNODE(GETENTRY(entryvec, array[FirstOffsetNumber].index), siglen);
+ lu = ltree_gist_alloc(lisat, ls, siglen, lu_l, lu_r);
+ ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index), siglen);
+ ru = ltree_gist_alloc(risat, rs, siglen, ru_l, ru_r);
- ru_l = LTG_GETLNODE(GETENTRY(entryvec, array[1 + ((maxoff - FirstOffsetNumber + 1) / 2)].index));
- isleqr = (ru_l == ru_r || ISEQ(ru_l, ru_r)) ? true : false;
- size = LTG_HDRSIZE + ((risat) ? 0 : SIGLEN) + VARSIZE(ru_l) + ((isleqr) ? 0 : VARSIZE(ru_r));
- ru = (ltree_gist *) palloc0(size);
- SET_VARSIZE(ru, size);
- ru->flag = 0;
- if (risat)
- ru->flag |= LTG_ALLTRUE;
- else
- memcpy((void *) LTG_SIGN(ru), rs, SIGLEN);
- memcpy((void *) LTG_LNODE(ru), (void *) ru_l, VARSIZE(ru_l));
- if (isleqr)
- ru->flag |= LTG_NORIGHT;
- else
- memcpy((void *) LTG_RNODE(ru), (void *) ru_r, VARSIZE(ru_r));
+ pfree(ls);
+ pfree(rs);
v->spl_ldatum = PointerGetDatum(lu);
v->spl_rdatum = PointerGetDatum(ru);
@@ -423,7 +424,7 @@ ltree_picksplit(PG_FUNCTION_ARGS)
}
static bool
-gist_isparent(ltree_gist *key, ltree *query)
+gist_isparent(ltree_gist *key, ltree *query, int siglen)
{
int32 numlevel = query->numlevel;
int i;
@@ -431,7 +432,8 @@ gist_isparent(ltree_gist *key, ltree *query)
for (i = query->numlevel; i >= 0; i--)
{
query->numlevel = i;
- if (ltree_compare(query, LTG_GETLNODE(key)) >= 0 && ltree_compare(query, LTG_GETRNODE(key)) <= 0)
+ if (ltree_compare(query, LTG_GETLNODE(key, siglen)) >= 0 &&
+ ltree_compare(query, LTG_GETRNODE(key, siglen)) <= 0)
{
query->numlevel = numlevel;
return true;
@@ -452,10 +454,10 @@ copy_ltree(ltree *src)
}
static bool
-gist_ischild(ltree_gist *key, ltree *query)
+gist_ischild(ltree_gist *key, ltree *query, int siglen)
{
- ltree *left = copy_ltree(LTG_GETLNODE(key));
- ltree *right = copy_ltree(LTG_GETRNODE(key));
+ ltree *left = copy_ltree(LTG_GETLNODE(key, siglen));
+ ltree *right = copy_ltree(LTG_GETRNODE(key, siglen));
bool res = true;
if (left->numlevel > query->numlevel)
@@ -477,7 +479,7 @@ gist_ischild(ltree_gist *key, ltree *query)
}
static bool
-gist_qe(ltree_gist *key, lquery *query)
+gist_qe(ltree_gist *key, lquery *query, int siglen)
{
lquery_level *curq = LQUERY_FIRST(query);
BITVECP sign = LTG_SIGN(key);
@@ -496,7 +498,7 @@ gist_qe(ltree_gist *key, lquery *query)
while (vlen > 0)
{
- if (GETBIT(sign, HASHVAL(curv->val)))
+ if (GETBIT(sign, HASHVAL(curv->val, siglen)))
{
isexist = true;
break;
@@ -545,41 +547,54 @@ gist_tqcmp(ltree *t, lquery *q)
}
static bool
-gist_between(ltree_gist *key, lquery *query)
+gist_between(ltree_gist *key, lquery *query, int siglen)
{
if (query->firstgood == 0)
return true;
- if (gist_tqcmp(LTG_GETLNODE(key), query) > 0)
+ if (gist_tqcmp(LTG_GETLNODE(key, siglen), query) > 0)
return false;
- if (gist_tqcmp(LTG_GETRNODE(key), query) < 0)
+ if (gist_tqcmp(LTG_GETRNODE(key, siglen), query) < 0)
return false;
return true;
}
+typedef struct LtreeSignature
+{
+ BITVECP sign;
+ int siglen;
+} LtreeSignature;
+
static bool
-checkcondition_bit(void *checkval, ITEM *val)
+checkcondition_bit(void *cxt, ITEM *val)
{
- return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(checkval, HASHVAL(val->val)) : true;
+ LtreeSignature *sig = cxt;
+
+ return (FLG_CANLOOKSIGN(val->flag)) ? GETBIT(sig->sign, HASHVAL(val->val, sig->siglen)) : true;
}
static bool
-gist_qtxt(ltree_gist *key, ltxtquery *query)
+gist_qtxt(ltree_gist *key, ltxtquery *query, int siglen)
{
+ LtreeSignature sig;
+
if (LTG_ISALLTRUE(key))
return true;
+ sig.sign = LTG_SIGN(key);
+ sig.siglen = siglen;
+
return ltree_execute(
GETQUERY(query),
- (void *) LTG_SIGN(key), false,
+ &sig, false,
checkcondition_bit
);
}
static bool
-arrq_cons(ltree_gist *key, ArrayType *_query)
+arrq_cons(ltree_gist *key, ArrayType *_query, int siglen)
{
lquery *query = (lquery *) ARR_DATA_PTR(_query);
int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
@@ -595,7 +610,7 @@ arrq_cons(ltree_gist *key, ArrayType *_query)
while (num > 0)
{
- if (gist_qe(key, query) && gist_between(key, query))
+ if (gist_qe(key, query, siglen) && gist_between(key, query, siglen))
return true;
num--;
query = NEXTVAL(query);
@@ -611,6 +626,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ LtreeGistOptions *options = (LtreeGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
ltree_gist *key = (ltree_gist *) DatumGetPointer(entry->key);
void *query = NULL;
bool res = false;
@@ -625,11 +642,11 @@ ltree_consistent(PG_FUNCTION_ARGS)
res = (GIST_LEAF(entry)) ?
(ltree_compare((ltree *) query, LTG_NODE(key)) > 0)
:
- (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+ (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
break;
case BTLessEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
- res = (ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0);
+ res = (ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0);
break;
case BTEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
@@ -637,35 +654,35 @@ ltree_consistent(PG_FUNCTION_ARGS)
res = (ltree_compare((ltree *) query, LTG_NODE(key)) == 0);
else
res = (
- ltree_compare((ltree *) query, LTG_GETLNODE(key)) >= 0
+ ltree_compare((ltree *) query, LTG_GETLNODE(key, siglen)) >= 0
&&
- ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0
+ ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0
);
break;
case BTGreaterEqualStrategyNumber:
query = PG_GETARG_LTREE_P(1);
- res = (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+ res = (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
break;
case BTGreaterStrategyNumber:
query = PG_GETARG_LTREE_P(1);
res = (GIST_LEAF(entry)) ?
- (ltree_compare((ltree *) query, LTG_GETRNODE(key)) < 0)
+ (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) < 0)
:
- (ltree_compare((ltree *) query, LTG_GETRNODE(key)) <= 0);
+ (ltree_compare((ltree *) query, LTG_GETRNODE(key, siglen)) <= 0);
break;
case 10:
query = PG_GETARG_LTREE_P_COPY(1);
res = (GIST_LEAF(entry)) ?
inner_isparent((ltree *) query, LTG_NODE(key))
:
- gist_isparent(key, (ltree *) query);
+ gist_isparent(key, (ltree *) query, siglen);
break;
case 11:
query = PG_GETARG_LTREE_P(1);
res = (GIST_LEAF(entry)) ?
inner_isparent(LTG_NODE(key), (ltree *) query)
:
- gist_ischild(key, (ltree *) query);
+ gist_ischild(key, (ltree *) query, siglen);
break;
case 12:
case 13:
@@ -676,7 +693,8 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((lquery *) query)
));
else
- res = (gist_qe(key, (lquery *) query) && gist_between(key, (lquery *) query));
+ res = (gist_qe(key, (lquery *) query, siglen) &&
+ gist_between(key, (lquery *) query, siglen));
break;
case 14:
case 15:
@@ -687,7 +705,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((ltxtquery *) query)
));
else
- res = gist_qtxt(key, (ltxtquery *) query);
+ res = gist_qtxt(key, (ltxtquery *) query, siglen);
break;
case 16:
case 17:
@@ -698,7 +716,7 @@ ltree_consistent(PG_FUNCTION_ARGS)
PointerGetDatum((ArrayType *) query)
));
else
- res = arrq_cons(key, (ArrayType *) query);
+ res = arrq_cons(key, (ArrayType *) query, siglen);
break;
default:
/* internal error */
@@ -708,3 +726,20 @@ ltree_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(res);
}
+
+Datum
+ltree_gist_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ LTREE_ASIGLEN_DEFAULT, 1, LTREE_ASIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(LtreeGistOptions, siglen) };
+ LtreeGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(LtreeGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index 3ddd335..dd37445 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -500,11 +500,17 @@ Europe & Russia*@ & !Transportation
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
</para>
<para>
- Example of creating such an index:
+ Example of creating such an index with a default signature length of 8 bytes:
</para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (path);
</programlisting>
+ <para>
+ Example of creating such an index with a signature length of 100 bytes:
+ </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
+</programlisting>
</listitem>
<listitem>
<para>
@@ -513,12 +519,18 @@ CREATE INDEX path_gist_idx ON test USING GIST (path);
<literal>@</literal>, <literal>~</literal>, <literal>?</literal>
</para>
<para>
- Example of creating such an index:
+ Example of creating such an index with a default signature length of 28 bytes:
</para>
<programlisting>
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
</programlisting>
<para>
+ Example of creating such an index with a signature length of 100 bytes:
+ </para>
+<programlisting>
+CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
+</programlisting>
+ <para>
Note: This index type is lossy.
</para>
</listitem>
--
2.7.4
0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patchtext/x-patch; name=0007-Add-opclass-parameters-to-contrib-pg_trgm-v02.patchDownload
From 585da1d8f6dd8f72a3b5f3d8d6ff0cdb4db90655 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Sat, 3 Feb 2018 02:12:56 +0300
Subject: [PATCH 7/9] Add opclass parameters to contrib/pg_trgm
---
contrib/pg_trgm/Makefile | 2 +-
contrib/pg_trgm/pg_trgm--1.4--1.5.sql | 33 +++++
contrib/pg_trgm/pg_trgm.control | 2 +-
contrib/pg_trgm/trgm.h | 17 ++-
contrib/pg_trgm/trgm_gist.c | 234 +++++++++++++++++++---------------
doc/src/sgml/pgtrgm.sgml | 9 ++
6 files changed, 184 insertions(+), 113 deletions(-)
create mode 100644 contrib/pg_trgm/pg_trgm--1.4--1.5.sql
diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile
index dfecc2a..6f452b5 100644
--- a/contrib/pg_trgm/Makefile
+++ b/contrib/pg_trgm/Makefile
@@ -4,7 +4,7 @@ MODULE_big = pg_trgm
OBJS = trgm_op.o trgm_gist.o trgm_gin.o trgm_regexp.o $(WIN32RES)
EXTENSION = pg_trgm
-DATA = pg_trgm--1.3--1.4.sql \
+DATA = pg_trgm--1.4--1.5.sql pg_trgm--1.3--1.4.sql \
pg_trgm--1.3.sql pg_trgm--1.2--1.3.sql pg_trgm--1.1--1.2.sql \
pg_trgm--1.0--1.1.sql pg_trgm--unpackaged--1.0.sql
PGFILEDESC = "pg_trgm - trigram matching"
diff --git a/contrib/pg_trgm/pg_trgm--1.4--1.5.sql b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
new file mode 100644
index 0000000..9ebb4f1
--- /dev/null
+++ b/contrib/pg_trgm/pg_trgm--1.4--1.5.sql
@@ -0,0 +1,33 @@
+/* contrib/pg_trgm/pg_trgm--1.5--1.5.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_trgm UPDATE TO '1.5'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('gtrgm_compress(internal)', '{internal,internal}'),
+('gtrgm_decompress(internal)', '{internal,internal}'),
+('gtrgm_same(gtrgm,gtrgm,internal)', '{gtrgm,gtrgm,internal,internal}'),
+('gtrgm_union(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('gtrgm_picksplit(internal,internal)', '{internal,internal,internal}'),
+('gtrgm_consistent(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}'),
+('gtrgm_distance(internal,text,smallint,oid,internal)', '{internal,text,smallint,oid,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION gtrgm_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'gtrgm_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_trgm_ops USING gist
+ADD FUNCTION 10 (text) gtrgm_options (internal, boolean);
diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control
index 3e325dd..d633eac 100644
--- a/contrib/pg_trgm/pg_trgm.control
+++ b/contrib/pg_trgm/pg_trgm.control
@@ -1,5 +1,5 @@
# pg_trgm extension
comment = 'text similarity measurement and index searching based on trigrams'
-default_version = '1.4'
+default_version = '1.5'
module_pathname = '$libdir/pg_trgm'
relocatable = true
diff --git a/contrib/pg_trgm/trgm.h b/contrib/pg_trgm/trgm.h
index f0ab50d..acdd83b 100644
--- a/contrib/pg_trgm/trgm.h
+++ b/contrib/pg_trgm/trgm.h
@@ -73,17 +73,16 @@ typedef struct
#define TRGMHDRSIZE (VARHDRSZ + sizeof(uint8))
/* gist */
+#define SIGLEN_DEFAULT (sizeof(int) * 3)
+#define SIGLEN_MAX (sizeof(int) * 122) /* key will toast, so very slow!!! */
#define BITBYTE 8
-#define SIGLENINT 3 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE - 1) /* see makesign */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE - 1) /* see makesign */
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
#define GETBITBYTE(x,i) ( (((char)(x)) >> (i)) & 0x01 )
@@ -91,8 +90,8 @@ typedef char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
#define ARRKEY 0x01
#define SIGNKEY 0x02
@@ -102,7 +101,7 @@ typedef char *BITVECP;
#define ISSIGNKEY(x) ( ((TRGM*)x)->flag & SIGNKEY )
#define ISALLTRUE(x) ( ((TRGM*)x)->flag & ALLISTRUE )
-#define CALCGTSIZE(flag, len) ( TRGMHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(trgm)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( TRGMHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(trgm)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+TRGMHDRSIZE ) )
#define GETARR(x) ( (trgm*)( (char*)x+TRGMHDRSIZE ) )
#define ARRNELEM(x) ( ( VARSIZE(x) - TRGMHDRSIZE )/sizeof(trgm) )
diff --git a/contrib/pg_trgm/trgm_gist.c b/contrib/pg_trgm/trgm_gist.c
index f1e0547..9f653ec 100644
--- a/contrib/pg_trgm/trgm_gist.c
+++ b/contrib/pg_trgm/trgm_gist.c
@@ -5,9 +5,16 @@
#include "trgm.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "fmgr.h"
+/* gist_trgm_ops opclass options */
+typedef struct TrgmGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} TrgmGistOptions;
typedef struct
{
@@ -38,6 +45,7 @@ PG_FUNCTION_INFO_V1(gtrgm_union);
PG_FUNCTION_INFO_V1(gtrgm_same);
PG_FUNCTION_INFO_V1(gtrgm_penalty);
PG_FUNCTION_INFO_V1(gtrgm_picksplit);
+PG_FUNCTION_INFO_V1(gtrgm_options);
/* Number of one-bits in an unsigned byte */
static const uint8 number_of_ones[256] = {
@@ -74,20 +82,41 @@ gtrgm_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static TRGM *
+gtrgm_alloc(bool isalltrue, int siglen, BITVECP sign)
+{
+ int flag = SIGNKEY | (isalltrue ? ALLISTRUE : 0);
+ int size = CALCGTSIZE(flag, siglen);
+ TRGM *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!isalltrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
static void
-makesign(BITVECP sign, TRGM *a)
+makesign(BITVECP sign, TRGM *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
trgm *ptr = GETARR(a);
int32 tmp = 0;
- MemSet((void *) sign, 0, sizeof(BITVEC));
- SETBIT(sign, SIGLENBIT); /* set last unused bit */
+ MemSet((void *) sign, 0, siglen);
+ SETBIT(sign, SIGLENBIT(siglen)); /* set last unused bit */
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- HASH(sign, tmp);
+ HASH(sign, tmp, siglen);
}
}
@@ -95,6 +124,8 @@ Datum
gtrgm_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
@@ -111,22 +142,17 @@ gtrgm_compress(PG_FUNCTION_ARGS)
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
TRGM *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
- res = (TRGM *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtrgm_alloc(true, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -160,7 +186,7 @@ gtrgm_decompress(PG_FUNCTION_ARGS)
}
static int32
-cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
+cnt_sml_sign_common(TRGM *qtrg, BITVECP sign, int siglen)
{
int32 count = 0;
int32 k,
@@ -171,7 +197,7 @@ cnt_sml_sign_common(TRGM *qtrg, BITVECP sign)
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- count += GETBIT(sign, HASHVAL(tmp));
+ count += GETBIT(sign, HASHVAL(tmp, siglen));
}
return count;
@@ -186,6 +212,8 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
TRGM *key = (TRGM *) DatumGetPointer(entry->key);
TRGM *qtrg;
bool res;
@@ -313,7 +341,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
}
else
{ /* non-leaf contains signature */
- int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key));
+ int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
int32 len = ARRNELEM(qtrg);
if (len == 0)
@@ -355,7 +383,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- if (!GETBIT(sign, HASHVAL(tmp)))
+ if (!GETBIT(sign, HASHVAL(tmp, siglen)))
{
res = false;
break;
@@ -408,7 +436,7 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
- check[k] = GETBIT(sign, HASHVAL(tmp));
+ check[k] = GETBIT(sign, HASHVAL(tmp, siglen));
}
res = trigramsMatchGraph(cache->graph, check);
pfree(check);
@@ -438,6 +466,8 @@ gtrgm_distance(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
TRGM *key = (TRGM *) DatumGetPointer(entry->key);
TRGM *qtrg;
float8 res;
@@ -495,7 +525,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
}
else
{ /* non-leaf contains signature */
- int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key));
+ int32 count = cnt_sml_sign_common(qtrg, GETSIGN(key), siglen);
int32 len = ARRNELEM(qtrg);
res = (len == 0) ? -1.0 : 1.0 - ((float8) count) / ((float8) len);
@@ -511,7 +541,7 @@ gtrgm_distance(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, TRGM *add)
+unionkey(BITVECP sbase, TRGM *add, int siglen)
{
int32 i;
@@ -522,7 +552,7 @@ unionkey(BITVECP sbase, TRGM *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -533,7 +563,7 @@ unionkey(BITVECP sbase, TRGM *add)
for (i = 0; i < ARRNELEM(add); i++)
{
CPTRGM(((char *) &tmp), ptr + i);
- HASH(sbase, tmp);
+ HASH(sbase, tmp, siglen);
}
}
return 0;
@@ -546,29 +576,23 @@ gtrgm_union(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int32 len = entryvec->n;
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
int32 i;
- int32 flag = 0;
- TRGM *result;
+ TRGM *result = gtrgm_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < len; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag = ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (TRGM *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -579,6 +603,8 @@ gtrgm_same(PG_FUNCTION_ARGS)
TRGM *a = (TRGM *) PG_GETARG_POINTER(0);
TRGM *b = (TRGM *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -595,7 +621,7 @@ gtrgm_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -632,24 +658,24 @@ gtrgm_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -658,19 +684,19 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(TRGM *a, TRGM *b)
+hemdist(TRGM *a, TRGM *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
Datum
@@ -679,6 +705,8 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
TRGM *origval = (TRGM *) DatumGetPointer(origentry->key);
TRGM *newval = (TRGM *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -688,7 +716,7 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
if (ISARRKEY(newval))
{
char *cache = (char *) fcinfo->flinfo->fn_extra;
- TRGM *cachedVal = (TRGM *) (cache + MAXALIGN(sizeof(BITVEC)));
+ TRGM *cachedVal = (TRGM *) (cache + MAXALIGN(siglen));
Size newvalsize = VARSIZE(newval);
BITVECP sign;
@@ -702,12 +730,12 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
char *newcache;
newcache = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
- MAXALIGN(sizeof(BITVEC)) +
+ MAXALIGN(siglen) +
newvalsize);
- makesign((BITVECP) newcache, newval);
+ makesign((BITVECP) newcache, newval, siglen);
- cachedVal = (TRGM *) (newcache + MAXALIGN(sizeof(BITVEC)));
+ cachedVal = (TRGM *) (newcache + MAXALIGN(siglen));
memcpy(cachedVal, newval, newvalsize);
if (cache)
@@ -719,31 +747,32 @@ gtrgm_penalty(PG_FUNCTION_ARGS)
sign = (BITVECP) cache;
if (ISALLTRUE(origval))
- *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+ *penalty = ((float) (SIGLENBIT(siglen) - sizebitvec(sign, siglen))) / (float) (SIGLENBIT(siglen) + 1);
else
- *penalty = hemdistsign(sign, orig);
+ *penalty = hemdistsign(sign, orig, siglen);
}
else
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
typedef struct
{
bool allistrue;
- BITVEC sign;
+ BITVECP sign;
} CACHESIGN;
static void
-fillcache(CACHESIGN *item, TRGM *key)
+fillcache(CACHESIGN *item, TRGM *key, BITVECP sign, int siglen)
{
item->allistrue = false;
+ item->sign = sign;
if (ISARRKEY(key))
- makesign(item->sign, key);
+ makesign(item->sign, key, siglen);
else if (ISALLTRUE(key))
item->allistrue = true;
else
- memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC));
+ memcpy((void *) item->sign, (void *) GETSIGN(key), siglen);
}
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
@@ -764,19 +793,19 @@ comparecost(const void *a, const void *b)
static int
-hemdistcache(CACHESIGN *a, CACHESIGN *b)
+hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
{
if (a->allistrue)
{
if (b->allistrue)
return 0;
else
- return SIGLENBIT - sizebitvec(b->sign);
+ return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
}
else if (b->allistrue)
- return SIGLENBIT - sizebitvec(a->sign);
+ return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
- return hemdistsign(a->sign, b->sign);
+ return hemdistsign(a->sign, b->sign, siglen);
}
Datum
@@ -785,6 +814,8 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
OffsetNumber maxoff = entryvec->n - 2;
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ TrgmGistOptions *options = (TrgmGistOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
TRGM *datum_l,
@@ -803,19 +834,23 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
/* cache the sign data for each existing item */
cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
+ cache_sign = palloc(siglen * (maxoff + 2));
+
for (k = FirstOffsetNumber; k <= maxoff; k = OffsetNumberNext(k))
- fillcache(&cache[k], GETENTRY(entryvec, k));
+ fillcache(&cache[k], GETENTRY(entryvec, k), &cache_sign[siglen * k],
+ siglen);
/* now find the two furthest-apart items */
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
{
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+ size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -840,44 +875,22 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
v->spl_nright = 0;
/* form initial .. */
- if (cache[seed_1].allistrue)
- {
- datum_l = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_l->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_l = (TRGM *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0));
- datum_l->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC));
- }
- if (cache[seed_2].allistrue)
- {
- datum_r = (TRGM *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_r->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_r = (TRGM *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0));
- datum_r->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC));
- }
+ datum_l = gtrgm_alloc(cache[seed_1].allistrue, siglen, cache[seed_1].sign);
+ datum_r = gtrgm_alloc(cache[seed_2].allistrue, siglen, cache[seed_2].sign);
union_l = GETSIGN(datum_l);
union_r = GETSIGN(datum_r);
maxoff = OffsetNumberNext(maxoff);
- fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff));
+ fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff),
+ &cache_sign[siglen * maxoff], siglen);
+
/* sort before ... */
costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
{
costvector[j - 1].pos = j;
- size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]));
- size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]));
+ size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
+ size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
costvector[j - 1].cost = abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -903,36 +916,36 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign),
+ siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign),
+ siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -943,12 +956,12 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_r), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -962,3 +975,20 @@ gtrgm_picksplit(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(v);
}
+
+Datum
+gtrgm_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(TrgmGistOptions, siglen) };
+ TrgmGistOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(TrgmGistOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/pgtrgm.sgml b/doc/src/sgml/pgtrgm.sgml
index 83b0033..a4151aa 100644
--- a/doc/src/sgml/pgtrgm.sgml
+++ b/doc/src/sgml/pgtrgm.sgml
@@ -368,6 +368,15 @@ CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
</para>
<para>
+ GiST opclass <literal>gist_trgm_ops</literal> has one optional parameter
+ <literal>siglen</literal> for specifying signature length in bytes
+ (default signature length is 12 bytes, maximal length is 488 bytes):
+<programlisting>
+CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));
+</programlisting>
+ </para>
+
+ <para>
At this point, you will have an index on the <structfield>t</structfield> column that
you can use for similarity searching. A typical query is
<programlisting>
--
2.7.4
0008-Add-opclass-parameters-to-contrib-hstore-v02.patchtext/x-patch; name=0008-Add-opclass-parameters-to-contrib-hstore-v02.patchDownload
From 413cd728ba7db42f23315b08ee00c163d3f1c7eb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 9 Feb 2018 18:34:29 +0300
Subject: [PATCH 8/9] Add opclass parameters to contrib/hstore
---
contrib/hstore/Makefile | 2 +-
contrib/hstore/hstore--1.5--1.6.sql | 32 ++++++
contrib/hstore/hstore.control | 2 +-
contrib/hstore/hstore_gist.c | 200 ++++++++++++++++++++----------------
doc/src/sgml/hstore.sgml | 9 ++
5 files changed, 154 insertions(+), 91 deletions(-)
create mode 100644 contrib/hstore/hstore--1.5--1.6.sql
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile
index 46d26f8..a8a5105 100644
--- a/contrib/hstore/Makefile
+++ b/contrib/hstore/Makefile
@@ -5,7 +5,7 @@ OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
$(WIN32RES)
EXTENSION = hstore
-DATA = hstore--1.4.sql hstore--1.4--1.5.sql \
+DATA = hstore--1.4.sql hstore--1.4--1.5.sql hstore--1.5--1.6.sql \
hstore--1.3--1.4.sql hstore--1.2--1.3.sql \
hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
hstore--unpackaged--1.0.sql
diff --git a/contrib/hstore/hstore--1.5--1.6.sql b/contrib/hstore/hstore--1.5--1.6.sql
new file mode 100644
index 0000000..08c85ee
--- /dev/null
+++ b/contrib/hstore/hstore--1.5--1.6.sql
@@ -0,0 +1,32 @@
+/* contrib/hstore/hstore--1.5--1.6.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION intarray UPDATE TO '1.6'" to load this file. \quit
+
+-- Update procedure signatures the hard way.
+-- We use to_regprocedure() so that query doesn't fail if run against 9.6beta1 definitions,
+-- wherein the signatures have been updated already. In that case to_regprocedure() will
+-- return NULL and no updates will happen.
+
+UPDATE pg_catalog.pg_proc SET
+ proargtypes = pg_catalog.array_to_string(newtypes::pg_catalog.oid[], ' ')::pg_catalog.oidvector,
+ pronargs = pg_catalog.array_length(newtypes, 1)
+FROM (VALUES
+(NULL::pg_catalog.text, NULL::pg_catalog.regtype[]), -- establish column types
+('ghstore_compress(internal)', '{internal,internal}'),
+('ghstore_decompress(internal)', '{internal,internal}'),
+('ghstore_consistent(internal,hstore,smallint,oid,internal)', '{internal,hstore,smallint,oid,internal,internal}'),
+('ghstore_penalty(internal,internal,internal)', '{internal,internal,internal,internal}'),
+('ghstore_picksplit(internal,internal)', '{internal,internal,internal}'),
+('ghstore_union(internal,internal)', '{internal,internal,internal}'),
+('ghstore_same(ghstore,ghstore,internal)', '{ghstore,ghstore,internal,internal}')
+) AS update_data (oldproc, newtypes)
+WHERE oid = pg_catalog.to_regprocedure(oldproc);
+
+CREATE FUNCTION ghstore_options(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'ghstore_options'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+ALTER OPERATOR FAMILY gist_hstore_ops USING gist
+ADD FUNCTION 10 (hstore) ghstore_options (internal, boolean);
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control
index 8a71947..93688cd 100644
--- a/contrib/hstore/hstore.control
+++ b/contrib/hstore/hstore.control
@@ -1,5 +1,5 @@
# hstore extension
comment = 'data type for storing sets of (key, value) pairs'
-default_version = '1.5'
+default_version = '1.6'
module_pathname = '$libdir/hstore'
relocatable = true
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index 6d24d2f..13be2c0 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -4,29 +4,33 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/stratnum.h"
#include "catalog/pg_type.h"
#include "utils/pg_crc.h"
#include "hstore.h"
+/* gist_hstore_ops opclass options */
+typedef struct HstoreGistOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length in bytes */
+} GistHstoreOptions;
+
/* bigint defines */
#define BITBYTE 8
-#define SIGLENINT 4 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITBYTE)
+#define SIGLEN_DEFAULT (sizeof(int32) * 4)
+#define SIGLEN_MAX (sizeof(int32) * 122) /* key will toast, so very slow!!! */
+#define SIGLENBIT(siglen) ((siglen) * BITBYTE)
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define SIGPTR(x) ( (BITVECP) ARR_DATA_PTR(x) )
-
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < (siglen); i++)
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
-
-#define LOOPBIT \
- for(i=0;i<SIGLENBIT;i++)
+#define LOOPBIT(siglen) \
+ for (i = 0; i < SIGLENBIT(siglen); i++)
/* beware of multiple evaluation of arguments to these macros! */
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
@@ -34,8 +38,8 @@ typedef char *BITVECP;
#define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) )
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
typedef struct
{
@@ -49,7 +53,7 @@ typedef struct
#define ISALLTRUE(x) ( ((GISTTYPE*)x)->flag & ALLISTRUE )
#define GTHDRSIZE (VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+#define CALCGTSIZE(flag, siglen) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : (siglen)) )
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
@@ -100,6 +104,27 @@ ghstore_out(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(0);
}
+static GISTTYPE *
+ghstore_alloc(bool allistrue, int siglen, BITVECP sign)
+{
+ int flag = allistrue ? ALLISTRUE : 0;
+ int size = CALCGTSIZE(flag, siglen);
+ GISTTYPE *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if (!allistrue)
+ {
+ if (sign)
+ memcpy(GETSIGN(res), sign, siglen);
+ else
+ memset(GETSIGN(res), 0, siglen);
+ }
+
+ return res;
+}
+
PG_FUNCTION_INFO_V1(ghstore_consistent);
PG_FUNCTION_INFO_V1(ghstore_compress);
PG_FUNCTION_INFO_V1(ghstore_decompress);
@@ -107,36 +132,37 @@ PG_FUNCTION_INFO_V1(ghstore_penalty);
PG_FUNCTION_INFO_V1(ghstore_picksplit);
PG_FUNCTION_INFO_V1(ghstore_union);
PG_FUNCTION_INFO_V1(ghstore_same);
+PG_FUNCTION_INFO_V1(ghstore_options);
Datum
ghstore_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(1);
+ int siglen = options->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
+ GISTTYPE *res = ghstore_alloc(false, siglen, NULL);
HStore *val = DatumGetHStoreP(entry->key);
HEntry *hsent = ARRPTR(val);
char *ptr = STRPTR(val);
int count = HS_COUNT(val);
int i;
- SET_VARSIZE(res, CALCGTSIZE(0));
-
for (i = 0; i < count; ++i)
{
int h;
h = crc32_sz((char *) HSTORE_KEY(hsent, ptr, i),
HSTORE_KEYLEN(hsent, i));
- HASH(GETSIGN(res), h);
+ HASH(GETSIGN(res), h, siglen);
if (!HSTORE_VALISNULL(hsent, i))
{
h = crc32_sz((char *) HSTORE_VAL(hsent, ptr, i),
HSTORE_VALLEN(hsent, i));
- HASH(GETSIGN(res), h);
+ HASH(GETSIGN(res), h, siglen);
}
}
@@ -152,15 +178,13 @@ ghstore_compress(PG_FUNCTION_ARGS)
GISTTYPE *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
- res->flag = ALLISTRUE;
+ res = ghstore_alloc(true, siglen, NULL);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
@@ -188,6 +212,9 @@ ghstore_same(PG_FUNCTION_ARGS)
GISTTYPE *a = (GISTTYPE *) PG_GETARG_POINTER(0);
GISTTYPE *b = (GISTTYPE *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
+
if (ISALLTRUE(a) && ISALLTRUE(b))
*result = true;
@@ -202,7 +229,7 @@ ghstore_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -215,12 +242,12 @@ ghstore_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
size += SUMBIT(sign);
sign = (BITVECP) (((char *) sign) + 1);
@@ -229,12 +256,12 @@ sizebitvec(BITVECP sign)
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
dist = 0;
- LOOPBIT
+ LOOPBIT(siglen)
{
if (GETBIT(a, i) != GETBIT(b, i))
dist++;
@@ -243,30 +270,30 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *b, int siglen)
{
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(b), siglen);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglen) - sizebitvec(GETSIGN(a), siglen);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglen);
}
static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, int siglen)
{
int32 i;
BITVECP sadd = GETSIGN(add);
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
return 0;
}
@@ -278,28 +305,23 @@ ghstore_union(PG_FUNCTION_ARGS)
int32 len = entryvec->n;
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
int32 i;
- int32 flag = 0;
- GISTTYPE *result;
+ GISTTYPE *result = ghstore_alloc(false, siglen, NULL);
+ BITVECP base = GETSIGN(result);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < len; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(ALLISTRUE, siglen));
break;
}
}
- len = CALCGTSIZE(flag);
- result = (GISTTYPE *) palloc(len);
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
- *size = len;
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -310,10 +332,12 @@ ghstore_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(3);
+ int siglen = options->siglen;
GISTTYPE *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
GISTTYPE *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
- *penalty = hemdist(origval, newval);
+ *penalty = hemdist(origval, newval, siglen);
PG_RETURN_POINTER(penalty);
}
@@ -338,6 +362,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
OffsetNumber maxoff = entryvec->n - 2;
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(2);
+ int siglen = options->siglen;
OffsetNumber k,
j;
GISTTYPE *datum_l,
@@ -368,7 +394,7 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
_k = GETENTRY(entryvec, k);
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
- size_waste = hemdist(_k, GETENTRY(entryvec, j));
+ size_waste = hemdist(_k, GETENTRY(entryvec, j), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -390,33 +416,10 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_l, GTHDRSIZE);
- datum_l->flag = ALLISTRUE;
- }
- else
- {
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
- datum_l->flag = 0;
- memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), sizeof(BITVEC))
- ;
- }
- if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE);
- SET_VARSIZE(datum_r, GTHDRSIZE);
- datum_r->flag = ALLISTRUE;
- }
- else
- {
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
- datum_r->flag = 0;
- memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), sizeof(BITVEC));
- }
+ datum_l = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_1)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_1)));
+ datum_r = ghstore_alloc(ISALLTRUE(GETENTRY(entryvec, seed_2)), siglen,
+ GETSIGN(GETENTRY(entryvec, seed_2)));
maxoff = OffsetNumberNext(maxoff);
/* sort before ... */
@@ -425,8 +428,8 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
{
costvector[j - 1].pos = j;
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
costvector[j - 1].cost = abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -450,20 +453,20 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
continue;
}
_j = GETENTRY(entryvec, j);
- size_alpha = hemdist(datum_l, _j);
- size_beta = hemdist(datum_r, _j);
+ size_alpha = hemdist(datum_l, _j, siglen);
+ size_beta = hemdist(datum_r, _j, siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.0001))
{
if (ISALLTRUE(datum_l) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) union_l, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_l, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -474,12 +477,12 @@ ghstore_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || ISALLTRUE(_j))
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) union_r, 0xff, sizeof(BITVEC));
+ MemSet((void *) union_r, 0xff, siglen);
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -504,6 +507,8 @@ ghstore_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ GistHstoreOptions *options = (GistHstoreOptions *) PG_GETARG_POINTER(5);
+ int siglen = options->siglen;
bool res = true;
BITVECP sign;
@@ -529,13 +534,13 @@ ghstore_consistent(PG_FUNCTION_ARGS)
int crc = crc32_sz((char *) HSTORE_KEY(qe, qv, i),
HSTORE_KEYLEN(qe, i));
- if (GETBIT(sign, HASHVAL(crc)))
+ if (GETBIT(sign, HASHVAL(crc, siglen)))
{
if (!HSTORE_VALISNULL(qe, i))
{
crc = crc32_sz((char *) HSTORE_VAL(qe, qv, i),
HSTORE_VALLEN(qe, i));
- if (!GETBIT(sign, HASHVAL(crc)))
+ if (!GETBIT(sign, HASHVAL(crc, siglen)))
res = false;
}
}
@@ -548,7 +553,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
text *query = PG_GETARG_TEXT_PP(1);
int crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
- res = (GETBIT(sign, HASHVAL(crc))) ? true : false;
+ res = (GETBIT(sign, HASHVAL(crc, siglen))) ? true : false;
}
else if (strategy == HStoreExistsAllStrategyNumber)
{
@@ -569,7 +574,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
if (key_nulls[i])
continue;
crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
- if (!(GETBIT(sign, HASHVAL(crc))))
+ if (!(GETBIT(sign, HASHVAL(crc, siglen))))
res = false;
}
}
@@ -594,7 +599,7 @@ ghstore_consistent(PG_FUNCTION_ARGS)
if (key_nulls[i])
continue;
crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
- if (GETBIT(sign, HASHVAL(crc)))
+ if (GETBIT(sign, HASHVAL(crc, siglen)))
res = true;
}
}
@@ -603,3 +608,20 @@ ghstore_consistent(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+
+Datum
+ghstore_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistHstoreOptions, siglen) };
+ GistHstoreOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistHstoreOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml
index 94ccd12..45a811f 100644
--- a/doc/src/sgml/hstore.sgml
+++ b/doc/src/sgml/hstore.sgml
@@ -462,6 +462,15 @@ CREATE INDEX hidx ON testhstore USING GIN (h);
</programlisting>
<para>
+ GiST opclass <literal>gist_hstore_ops</literal> has one optional parameter
+ <literal>siglen</literal> for specifying signature length in bytes
+ (default signature length is 16 bytes, maximal length is 488 bytes):
+<programlisting>
+ CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32));
+</programlisting>
+ </para>
+
+ <para>
<type>hstore</type> also supports <type>btree</type> or <type>hash</type> indexes for
the <literal>=</literal> operator. This allows <type>hstore</type> columns to be
declared <literal>UNIQUE</literal>, or to be used in <literal>GROUP BY</literal>,
--
2.7.4
В письме от 15 ноября 2018 18:26:43 пользователь Nikita Glukhov написал:
But since it is now "Rejected with feedback", let's wait till autumn.
We don't want to wait that long. But now we only need to сome to an
agreement
about CREATE INDEX syntax and where to store the opclass parameters.Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.CREATE INDEX syntax and parameters storage method still need discussion.
Hi! I'd like to review your patch if you do not mind. Since I am familiar
with the subject. I'll add it to the TOP 3 of my IT TODO list :-)
But even without carefully reading the code I have several important
questions:
1. I am sure that this patch should contain code for opclass praram generic
implementation, and _one_ example of how it is used for certain opclass.
From my point of view, we should not add all hardcoded constant as opclass
params just because we can. It should be done only when it is really needed.
And each case needs special consideration. This is not only about code
complexity, this worries me less, but also about documentation complexity.
Once option is added, it should be documented. This will make documentation
more hard to understand (and completely unexperienced users reads
documentation too). If this option is needed, this is price we all pay. But if
nobody really use it, I see no reason to make things more complex for
everyone.
2. The second question, is why you create new column in one of the table of
pg_catalog, when we have attopitions in attribute description. And each
indexed column in index is technically a relation attribute. Options of index
column should be stored there, from my point of view (yes I know it is a
hassle to pass it there, but I did in in my preview, it can be done)
2.1. I did not read the code, I'll do it some time late, but the main question
I have is, how you will manage case, when you set different values for same
options of different columns. like:
CREATE INDEX ON arrays USING gist (
arr1 gist__intbig_ops (siglen = 32),
arr2 gist__intbig_ops (siglen = 64)
);
It is easitly solved when you store them in attoptions. But my question how do
you manage it... (I'll get the answer from the code soon, I hope)
--
Do code for fun.
В письме от 15 ноября 2018 18:26:43 пользователь Nikita Glukhov написал:
Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.CREATE INDEX syntax and parameters storage method still need discussion.
I've played around a bit with you patch and come to some conclusions, I'd like
to share. They are almost same as those before, but now there are more
details.
Again some issues about storing opclass options in pg_inedx:
1. Having both indoption and indoptions column in pg_index will make someone's
brain explode for sure. If not, it will bring troubles when people start
confusing them.
2. Now I found out how do you store option values for each opclass: text[] of
indoptions in pg_index is not the same as text[] in
reloptions in pg_catalog (and it brings more confusion). In reloption each
member of the array is a single option.
reloptions | {fillfactor=90,autovacuum_enabled=false}
In indoptions, is a whole string of options for one of the indexed attributes,
each array item has all options for one indexed attribute. And this string
needs furthermore parsing, that differs from reloption parsing.
indoptions | {"{numranges=150}","{numranges=160}"}
This brings us to the following issues:
2a. pg_index stores properties of index in general. Properties of each indexed
attributes is stored in pg_attribute table. If we follow this philosophy
it is wrong to add any kind of per-attribute array values into pg_index. These
values should be added to pg_attribute one per each pg_attribute entry.
2b. Since you've chosen method of storage that differs from one that is used
in reloptions, that will lead to two verstions of code that processes the
attributes. And from now on, if we accept this, we should support both of them
and keep them in sync. (I see that you tried to reuse as much code as
possible, but still you added some more that duplicate current reloptions
functionality.)
I know that relotions code is not really suitable for reusing. This was the
reason why I started solving oplcass option task with rewriting reloptions
code,to make it 100% reusable for any kind of options. So I would offer you
again to join me as a reviewer of that code. This will make opclass code more
simple and more sensible, if my option code is used...
3. Speaking of sensible code
Datum
g_int_options(PG_FUNCTION_ARGS)
{
Datum raw_options = PG_GETARG_DATUM(0);
bool validate = PG_GETARG_BOOL(1);
relopt_int siglen =
{ {"numranges", "number of ranges for compression", 0, 0, 9,
RELOPT_TYPE_INT },
G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
relopt_gen *optgen[] = { &siglen.gen };
int offsets[] = { offsetof(IntArrayOptions, num_ranges) };
IntArrayOptions *options =
parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
sizeof(IntArrayOptions), validate);
PG_RETURN_POINTER(options);
}
It seems to be not a very nice hack.
What would you do if you would like to have both int, real and boolean options
for one opclass? I do not know how to implement it using this code.
We have only int opclass options for now, but more will come and we should be
ready for it.
4. Now getting back to not adding opclass options wherever we can, just
because we can:
4a. For inrarray there were no opclass options tests added. I am sure there
should be one, at least just to make sure it still does not segfault when you
try to set one. And in some cases more tests can be needed. To add and review
them one should be familiar with this opclass internals. So it is good when
different people do it for different opclasses
4b. When you add opclass options instead of hardcoded values, it comes to
setting minimum and maximum value. Why do you choose 1000 as maximum
for num_ranges in gist_int_ops in intarray? Why 1 as minimum? All these
decisions needs careful considerations and can't be done for bunch of
opclasses just in one review.
4c. Patch usually take a long path from prototype to final commit. Do you
really want to update all these opclasses code each time when some changes
in the main opclass option code is made? ;-)
So I would suggest to work only with intarray and add other opclasses later.
5. You've been asking about SQL grammar
CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);
As for me I do not really care about it. For me all the solutions is
acceptable. But looking at is i came to one notion:
I've never seen before DEFAULT keyword to be used in this way. There is logic
in such usage, but I did not remember any practical usage case.
If there are such usages (I can easily missed it) or if it is somehow
recommended in SQL standard -- let it be. But if none above, I would suggest
to use WITH keyword instead. As it is already used for reloptions. As far as I
remember in my prototype I used "WITH OPTIONS" but did if just because did not
find my way through yac with single "WITH". So ideal way for me would be
create index ON test USING GIST (i WITH (sig_len_int=22));
But as I said it is not thing of importance for me. Just an observation.
--
Do code for fun.
On Wed, Feb 28, 2018 at 9:46 AM Nikolay Shaplov <dhyan@nataraj.su> wrote:
1. I've seen you've added a new attribute into pg_index. Why??!!
As far as I can get, if have index built on several columns (A1, A2, A3) you
can set, own opclass for each column. And set individual options for each
opclass if we are speaking about options. So I would expect to have these
options not in pg_index, but in pg_attribute. And we already have one there:
attoptions.I just do not get how you have come to per-index options. May be I
should read code more attentively...
It seems sensible to have both per-column options and per-index
options. For example, we've got the fastupdate option for GIN, which
is a property of the index as a whole, not any individual column. But
you could also want to specify some column-specific options, which
seems to be what this patch is about, since an opclass is associated
with an individual column. And since an index can have more than one
column, I agree that it seems more appropriate to store this
information in pg_attribute than pg_index.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Attached 3rd version of the patches.
On 20.11.2018 14:15, Nikolay Shaplov wrote:
В письме от 15 ноября 2018 18:26:43 пользователь Nikita Glukhov написал:
Attached 2nd version of the patches. Nothing has changed since March,
this is just a rebased version.CREATE INDEX syntax and parameters storage method still need discussion.
I've played around a bit with you patch and come to some conclusions, I'd like
to share. They are almost same as those before, but now there are more
details.
Again some issues about storing opclass options in pg_inedx:
1. Having both indoption and indoptions column in pg_index will make someone's
brain explode for sure. If not, it will bring troubles when people start
confusing them.2. Now I found out how do you store option values for each opclass: text[] of
indoptions in pg_index is not the same as text[] in
reloptions in pg_catalog (and it brings more confusion). In reloption each
member of the array is a single option.reloptions | {fillfactor=90,autovacuum_enabled=false}
In indoptions, is a whole string of options for one of the indexed attributes,
each array item has all options for one indexed attribute. And this string
needs furthermore parsing, that differs from reloption parsing.indoptions | {"{numranges=150}","{numranges=160}"}
This brings us to the following issues:
2a. pg_index stores properties of index in general. Properties of each indexed
attributes is stored in pg_attribute table. If we follow this philosophy
it is wrong to add any kind of per-attribute array values into pg_index. These
values should be added to pg_attribute one per each pg_attribute entry.2b. Since you've chosen method of storage that differs from one that is used
in reloptions, that will lead to two verstions of code that processes the
attributes. And from now on, if we accept this, we should support both of them
and keep them in sync. (I see that you tried to reuse as much code as
possible, but still you added some more that duplicate current reloptions
functionality.)
On 21.11.2018 18:04, Robert Haas wrote:
It seems sensible to have both per-column options and per-index
options. For example, we've got the fastupdate option for GIN, which
is a property of the index as a whole, not any individual column. But
you could also want to specify some column-specific options, which
seems to be what this patch is about, since an opclass is associated
with an individual column. And since an index can have more than one
column, I agree that it seems more appropriate to store this
information in pg_attribute than pg_index.
Ok, I have switched from pg_index.indoptions to pg_attribute.attoptions.
I agree that we should distinguish per-index and per-column options, but they
can also be AM-specific and opclass-specific.
'fastupdate' option for GIN is an example of AM-specific per-index option.
ASC/DESC and NULLS LAST/FIRST are examples of AM-class-specific per-column
options having special SQL syntax. "AM-class-specific" here means "specific
only for the class of AMs that support ordering". Now they are stored as flags
in pg_index.indoption[] but later can be moved to pg_attribute.attoptions.
Don't know should per-column AM-specific and opclass-specific options be stored
in the single column attoptions or have separate columns (like attamoptions and
attopclassoptions). If we will store them together, we can run into name
collisions, but this collisions can be easily resolved using autogenerated
prefixes in option names ("am.foo=bar", "opclass.foo=baz").
And another problem is the options with default values. They may be not
explicitly specified by the user, and in this case in current implementation
nothing will be stored in the catalog because default values can be obtained
from the code. But this will not allow changing of default values without
compatibility issues. So I think it would be better to store both default and
explicitly specified values of all opclass options, but this will require a
major refactoring of current API.
Also I have idea to define list of opclass parameters declaratively when opclass
is created using syntax like the following:
CREATE OPERATOR CLASS name [ (param_name param_type [= default_value] [,...]) ]
FOR TYPE ... AS (
{ OPTIONS function_name ( arg_type [,...] ) /* reloptions => binary */
| OPERATOR ...
} [,...]
)
But if we remember about the min/max values etc. the syntax will definitely
become more complicated, and it will require much more changes in the catalog
(up to creation of new table pg_opclassparams).
In any case, I think it would be nice to give somehow the user the ability to
obtain a list of opclass parameters not only from the documentation.
On 20.11.2018 14:15, Nikolay Shaplov wrote:
I know that relotions code is not really suitable for reusing. This was the
reason why I started solving oplcass option task with rewriting reloptions
code,to make it 100% reusable for any kind of options. So I would offer you
again to join me as a reviewer of that code. This will make opclass code more
simple and more sensible, if my option code is used...
I absolutely agree that reloptions code needs such refactoring, and I am
planning to continue reviewing your patches.
3. Speaking of sensible code
Datum
g_int_options(PG_FUNCTION_ARGS)
{
Datum raw_options = PG_GETARG_DATUM(0);
bool validate = PG_GETARG_BOOL(1);
relopt_int siglen =
{ {"numranges", "number of ranges for compression", 0, 0, 9,
RELOPT_TYPE_INT },
G_INT_NUMRANGES_DEFAULT, 1, G_INT_NUMRANGES_MAX };
relopt_gen *optgen[] = { &siglen.gen };
int offsets[] = { offsetof(IntArrayOptions, num_ranges) };
IntArrayOptions *options =
parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
sizeof(IntArrayOptions), validate);PG_RETURN_POINTER(options);
}It seems to be not a very nice hack.
What would you do if you would like to have both int, real and boolean options
for one opclass? I do not know how to implement it using this code.
We have only int opclass options for now, but more will come and we should be
ready for it.
Don't understand where is hack.
It's possible to parse options of different types, see the following example:
typedef struct FooOptions
{
int int_opt;
int str_opt_offset;
} FooOptions;
relopt_int int_option = { {"int_opt", "desc", 0, 0, 7, RELOPT_TYPE_INT },
INT_OPT_DEFAULT, INT_OPT_MIN, INT_OPT_MAX };
relopt_string str_option = { {"str_opt", "desc", 0, 0, 7, RELOPT_TYPE_STRING },
0, true, validate_str_option, NULL };
relopt_gen *gen_options[] = { &int_option.gen, &str_option.gen };
int offsets[] = { offsetof(FooOptions, int_opt),
offsetof(FooOptions, str_opt_offset) };
FooOptions *opts =
parseAndFillLocalRelOptions(raw_options, gen_options, offsets, 2,
sizeof(FooOptions), validate);
int int_option_value = opts->int_opt;
char *str_option_value = (char *) opts + opts->str_opt_offset;
Also I can propose to combine this two arrays into the one:
relopt_gen_offset options[] = {
&int_option.gen, offsetof(FooOptions, int_opt),
&str_option.gen, offsetof(FooOptions, str_opt_offset)
};
4. Now getting back to not adding opclass options wherever we can, just
because we can:4a. For inrarray there were no opclass options tests added. I am sure there
should be one, at least just to make sure it still does not segfault when you
try to set one. And in some cases more tests can be needed. To add and review
them one should be familiar with this opclass internals. So it is good when
different people do it for different opclasses4b. When you add opclass options instead of hardcoded values, it comes to
setting minimum and maximum value. Why do you choose 1000 as maximum
for num_ranges in gist_int_ops in intarray? Why 1 as minimum? All these
decisions needs careful considerations and can't be done for bunch of
opclasses just in one review.4c. Patch usually take a long path from prototype to final commit. Do you
really want to update all these opclasses code each time when some changes
in the main opclass option code is made? ;-)So I would suggest to work only with intarray and add other opclasses later.
I decided to leave only GiST tsvector_ops as an example, because it is an
in-core opclass, not an extension like intarray.
5. You've been asking about SQL grammar
CREATE INDEX idx ON tab USING am (
{expr {opclass | DEFAULT} ({name=value} [,...])} [,...]
);As for me I do not really care about it. For me all the solutions is
acceptable. But looking at is i came to one notion:I've never seen before DEFAULT keyword to be used in this way. There is logic
in such usage, but I did not remember any practical usage case.
If there are such usages (I can easily missed it) or if it is somehow
recommended in SQL standard -- let it be. But if none above, I would suggest
to use WITH keyword instead. As it is already used for reloptions. As far as I
remember in my prototype I used "WITH OPTIONS" but did if just because did not
find my way through yac with single "WITH". So ideal way for me would becreate index ON test USING GIST (i WITH (sig_len_int=22));
But as I said it is not thing of importance for me. Just an observation.
"[opclass] WITH OPTIONS (options)" looks too verbose, of course.
"[opclass] WITH (options)" looks acceptable, but it seems to conflict with
exclusion constraints syntax ("index_elem WITH operator").
"opclass (options)" looks the most natural to me. But we still need some
keyword before the parentheses when the opclass is not specified since we
can't distinguish "func_name (func_params)" and "col_name (opclass_options)"
in grammar.
--
Nikita Glukhov
Postgres Professional:http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-Add-opclass-parameters-v03.patchtext/x-patch; name=0001-Add-opclass-parameters-v03.patchDownload
From e28cea53ec3d088a3d87093bedc137aa599bd01d Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Wed, 10 Jan 2018 00:46:28 +0300
Subject: [PATCH 1/9] Add opclass parameters
---
doc/src/sgml/indices.sgml | 2 +-
doc/src/sgml/ref/create_index.sgml | 16 +++-
src/backend/access/common/reloptions.c | 142 +++++++++++++++++++++---------
src/backend/access/index/indexam.c | 81 +++++++++++++++++
src/backend/catalog/heap.c | 8 +-
src/backend/catalog/index.c | 23 ++++-
src/backend/catalog/toasting.c | 1 +
src/backend/commands/indexcmds.c | 17 +++-
src/backend/commands/tablecmds.c | 2 +-
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/plancat.c | 4 +
src/backend/parser/gram.y | 72 +++++++++------
src/backend/utils/adt/ruleutils.c | 126 +++++++++++++++-----------
src/backend/utils/cache/relcache.c | 99 +++++++++++++++++++++
src/include/access/amapi.h | 7 ++
src/include/access/genam.h | 6 ++
src/include/access/reloptions.h | 5 ++
src/include/catalog/heap.h | 1 +
src/include/nodes/execnodes.h | 2 +
src/include/nodes/parsenodes.h | 1 +
src/include/nodes/relation.h | 1 +
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 3 +
src/include/utils/ruleutils.h | 2 +
src/test/regress/expected/btree_index.out | 5 ++
src/test/regress/sql/btree_index.sql | 4 +
28 files changed, 502 insertions(+), 132 deletions(-)
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 46f427b..82a3e41 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1239,7 +1239,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
An index definition can specify an <firstterm>operator
class</firstterm> for each column of an index.
<synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
</synopsis>
The operator class identifies the operators to be used by the index
for that column. For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index ad619cd..1946d3b 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
- ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+ ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</varlistentry>
<varlistentry>
+ <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of an operator class parameter. See below for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>ASC</literal></term>
<listitem>
<para>
@@ -607,8 +616,9 @@ Indexes:
</para>
<para>
- An <firstterm>operator class</firstterm> can be specified for each
- column of an index. The operator class identifies the operators to be
+ An <firstterm>operator class</firstterm> with its optional parameters
+ can be specified for each column of an index.
+ The operator class identifies the operators to be
used by the index for that column. For example, a B-tree index on
four-byte integers would use the <literal>int4_ops</literal> class;
this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index eece89a..87c8e5c 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1040,6 +1040,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
return options;
}
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+ relopt_value *reloptions, int numoptions)
+{
+ ArrayType *array = DatumGetArrayTypeP(options);
+ Datum *optiondatums;
+ int noptions;
+ int i;
+
+ deconstruct_array(array, TEXTOID, -1, false, 'i',
+ &optiondatums, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *text_str = VARDATA(optiondatums[i]);
+ int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+ int j;
+
+ /* Search for a match in reloptions */
+ for (j = 0; j < numoptions; j++)
+ {
+ int kw_len = reloptions[j].gen->namelen;
+
+ if (text_len > kw_len && text_str[kw_len] == '=' &&
+ strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+ {
+ parse_one_reloption(&reloptions[j], text_str, text_len,
+ validate);
+ break;
+ }
+ }
+
+ if (j >= numoptions && validate)
+ {
+ char *s;
+ char *p;
+
+ s = TextDatumGetCString(optiondatums[i]);
+ p = strchr(s, '=');
+ if (p)
+ *p = '\0';
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s\"", s)));
+ }
+ }
+
+ /* It's worth avoiding memory leaks in this function */
+ pfree(optiondatums);
+
+ if (((void *) array) != DatumGetPointer(options))
+ pfree(array);
+}
+
/*
* Interpret reloptions that are given in text-array format.
*
@@ -1094,57 +1148,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
/* Done if no options */
if (PointerIsValid(DatumGetPointer(options)))
- {
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
+ parseRelOptionsInternal(options, validate, reloptions, numoptions);
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
+ *numrelopts = numoptions;
+ return reloptions;
+}
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen *optgen[], int nopts)
+{
+ relopt_value *values = palloc(sizeof(*values) * nopts);
+ int i;
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
+ for (i = 0; i < nopts; i++)
+ {
+ values[i].gen = optgen[i];
+ values[i].isset = false;
+ }
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
- {
- parse_one_reloption(&reloptions[j], text_str, text_len,
- validate);
- break;
- }
- }
+ if (options != (Datum) 0)
+ parseRelOptionsInternal(options, validate, values, nopts);
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
+ return values;
+}
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+ int noptions, size_t base_size, bool validate)
+{
+ relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+ relopt_value *vals;
+ void *opts;
+ int i;
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
+ for (i = 0; i < noptions; i++)
+ {
+ elems[i].optname = optgen[i]->name;
+ elems[i].opttype = optgen[i]->type;
+ elems[i].offset = offsets[i];
}
- *numrelopts = numoptions;
- return reloptions;
+ vals = parseLocalRelOptions(options, validate, optgen, noptions);
+ opts = allocateReloptStruct(base_size, vals, noptions);
+ fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
}
/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index eade540..2b4a7d5 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -75,11 +75,14 @@
#include "access/xlog.h"
#include "catalog/index.h"
#include "catalog/pg_type.h"
+#include "commands/defrem.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
+#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
@@ -967,3 +970,81 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
}
}
}
+
+/* ----------------
+ * index_opclass_options
+ *
+ * Parse opclass-specific options for index column.
+ * ----------------
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum attoptions,
+ bool validate)
+{
+ amopclassoptions_function amopclassoptions =
+ relation->rd_amroutine->amopclassoptions;
+
+ if (!amopclassoptions)
+ {
+ if (validate && PointerIsValid(DatumGetPointer(attoptions)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("access method \"%s\" does not support opclass options ",
+ get_am_name(relation->rd_rel->relam))));
+
+ return NULL;
+ }
+
+ return amopclassoptions(relation, attnum, attoptions, validate);
+}
+
+/* ----------------
+ * index_opclass_options_generic
+ *
+ * Parse opclass options for index column using the specified support
+ * function 'procnum' of column's opclass.
+ * ----------------
+ */
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+ uint16 procnum, Datum attoptions, bool validate)
+{
+ Oid procid = index_getprocid(indrel, attnum, procnum);
+ FmgrInfo *procinfo;
+
+ if (!OidIsValid(procid))
+ {
+ StringInfoData opclassname;
+ Oid opclass;
+ Datum indclassDatum;
+ oidvector *indclass;
+ bool isnull;
+
+ if (!DatumGetPointer(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];
+
+ initStringInfo(&opclassname);
+ get_opclass_name(opclass, InvalidOid, &opclassname);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("operator class \"%s\" has no options",
+ opclassname.data)));
+ }
+
+ procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+ return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+ attoptions,
+ BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 11debaa..dc42338 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -647,6 +647,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];
@@ -677,10 +678,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;
@@ -734,7 +736,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;
@@ -772,7 +774,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 8709e8c..f676796 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -25,6 +25,7 @@
#include "access/amapi.h"
#include "access/multixact.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
@@ -113,7 +114,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,
@@ -517,7 +519,7 @@ InitializeAttributeOids(Relation indexRelation,
* ----------------------------------------------------------------
*/
static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
{
Relation pg_attribute;
CatalogIndexState indstate;
@@ -539,10 +541,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);
@@ -622,6 +625,7 @@ UpdateIndexRelation(Oid indexoid,
else
predDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
@@ -957,7 +961,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
@@ -1162,6 +1167,12 @@ index_create(Relation heapRelation,
indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
+ /* Validate opclass-specific options */
+ if (indexInfo->ii_OpclassOptions)
+ for (i = 0; i < indexInfo->ii_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
@@ -1790,6 +1801,10 @@ BuildIndexInfo(Relation index)
ii->ii_ExclusionStrats = NULL;
}
+ ii->ii_OpclassOptions =
+ RelationGetRawOpclassOptions(RelationGetRelid(index),
+ RelationGetNumberOfAttributes(index));
+
/* other info */
ii->ii_Unique = indexStruct->indisunique;
ii->ii_ReadyForInserts = IndexIsReady(indexStruct);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 462969a..19d52da 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -311,6 +311,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 6c06167..1770081 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -642,6 +642,7 @@ DefineIndex(Oid relationId,
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL; /* for now */
indexInfo->ii_Unique = stmt->unique;
/* In a concurrent build, mark it not-ready-for-inserts */
indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1403,7 +1404,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
@@ -1704,6 +1705,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/tablecmds.c b/src/backend/commands/tablecmds.c
index 843ed48..63df4f6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5464,7 +5464,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
ReleaseSysCache(typeTuple);
- InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+ InsertPgAttributeTuple(attrdesc, &attribute, (Datum) 0, NULL);
heap_close(attrdesc, RowExclusiveLock);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index db49968..ad3ff3c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2856,6 +2856,7 @@ _copyIndexElem(const IndexElem *from)
COPY_STRING_FIELD(indexcolname);
COPY_NODE_FIELD(collation);
COPY_NODE_FIELD(opclass);
+ COPY_NODE_FIELD(opclassopts);
COPY_SCALAR_FIELD(ordering);
COPY_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 3a084b4..e3dfe6e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2538,6 +2538,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
COMPARE_STRING_FIELD(indexcolname);
COMPARE_NODE_FIELD(collation);
COMPARE_NODE_FIELD(opclass);
+ COMPARE_NODE_FIELD(opclassopts);
COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f0c3965..a23c508 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2931,6 +2931,7 @@ _outIndexElem(StringInfo str, const IndexElem *node)
WRITE_STRING_FIELD(indexcolname);
WRITE_NODE_FIELD(collation);
WRITE_NODE_FIELD(opclass);
+ WRITE_NODE_FIELD(opclassopts);
WRITE_ENUM_FIELD(ordering, SortByDir);
WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a570ac0..0dad458 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -361,6 +361,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->nulls_first = NULL;
}
+ /* Fetch index opclass options */
+ info->opclassoptions =
+ RelationGetParsedOpclassOptions(indexRelation);
+
/*
* Fetch the index expressions and predicate, if any. We must
* modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2c2208f..e5d3a74 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -485,7 +485,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <alias> alias_clause opt_alias_clause
%type <list> func_alias_clause
%type <sortby> sortby
-%type <ielem> index_elem
+%type <ielem> index_elem index_elem_options
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
@@ -7411,43 +7411,65 @@ index_params: index_elem { $$ = list_make1($1); }
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
+
+index_elem_options:
+ opt_collate opt_class opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = NIL;
+ $$->ordering = $3;
+ $$->nulls_ordering = $4;
+ }
+ | opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ | opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = NIL;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ ;
+
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
{
- $$ = makeNode(IndexElem);
+ $$ = $2;
$$->name = $1;
- $$->expr = NULL;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $2;
$$->expr = $1;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $4;
$$->expr = $2;
- $$->indexcolname = NULL;
- $$->collation = $4;
- $$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
}
;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 4857cae..0ba0bf5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -452,8 +452,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
deparse_context *context);
static void get_tablesample_def(TableSampleClause *tablesample,
deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
- StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
@@ -468,6 +466,7 @@ static void add_cast_to(StringInfo buf, Oid typid);
static char *generate_qualified_type_name(Oid typid);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
+static void get_reloptions(StringInfo buf, Datum reloptions);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -1197,6 +1196,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
oidvector *indcollation;
oidvector *indclass;
int2vector *indoption;
+ Datum *opcoptions = NULL;
StringInfoData buf;
char *str;
char *sep;
@@ -1232,6 +1232,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Assert(!isnull);
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+ if (!attrsOnly)
+ opcoptions = RelationGetRawOpclassOptions(indexrelid, idxrec->indnatts);
+
/*
* Fetch the pg_class tuple of the index relation
*/
@@ -1370,6 +1373,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
(!colno || colno == keyno + 1))
{
Oid indcoll;
+ bool has_options =
+ opcoptions && opcoptions[keyno] != (Datum) 0;
/* Add collation, if not default for column */
indcoll = indcollation->values[keyno];
@@ -1378,7 +1383,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, opcoptions[keyno]);
+ appendStringInfoChar(&buf, ')');
+ }
/* Add options if relevant */
if (amroutine->amcanorder)
@@ -10400,7 +10413,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
* actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.)
*/
-static void
+void
get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf)
{
@@ -11111,6 +11124,62 @@ string_to_text(char *str)
}
/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+ Datum *options;
+ int noptions;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(reloptions),
+ TEXTOID, -1, false, 'i',
+ &options, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *option = TextDatumGetCString(options[i]);
+ char *name;
+ char *separator;
+ char *value;
+
+ /*
+ * Each array element should have the form name=value. If the "="
+ * is missing for some reason, treat it like an empty value.
+ */
+ name = option;
+ separator = strchr(option, '=');
+ if (separator)
+ {
+ *separator = '\0';
+ value = separator + 1;
+ }
+ else
+ value = "";
+
+ if (i > 0)
+ appendStringInfoString(buf, ", ");
+ appendStringInfo(buf, "%s=", quote_identifier(name));
+
+ /*
+ * In general we need to quote the value; but to avoid unnecessary
+ * clutter, do not quote if it is an identifier that would not
+ * need quoting. (We could also allow numbers, but that is a bit
+ * trickier than it looks --- for example, are leading zeroes
+ * significant? We don't want to assume very much here about what
+ * custom reloptions might mean.)
+ */
+ if (quote_identifier(value) == value)
+ appendStringInfoString(buf, value);
+ else
+ simple_quote_literal(buf, value);
+
+ pfree(option);
+ }
+}
+
+/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
static char *
@@ -11130,56 +11199,9 @@ flatten_reloptions(Oid relid)
if (!isnull)
{
StringInfoData buf;
- Datum *options;
- int noptions;
- int i;
initStringInfo(&buf);
-
- deconstruct_array(DatumGetArrayTypeP(reloptions),
- TEXTOID, -1, false, 'i',
- &options, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *option = TextDatumGetCString(options[i]);
- char *name;
- char *separator;
- char *value;
-
- /*
- * Each array element should have the form name=value. If the "="
- * is missing for some reason, treat it like an empty value.
- */
- name = option;
- separator = strchr(option, '=');
- if (separator)
- {
- *separator = '\0';
- value = separator + 1;
- }
- else
- value = "";
-
- if (i > 0)
- appendStringInfoString(&buf, ", ");
- appendStringInfo(&buf, "%s=", quote_identifier(name));
-
- /*
- * In general we need to quote the value; but to avoid unnecessary
- * clutter, do not quote if it is an identifier that would not
- * need quoting. (We could also allow numbers, but that is a bit
- * trickier than it looks --- for example, are leading zeroes
- * significant? We don't want to assume very much here about what
- * custom reloptions might mean.)
- */
- if (quote_identifier(value) == value)
- appendStringInfoString(&buf, value);
- else
- simple_quote_literal(&buf, value);
-
- pfree(option);
- }
+ get_reloptions(&buf, reloptions);
result = buf.data;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index c3071db..fa87ff5 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5192,6 +5192,105 @@ GetRelationPublicationActions(Relation relation)
}
/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for the index
+ */
+Datum *
+RelationGetRawOpclassOptions(Oid indexrelid, int16 natts)
+{
+ Datum *options = NULL;
+ int16 attnum;
+
+ for (attnum = 1; attnum <= natts; attnum++)
+ {
+ HeapTuple tuple;
+ Datum attopts;
+ bool isnull;
+
+ tuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(indexrelid),
+ Int16GetDatum(attnum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, indexrelid);
+
+ attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+ &isnull);
+
+ if (!isnull)
+ {
+ if (!options)
+ options = palloc0(sizeof(Datum) * natts);
+
+ options[attnum - 1] = datumCopy(attopts, false, -1); /* text */
+ }
+
+ ReleaseSysCache(tuple);
+ }
+
+ return options;
+}
+
+/*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+ MemoryContext oldcxt;
+ bytea **opts;
+ Datum *rawopts;
+ int natts = RelationGetNumberOfAttributes(relation);
+ int i;
+
+ /* Try to copy cached options. */
+ if (relation->rd_opcoptions)
+ {
+ opts = palloc(sizeof(*opts) * natts);
+
+ for (i = 0; i < natts; i++)
+ {
+ bytea *opt = relation->rd_opcoptions[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+ }
+
+ /* Get and parse opclass options. */
+ opts = palloc0(sizeof(*opts) * natts);
+
+ rawopts = RelationGetRawOpclassOptions(RelationGetRelid(relation), natts);
+
+ for (i = 0; i < natts; i++)
+ {
+ Datum options = rawopts ? rawopts[i] : (Datum) 0;
+
+ opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+ if (options != (Datum) 0)
+ pfree(DatumGetPointer(options));
+ }
+
+ if (rawopts)
+ pfree(rawopts);
+
+ /* Copy parsed options to the cache. */
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ relation->rd_opcoptions = palloc(sizeof(*opts) * natts);
+
+ for (i = 0; i < natts; i++)
+ relation->rd_opcoptions[i] = !opts[i] ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return opts;
+}
+
+/*
* Routines to support ereport() reports of relation-related errors
*
* These could have been put into elog.c, but it seems like a module layering
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 14526a6..de55802 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -103,6 +103,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
typedef bytea *(*amoptions_function) (Datum reloptions,
bool validate);
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+ AttrNumber colno,
+ Datum attoptions,
+ bool validate);
+
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -212,6 +218,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
+ amopclassoptions_function amopclassoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 534fac7..ba2fa92 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -177,6 +177,12 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
extern void index_store_float8_orderby_distances(IndexScanDesc scan,
Oid *orderByTypes, double *distances,
bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+ Datum attoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+ AttrNumber attnum, uint16 procnum,
+ Datum attoptions, bool validate);
+
/*
* index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 50690b9..7eadb0e 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -264,12 +264,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen **gen, int nelems);
extern void *allocateReloptStruct(Size base, relopt_value *options,
int numoptions);
extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+ int offsets[], int noptions, size_t base_size,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 56a341a..a92517c 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -87,6 +87,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/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5ed0f40..eddacd4 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -137,6 +137,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?
@@ -165,6 +166,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 e5bdc1c..4a304c0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -699,6 +699,7 @@ typedef struct IndexElem
char *indexcolname; /* name for index column; NULL = default */
List *collation; /* name of collation; NIL = default */
List *opclass; /* name of desired opclass; NIL = default */
+ List *opclassopts; /* opclass-specific options, or NIL */
SortByDir ordering; /* ASC/DESC/default */
SortByNulls nulls_ordering; /* FIRST/LAST/default */
} IndexElem;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6fd2420..fba493e 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -783,6 +783,7 @@ typedef struct IndexOptInfo
Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
+ bytea **opclassoptions; /* opclass-specific options for columns */
bool *canreturn; /* which index cols can be returned in an
* index-only scan? */
Oid relam; /* OID of the access method (in pg_am) */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 2217081..0f649c1 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -162,6 +162,7 @@ typedef struct RelationData
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
void *rd_amcache; /* available for use by index AM */
Oid *rd_indcollation; /* OIDs of index collations */
+ bytea **rd_opcoptions; /* parsed opclass-specific options */
/*
* foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index a99d6b6..79719e2 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"
@@ -49,6 +50,8 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Oid indexrelid, int16 natts);
typedef enum IndexAttrBitmapKind
{
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index 9f9b029..86e6953 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,5 +34,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 0bd48dc..e420f10 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -179,3 +179,8 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: access method "btree" does not support opclass options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR: access method "btree" does not support opclass options
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 21171f7..a3f7d9b 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -111,3 +111,7 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
-- Simple ALTER INDEX
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
--
2.7.4
0002-Add-opclass-parameters-to-GiST-v03.patchtext/x-patch; name=0002-Add-opclass-parameters-to-GiST-v03.patchDownload
From 4f8072a2bdb9ca83db9da45df597404a1544faa1 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Mon, 15 Jan 2018 18:58:39 +0300
Subject: [PATCH 2/9] Add opclass parameters to GiST
---
doc/src/sgml/xindex.sgml | 5 ++++
src/backend/access/gist/gist.c | 2 ++
src/backend/access/gist/gistget.c | 12 ++++++----
src/backend/access/gist/gistsplit.c | 15 +++++++-----
src/backend/access/gist/gistutil.c | 44 +++++++++++++++++++++++-----------
src/backend/access/gist/gistvalidate.c | 34 ++++++++++++++++----------
src/include/access/gist.h | 3 ++-
src/include/access/gist_private.h | 4 ++++
8 files changed, 82 insertions(+), 37 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b..8c5b528 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
index-only scans (optional)</entry>
<entry>9</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>parse opclass-specific options (optional)</entry>
+ <entry>10</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 8a42eff..905cbbf 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -97,6 +97,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amopclassoptions = gistopclassoptions;
PG_RETURN_POINTER(amroutine);
}
@@ -1457,6 +1458,7 @@ initGISTstate(Relation index)
giststate->scanCxt = scanCxt;
giststate->tempCxt = scanCxt; /* caller must change this if needed */
giststate->tupdesc = index->rd_att;
+ giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < index->rd_att->natts; i++)
{
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index e4a3786..9302e68 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum test;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan,
*/
recheck = true;
- test = FunctionCall5Coll(&key->sk_func,
+ test = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
if (!DatumGetBool(test))
return false;
@@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum dist;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan,
* about the flag, but are expected to never be lossy.
*/
recheck = false;
- dist = FunctionCall5Coll(&key->sk_func,
+ dist = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
*recheck_distances_p |= recheck;
*distance_p = DatumGetFloat8(dist);
}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index a7038cc..96a9290 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
evec->n = v->spl_nleft;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
sizeof(GISTENTRY) * evec->n);
- v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
evec->n = v->spl_nright;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
sizeof(GISTENTRY) * evec->n);
- v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
/*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
* Let the opclass-specific PickSplit method do its thing. Note that at
* this point we know there are no null keys in the entryvec.
*/
- FunctionCall2Coll(&giststate->picksplitFn[attno],
+ FunctionCall3Coll(&giststate->picksplitFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(entryvec),
- PointerGetDatum(sv));
+ PointerGetDatum(sv),
+ PointerGetDatum(giststate->opclassoptions[attno]));
if (sv->spl_nleft == 0 || sv->spl_nright == 0)
{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 70627e5..567cdf1 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -199,10 +199,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
}
/* Make union and store in attr array */
- attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+ attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
giststate->supportCollation[i],
PointerGetDatum(evec),
- PointerGetDatum(&attrsize));
+ PointerGetDatum(&attrsize),
+ PointerGetDatum(giststate->opclassoptions[i]));
isnull[i] = false;
}
@@ -268,10 +269,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
}
*dstisnull = false;
- *dst = FunctionCall2Coll(&giststate->unionFn[attno],
+ *dst = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&dstsize));
+ PointerGetDatum(&dstsize),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
}
@@ -280,10 +282,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
bool result;
- FunctionCall3Coll(&giststate->equalFn[attno],
+ FunctionCall4Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
a, b,
- PointerGetDatum(&result));
+ PointerGetDatum(&result),
+ PointerGetDatum(giststate->opclassoptions[attno]));
return result;
}
@@ -556,9 +559,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
return;
dep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(e)));
+ PointerGetDatum(e),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* decompressFn may just return the given pointer */
if (dep != e)
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -593,9 +597,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
/* there may not be a compress function in opclass */
if (OidIsValid(giststate->compressFn[i].fn_oid))
cep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+ DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
giststate->supportCollation[i],
- PointerGetDatum(¢ry)));
+ PointerGetDatum(¢ry),
+ PointerGetDatum(giststate->opclassoptions[i])));
else
cep = ¢ry;
compatt[i] = cep->key;
@@ -624,9 +629,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
fep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(&fentry)));
+ PointerGetDatum(&fentry),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* fetchFn set 'key', return it to the caller */
return fep->key;
@@ -694,11 +700,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
if (giststate->penaltyFn[attno].fn_strict == false ||
(isNullOrig == false && isNullAdd == false))
{
- FunctionCall3Coll(&giststate->penaltyFn[attno],
+ FunctionCall4Coll(&giststate->penaltyFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(orig),
PointerGetDatum(add),
- PointerGetDatum(&penalty));
+ PointerGetDatum(&penalty),
+ PointerGetDatum(giststate->opclassoptions[attno]));
/* disallow negative or NaN penalty */
if (isnan(penalty) || penalty < 0.0)
penalty = 0.0;
@@ -860,6 +867,15 @@ gistoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum attoptions,
+ bool validate)
+{
+ return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+ attoptions, validate);
+}
+
+
/*
* gistproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index c300e52..c5e1b23 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
{
case GIST_CONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_UNION_PROC:
ok = check_amproc_signature(procform->amproc, opckeytype, false,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_COMPRESS_PROC:
case GIST_DECOMPRESS_PROC:
case GIST_FETCH_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 1, 1, INTERNALOID);
+ 1, 2, INTERNALOID, INTERNALOID);
break;
case GIST_PENALTY_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 3, 3, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ 3, 4, INTERNALOID,
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_PICKSPLIT_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_EQUAL_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 3, 3, opckeytype, opckeytype,
- INTERNALOID);
+ 3, 4, opckeytype, opckeytype,
+ INTERNALOID, INTERNALOID);
break;
case GIST_DISTANCE_PROC:
ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
+ break;
+ case GIST_OPCLASSOPT_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+ 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
- i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+ i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+ i == GIST_OPCLASSOPT_PROC)
continue; /* optional methods */
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 827566d..08e7ada 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
#define GIST_EQUAL_PROC 7
#define GIST_DISTANCE_PROC 8
#define GIST_FETCH_PROC 9
-#define GISTNProcs 9
+#define GIST_OPCLASSOPT_PROC 10
+#define GISTNProcs 10
/*
* Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 36ed724..eedd896 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -94,6 +94,8 @@ typedef struct GISTSTATE
/* Collations to pass to the support functions */
Oid supportCollation[INDEX_MAX_KEYS];
+
+ bytea **opclassoptions; /* parsed opclass-specific options */
} GISTSTATE;
@@ -436,6 +438,8 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_DEFAULT_FILLFACTOR 90
extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+ Datum options, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
--
2.7.4
0003-Add-opclass-parameters-to-GIN-v03.patchtext/x-patch; name=0003-Add-opclass-parameters-to-GIN-v03.patchDownload
From 332930913ea6d90e6c61cf7093168ebd4d75cd05 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 01:50:09 +0300
Subject: [PATCH 3/9] Add opclass parameters to GIN
---
doc/src/sgml/xindex.sgml | 7 +++++++
src/backend/access/gin/ginget.c | 11 ++++++-----
src/backend/access/gin/ginlogic.c | 15 +++++++++------
src/backend/access/gin/ginscan.c | 6 ++++--
src/backend/access/gin/ginutil.c | 27 +++++++++++++++++++++------
src/backend/access/gin/ginvalidate.c | 32 ++++++++++++++++++++------------
src/backend/utils/adt/selfuncs.c | 6 ++++--
src/include/access/gin.h | 3 ++-
src/include/access/gin_private.h | 5 +++++
9 files changed, 78 insertions(+), 34 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b528..658ec9b 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
</entry>
<entry>6</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>
+ parse opclass-specific options (optional)
+ </entry>
+ <entry>7</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 8466d94..5eb47e0 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
btree->ginstate->supportCollation[attnum - 1],
scanEntry->queryKey,
idatum,
UInt16GetDatum(scanEntry->strategy),
- PointerGetDatum(scanEntry->extra_data)));
-
+ PointerGetDatum(scanEntry->extra_data),
+ PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
if (cmp > 0)
return true;
else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
ginstate->supportCollation[entry->attnum - 1],
entry->queryKey,
datum[off - 1],
UInt16GetDatum(entry->strategy),
- PointerGetDatum(entry->extra_data)));
+ PointerGetDatum(entry->extra_data),
+ PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
if (cmp == 0)
return true;
else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
*/
key->recheckCurItem = true;
- return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+ return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
PointerGetDatum(key->extra_data),
PointerGetDatum(&key->recheckCurItem),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
static GinTernaryValue
directTriConsistentFn(GinScanKey key)
{
- return DatumGetGinTernaryValue(FunctionCall7Coll(
+ return DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
{
GinTernaryValue result;
- result = DatumGetGinTernaryValue(FunctionCall7Coll(
+ result = DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
if (result == GIN_MAYBE)
{
key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
key->strategy = strategy;
key->searchMode = searchMode;
key->attnum = attnum;
+ key->opclassOptions = ginstate->opclassOptions[attnum - 1];
ItemPointerSetMin(&key->curItem);
key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
/* OK to call the extractQueryFn */
queryValues = (Datum *)
- DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+ DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
so->ginstate.supportCollation[skey->sk_attno - 1],
skey->sk_argument,
PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode)));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
/*
* If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index d7696a1..d4f5b4c 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amoptions = ginoptions;
+ amroutine->amopclassoptions = ginopclassoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -95,6 +96,7 @@ initGinState(GinState *state, Relation index)
state->index = index;
state->oneCol = (origTupdesc->natts == 1) ? true : false;
state->origTupdesc = origTupdesc;
+ state->opclassOptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < origTupdesc->natts; i++)
{
@@ -403,9 +405,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
return 0;
/* both not null, so safe to call the compareFn */
- return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+ return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
- a, b));
+ a, b,
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
}
/*
@@ -441,6 +444,7 @@ typedef struct
{
FmgrInfo *cmpDatumFunc;
Oid collation;
+ Datum options;
bool haveDups;
} cmpEntriesArg;
@@ -462,9 +466,10 @@ cmpEntries(const void *a, const void *b, void *arg)
else if (bb->isnull)
res = -1; /* not-NULL "<" NULL */
else
- res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+ res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
data->collation,
- aa->datum, bb->datum));
+ aa->datum, bb->datum,
+ data->options));
/*
* Detect if we have any duplicates. If there are equal keys, qsort must
@@ -510,11 +515,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
/* OK, call the opclass's extractValueFn */
nullFlags = NULL; /* in case extractValue doesn't set it */
entries = (Datum *)
- DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+ DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
value,
PointerGetDatum(nentries),
- PointerGetDatum(&nullFlags)));
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
/*
* Generate a placeholder if the item contained no keys.
@@ -557,6 +563,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.collation = ginstate->supportCollation[attnum - 1];
+ arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
arg.haveDups = false;
qsort_arg(keydata, *nentries, sizeof(keyEntryData),
cmpEntries, (void *) &arg);
@@ -632,6 +639,14 @@ ginoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum attoptions,
+ bool validate)
+{
+ return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+ attoptions, validate);
+}
+
/*
* Fetch index's statistical data into *stats
*
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1922260..4f87d12 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
{
case GIN_COMPARE_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 2, 2, opckeytype, opckeytype);
+ 2, 3, opckeytype, opckeytype,
+ INTERNALOID);
break;
case GIN_EXTRACTVALUE_PROC:
/* Some opclasses omit nullFlags */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 2, 3, opcintype, INTERNALOID,
- INTERNALOID);
+ 2, 4, opcintype, INTERNALOID,
+ INTERNALOID, INTERNALOID);
break;
case GIN_EXTRACTQUERY_PROC:
/* Some opclasses omit nullFlags and searchMode */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 5, 7, opcintype, INTERNALOID,
+ 5, 8, opcintype, INTERNALOID,
INT2OID, INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_CONSISTENT_PROC:
/* Some opclasses omit queryKeys and nullFlags */
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 6, 8, INTERNALOID, INT2OID,
+ 6, 9, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_COMPARE_PARTIAL_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 4, 4, opckeytype, opckeytype,
- INT2OID, INTERNALOID);
+ 4, 5, opckeytype, opckeytype,
+ INT2OID, INTERNALOID, INTERNALOID);
break;
case GIN_TRICONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, CHAROID, false,
- 7, 7, INTERNALOID, INT2OID,
+ 7, 8, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID);
+ INTERNALOID, INTERNALOID);
+ break;
+ case GIN_OPCLASSOPTIONS_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID,
+ false, 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
if (opclassgroup &&
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
- if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+ if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+ i == GIN_OPCLASSOPTIONS_PROC)
continue; /* optional method */
if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
continue; /* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index ffca0fe..67431c7 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -139,6 +139,7 @@
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
+#include "utils/relcache.h"
#include "utils/selfuncs.h"
#include "utils/snapmgr.h"
#include "utils/spccache.h"
@@ -7474,7 +7475,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
else
collation = DEFAULT_COLLATION_OID;
- OidFunctionCall7Coll(extractProcOid,
+ OidFunctionCall8Coll(extractProcOid,
collation,
query,
PointerGetDatum(&nentries),
@@ -7482,7 +7483,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(index->opclassoptions[indexcol]));
if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 3d8a130..20ce792 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
#define GIN_TRICONSISTENT_PROC 6
-#define GINNProcs 6
+#define GIN_OPCLASSOPTIONS_PROC 7
+#define GINNProcs 7
/*
* searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 81bf873..6fecfe0 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
TupleDesc origTupdesc;
TupleDesc tupdesc[INDEX_MAX_KEYS];
+ bytea **opclassOptions; /* per-index-column opclass options */
+
/*
* Per-index-column opclass support functions
*/
@@ -85,6 +87,8 @@ typedef struct GinState
/* ginutil.c */
extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+ Datum attoptions, bool validate);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -296,6 +300,7 @@ typedef struct GinScanKeyData
StrategyNumber strategy;
int32 searchMode;
OffsetNumber attnum;
+ bytea *opclassOptions;
/*
* Match status data. curItem is the TID most recently tested (could be a
--
2.7.4
0004-Add-opclass-parameters-to-GiST-tsvector_ops-v03.patchtext/x-patch; name=0004-Add-opclass-parameters-to-GiST-tsvector_ops-v03.patchDownload
From 3ecdef58eb4e3413f1a700179d6d38b3d7637d54 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 18:31:23 +0300
Subject: [PATCH 4/9] Add opclass parameters to GiST tsvector_ops
---
doc/src/sgml/textsearch.sgml | 9 +-
src/backend/utils/adt/tsgistidx.c | 269 +++++++++++++++++++---------------
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_proc.dat | 19 ++-
src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
src/test/regress/sql/tsearch.sql | 45 ++++++
6 files changed, 392 insertions(+), 131 deletions(-)
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index ecebade..92abf6e 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3635,7 +3635,7 @@ SELECT plainto_tsquery('supernovae stars');
<tertiary>text search</tertiary>
</indexterm>
- <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+ <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
</term>
<listitem>
@@ -3643,6 +3643,8 @@ SELECT plainto_tsquery('supernovae stars');
Creates a GiST (Generalized Search Tree)-based index.
The <replaceable>column</replaceable> can be of <type>tsvector</type> or
<type>tsquery</type> type.
+ Optional integer parameter <literal>siglen</literal> determines
+ signature length in bytes (see below for details).
</para>
</listitem>
</varlistentry>
@@ -3666,7 +3668,10 @@ SELECT plainto_tsquery('supernovae stars');
to check the actual table row to eliminate such false matches.
(<productname>PostgreSQL</productname> does this automatically when needed.)
GiST indexes are lossy because each document is represented in the
- index by a fixed-length signature. The signature is generated by hashing
+ index by a fixed-length signature. Signature length in bytes is determined
+ by the value of the optional integer parameter <literal>siglen</literal>.
+ Default signature length (when <literal>siglen</literal> is not specied) is
+ 124 bytes, maximal length is 484 bytes. The signature is generated by hashing
each word into a single bit in an n-bit string, with all these bits OR-ed
together to produce an n-bit document signature. When two words hash to
the same bit position there will be a false match. If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 2d9ecc4..462791a 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,23 +15,29 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/pg_crc.h"
-#define SIGLENINT 31 /* >121 => key will toast, so it will not work
- * !!! */
+#define SIGLEN_DEFAULT (31 * 4)
+#define SIGLEN_MAX (121 * 4) /* key will toast, so it will not work !!! */
-#define SIGLEN ( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length */
+} GistTsVectorOptions;
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
#define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -39,8 +45,8 @@ typedef char *BITVECP;
#define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITS_PER_BYTE ) )
#define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITS_PER_BYTE )) & 0x01 )
-#define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT)
-#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASHVAL(val, siglen) (((unsigned int)(val)) % SIGLENBIT(siglen))
+#define HASH(sign, val, siglen) SETBIT((sign), HASHVAL(val, siglen))
#define GETENTRY(vec,pos) ((SignTSVector *) DatumGetPointer((vec)->vector[(pos)].key))
@@ -64,9 +70,10 @@ typedef struct
#define ISALLTRUE(x) ( ((SignTSVector*)(x))->flag & ALLISTRUE )
#define GTHDRSIZE ( VARHDRSZ + sizeof(int32) )
-#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : SIGLEN) ) )
+#define CALCGTSIZE(flag, len) ( GTHDRSIZE + ( ( (flag) & ARRKEY ) ? ((len)*sizeof(int32)) : (((flag) & ALLISTRUE) ? 0 : (len)) ) )
#define GETSIGN(x) ( (BITVECP)( (char*)(x)+GTHDRSIZE ) )
+#define GETSIGLEN(x)( VARSIZE(x) - GTHDRSIZE )
#define GETARR(x) ( (int32*)( (char*)(x)+GTHDRSIZE ) )
#define ARRNELEM(x) ( ( VARSIZE(x) - GTHDRSIZE )/sizeof(int32) )
@@ -90,7 +97,7 @@ static const uint8 number_of_ones[256] = {
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};
-static int32 sizebitvec(BITVECP sign);
+static int32 sizebitvec(BITVECP sign, int siglen);
Datum
gtsvectorin(PG_FUNCTION_ARGS)
@@ -121,9 +128,10 @@ gtsvectorout(PG_FUNCTION_ARGS)
sprintf(outbuf, ARROUTSTR, (int) ARRNELEM(key));
else
{
- int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT : sizebitvec(GETSIGN(key));
+ int siglen = GETSIGLEN(key);
+ int cnttrue = (ISALLTRUE(key)) ? SIGLENBIT(siglen) : sizebitvec(GETSIGN(key), siglen);
- sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT - cnttrue);
+ sprintf(outbuf, SINGOUTSTR, cnttrue, (int) SIGLENBIT(siglen) - cnttrue);
}
PG_FREE_IF_COPY(key, 0);
@@ -167,36 +175,49 @@ uniqueint(int32 *a, int32 l)
}
static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
int32 *ptr = GETARR(a);
- MemSet((void *) sign, 0, sizeof(BITVEC));
+ MemSet((void *) sign, 0, siglen);
for (k = 0; k < len; k++)
- HASH(sign, ptr[k]);
+ HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+ int size = CALCGTSIZE(flag, len);
+ SignTSVector *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+ memcpy(GETSIGN(res), sign, len);
+
+ return res;
}
+
Datum
gtsvector_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{ /* tsvector */
- SignTSVector *res;
TSVector val = DatumGetTSVector(entry->key);
+ SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
int32 len;
int32 *arr;
WordEntry *ptr = ARRPTR(val);
char *words = STRPTR(val);
- len = CALCGTSIZE(ARRKEY, val->size);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = ARRKEY;
arr = GETARR(res);
len = val->size;
while (len--)
@@ -227,13 +248,9 @@ gtsvector_compress(PG_FUNCTION_ARGS)
/* make signature, if array is too long */
if (VARSIZE(res) > TOAST_INDEX_TARGET)
{
- SignTSVector *ressign;
+ SignTSVector *ressign = gtsvector_alloc(SIGNKEY, siglen, NULL);
- len = CALCGTSIZE(SIGNKEY, 0);
- ressign = (SignTSVector *) palloc(len);
- SET_VARSIZE(ressign, len);
- ressign->flag = SIGNKEY;
- makesign(GETSIGN(ressign), res);
+ makesign(GETSIGN(ressign), res, siglen);
res = ressign;
}
@@ -245,22 +262,17 @@ gtsvector_compress(PG_FUNCTION_ARGS)
else if (ISSIGNKEY(DatumGetPointer(entry->key)) &&
!ISALLTRUE(DatumGetPointer(entry->key)))
{
- int32 i,
- len;
+ int32 i;
SignTSVector *res;
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(retval);
}
- len = CALCGTSIZE(SIGNKEY | ALLISTRUE, 0);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = SIGNKEY | ALLISTRUE;
-
+ res = gtsvector_alloc(SIGNKEY | ALLISTRUE, siglen, sign);
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
gistentryinit(*retval, PointerGetDatum(res),
entry->rel, entry->page,
@@ -334,12 +346,14 @@ checkcondition_arr(void *checkval, QueryOperand *val, ExecPhraseData *data)
static bool
checkcondition_bit(void *checkval, QueryOperand *val, ExecPhraseData *data)
{
+ void *key = (SignTSVector *) checkval;
+
/*
* we are not able to find a prefix in signature tree
*/
if (val->prefix)
return true;
- return GETBIT(checkval, HASHVAL(val->valcrc));
+ return GETBIT(GETSIGN(key), HASHVAL(val->valcrc, GETSIGLEN(key)));
}
Datum
@@ -366,7 +380,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
/* since signature is lossy, cannot specify CALC_NOT here */
PG_RETURN_BOOL(TS_execute(GETQUERY(query),
- (void *) GETSIGN(key),
+ key,
TS_EXEC_PHRASE_NO_POS,
checkcondition_bit));
}
@@ -384,7 +398,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
@@ -395,7 +409,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -403,7 +419,7 @@ unionkey(BITVECP sbase, SignTSVector *add)
int32 *ptr = GETARR(add);
for (i = 0; i < ARRNELEM(add); i++)
- HASH(sbase, ptr[i]);
+ HASH(sbase, ptr[i], siglen);
}
return 0;
}
@@ -414,30 +430,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- SignTSVector *result;
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+ SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+ BITVECP base = GETSIGN(result);
+ int32 i;
+
+ memset(base, 0, siglen);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (SignTSVector *) palloc(len);
- *size = len;
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -448,6 +458,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -463,8 +474,10 @@ gtsvector_same(PG_FUNCTION_ARGS)
BITVECP sa = GETSIGN(a),
sb = GETSIGN(b);
+ Assert(GETSIGLEN(a) == siglen && GETSIGLEN(b) == siglen);
+
*result = true;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
if (sa[i] != sb[i])
{
@@ -501,24 +514,24 @@ gtsvector_same(PG_FUNCTION_ARGS)
}
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, int siglen)
{
int32 size = 0,
i;
- LOOPBYTE
+ LOOPBYTE(siglen)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, int siglen)
{
int i,
diff,
dist = 0;
- LOOPBYTE
+ LOOPBYTE(siglen)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -529,17 +542,22 @@ hemdistsign(BITVECP a, BITVECP b)
static int
hemdist(SignTSVector *a, SignTSVector *b)
{
+ int siglena = GETSIGLEN(a);
+ int siglenb = GETSIGLEN(b);
+
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ Assert(siglena == siglenb);
+
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
}
Datum
@@ -548,6 +566,7 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -556,14 +575,22 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
if (ISARRKEY(newval))
{
- BITVEC sign;
+ BITVECP sign = palloc(siglen);
- makesign(sign, newval);
+ makesign(sign, newval, siglen);
if (ISALLTRUE(origval))
- *penalty = ((float) (SIGLENBIT - sizebitvec(sign))) / (float) (SIGLENBIT + 1);
+ {
+ int siglenbit = SIGLENBIT(siglen);
+
+ *penalty =
+ (float) (siglenbit - sizebitvec(sign, siglen)) /
+ (float) (siglenbit + 1);
+ }
else
- *penalty = hemdistsign(sign, orig);
+ *penalty = hemdistsign(sign, orig, siglen);
+
+ pfree(sign);
}
else
*penalty = hemdist(origval, newval);
@@ -573,19 +600,19 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
typedef struct
{
bool allistrue;
- BITVEC sign;
+ BITVECP sign;
} CACHESIGN;
static void
-fillcache(CACHESIGN *item, SignTSVector *key)
+fillcache(CACHESIGN *item, SignTSVector *key, int siglen)
{
item->allistrue = false;
if (ISARRKEY(key))
- makesign(item->sign, key);
+ makesign(item->sign, key, siglen);
else if (ISALLTRUE(key))
item->allistrue = true;
else
- memcpy((void *) item->sign, (void *) GETSIGN(key), sizeof(BITVEC));
+ memcpy((void *) item->sign, (void *) GETSIGN(key), siglen);
}
#define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) )
@@ -609,19 +636,19 @@ comparecost(const void *va, const void *vb)
static int
-hemdistcache(CACHESIGN *a, CACHESIGN *b)
+hemdistcache(CACHESIGN *a, CACHESIGN *b, int siglen)
{
if (a->allistrue)
{
if (b->allistrue)
return 0;
else
- return SIGLENBIT - sizebitvec(b->sign);
+ return SIGLENBIT(siglen) - sizebitvec(b->sign, siglen);
}
else if (b->allistrue)
- return SIGLENBIT - sizebitvec(a->sign);
+ return SIGLENBIT(siglen) - sizebitvec(a->sign, siglen);
- return hemdistsign(a->sign, b->sign);
+ return hemdistsign(a->sign, b->sign, siglen);
}
Datum
@@ -629,6 +656,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
OffsetNumber k,
j;
SignTSVector *datum_l,
@@ -648,6 +676,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
@@ -656,16 +685,22 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
v->spl_right = (OffsetNumber *) palloc(nbytes);
cache = (CACHESIGN *) palloc(sizeof(CACHESIGN) * (maxoff + 2));
- fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber));
+ cache_sign = palloc(siglen * (maxoff + 2));
+
+ for (j = 0; j < maxoff + 2; j++)
+ cache[j].sign = &cache_sign[siglen * j];
+
+ fillcache(&cache[FirstOffsetNumber], GETENTRY(entryvec, FirstOffsetNumber),
+ siglen);
for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k))
{
for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j))
{
if (k == FirstOffsetNumber)
- fillcache(&cache[j], GETENTRY(entryvec, j));
+ fillcache(&cache[j], GETENTRY(entryvec, j), siglen);
- size_waste = hemdistcache(&(cache[j]), &(cache[k]));
+ size_waste = hemdistcache(&(cache[j]), &(cache[k]), siglen);
if (size_waste > waste)
{
waste = size_waste;
@@ -687,44 +722,21 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
}
/* form initial .. */
- if (cache[seed_1].allistrue)
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_l->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_l = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_l, CALCGTSIZE(SIGNKEY, 0));
- datum_l->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_l), (void *) cache[seed_1].sign, sizeof(BITVEC));
- }
- if (cache[seed_2].allistrue)
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY | ALLISTRUE, 0));
- datum_r->flag = SIGNKEY | ALLISTRUE;
- }
- else
- {
- datum_r = (SignTSVector *) palloc(CALCGTSIZE(SIGNKEY, 0));
- SET_VARSIZE(datum_r, CALCGTSIZE(SIGNKEY, 0));
- datum_r->flag = SIGNKEY;
- memcpy((void *) GETSIGN(datum_r), (void *) cache[seed_2].sign, sizeof(BITVEC));
- }
-
+ datum_l = gtsvector_alloc(SIGNKEY | (cache[seed_1].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_1].sign);
+ datum_r = gtsvector_alloc(SIGNKEY | (cache[seed_2].allistrue ? ALLISTRUE : 0),
+ siglen, cache[seed_2].sign);
union_l = GETSIGN(datum_l);
union_r = GETSIGN(datum_r);
maxoff = OffsetNumberNext(maxoff);
- fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff));
+ fillcache(&cache[maxoff], GETENTRY(entryvec, maxoff), siglen);
/* sort before ... */
costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff);
for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j))
{
costvector[j - 1].pos = j;
- size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]));
- size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]));
+ size_alpha = hemdistcache(&(cache[seed_1]), &(cache[j]), siglen);
+ size_beta = hemdistcache(&(cache[seed_2]), &(cache[j]), siglen);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -750,36 +762,34 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign), siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign), siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -790,12 +800,12 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_r))
- MemSet((void *) GETSIGN(datum_r), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_r), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -822,3 +832,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
{
return gtsvector_consistent(fcinfo);
}
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistTsVectorOptions, siglen) };
+ GistTsVectorOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistTsVectorOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 0ef2c08..c1ee3db 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -450,7 +450,7 @@
amproc => 'gist_circle_distance' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '1',
- amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
+ amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '2',
amproc => 'gtsvector_union' },
@@ -468,6 +468,9 @@
amproc => 'gtsvector_picksplit' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '7', amproc => 'gtsvector_same' },
+{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
+ amprocrighttype => 'tsvector', amprocnum => '10',
+ amproc => 'gtsvector_options' },
{ amprocfamily => 'gist/tsquery_ops', amproclefttype => 'tsquery',
amprocrighttype => 'tsquery', amprocnum => '1',
amproc => 'gtsquery_consistent(internal,tsquery,int2,oid,internal)' },
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 034a41e..110d4a5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8435,30 +8435,35 @@
{ oid => '3648', descr => 'GiST tsvector support',
proname => 'gtsvector_compress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_compress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_compress' },
{ oid => '3649', descr => 'GiST tsvector support',
proname => 'gtsvector_decompress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_decompress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' },
{ oid => '3650', descr => 'GiST tsvector support',
proname => 'gtsvector_picksplit', prorettype => 'internal',
- proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' },
{ oid => '3651', descr => 'GiST tsvector support',
proname => 'gtsvector_union', prorettype => 'gtsvector',
- proargtypes => 'internal internal', prosrc => 'gtsvector_union' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' },
{ oid => '3652', descr => 'GiST tsvector support',
proname => 'gtsvector_same', prorettype => 'internal',
- proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' },
+ proargtypes => 'gtsvector gtsvector internal internal',
+ prosrc => 'gtsvector_same' },
{ oid => '3653', descr => 'GiST tsvector support',
proname => 'gtsvector_penalty', prorettype => 'internal',
- proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' },
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'gtsvector_penalty' },
{ oid => '3654', descr => 'GiST tsvector support',
proname => 'gtsvector_consistent', prorettype => 'bool',
- proargtypes => 'internal tsvector int2 oid internal',
+ proargtypes => 'internal tsvector int2 oid internal internal',
prosrc => 'gtsvector_consistent' },
{ oid => '3790', descr => 'GiST tsvector support (obsolete)',
proname => 'gtsvector_consistent', prorettype => 'bool',
proargtypes => 'internal gtsvector int4 oid internal',
prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3996', descr => 'GiST tsvector support',
+ proname => 'gtsvector_options', prorettype => 'internal',
+ proargtypes => 'internal bool', prosrc => 'gtsvector_options' },
{ oid => '3656', descr => 'GIN tsvector support',
proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index b088ff0..bcadcf7 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
508
(1 row)
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR: value 485 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR: parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a)
+ "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx2
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 637bfb3..9aed780 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
--
2.7.4
On Wed, Dec 5, 2018 at 6:58 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
I agree that we should distinguish per-index and per-column options, but they
can also be AM-specific and opclass-specific.
True, but an index is bound to a single AM, and a column is bound to a
single opclass which is bound to a single AM. So I'm not very worried
about name collisions. Can't we just tell opclass authors to pick
names that are unlikely to collide with names chose by the AM or names
that are globally reserved? It's hard to imagine that we're ever
going to have more than a dozen or so options that could possibly
apply to a column, so splitting things up into different namespaces
seems like an unnecessary burden on the user.
'fastupdate' option for GIN is an example of AM-specific per-index option.
ASC/DESC and NULLS LAST/FIRST are examples of AM-class-specific per-column
options having special SQL syntax. "AM-class-specific" here means "specific
only for the class of AMs that support ordering". Now they are stored as flags
in pg_index.indoption[] but later can be moved to pg_attribute.attoptions.
Or left where they are.
And another problem is the options with default values. They may be not
explicitly specified by the user, and in this case in current implementation
nothing will be stored in the catalog because default values can be obtained
from the code. But this will not allow changing of default values without
compatibility issues. So I think it would be better to store both default and
explicitly specified values of all opclass options, but this will require a
major refactoring of current API.
Hmm. I think if the default ever changes, it will require a new major
release, and we can compensate in pg_dump. That means that a dump
taken with an old version will not preserve the options. However,
using the pg_dump from the newest release is the recommended
procedure, and if you don't do that, you might get outright failures.
Failing to preserve an option value in the rare case that a default
was changed seems less bad than that.
Also I have idea to define list of opclass parameters declaratively when opclass
is created using syntax like the following:CREATE OPERATOR CLASS name [ (param_name param_type [= default_value] [,...]) ]
FOR TYPE ... AS (
{ OPTIONS function_name ( arg_type [,...] ) /* reloptions => binary */
| OPERATOR ...
} [,...]
)
I'm not sure exposing SQL syntax for this is a very good idea.
"[opclass] WITH OPTIONS (options)" looks too verbose, of course.
It's not that bad.
"[opclass] WITH (options)" looks acceptable, but it seems to conflict with
exclusion constraints syntax ("index_elem WITH operator").
Are you sure? The operator can't be (
But it might be confusing anyhow.
"opclass (options)" looks the most natural to me. But we still need some
keyword before the parentheses when the opclass is not specified since we
can't distinguish "func_name (func_params)" and "col_name (opclass_options)"
in grammar.
Are you sure? What's the SQL syntax where there is actually a problem
here? CREATE INDEX requires parentheses around a non-trivial
expression.
How about just OPTIONS (options) ?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
On Wed, Dec 5, 2018 at 6:58 PM Nikita Glukhov <n.gluhov@postgrespro.ru> wrote:
"opclass (options)" looks the most natural to me. But we still need some
keyword before the parentheses when the opclass is not specified since we
can't distinguish "func_name (func_params)" and "col_name (opclass_options)"
in grammar.
Are you sure? What's the SQL syntax where there is actually a problem
here? CREATE INDEX requires parentheses around a non-trivial
expression.
Well, the reason we have to require parens around nontrivial expressions
is mostly lack of forethought about making the syntax non-ambiguous :-(
How about just OPTIONS (options) ?
That would require making OPTIONS a fully reserved word, I think,
else it's ambiguous with an opclass name.
How about saying that you must give an opclass name if you want to
specify options, ie the syntax is
[ opclass_name [ ( options... ) ] ]
I'm not necessarily wedded to that, but it seems worth throwing
out the idea.
regards, tom lane
On Thu, Dec 6, 2018 at 11:55 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
How about saying that you must give an opclass name if you want to
specify options, ie the syntax is[ opclass_name [ ( options... ) ] ]
I'm not necessarily wedded to that, but it seems worth throwing
out the idea.
Agreed, that's not bad, certainly better than making OPTIONS more reserved.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi,
while rebasing the patch series [1] adding bloom/multi-minmax BRIN
opclasses, I've decided to also rebase it on top of this patch, because it
needs the opclass parameters. So I had to rebase this too - it went mostly
fine, with reasonably limited bitrot. The rebased patch series is attached.
Using this patch series in [1] was mostly smooth, I only have two minor
comments at this point:
1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:
Datum
gtsvector_options(PG_FUNCTION_ARGS)
{
Datum raw_options = PG_GETARG_DATUM(0);
bool validate = PG_GETARG_BOOL(1);
relopt_int siglen =
{ {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
SIGLEN_DEFAULT, 1, SIGLEN_MAX };
relopt_gen *optgen[] = { &siglen.gen };
int offsets[] = { offsetof(GistTsVectorOptions, siglen) };
GistTsVectorOptions *options =
parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
sizeof(GistTsVectorOptions), validate);
PG_RETURN_POINTER(options);
}
So in other words, it builds all the various pieces (relopts, optgen,
offsets, lengths etc.) manually, which is really error-prone and difficult
to maintain. We need to make it simpler - ideally as simple as defining a
custom GUC, or just an array of relopt_* structs.
2) The 0001 part does this in index_opclass_options_generic:
get_opclass_name(opclass, InvalidOid, &str);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));
But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning. So this produces
messages like
ERROR: operator class " int4_bloom_ops" has no options
which is not right. I haven't checked if a better function already exists,
or whether we need to implement it.
regards
/messages/by-id/c1138ead-7668-f0e1-0638-c3be3237e812@2ndquadrant.com
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
0001-Add-opclass-parameters-20190611.patchtext/plain; charset=us-asciiDownload
From 436eca7a1dd45b78b50c3868fd4be518b814cecd Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 20:59:01 +0200
Subject: [PATCH 01/10] Add opclass parameters
---
doc/src/sgml/indices.sgml | 2 +-
doc/src/sgml/ref/create_index.sgml | 16 ++-
src/backend/access/common/reloptions.c | 142 +++++++++++++++-------
src/backend/access/index/indexam.c | 81 ++++++++++++
src/backend/catalog/heap.c | 8 +-
src/backend/catalog/index.c | 23 +++-
src/backend/catalog/toasting.c | 1 +
src/backend/commands/indexcmds.c | 17 ++-
src/backend/commands/tablecmds.c | 2 +-
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/plancat.c | 4 +
src/backend/parser/gram.y | 72 +++++++----
src/backend/utils/adt/ruleutils.c | 128 +++++++++++--------
src/backend/utils/cache/relcache.c | 99 +++++++++++++++
src/include/access/amapi.h | 7 ++
src/include/access/genam.h | 5 +
src/include/access/reloptions.h | 5 +
src/include/catalog/heap.h | 1 +
src/include/nodes/execnodes.h | 2 +
src/include/nodes/parsenodes.h | 1 +
src/include/nodes/pathnodes.h | 1 +
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 3 +
src/include/utils/ruleutils.h | 2 +
src/test/regress/expected/btree_index.out | 5 +
src/test/regress/sql/btree_index.sql | 4 +
28 files changed, 503 insertions(+), 132 deletions(-)
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 95c0a1926c..ea3acea88e 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1253,7 +1253,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
An index definition can specify an <firstterm>operator
class</firstterm> for each column of an index.
<synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
</synopsis>
The operator class identifies the operators to be used by the index
for that column. For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 629a31ef79..61401f3645 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
- ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+ ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -278,6 +278,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of an operator class parameter. See below for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>ASC</literal></term>
<listitem>
@@ -646,8 +655,9 @@ Indexes:
</para>
<para>
- An <firstterm>operator class</firstterm> can be specified for each
- column of an index. The operator class identifies the operators to be
+ An <firstterm>operator class</firstterm> with its optional parameters
+ can be specified for each column of an index.
+ The operator class identifies the operators to be
used by the index for that column. For example, a B-tree index on
four-byte integers would use the <literal>int4_ops</literal> class;
this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index de06c92574..abc0082ce7 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1051,6 +1051,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
return options;
}
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+ relopt_value *reloptions, int numoptions)
+{
+ ArrayType *array = DatumGetArrayTypeP(options);
+ Datum *optiondatums;
+ int noptions;
+ int i;
+
+ deconstruct_array(array, TEXTOID, -1, false, 'i',
+ &optiondatums, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *text_str = VARDATA(optiondatums[i]);
+ int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+ int j;
+
+ /* Search for a match in reloptions */
+ for (j = 0; j < numoptions; j++)
+ {
+ int kw_len = reloptions[j].gen->namelen;
+
+ if (text_len > kw_len && text_str[kw_len] == '=' &&
+ strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+ {
+ parse_one_reloption(&reloptions[j], text_str, text_len,
+ validate);
+ break;
+ }
+ }
+
+ if (j >= numoptions && validate)
+ {
+ char *s;
+ char *p;
+
+ s = TextDatumGetCString(optiondatums[i]);
+ p = strchr(s, '=');
+ if (p)
+ *p = '\0';
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s\"", s)));
+ }
+ }
+
+ /* It's worth avoiding memory leaks in this function */
+ pfree(optiondatums);
+
+ if (((void *) array) != DatumGetPointer(options))
+ pfree(array);
+}
+
/*
* Interpret reloptions that are given in text-array format.
*
@@ -1105,57 +1159,61 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
/* Done if no options */
if (PointerIsValid(DatumGetPointer(options)))
- {
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
+ parseRelOptionsInternal(options, validate, reloptions, numoptions);
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
+ *numrelopts = numoptions;
+ return reloptions;
+}
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
+/* Parse local unregistered options. */
+relopt_value *
+parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen *optgen[], int nopts)
+{
+ relopt_value *values = palloc(sizeof(*values) * nopts);
+ int i;
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
+ for (i = 0; i < nopts; i++)
+ {
+ values[i].gen = optgen[i];
+ values[i].isset = false;
+ }
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
- {
- parse_one_reloption(&reloptions[j], text_str, text_len,
- validate);
- break;
- }
- }
+ if (options != (Datum) 0)
+ parseRelOptionsInternal(options, validate, values, nopts);
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
+ return values;
+}
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[], int offsets[],
+ int noptions, size_t base_size, bool validate)
+{
+ relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
+ relopt_value *vals;
+ void *opts;
+ int i;
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
+ for (i = 0; i < noptions; i++)
+ {
+ elems[i].optname = optgen[i]->name;
+ elems[i].opttype = optgen[i]->type;
+ elems[i].offset = offsets[i];
}
- *numrelopts = numoptions;
- return reloptions;
+ vals = parseLocalRelOptions(options, validate, optgen, noptions);
+ opts = allocateReloptStruct(base_size, vals, noptions);
+ fillRelOptions(opts, base_size, vals, noptions, validate, elems, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
}
/*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index aefdd2916d..6cc76914de 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -51,11 +51,14 @@
#include "access/xlog.h"
#include "catalog/index.h"
#include "catalog/pg_type.h"
+#include "commands/defrem.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
+#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
/* ----------------------------------------------------------------
@@ -905,3 +908,81 @@ index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
}
}
}
+
+/* ----------------
+ * index_opclass_options
+ *
+ * Parse opclass-specific options for index column.
+ * ----------------
+ */
+bytea *
+index_opclass_options(Relation relation, AttrNumber attnum, Datum attoptions,
+ bool validate)
+{
+ amopclassoptions_function amopclassoptions =
+ relation->rd_indam->amopclassoptions;
+
+ if (!amopclassoptions)
+ {
+ if (validate && PointerIsValid(DatumGetPointer(attoptions)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("access method \"%s\" does not support opclass options ",
+ get_am_name(relation->rd_rel->relam))));
+
+ return NULL;
+ }
+
+ return amopclassoptions(relation, attnum, attoptions, validate);
+}
+
+/* ----------------
+ * index_opclass_options_generic
+ *
+ * Parse opclass options for index column using the specified support
+ * function 'procnum' of column's opclass.
+ * ----------------
+ */
+bytea *
+index_opclass_options_generic(Relation indrel, AttrNumber attnum,
+ uint16 procnum, Datum attoptions, bool validate)
+{
+ Oid procid = index_getprocid(indrel, attnum, procnum);
+ FmgrInfo *procinfo;
+
+ if (!OidIsValid(procid))
+ {
+ StringInfoData opclassname;
+ Oid opclass;
+ Datum indclassDatum;
+ oidvector *indclass;
+ bool isnull;
+
+ if (!DatumGetPointer(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];
+
+ initStringInfo(&opclassname);
+ get_opclass_name(opclass, InvalidOid, &opclassname);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("operator class \"%s\" has no options",
+ opclassname.data)));
+ }
+
+ procinfo = index_getprocinfo(indrel, attnum, procnum);
+
+ return (bytea *) DatumGetPointer(FunctionCall2(procinfo,
+ attoptions,
+ BoolGetDatum(validate)));
+}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 86820eecfc..4a42cb1523 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -694,6 +694,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];
@@ -725,10 +726,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;
@@ -782,7 +784,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;
@@ -820,7 +822,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 d2e4f53a80..125174852b 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"
@@ -106,7 +107,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,
@@ -486,7 +488,7 @@ InitializeAttributeOids(Relation indexRelation,
* ----------------------------------------------------------------
*/
static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
{
Relation pg_attribute;
CatalogIndexState indstate;
@@ -508,10 +510,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);
@@ -591,6 +594,7 @@ UpdateIndexRelation(Oid indexoid,
else
predDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
@@ -933,7 +937,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
@@ -1146,6 +1151,12 @@ index_create(Relation heapRelation,
indexRelation->rd_index->indnkeyatts = indexInfo->ii_NumIndexKeyAttrs;
+ /* Validate opclass-specific options */
+ if (indexInfo->ii_OpclassOptions)
+ for (i = 0; i < indexInfo->ii_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
@@ -2208,6 +2219,10 @@ BuildIndexInfo(Relation index)
ii->ii_ExclusionStrats = NULL;
}
+ ii->ii_OpclassOptions =
+ RelationGetRawOpclassOptions(RelationGetRelid(index),
+ RelationGetNumberOfAttributes(index));
+
/* other info */
ii->ii_Unique = indexStruct->indisunique;
ii->ii_ReadyForInserts = indexStruct->indisready;
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index de6282a667..7290731b3d 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 d05d2fd3d5..23a57be749 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -792,6 +792,7 @@ DefineIndex(Oid relationId,
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_OpclassOptions = NULL; /* for now */
indexInfo->ii_Unique = stmt->unique;
/* In a concurrent build, mark it not-ready-for-inserts */
indexInfo->ii_ReadyForInserts = !stmt->concurrent;
@@ -1513,7 +1514,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
@@ -1814,6 +1815,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/tablecmds.c b/src/backend/commands/tablecmds.c
index 98519ef836..a7c8fa38a0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5688,7 +5688,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 78deade89b..f5fad43eba 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2864,6 +2864,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 4f2ebe5118..6b14095c82 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2546,6 +2546,7 @@ _equalIndexElem(const IndexElem *a, const IndexElem *b)
COMPARE_STRING_FIELD(indexcolname);
COMPARE_NODE_FIELD(collation);
COMPARE_NODE_FIELD(opclass);
+ COMPARE_NODE_FIELD(opclassopts);
COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 237598e110..35806c0681 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2844,6 +2844,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 2405acbf6f..5c6be745c0 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -362,6 +362,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->nulls_first = NULL;
}
+ /* Fetch index opclass options */
+ info->opclassoptions =
+ RelationGetParsedOpclassOptions(indexRelation);
+
/*
* Fetch the index expressions and predicate, if any. We must
* modify the copies we obtain from the relcache to have the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8311b1dd46..2feade5327 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -491,7 +491,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <alias> alias_clause opt_alias_clause
%type <list> func_alias_clause
%type <sortby> sortby
-%type <ielem> index_elem
+%type <ielem> index_elem index_elem_options
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
@@ -7420,43 +7420,65 @@ index_params: index_elem { $$ = list_make1($1); }
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
+
+index_elem_options:
+ opt_collate opt_class opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = NIL;
+ $$->ordering = $3;
+ $$->nulls_ordering = $4;
+ }
+ | opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ | opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = NIL;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ ;
+
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
{
- $$ = makeNode(IndexElem);
+ $$ = $2;
$$->name = $1;
- $$->expr = NULL;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $2;
$$->expr = $1;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $4;
$$->expr = $2;
- $$->indexcolname = NULL;
- $$->collation = $4;
- $$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
}
;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9dda4820af..ea49bef807 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -453,8 +453,6 @@ static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
deparse_context *context);
static void get_tablesample_def(TableSampleClause *tablesample,
deparse_context *context);
-static void get_opclass_name(Oid opclass, Oid actual_datatype,
- StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
static char *get_relation_name(Oid relid);
@@ -469,6 +467,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 ")
@@ -1198,6 +1197,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
oidvector *indcollation;
oidvector *indclass;
int2vector *indoption;
+ Datum *opcoptions = NULL;
StringInfoData buf;
char *str;
char *sep;
@@ -1233,6 +1233,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Assert(!isnull);
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+ if (!attrsOnly)
+ opcoptions = RelationGetRawOpclassOptions(indexrelid, idxrec->indnatts);
+
/*
* Fetch the pg_class tuple of the index relation
*/
@@ -1369,16 +1372,28 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
if (!attrsOnly && keyno < idxrec->indnkeyatts &&
(!colno || colno == keyno + 1))
{
+ bool has_options = false;
int16 opt = indoption->values[keyno];
Oid indcoll = indcollation->values[keyno];
+ if (opcoptions)
+ has_options = (opcoptions[keyno] != (Datum) 0);
+
/* Add collation, if not default for column */
if (OidIsValid(indcoll) && indcoll != keycolcollation)
appendStringInfo(&buf, " COLLATE %s",
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, opcoptions[keyno]);
+ appendStringInfoChar(&buf, ')');
+ }
/* Add options if relevant */
if (amroutine->amcanorder)
@@ -10459,7 +10474,7 @@ get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
* actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.)
*/
-static void
+void
get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf)
{
@@ -11168,6 +11183,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, 'i',
+ &options, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *option = TextDatumGetCString(options[i]);
+ char *name;
+ char *separator;
+ char *value;
+
+ /*
+ * Each array element should have the form name=value. If the "="
+ * is missing for some reason, treat it like an empty value.
+ */
+ name = option;
+ separator = strchr(option, '=');
+ if (separator)
+ {
+ *separator = '\0';
+ value = separator + 1;
+ }
+ else
+ value = "";
+
+ if (i > 0)
+ appendStringInfoString(buf, ", ");
+ appendStringInfo(buf, "%s=", quote_identifier(name));
+
+ /*
+ * In general we need to quote the value; but to avoid unnecessary
+ * clutter, do not quote if it is an identifier that would not
+ * need quoting. (We could also allow numbers, but that is a bit
+ * trickier than it looks --- for example, are leading zeroes
+ * significant? We don't want to assume very much here about what
+ * custom reloptions might mean.)
+ */
+ if (quote_identifier(value) == value)
+ appendStringInfoString(buf, value);
+ else
+ simple_quote_literal(buf, value);
+
+ pfree(option);
+ }
+}
+
/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
@@ -11188,56 +11259,9 @@ flatten_reloptions(Oid relid)
if (!isnull)
{
StringInfoData buf;
- Datum *options;
- int noptions;
- int i;
initStringInfo(&buf);
-
- deconstruct_array(DatumGetArrayTypeP(reloptions),
- TEXTOID, -1, false, 'i',
- &options, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *option = TextDatumGetCString(options[i]);
- char *name;
- char *separator;
- char *value;
-
- /*
- * Each array element should have the form name=value. If the "="
- * is missing for some reason, treat it like an empty value.
- */
- name = option;
- separator = strchr(option, '=');
- if (separator)
- {
- *separator = '\0';
- value = separator + 1;
- }
- else
- value = "";
-
- if (i > 0)
- appendStringInfoString(&buf, ", ");
- appendStringInfo(&buf, "%s=", quote_identifier(name));
-
- /*
- * In general we need to quote the value; but to avoid unnecessary
- * clutter, do not quote if it is an identifier that would not
- * need quoting. (We could also allow numbers, but that is a bit
- * trickier than it looks --- for example, are leading zeroes
- * significant? We don't want to assume very much here about what
- * custom reloptions might mean.)
- */
- if (quote_identifier(value) == value)
- appendStringInfoString(&buf, value);
- else
- simple_quote_literal(&buf, value);
-
- pfree(option);
- }
+ get_reloptions(&buf, reloptions);
result = buf.data;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2b992d7832..62caa4cfaf 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5173,6 +5173,105 @@ GetRelationPublicationActions(Relation relation)
return pubactions;
}
+/*
+ * RelationGetIndexOpclassOptions -- get opclass-specific options for the index
+ */
+Datum *
+RelationGetRawOpclassOptions(Oid indexrelid, int16 natts)
+{
+ Datum *options = NULL;
+ int16 attnum;
+
+ for (attnum = 1; attnum <= natts; attnum++)
+ {
+ HeapTuple tuple;
+ Datum attopts;
+ bool isnull;
+
+ tuple = SearchSysCache2(ATTNUM, ObjectIdGetDatum(indexrelid),
+ Int16GetDatum(attnum));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, indexrelid);
+
+ attopts = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
+ &isnull);
+
+ if (!isnull)
+ {
+ if (!options)
+ options = palloc0(sizeof(Datum) * natts);
+
+ options[attnum - 1] = datumCopy(attopts, false, -1); /* text */
+ }
+
+ ReleaseSysCache(tuple);
+ }
+
+ return options;
+}
+
+/*
+ * RelationGetOpclassOptions -- get parsed opclass-specific options for an index
+ */
+bytea **
+RelationGetParsedOpclassOptions(Relation relation)
+{
+ MemoryContext oldcxt;
+ bytea **opts;
+ Datum *rawopts;
+ int natts = RelationGetNumberOfAttributes(relation);
+ int i;
+
+ /* Try to copy cached options. */
+ if (relation->rd_opcoptions)
+ {
+ opts = palloc(sizeof(*opts) * natts);
+
+ for (i = 0; i < natts; i++)
+ {
+ bytea *opt = relation->rd_opcoptions[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+ }
+
+ /* Get and parse opclass options. */
+ opts = palloc0(sizeof(*opts) * natts);
+
+ rawopts = RelationGetRawOpclassOptions(RelationGetRelid(relation), natts);
+
+ for (i = 0; i < natts; i++)
+ {
+ Datum options = rawopts ? rawopts[i] : (Datum) 0;
+
+ opts[i] = index_opclass_options(relation, i + 1, options, false);
+
+ if (options != (Datum) 0)
+ pfree(DatumGetPointer(options));
+ }
+
+ if (rawopts)
+ pfree(rawopts);
+
+ /* Copy parsed options to the cache. */
+ oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+
+ relation->rd_opcoptions = palloc(sizeof(*opts) * natts);
+
+ for (i = 0; i < natts; i++)
+ relation->rd_opcoptions[i] = !opts[i] ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opts[i]), false, -1));
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return opts;
+}
+
/*
* Routines to support ereport() reports of relation-related errors
*
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 6e3db06eed..9c06d1a094 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -103,6 +103,12 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
typedef bytea *(*amoptions_function) (Datum reloptions,
bool validate);
+/* parse column opclass-specific options */
+typedef bytea *(*amopclassoptions_function) (Relation index,
+ AttrNumber colno,
+ Datum attoptions,
+ bool validate);
+
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -215,6 +221,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
+ amopclassoptions_function amopclassoptions;
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 8c053be2ca..aa170f6de6 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -180,6 +180,11 @@ extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
extern void index_store_float8_orderby_distances(IndexScanDesc scan,
Oid *orderByTypes, double *distances,
bool recheckOrderBy);
+extern bytea *index_opclass_options(Relation relation, AttrNumber attnum,
+ Datum attoptions, bool validate);
+extern bytea *index_opclass_options_generic(Relation relation,
+ AttrNumber attnum, uint16 procnum,
+ Datum attoptions, bool validate);
/*
* index access method support routines (in genam.c)
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a1912f41e6..8dea2d8e69 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -263,12 +263,17 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(Datum options, bool validate,
+ relopt_gen **gen, int nelems);
extern void *allocateReloptStruct(Size base, relopt_value *options,
int numoptions);
extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(Datum options, relopt_gen *optgen[],
+ int offsets[], int noptions, size_t base_size,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index eec71c29d5..866e94d04f 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -94,6 +94,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/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 99b9fa414f..e0d6c6c375 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -138,6 +138,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?
@@ -166,6 +167,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 2a8edf934f..15699971a7 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 4b7703d478..f8a935f501 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -802,6 +802,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/rel.h b/src/include/utils/rel.h
index d7f33abce3..8440e225ee 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -175,6 +175,7 @@ typedef struct RelationData
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
void *rd_amcache; /* available for use by index AM */
Oid *rd_indcollation; /* OIDs of index collations */
+ bytea **rd_opcoptions; /* parsed opclass-specific options */
/*
* foreign-table support
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index d9c10ffcba..562929c058 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"
@@ -49,6 +50,8 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern bytea **RelationGetParsedOpclassOptions(Relation relation);
+extern Datum *RelationGetRawOpclassOptions(Oid indexrelid, int16 natts);
typedef enum IndexAttrBitmapKind
{
diff --git a/src/include/utils/ruleutils.h b/src/include/utils/ruleutils.h
index d34cad2f4b..68d7ca892e 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -35,5 +35,7 @@ extern List *select_rtable_names_for_explain(List *rtable,
Bitmapset *rels_used);
extern char *generate_collation_name(Oid collid);
extern char *get_range_partbound_string(List *bound_datums);
+extern void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf);
+
#endif /* RULEUTILS_H */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index acab8e0b11..33f2bf0e3f 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -244,6 +244,11 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
{vacuum_cleanup_index_scale_factor=70.0}
(1 row)
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+ERROR: access method "btree" does not support opclass options
+create index on btree_tall_tbl (id default(foo=1));
+ERROR: access method "btree" does not support opclass options
--
-- Test for multilevel page deletion
--
diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql
index 48eaf4fe42..aecd690a01 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -121,6 +121,10 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac
alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0);
select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass;
+-- Test unsupported btree opclass parameters
+create index on btree_tall_tbl (id int4_ops(foo=1));
+create index on btree_tall_tbl (id default(foo=1));
+
--
-- Test for multilevel page deletion
--
--
2.20.1
0002-Add-opclass-parameters-to-GiST-20190611.patchtext/plain; charset=us-asciiDownload
From ddd99a8fb48774cfaf9ca151ebb8a5238bc1d2b3 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 21:17:42 +0200
Subject: [PATCH 02/10] Add opclass parameters to GiST
---
doc/src/sgml/xindex.sgml | 5 +++
src/backend/access/gist/gist.c | 2 ++
src/backend/access/gist/gistget.c | 12 ++++---
src/backend/access/gist/gistsplit.c | 15 +++++----
src/backend/access/gist/gistutil.c | 44 ++++++++++++++++++--------
src/backend/access/gist/gistvalidate.c | 34 +++++++++++++-------
src/include/access/gist.h | 3 +-
src/include/access/gist_private.h | 4 +++
8 files changed, 82 insertions(+), 37 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 9446f8b836..8c5b5289d7 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -546,6 +546,11 @@
index-only scans (optional)</entry>
<entry>9</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>parse opclass-specific options (optional)</entry>
+ <entry>10</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 470b121e7d..8e3460f456 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -98,6 +98,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
+ amroutine->amopclassoptions = gistopclassoptions;
PG_RETURN_POINTER(amroutine);
}
@@ -1528,6 +1529,7 @@ initGISTstate(Relation index)
giststate->scanCxt = scanCxt;
giststate->tempCxt = scanCxt; /* caller must change this if needed */
giststate->leafTupdesc = index->rd_att;
+ giststate->opclassoptions = RelationGetParsedOpclassOptions(index);
/*
* The truncated tupdesc for non-leaf index tuples, which doesn't contain
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 8108fbb7d8..abfe659ca5 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -196,6 +196,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum test;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -216,13 +217,14 @@ gistindex_keytest(IndexScanDesc scan,
*/
recheck = true;
- test = FunctionCall5Coll(&key->sk_func,
+ test = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
if (!DatumGetBool(test))
return false;
@@ -257,6 +259,7 @@ gistindex_keytest(IndexScanDesc scan,
Datum dist;
bool recheck;
GISTENTRY de;
+ bytea *options = giststate->opclassoptions[key->sk_attno - 1];
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
@@ -279,13 +282,14 @@ gistindex_keytest(IndexScanDesc scan,
* about the flag, but are expected to never be lossy.
*/
recheck = false;
- dist = FunctionCall5Coll(&key->sk_func,
+ dist = FunctionCall6Coll(&key->sk_func,
key->sk_collation,
PointerGetDatum(&de),
key->sk_argument,
Int16GetDatum(key->sk_strategy),
ObjectIdGetDatum(key->sk_subtype),
- PointerGetDatum(&recheck));
+ PointerGetDatum(&recheck),
+ PointerGetDatum(options));
*recheck_distances_p |= recheck;
*distance_p = DatumGetFloat8(dist);
}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index 6a9c54d86c..2b3cb967e1 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -378,18 +378,20 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
evec->n = v->spl_nleft;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
sizeof(GISTENTRY) * evec->n);
- v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_ldatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
evec->n = v->spl_nright;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
sizeof(GISTENTRY) * evec->n);
- v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ v->spl_rdatum = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ PointerGetDatum(&nbytes),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
/*
@@ -430,10 +432,11 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
* Let the opclass-specific PickSplit method do its thing. Note that at
* this point we know there are no null keys in the entryvec.
*/
- FunctionCall2Coll(&giststate->picksplitFn[attno],
+ FunctionCall3Coll(&giststate->picksplitFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(entryvec),
- PointerGetDatum(sv));
+ PointerGetDatum(sv),
+ PointerGetDatum(giststate->opclassoptions[attno]));
if (sv->spl_nleft == 0 || sv->spl_nright == 0)
{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 49df05653b..000f10103c 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -201,10 +201,11 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
}
/* Make union and store in attr array */
- attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+ attr[i] = FunctionCall3Coll(&giststate->unionFn[i],
giststate->supportCollation[i],
PointerGetDatum(evec),
- PointerGetDatum(&attrsize));
+ PointerGetDatum(&attrsize),
+ PointerGetDatum(giststate->opclassoptions[i]));
isnull[i] = false;
}
@@ -270,10 +271,11 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
}
*dstisnull = false;
- *dst = FunctionCall2Coll(&giststate->unionFn[attno],
+ *dst = FunctionCall3Coll(&giststate->unionFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(evec),
- PointerGetDatum(&dstsize));
+ PointerGetDatum(&dstsize),
+ PointerGetDatum(giststate->opclassoptions[attno]));
}
}
@@ -282,10 +284,11 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
bool result;
- FunctionCall3Coll(&giststate->equalFn[attno],
+ FunctionCall4Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
a, b,
- PointerGetDatum(&result));
+ PointerGetDatum(&result),
+ PointerGetDatum(giststate->opclassoptions[attno]));
return result;
}
@@ -559,9 +562,10 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
return;
dep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->decompressFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(e)));
+ PointerGetDatum(e),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* decompressFn may just return the given pointer */
if (dep != e)
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -596,9 +600,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
/* there may not be a compress function in opclass */
if (OidIsValid(giststate->compressFn[i].fn_oid))
cep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+ DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
giststate->supportCollation[i],
- PointerGetDatum(¢ry)));
+ PointerGetDatum(¢ry),
+ PointerGetDatum(giststate->opclassoptions[i])));
else
cep = ¢ry;
compatt[i] = cep->key;
@@ -643,9 +648,10 @@ gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
fep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
+ DatumGetPointer(FunctionCall2Coll(&giststate->fetchFn[nkey],
giststate->supportCollation[nkey],
- PointerGetDatum(&fentry)));
+ PointerGetDatum(&fentry),
+ PointerGetDatum(giststate->opclassoptions[nkey])));
/* fetchFn set 'key', return it to the caller */
return fep->key;
@@ -722,11 +728,12 @@ gistpenalty(GISTSTATE *giststate, int attno,
if (giststate->penaltyFn[attno].fn_strict == false ||
(isNullOrig == false && isNullAdd == false))
{
- FunctionCall3Coll(&giststate->penaltyFn[attno],
+ FunctionCall4Coll(&giststate->penaltyFn[attno],
giststate->supportCollation[attno],
PointerGetDatum(orig),
PointerGetDatum(add),
- PointerGetDatum(&penalty));
+ PointerGetDatum(&penalty),
+ PointerGetDatum(giststate->opclassoptions[attno]));
/* disallow negative or NaN penalty */
if (isnan(penalty) || penalty < 0.0)
penalty = 0.0;
@@ -915,6 +922,15 @@ gistoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+gistopclassoptions(Relation index, AttrNumber attnum, Datum attoptions,
+ bool validate)
+{
+ return index_opclass_options_generic(index, attnum, GIST_OPCLASSOPT_PROC,
+ attoptions, validate);
+}
+
+
/*
* gistproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index dfc1a87a75..7ae820070b 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -108,37 +108,46 @@ gistvalidate(Oid opclassoid)
{
case GIST_CONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_UNION_PROC:
ok = check_amproc_signature(procform->amproc, opckeytype, false,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_COMPRESS_PROC:
case GIST_DECOMPRESS_PROC:
case GIST_FETCH_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 1, 1, INTERNALOID);
+ 1, 2, INTERNALOID, INTERNALOID);
break;
case GIST_PENALTY_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 3, 3, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ 3, 4, INTERNALOID,
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_PICKSPLIT_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
- 2, 2, INTERNALOID, INTERNALOID);
+ 2, 3, INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIST_EQUAL_PROC:
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 3, 3, opckeytype, opckeytype,
- INTERNALOID);
+ 3, 4, opckeytype, opckeytype,
+ INTERNALOID, INTERNALOID);
break;
case GIST_DISTANCE_PROC:
ok = check_amproc_signature(procform->amproc, FLOAT8OID, false,
- 5, 5, INTERNALOID, opcintype,
- INT2OID, OIDOID, INTERNALOID);
+ 5, 6, INTERNALOID, opcintype,
+ INT2OID, OIDOID, INTERNALOID,
+ INTERNALOID);
+ break;
+ case GIST_OPCLASSOPT_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
+ 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -259,7 +268,8 @@ gistvalidate(Oid opclassoid)
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
- i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
+ i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
+ i == GIST_OPCLASSOPT_PROC)
continue; /* optional methods */
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 6902f4115b..0c57a64369 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -34,7 +34,8 @@
#define GIST_EQUAL_PROC 7
#define GIST_DISTANCE_PROC 8
#define GIST_FETCH_PROC 9
-#define GISTNProcs 9
+#define GIST_OPCLASSOPT_PROC 10
+#define GISTNProcs 10
/*
* Page opaque data in a GiST index page.
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index f80694bf9a..d46b6b89f9 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -96,6 +96,8 @@ typedef struct GISTSTATE
/* Collations to pass to the support functions */
Oid supportCollation[INDEX_MAX_KEYS];
+
+ bytea **opclassoptions; /* parsed opclass-specific options */
} GISTSTATE;
@@ -462,6 +464,8 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_DEFAULT_FILLFACTOR 90
extern bytea *gistoptions(Datum reloptions, bool validate);
+extern bytea *gistopclassoptions(Relation index, AttrNumber colno,
+ Datum options, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
--
2.20.1
0003-Add-opclass-parameters-to-GIN-20190611.patchtext/plain; charset=us-asciiDownload
From f056cb2deddde2ff0cbef894be2699710720507b Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 21:18:18 +0200
Subject: [PATCH 03/10] Add opclass parameters to GIN
---
doc/src/sgml/xindex.sgml | 7 ++++++
src/backend/access/gin/ginget.c | 11 +++++-----
src/backend/access/gin/ginlogic.c | 15 +++++++------
src/backend/access/gin/ginscan.c | 6 ++++--
src/backend/access/gin/ginutil.c | 27 +++++++++++++++++------
src/backend/access/gin/ginvalidate.c | 32 +++++++++++++++++-----------
src/backend/utils/adt/selfuncs.c | 6 ++++--
src/include/access/gin.h | 3 ++-
src/include/access/gin_private.h | 5 +++++
9 files changed, 78 insertions(+), 34 deletions(-)
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b5289d7..658ec9bc5a 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
</entry>
<entry>6</entry>
</row>
+ <row>
+ <entry><function>options</function></entry>
+ <entry>
+ parse opclass-specific options (optional)
+ </entry>
+ <entry>7</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index b18ae2b3ed..547b7b4762 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
btree->ginstate->supportCollation[attnum - 1],
scanEntry->queryKey,
idatum,
UInt16GetDatum(scanEntry->strategy),
- PointerGetDatum(scanEntry->extra_data)));
-
+ PointerGetDatum(scanEntry->extra_data),
+ PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
if (cmp > 0)
return true;
else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
* case cmp < 0 => not match and continue scan
*----------
*/
- cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+ cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
ginstate->supportCollation[entry->attnum - 1],
entry->queryKey,
datum[off - 1],
UInt16GetDatum(entry->strategy),
- PointerGetDatum(entry->extra_data)));
+ PointerGetDatum(entry->extra_data),
+ PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
if (cmp == 0)
return true;
else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 8f85978972..028b29a8c0 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
*/
key->recheckCurItem = true;
- return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+ return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
PointerGetDatum(key->extra_data),
PointerGetDatum(&key->recheckCurItem),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
static GinTernaryValue
directTriConsistentFn(GinScanKey key)
{
- return DatumGetGinTernaryValue(FunctionCall7Coll(
+ return DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
}
/*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
{
GinTernaryValue result;
- result = DatumGetGinTernaryValue(FunctionCall7Coll(
+ result = DatumGetGinTernaryValue(FunctionCall8Coll(
key->triConsistentFmgrInfo,
key->collation,
PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
UInt32GetDatum(key->nuserentries),
PointerGetDatum(key->extra_data),
PointerGetDatum(key->queryValues),
- PointerGetDatum(key->queryCategories)));
+ PointerGetDatum(key->queryCategories),
+ PointerGetDatum(key->opclassOptions)));
if (result == GIN_MAYBE)
{
key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 74d9821ac1..d1384a33d4 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
key->strategy = strategy;
key->searchMode = searchMode;
key->attnum = attnum;
+ key->opclassOptions = ginstate->opclassOptions[attnum - 1];
ItemPointerSetMin(&key->curItem);
key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
/* OK to call the extractQueryFn */
queryValues = (Datum *)
- DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+ DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
so->ginstate.supportCollation[skey->sk_attno - 1],
skey->sk_argument,
PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode)));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
/*
* If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index cf9699ad18..03874909b0 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amoptions = ginoptions;
+ amroutine->amopclassoptions = ginopclassoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
@@ -96,6 +97,7 @@ initGinState(GinState *state, Relation index)
state->index = index;
state->oneCol = (origTupdesc->natts == 1) ? true : false;
state->origTupdesc = origTupdesc;
+ state->opclassOptions = RelationGetParsedOpclassOptions(index);
for (i = 0; i < origTupdesc->natts; i++)
{
@@ -399,9 +401,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
return 0;
/* both not null, so safe to call the compareFn */
- return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+ return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
- a, b));
+ a, b,
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
}
/*
@@ -437,6 +440,7 @@ typedef struct
{
FmgrInfo *cmpDatumFunc;
Oid collation;
+ Datum options;
bool haveDups;
} cmpEntriesArg;
@@ -458,9 +462,10 @@ cmpEntries(const void *a, const void *b, void *arg)
else if (bb->isnull)
res = -1; /* not-NULL "<" NULL */
else
- res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+ res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
data->collation,
- aa->datum, bb->datum));
+ aa->datum, bb->datum,
+ data->options));
/*
* Detect if we have any duplicates. If there are equal keys, qsort must
@@ -506,11 +511,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
/* OK, call the opclass's extractValueFn */
nullFlags = NULL; /* in case extractValue doesn't set it */
entries = (Datum *)
- DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+ DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
ginstate->supportCollation[attnum - 1],
value,
PointerGetDatum(nentries),
- PointerGetDatum(&nullFlags)));
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
/*
* Generate a placeholder if the item contained no keys.
@@ -553,6 +559,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.collation = ginstate->supportCollation[attnum - 1];
+ arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
arg.haveDups = false;
qsort_arg(keydata, *nentries, sizeof(keyEntryData),
cmpEntries, (void *) &arg);
@@ -628,6 +635,14 @@ ginoptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum attoptions,
+ bool validate)
+{
+ return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+ attoptions, validate);
+}
+
/*
* Fetch index's statistical data into *stats
*
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 63bd7f2adc..a000052f09 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
{
case GIN_COMPARE_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 2, 2, opckeytype, opckeytype);
+ 2, 3, opckeytype, opckeytype,
+ INTERNALOID);
break;
case GIN_EXTRACTVALUE_PROC:
/* Some opclasses omit nullFlags */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 2, 3, opcintype, INTERNALOID,
- INTERNALOID);
+ 2, 4, opcintype, INTERNALOID,
+ INTERNALOID, INTERNALOID);
break;
case GIN_EXTRACTQUERY_PROC:
/* Some opclasses omit nullFlags and searchMode */
ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
- 5, 7, opcintype, INTERNALOID,
+ 5, 8, opcintype, INTERNALOID,
INT2OID, INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_CONSISTENT_PROC:
/* Some opclasses omit queryKeys and nullFlags */
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
- 6, 8, INTERNALOID, INT2OID,
+ 6, 9, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID, INTERNALOID);
+ INTERNALOID, INTERNALOID,
+ INTERNALOID);
break;
case GIN_COMPARE_PARTIAL_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
- 4, 4, opckeytype, opckeytype,
- INT2OID, INTERNALOID);
+ 4, 5, opckeytype, opckeytype,
+ INT2OID, INTERNALOID, INTERNALOID);
break;
case GIN_TRICONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, CHAROID, false,
- 7, 7, INTERNALOID, INT2OID,
+ 7, 8, INTERNALOID, INT2OID,
opcintype, INT4OID,
INTERNALOID, INTERNALOID,
- INTERNALOID);
+ INTERNALOID, INTERNALOID);
+ break;
+ case GIN_OPCLASSOPTIONS_PROC:
+ ok = check_amproc_signature(procform->amproc, INTERNALOID,
+ false, 2, 2, INTERNALOID, BOOLOID);
break;
default:
ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
if (opclassgroup &&
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
- if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+ if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+ i == GIN_OPCLASSOPTIONS_PROC)
continue; /* optional method */
if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
continue; /* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index d7e3f09f1a..d491bdd7ae 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -135,6 +135,7 @@
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
+#include "utils/relcache.h"
#include "utils/selfuncs.h"
#include "utils/snapmgr.h"
#include "utils/spccache.h"
@@ -6242,7 +6243,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
else
collation = DEFAULT_COLLATION_OID;
- OidFunctionCall7Coll(extractProcOid,
+ OidFunctionCall8Coll(extractProcOid,
collation,
query,
PointerGetDatum(&nentries),
@@ -6250,7 +6251,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode));
+ PointerGetDatum(&searchMode),
+ PointerGetDatum(index->opclassoptions[indexcol]));
if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index a8eef5a379..aabfbcd5ce 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
#define GIN_TRICONSISTENT_PROC 6
-#define GINNProcs 6
+#define GIN_OPCLASSOPTIONS_PROC 7
+#define GINNProcs 7
/*
* searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index afb3e15721..0f626a7dbb 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
TupleDesc origTupdesc;
TupleDesc tupdesc[INDEX_MAX_KEYS];
+ bytea **opclassOptions; /* per-index-column opclass options */
+
/*
* Per-index-column opclass support functions
*/
@@ -85,6 +87,8 @@ typedef struct GinState
/* ginutil.c */
extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+ Datum attoptions, bool validate);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -297,6 +301,7 @@ typedef struct GinScanKeyData
StrategyNumber strategy;
int32 searchMode;
OffsetNumber attnum;
+ bytea *opclassOptions;
/*
* Match status data. curItem is the TID most recently tested (could be a
--
2.20.1
0004-Add-opclass-parameters-to-GiST-tsvector_ops-20190611.patchtext/plain; charset=us-asciiDownload
From ae97174555cbaf059d2df45df3c80d7e67007fdd Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas@2ndquadrant.com>
Date: Sun, 9 Jun 2019 21:38:42 +0200
Subject: [PATCH 04/10] Add opclass parameters to GiST tsvector_ops
---
doc/src/sgml/textsearch.sgml | 9 +-
src/backend/utils/adt/tsgistidx.c | 269 ++++++++++++++------------
src/include/catalog/pg_amproc.dat | 5 +-
src/include/catalog/pg_proc.dat | 19 +-
src/test/regress/expected/tsearch.out | 176 +++++++++++++++++
src/test/regress/sql/tsearch.sql | 45 +++++
6 files changed, 392 insertions(+), 131 deletions(-)
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 40888a4d20..54b796ecf1 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3637,7 +3637,7 @@ SELECT plainto_tsquery('supernovae stars');
<tertiary>text search</tertiary>
</indexterm>
- <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+ <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
</term>
<listitem>
@@ -3645,6 +3645,8 @@ SELECT plainto_tsquery('supernovae stars');
Creates a GiST (Generalized Search Tree)-based index.
The <replaceable>column</replaceable> can be of <type>tsvector</type> or
<type>tsquery</type> type.
+ Optional integer parameter <literal>siglen</literal> determines
+ signature length in bytes (see below for details).
</para>
</listitem>
</varlistentry>
@@ -3668,7 +3670,10 @@ SELECT plainto_tsquery('supernovae stars');
to check the actual table row to eliminate such false matches.
(<productname>PostgreSQL</productname> does this automatically when needed.)
GiST indexes are lossy because each document is represented in the
- index by a fixed-length signature. The signature is generated by hashing
+ index by a fixed-length signature. Signature length in bytes is determined
+ by the value of the optional integer parameter <literal>siglen</literal>.
+ Default signature length (when <literal>siglen</literal> is not specied) is
+ 124 bytes, maximal length is 484 bytes. The signature is generated by hashing
each word into a single bit in an n-bit string, with all these bits OR-ed
together to produce an n-bit document signature. When two words hash to
the same bit position there will be a false match. If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 4f256260fd..91661cf8b8 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/gist.h"
+#include "access/reloptions.h"
#include "access/tuptoaster.h"
#include "port/pg_bitutils.h"
#include "tsearch/ts_utils.h"
@@ -22,17 +23,22 @@
#include "utils/pg_crc.h"
-#define SIGLENINT 31 /* >121 => key will toast, so it will not work
- * !!! */
+#define SIGLEN_DEFAULT (31 * 4)
+#define SIGLEN_MAX (121 * 4) /* key will toast, so it will not work !!! */
-#define SIGLEN ( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length */
+} GistTsVectorOptions;
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
#define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -40,8 +46,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))
@@ -65,13 +71,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)
@@ -102,9 +109,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);
@@ -148,36 +156,49 @@ uniqueint(int32 *a, int32 l)
}
static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
int32 *ptr = GETARR(a);
- MemSet((void *) sign, 0, sizeof(BITVEC));
+ MemSet((void *) sign, 0, siglen);
for (k = 0; k < len; k++)
- HASH(sign, ptr[k]);
+ HASH(sign, ptr[k], siglen);
+}
+
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+ int size = CALCGTSIZE(flag, len);
+ SignTSVector *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+ memcpy(GETSIGN(res), sign, len);
+
+ return res;
}
+
Datum
gtsvector_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(1))->siglen;
GISTENTRY *retval = entry;
if (entry->leafkey)
{ /* tsvector */
- SignTSVector *res;
TSVector val = DatumGetTSVector(entry->key);
+ SignTSVector *res = gtsvector_alloc(ARRKEY, val->size, NULL);
int32 len;
int32 *arr;
WordEntry *ptr = ARRPTR(val);
char *words = STRPTR(val);
- len = CALCGTSIZE(ARRKEY, val->size);
- res = (SignTSVector *) palloc(len);
- SET_VARSIZE(res, len);
- res->flag = ARRKEY;
arr = GETARR(res);
len = val->size;
while (len--)
@@ -208,13 +229,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;
}
@@ -226,22 +243,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,
@@ -315,12 +327,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
@@ -347,7 +361,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));
}
@@ -365,7 +379,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
@@ -376,7 +390,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -384,7 +400,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;
}
@@ -395,30 +411,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- SignTSVector *result;
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
+ SignTSVector *result = gtsvector_alloc(SIGNKEY, siglen, NULL);
+ BITVECP base = GETSIGN(result);
+ int32 i;
+
+ memset(base, 0, siglen);
- MemSet((void *) base, 0, sizeof(BITVEC));
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), siglen))
{
- flag = ALLISTRUE;
+ result->flag |= ALLISTRUE;
+ SET_VARSIZE(result, CALCGTSIZE(result->flag, siglen));
break;
}
}
- flag |= SIGNKEY;
- len = CALCGTSIZE(flag, 0);
- result = (SignTSVector *) palloc(len);
- *size = len;
- SET_VARSIZE(result, len);
- result->flag = flag;
- if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+ *size = VARSIZE(result);
PG_RETURN_POINTER(result);
}
@@ -429,6 +439,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -444,8 +455,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])
{
@@ -482,19 +495,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 */
@@ -506,17 +519,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
@@ -525,6 +543,7 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(3))->siglen;
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -533,14 +552,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);
@@ -550,19 +577,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) )
@@ -586,19 +613,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
@@ -606,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = ((GistTsVectorOptions *) PG_GETARG_POINTER(2))->siglen;
OffsetNumber k,
j;
SignTSVector *datum_l,
@@ -625,6 +653,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
@@ -633,16 +662,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;
@@ -664,44 +699,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);
@@ -727,36 +739,34 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign), siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign), siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -767,12 +777,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;
@@ -799,3 +809,20 @@ gtsvector_consistent_oldsig(PG_FUNCTION_ARGS)
{
return gtsvector_consistent(fcinfo);
}
+
+Datum
+gtsvector_options(PG_FUNCTION_ARGS)
+{
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_int siglen =
+ { {"siglen", "signature length", 0, 0, 6, RELOPT_TYPE_INT },
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX };
+ relopt_gen *optgen[] = { &siglen.gen };
+ int offsets[] = { offsetof(GistTsVectorOptions, siglen) };
+ GistTsVectorOptions *options =
+ parseAndFillLocalRelOptions(raw_options, optgen, offsets, 1,
+ sizeof(GistTsVectorOptions), validate);
+
+ PG_RETURN_POINTER(options);
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 020b7413cc..5ceee11ab1 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -458,7 +458,7 @@
amproc => 'gist_circle_distance' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '1',
- amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
+ amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal,internal)' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '2',
amproc => 'gtsvector_union' },
@@ -476,6 +476,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 87335248a0..82b51fc1bb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8569,30 +8569,35 @@
{ oid => '3648', descr => 'GiST tsvector support',
proname => 'gtsvector_compress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_compress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_compress' },
{ oid => '3649', descr => 'GiST tsvector support',
proname => 'gtsvector_decompress', prorettype => 'internal',
- proargtypes => 'internal', prosrc => 'gtsvector_decompress' },
+ proargtypes => 'internal internal', prosrc => 'gtsvector_decompress' },
{ oid => '3650', descr => 'GiST tsvector support',
proname => 'gtsvector_picksplit', prorettype => 'internal',
- proargtypes => 'internal internal', prosrc => 'gtsvector_picksplit' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_picksplit' },
{ oid => '3651', descr => 'GiST tsvector support',
proname => 'gtsvector_union', prorettype => 'gtsvector',
- proargtypes => 'internal internal', prosrc => 'gtsvector_union' },
+ proargtypes => 'internal internal internal', prosrc => 'gtsvector_union' },
{ oid => '3652', descr => 'GiST tsvector support',
proname => 'gtsvector_same', prorettype => 'internal',
- proargtypes => 'gtsvector gtsvector internal', prosrc => 'gtsvector_same' },
+ proargtypes => 'gtsvector gtsvector internal internal',
+ prosrc => 'gtsvector_same' },
{ oid => '3653', descr => 'GiST tsvector support',
proname => 'gtsvector_penalty', prorettype => 'internal',
- proargtypes => 'internal internal internal', prosrc => 'gtsvector_penalty' },
+ proargtypes => 'internal internal internal internal',
+ prosrc => 'gtsvector_penalty' },
{ oid => '3654', descr => 'GiST tsvector support',
proname => 'gtsvector_consistent', prorettype => 'bool',
- proargtypes => 'internal tsvector int2 oid internal',
+ proargtypes => 'internal tsvector int2 oid internal internal',
prosrc => 'gtsvector_consistent' },
{ oid => '3790', descr => 'GiST tsvector support (obsolete)',
proname => 'gtsvector_consistent', prorettype => 'bool',
proargtypes => 'internal gtsvector int4 oid internal',
prosrc => 'gtsvector_consistent_oldsig' },
+{ oid => '3998', descr => 'GiST tsvector support',
+ proname => 'gtsvector_options', prorettype => 'internal',
+ proargtypes => 'internal bool', prosrc => 'gtsvector_options' },
{ oid => '3656', descr => 'GIN tsvector support',
proname => 'gin_extract_tsvector', prorettype => 'internal',
diff --git a/src/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index 6f61acc1ed..a1873dc722 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
508
(1 row)
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR: value 485 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR: parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a)
+ "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx2
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index 637bfb3012..9aed780d0c 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
--
2.20.1
On 2019-Jun-11, Tomas Vondra wrote:
1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:
I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.
2) The 0001 part does this in index_opclass_options_generic:
get_opclass_name(opclass, InvalidOid, &str);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.
Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.
Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 04.09.2019 1:02, Alvaro Herrera wrote:
On 2019-Jun-11, Tomas Vondra wrote:
1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.
API was simplified in the new version of the patches (see below).
2) The 0001 part does this in index_opclass_options_generic:
get_opclass_name(opclass, InvalidOid, &str);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.
I decided to add new function generate_opclass_name() like existing
generate_collation_name(), and to reuse static get_opclass_name().
Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.
New version of rebased patches is attached:
1. Opclass parameters infrastructure.
API was completely refactored since the previous version:
- API was generalized for all AMs. Previously, each AM should implement
opclass options parsing/passing in its own way using its own support
function numbers.
Now each AMs uses 0th support function (discussable). Binary bytea values
of parsed options are passed to support functions using special expression
node initialized in FmgrInfo.fn_expr (see macro PG_GET_OPCLASS_OPTIONS(),
get_fn_opclass_options(), set_fn_opclass_options).
- Introduced new structure local_relopts (needs a better name, of course)
with a set of functions for opclass/AM options definition. The parsing
was moved into index_opclass_option(). That was necessary for mixing of
AM- and opclass-specific options. Opclasses now extend the structure with
AM's options adding their own options. See patch #4 for an example.
2. New AM method amattoptions().
amattoptions() is used to specify per-column AM-specific options.
The example is signature length for bloom indexes (patch #3).
3. Use amattoptions() in contrib/bloom.
This is an attempt to replace "col1"-"col32" AM reloptions with
per-column option "bits" using new amattoptions API:
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
4. An example of using new API in GiST tsvector_ops.
Opclass options definition looks much simpler now:
typedef struct GistTsVectorOptions
{
/* GistAttOptions am_att_options; (future) */
int siglen;
} GistTsVectorOptions;
Datum
gtsvector_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
GistTsVectorOptions *options = NULL;
extend_local_reloptions(relopts, options, sizeof(*options));
add_local_int_reloption(relopts, "siglen", "signature length",
SIGLEN_DEFAULT, 1, SIGLEN_MAX,
&options->siglen);
PG_RETURN_VOID();
}
5. Remove pg_index.indoption column (experimental).
pg_index.indoption (array of per-column ASC/DESC, NULLS FIRST/LAST
flags) can be removed now, and these flags can be stored as
special per-column AM-specific options "desc" and "nulls_first".
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0003-Use-amattoptions-in-contrib-bloom-20190910.patchtext/x-patch; name=0003-Use-amattoptions-in-contrib-bloom-20190910.patchDownload
From 735e199d6736569eb648e33256b3c5268c2a36eb Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 3/5] Use amattoptions in contrib/bloom
---
contrib/bloom/bloom.h | 10 +++++++++
contrib/bloom/blutils.c | 44 +++++++++++++++++++++-------------------
contrib/bloom/expected/bloom.out | 23 +++++++++++++--------
contrib/bloom/sql/bloom.sql | 9 ++++----
4 files changed, 53 insertions(+), 33 deletions(-)
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 010148e..681aaaa 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -16,6 +16,7 @@
#include "access/amapi.h"
#include "access/generic_xlog.h"
#include "access/itup.h"
+#include "access/reloptions.h"
#include "access/xlog.h"
#include "nodes/pathnodes.h"
#include "fmgr.h"
@@ -105,6 +106,13 @@ typedef struct BloomOptions
* index key */
} BloomOptions;
+/* Bloom attribute options */
+typedef struct BloomAttOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int bitSize; /* # of bits generated for this index key */
+} BloomAttOptions;
+
/*
* FreeBlockNumberArray - array of block numbers sized so that metadata fill
* all space in metapage.
@@ -206,6 +214,8 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
extern bytea *bloptions(Datum reloptions, bool validate);
+extern void blattoptions(local_relopts *relopts, int attno);
+
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index b62ba7f..b286081 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -39,7 +39,7 @@ PG_FUNCTION_INFO_V1(blhandler);
static relopt_kind bl_relopt_kind;
/* parse table for fillRelOptions */
-static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];
+static relopt_parse_elt bl_relopt_tab[1];
static int32 myRand(void);
static void mySrand(uint32 seed);
@@ -52,9 +52,6 @@ static void mySrand(uint32 seed);
void
_PG_init(void)
{
- int i;
- char buf[16];
-
bl_relopt_kind = add_reloption_kind();
/* Option for length of signature */
@@ -64,19 +61,6 @@ _PG_init(void)
bl_relopt_tab[0].optname = "length";
bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
-
- /* Number of bits for each possible index column: col1, col2, ... */
- for (i = 0; i < INDEX_MAX_KEYS; i++)
- {
- snprintf(buf, sizeof(buf), "col%d", i + 1);
- add_int_reloption(bl_relopt_kind, buf,
- "Number of bits generated for each index column",
- DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
- bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
- buf);
- bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
- bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i;
- }
}
/*
@@ -86,13 +70,10 @@ static BloomOptions *
makeDefaultBloomOptions(void)
{
BloomOptions *opts;
- int i;
opts = (BloomOptions *) palloc0(sizeof(BloomOptions));
/* Convert DEFAULT_BLOOM_LENGTH from # of bits to # of words */
opts->bloomLength = (DEFAULT_BLOOM_LENGTH + SIGNWORDBITS - 1) / SIGNWORDBITS;
- for (i = 0; i < INDEX_MAX_KEYS; i++)
- opts->bitSize[i] = DEFAULT_BLOOM_BITS;
SET_VARSIZE(opts, sizeof(BloomOptions));
return opts;
}
@@ -131,7 +112,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amoptions = bloptions;
- amroutine->amattoptions = NULL;
+ amroutine->amattoptions = blattoptions;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
@@ -418,6 +399,7 @@ void
BloomFillMetapage(Relation index, Page metaPage)
{
BloomOptions *opts;
+ BloomAttOptions **attopts;
BloomMetaPageData *metadata;
/*
@@ -428,6 +410,11 @@ BloomFillMetapage(Relation index, Page metaPage)
if (!opts)
opts = makeDefaultBloomOptions();
+ attopts = (BloomAttOptions **) RelationGetIndexAttOptions(index, false);
+
+ for (int i = 0; i < RelationGetNumberOfAttributes(index); i++)
+ opts->bitSize[i] = attopts[i]->bitSize;
+
/*
* Initialize contents of meta page, including a copy of the options,
* which are now frozen for the life of the index.
@@ -491,3 +478,18 @@ bloptions(Datum reloptions, bool validate)
return (bytea *) rdopts;
}
+
+/*
+ * Parse per-attribute reloptions for bloom index, producing a BloomOptions struct.
+ */
+void
+blattoptions(local_relopts *relopts, int attno)
+{
+ BloomAttOptions *attopts = NULL;
+
+ init_local_reloptions(relopts, attopts, sizeof(*attopts));
+ add_local_int_reloption(relopts, "bits",
+ "Number of bits generated for index column",
+ DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS,
+ &attopts->bitSize);
+}
diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
index 5ab9e34..3ade0b3 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -4,7 +4,7 @@ CREATE TABLE tst (
t text
);
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
SET enable_seqscan=on;
SET enable_bitmapscan=off;
SET enable_indexscan=off;
@@ -144,7 +144,7 @@ CREATE UNLOGGED TABLE tstu (
t text
);
INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
+CREATE INDEX bloomidxu ON tstu USING bloom (i, t DEFAULT (bits = 4));
SET enable_seqscan=off;
SET enable_bitmapscan=on;
SET enable_indexscan=on;
@@ -214,16 +214,23 @@ ORDER BY 1;
-- relation options
--
DROP INDEX bloomidx;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits=4), t) WITH (length=7);
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
- reloptions
--------------------
- {length=7,col1=4}
+ reloptions
+------------
+ {length=7}
(1 row)
+SELECT attnum, attoptions FROM pg_attribute WHERE attrelid = 'bloomidx'::regclass ORDER BY 1;
+ attnum | attoptions
+--------+------------
+ 1 | {bits=4}
+ 2 |
+(2 rows)
+
-- check for min and max values
\set VERBOSITY terse
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
ERROR: value 0 out of bounds for option "length"
-CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
-ERROR: value 0 out of bounds for option "col1"
+CREATE INDEX bloomidx2 ON tst USING bloom (i DEFAULT (bits=0), t);
+ERROR: value 0 out of bounds for option "bits"
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 32755f2..362647a 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -6,7 +6,7 @@ CREATE TABLE tst (
);
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits = 3), t);
SET enable_seqscan=on;
SET enable_bitmapscan=off;
@@ -58,7 +58,7 @@ CREATE UNLOGGED TABLE tstu (
);
INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
-CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
+CREATE INDEX bloomidxu ON tstu USING bloom (i, t DEFAULT (bits = 4));
SET enable_seqscan=off;
SET enable_bitmapscan=on;
@@ -86,9 +86,10 @@ ORDER BY 1;
-- relation options
--
DROP INDEX bloomidx;
-CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
+CREATE INDEX bloomidx ON tst USING bloom (i DEFAULT (bits=4), t) WITH (length=7);
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+SELECT attnum, attoptions FROM pg_attribute WHERE attrelid = 'bloomidx'::regclass ORDER BY 1;
-- check for min and max values
\set VERBOSITY terse
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
-CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i DEFAULT (bits=0), t);
--
2.7.4
0001-Introduce-opclass-parameters-20190910.patchtext/x-patch; name=0001-Introduce-opclass-parameters-20190910.patchDownload
From 3a9b74780956951bdbe60cdaa96e9752675d3a34 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 1/5] Introduce opclass parameters
---
contrib/bloom/blvalidate.c | 3 +
doc/src/sgml/indices.sgml | 2 +-
doc/src/sgml/ref/create_index.sgml | 16 +-
src/backend/access/brin/brin_validate.c | 3 +
src/backend/access/common/reloptions.c | 410 +++++++++++++++++++----
src/backend/access/gin/ginvalidate.c | 3 +
src/backend/access/gist/gistvalidate.c | 3 +
src/backend/access/hash/hashvalidate.c | 2 +
src/backend/access/index/indexam.c | 74 +++-
src/backend/access/nbtree/nbtvalidate.c | 3 +
src/backend/access/spgist/spgvalidate.c | 3 +
src/backend/catalog/heap.c | 8 +-
src/backend/catalog/index.c | 22 +-
src/backend/catalog/toasting.c | 1 +
src/backend/commands/indexcmds.c | 65 +++-
src/backend/commands/opclasscmds.c | 41 ++-
src/backend/commands/tablecmds.c | 2 +-
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/makefuncs.c | 3 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/optimizer/util/plancat.c | 3 +
src/backend/parser/gram.y | 72 ++--
src/backend/parser/parse_utilcmd.c | 8 +
src/backend/utils/adt/ruleutils.c | 135 +++++---
src/backend/utils/adt/selfuncs.c | 23 +-
src/backend/utils/cache/lsyscache.c | 35 ++
src/backend/utils/cache/relcache.c | 151 ++++++++-
src/backend/utils/fmgr/fmgr.c | 28 ++
src/include/access/genam.h | 3 +
src/include/access/reloptions.h | 36 ++
src/include/catalog/heap.h | 1 +
src/include/catalog/pg_amproc.h | 2 +
src/include/fmgr.h | 4 +
src/include/nodes/execnodes.h | 2 +
src/include/nodes/parsenodes.h | 1 +
src/include/nodes/pathnodes.h | 1 +
src/include/utils/lsyscache.h | 1 +
src/include/utils/rel.h | 1 +
src/include/utils/relcache.h | 3 +
src/include/utils/ruleutils.h | 1 +
src/test/regress/expected/alter_generic.out | 22 +-
src/test/regress/expected/btree_index.out | 5 +
src/test/regress/expected/opr_sanity.out | 2 +-
src/test/regress/input/create_function_1.source | 5 +
src/test/regress/output/create_function_1.source | 4 +
src/test/regress/regress.c | 7 +
src/test/regress/sql/alter_generic.sql | 13 +-
src/test/regress/sql/btree_index.sql | 4 +
src/test/regress/sql/opr_sanity.sql | 2 +-
50 files changed, 1050 insertions(+), 192 deletions(-)
diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c
index e9bd1b4..dbd675b 100644
--- a/contrib/bloom/blvalidate.c
+++ b/contrib/bloom/blvalidate.c
@@ -105,6 +105,9 @@ blvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case BLOOM_HASH_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
1, 1, opckeytype);
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 95c0a19..ea3acea 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1253,7 +1253,7 @@ SELECT target FROM tests WHERE subject = 'some-subject' AND success;
An index definition can specify an <firstterm>operator
class</firstterm> for each column of an index.
<synopsis>
-CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
+CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> (<replaceable>column</replaceable> <replaceable>opclass</replaceable> [ ( <replaceable>opclass_options</replaceable> ) ] <optional><replaceable>sort options</replaceable></optional> <optional>, ...</optional>);
</synopsis>
The operator class identifies the operators to be used by the index
for that column. For example, a B-tree index on the type <type>int4</type>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 629a31e..61401f3 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -22,7 +22,7 @@ PostgreSQL documentation
<refsynopsisdiv>
<synopsis>
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class="parameter">name</replaceable> ] ON [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
- ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
+ ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] { <replaceable class="parameter">opclass</replaceable> | DEFAULT } [ ( <replaceable class="parameter">opclass_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> = <replaceable class="parameter">value</replaceable> [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
@@ -279,6 +279,15 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</varlistentry>
<varlistentry>
+ <term><replaceable class="parameter">opclass_parameter</replaceable></term>
+ <listitem>
+ <para>
+ The name of an operator class parameter. See below for details.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>ASC</literal></term>
<listitem>
<para>
@@ -646,8 +655,9 @@ Indexes:
</para>
<para>
- An <firstterm>operator class</firstterm> can be specified for each
- column of an index. The operator class identifies the operators to be
+ An <firstterm>operator class</firstterm> with its optional parameters
+ can be specified for each column of an index.
+ The operator class identifies the operators to be
used by the index for that column. For example, a B-tree index on
four-byte integers would use the <literal>int4_ops</literal> class;
this operator class includes comparison functions for four-byte
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c
index 0129338..9e6694e 100644
--- a/src/backend/access/brin/brin_validate.c
+++ b/src/backend/access/brin/brin_validate.c
@@ -87,6 +87,9 @@ brinvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case BRIN_PROCNUM_OPCINFO:
ok = check_amproc_signature(procform->amproc, INTERNALOID, true,
1, 1, INTERNALOID);
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 20f4ed3..3ad1324 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -445,6 +445,7 @@ static relopt_string stringRelOpts[] =
4,
false,
gistValidateBufferingOption,
+ NULL,
"auto"
},
{
@@ -457,6 +458,7 @@ static relopt_string stringRelOpts[] =
0,
true,
validateWithCheckOption,
+ NULL,
NULL
},
/* list terminator */
@@ -615,6 +617,50 @@ add_reloption(relopt_gen *newoption)
need_initialization = true;
}
+typedef struct local_relopt
+{
+ relopt_gen *option;
+ int offset;
+} local_relopt;
+
+void
+init_local_reloptions(local_relopts *opts, void *base, Size base_size)
+{
+ opts->options = NIL;
+ opts->validators = NIL;
+ opts->base = base;
+ opts->base_size = base_size;
+}
+
+void
+extend_local_reloptions(local_relopts *opts, void *base, Size base_size)
+{
+ Assert(opts->base_size < base_size);
+ opts->base = base;
+ opts->base_size = base_size;
+}
+
+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, void *pval)
+{
+ local_relopt *opt = palloc(sizeof(*opt));
+
+ opt->option = newoption;
+ opt->offset = (char *) pval - (char *) relopts->base;
+
+ relopts->options = lappend(relopts->options, opt);
+}
+
/*
* allocate_reloption
* Allocate a new reloption and initialize the type-agnostic fields
@@ -627,7 +673,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)
{
@@ -659,17 +708,19 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
newoption->namelen = strlen(name);
newoption->type = type;
- MemoryContextSwitchTo(oldcxt);
+ if (oldcxt != NULL)
+ MemoryContextSwitchTo(oldcxt);
return newoption;
}
/*
- * add_bool_reloption
- * Add a new boolean reloption
+ * init_bool_reloption
+ * Allocate and initialize a new boolean reloption
*/
-void
-add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
+static relopt_bool *
+init_bool_reloption(bits32 kinds, const char *name, const char *desc,
+ bool default_val)
{
relopt_bool *newoption;
@@ -677,16 +728,43 @@ add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool defaul
name, desc);
newoption->default_val = default_val;
+ return newoption;
+}
+
+/*
+ * add_bool_reloption
+ * Add a new boolean reloption
+ */
+void
+add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
+{
+ relopt_bool *newoption = init_bool_reloption(kinds, name, desc, default_val);
+
add_reloption((relopt_gen *) newoption);
}
/*
- * add_int_reloption
- * Add a new integer reloption
+ * add_local_bool_reloption
+ * Add a new boolean local reloption
*/
void
-add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
- int min_val, int max_val)
+add_local_bool_reloption(local_relopts *relopts, const char *name,
+ const char *desc, bool default_val, bool *pval)
+{
+ relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL, name, desc,
+ default_val);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+
+/*
+ * 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)
{
relopt_int *newoption;
@@ -696,16 +774,45 @@ add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_
newoption->min = min_val;
newoption->max = max_val;
+ return newoption;
+}
+
+/*
+ * add_int_reloption
+ * Add a new integer reloption
+ */
+void
+add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
+ int min_val, int max_val)
+{
+ relopt_int *newoption = init_int_reloption(kinds, name, desc, default_val,
+ min_val, max_val);
+
add_reloption((relopt_gen *) newoption);
}
/*
- * add_real_reloption
- * Add a new float reloption
+ * add_local_int_reloption
+ * Add a new local integer reloption
*/
void
-add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
- double min_val, double max_val)
+add_local_int_reloption(local_relopts *relopts, const char *name,
+ const char *desc, int default_val, int min_val,
+ int max_val, int *pval)
+{
+ relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL, name, desc,
+ default_val, min_val, max_val);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+/*
+ * 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)
{
relopt_real *newoption;
@@ -715,21 +822,46 @@ 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)
+{
+ relopt_real *newoption = init_real_reloption(kinds, name, desc, default_val,
+ min_val, max_val);
+
add_reloption((relopt_gen *) newoption);
}
/*
- * add_string_reloption
- * Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values. It must elog(ERROR) when the argument string is
- * not acceptable for the variable. Note that the default value must pass
- * the validation.
+ * add_local_real_reloption
+ * Add a new local float reloption
*/
void
-add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
- validate_string_relopt validator)
+add_local_real_reloption(local_relopts *relopts, const char *name, const char *desc,
+ double default_val, double min_val, double max_val,
+ double *pval)
+{
+ relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL, name, desc,
+ default_val, min_val, max_val);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, pval);
+}
+
+/*
+ * 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)
{
relopt_string *newoption;
@@ -740,10 +872,13 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
name, desc);
newoption->validate_cb = validator;
+ newoption->fill_cb = filler;
if (default_val)
{
- newoption->default_val = MemoryContextStrdup(TopMemoryContext,
- default_val);
+ if (kinds == RELOPT_KIND_LOCAL)
+ newoption->default_val = strdup(default_val);
+ else
+ newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
newoption->default_len = strlen(default_val);
newoption->default_isnull = false;
}
@@ -754,10 +889,46 @@ add_string_reloption(bits32 kinds, const char *name, const char *desc, const cha
newoption->default_isnull = true;
}
+ return newoption;
+}
+
+/*
+ * add_string_reloption
+ * Add a new string reloption
+ *
+ * "validator" is an optional function pointer that can be used to test the
+ * validity of the values. It must elog(ERROR) when the argument string is
+ * not acceptable for the variable. Note that the default value must pass
+ * the validation.
+ */
+void
+add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
+ validate_string_relopt validator)
+{
+ relopt_string *newoption = init_string_reloption(kinds, name, desc,
+ default_val, validator, NULL);
+
add_reloption((relopt_gen *) newoption);
}
/*
+ * add_local_string_reloption
+ * Add a new local string reloption
+ */
+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 *poffset)
+{
+ relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL, name,
+ desc, default_val,
+ validator, filler);
+
+ add_local_reloption(relopts, (relopt_gen *) newoption, poffset);
+}
+
+/*
* Transform a relation options list (list of DefElem) into the text array
* format that is kept in pg_class.reloptions, including only those options
* that are in the passed namespace. The output values do not include the
@@ -1051,6 +1222,60 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
return options;
}
+static void
+parseRelOptionsInternal(Datum options, bool validate,
+ relopt_value *reloptions, int numoptions)
+{
+ ArrayType *array = DatumGetArrayTypeP(options);
+ Datum *optiondatums;
+ int noptions;
+ int i;
+
+ deconstruct_array(array, TEXTOID, -1, false, 'i',
+ &optiondatums, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *text_str = VARDATA(optiondatums[i]);
+ int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
+ int j;
+
+ /* Search for a match in reloptions */
+ for (j = 0; j < numoptions; j++)
+ {
+ int kw_len = reloptions[j].gen->namelen;
+
+ if (text_len > kw_len && text_str[kw_len] == '=' &&
+ strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
+ {
+ parse_one_reloption(&reloptions[j], text_str, text_len,
+ validate);
+ break;
+ }
+ }
+
+ if (j >= numoptions && validate)
+ {
+ char *s;
+ char *p;
+
+ s = TextDatumGetCString(optiondatums[i]);
+ p = strchr(s, '=');
+ if (p)
+ *p = '\0';
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized parameter \"%s\"", s)));
+ }
+ }
+
+ /* It's worth avoiding memory leaks in this function */
+ pfree(optiondatums);
+
+ if (((void *) array) != DatumGetPointer(options))
+ pfree(array);
+}
+
/*
* Interpret reloptions that are given in text-array format.
*
@@ -1105,57 +1330,74 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
/* Done if no options */
if (PointerIsValid(DatumGetPointer(options)))
+ parseRelOptionsInternal(options, validate, reloptions, numoptions);
+
+ *numrelopts = numoptions;
+ return reloptions;
+}
+
+/* Parse local unregistered options. */
+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)
{
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
+ local_relopt *opt = lfirst(lc);
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
+ values[i].gen = opt->option;
+ values[i].isset = false;
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
+ i++;
+ }
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
+ if (options != (Datum) 0)
+ parseRelOptionsInternal(options, validate, values, nopts);
- 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;
- }
- }
+ return values;
+}
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
+/*
+ * Parse local options, allocate a bytea struct that's of the specified
+ * 'base_size' plus any extra space that's needed for string variables,
+ * fill its option's fields located at the given offsets and return it.
+ */
+void *
+parseAndFillLocalRelOptions(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);
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
+ elems[i].optname = opt->option->name;
+ elems[i].opttype = opt->option->type;
+ elems[i].offset = opt->offset;
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
+ i++;
}
- *numrelopts = numoptions;
- return reloptions;
+ vals = parseLocalRelOptions(relopts, options, validate);
+ opts = allocateReloptStruct(relopts->base_size, vals, noptions);
+ fillRelOptions(opts, relopts->base_size, vals, noptions, validate, elems, noptions);
+
+ foreach(lc, relopts->validators)
+ ((relopts_validator) lfirst(lc))(opts, vals, noptions);
+
+ if (elems)
+ pfree(elems);
+
+ return opts;
}
/*
@@ -1271,8 +1513,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);
}
@@ -1336,7 +1594,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
{
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 63bd7f2..337eeec 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -106,6 +106,9 @@ ginvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case GIN_COMPARE_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, false,
2, 2, opckeytype, opckeytype);
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index dfc1a87..ea8cd0e 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -106,6 +106,9 @@ gistvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case GIST_CONSISTENT_PROC:
ok = check_amproc_signature(procform->amproc, BOOLOID, false,
5, 5, INTERNALOID, opcintype,
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 9315872..7650a30 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -105,6 +105,8 @@ hashvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ break;
case HASHSTANDARD_PROC:
case HASHEXTENDED_PROC:
if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 2e8f53a..bbed726 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;
@@ -801,9 +807,9 @@ index_getprocinfo(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;
locinfo = irel->rd_supportinfo;
@@ -832,6 +838,17 @@ index_getprocinfo(Relation irel,
procnum, attnum, RelationGetRelationName(irel));
fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
+
+ if (procnum != OPCLASS_OPTIONS_PROC)
+ {
+ /* 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;
@@ -911,3 +928,52 @@ 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)
+{
+ Oid procid = index_getprocid(indrel, attnum, 0);
+ 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, NULL, 0);
+
+ procinfo = index_getprocinfo(indrel, attnum, 0);
+
+ (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+
+ return parseAndFillLocalRelOptions(&relopts, attoptions, validate);
+}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
index 0148ea7..418edc8 100644
--- a/src/backend/access/nbtree/nbtvalidate.c
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -87,6 +87,9 @@ btvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case BTORDER_PROC:
ok = check_amproc_signature(procform->amproc, INT4OID, true,
2, 2, procform->amproclefttype,
diff --git a/src/backend/access/spgist/spgvalidate.c b/src/backend/access/spgist/spgvalidate.c
index 4b9fdbd..a5bb850 100644
--- a/src/backend/access/spgist/spgvalidate.c
+++ b/src/backend/access/spgist/spgvalidate.c
@@ -105,6 +105,9 @@ spgvalidate(Oid opclassoid)
/* Check procedure numbers and function signatures */
switch (procform->amprocnum)
{
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
case SPGIST_CONFIG_PROC:
ok = check_amproc_signature(procform->amproc, VOIDOID, true,
2, 2, INTERNALOID, INTERNALOID);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index b7bcdd9..d5a4900 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -693,6 +693,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];
@@ -724,10 +725,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;
@@ -781,7 +783,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;
@@ -819,7 +821,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 3e1d406..cb7f0ba 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"
@@ -106,7 +107,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,
@@ -486,7 +488,7 @@ InitializeAttributeOids(Relation indexRelation,
* ----------------------------------------------------------------
*/
static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attopts)
{
Relation pg_attribute;
CatalogIndexState indstate;
@@ -508,10 +510,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);
@@ -591,6 +594,7 @@ UpdateIndexRelation(Oid indexoid,
else
predDatum = (Datum) 0;
+
/*
* open the system catalog index relation
*/
@@ -933,7 +937,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
@@ -1146,6 +1151,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
@@ -2266,6 +2278,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 de6282a..7290731 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 1477755..04148e5 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
@@ -1509,7 +1558,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
@@ -1810,6 +1859,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 6a1ccdeb..f2777a5 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -523,11 +523,11 @@ DefineOpClass(CreateOpClassStmt *stmt)
addFamilyMember(&operators, member, false);
break;
case OPCLASS_ITEM_FUNCTION:
- if (item->number <= 0 || item->number > maxProcNumber)
+ if (item->number < 0 || item->number > maxProcNumber)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("invalid function number %d,"
- " must be between 1 and %d",
+ " must be between 0 and %d",
item->number, maxProcNumber)));
funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
#ifdef NOT_USED
@@ -537,7 +537,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;
@@ -902,11 +901,11 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
addFamilyMember(&operators, member, false);
break;
case OPCLASS_ITEM_FUNCTION:
- if (item->number <= 0 || item->number > maxProcNumber)
+ if (item->number < 0 || item->number > maxProcNumber)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("invalid function number %d,"
- " must be between 1 and %d",
+ " must be between 0 and %d",
item->number, maxProcNumber)));
funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
#ifdef NOT_USED
@@ -1141,6 +1140,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 == OPCLASS_OPTIONS_PROC)
+ {
+ 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 +1177,7 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
* a 1-arg proc 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 05593f3..afef6a9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5737,7 +5737,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 a2617c7..d44a5b0 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2868,6 +2868,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 4f2ebe5..6b14095 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2546,6 +2546,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 18466ac..12d43bd 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 e6ce8e2..37b6d6f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2850,6 +2850,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 cf17614..de9b6f4 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -279,6 +279,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 a954acf..c8b5d91 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -491,7 +491,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <alias> alias_clause opt_alias_clause
%type <list> func_alias_clause
%type <sortby> sortby
-%type <ielem> index_elem
+%type <ielem> index_elem index_elem_options
%type <node> table_ref
%type <jexpr> joined_table
%type <range> relation_expr
@@ -7420,43 +7420,65 @@ index_params: index_elem { $$ = list_make1($1); }
| index_params ',' index_elem { $$ = lappend($1, $3); }
;
+
+index_elem_options:
+ opt_collate opt_class opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = NIL;
+ $$->ordering = $3;
+ $$->nulls_ordering = $4;
+ }
+ | opt_collate any_name reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = $2;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ | opt_collate DEFAULT reloptions opt_asc_desc opt_nulls_order
+ {
+ $$ = makeNode(IndexElem);
+ $$->name = NULL;
+ $$->expr = NULL;
+ $$->indexcolname = NULL;
+ $$->collation = $1;
+ $$->opclass = NIL;
+ $$->opclassopts = $3;
+ $$->ordering = $4;
+ $$->nulls_ordering = $5;
+ }
+ ;
+
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId index_elem_options
{
- $$ = makeNode(IndexElem);
+ $$ = $2;
$$->name = $1;
- $$->expr = NULL;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $2;
$$->expr = $1;
- $$->indexcolname = NULL;
- $$->collation = $2;
- $$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' index_elem_options
{
- $$ = makeNode(IndexElem);
- $$->name = NULL;
+ $$ = $4;
$$->expr = $2;
- $$->indexcolname = NULL;
- $$->collation = $4;
- $$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
}
;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6e5768c..5872376 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1577,6 +1577,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;
@@ -2153,9 +2155,13 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
* semantics as you'd get from a normally-created 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 ||
+ attoptions != (Datum) 0 ||
index_rel->rd_indoption[i] != 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -2335,6 +2341,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);
@@ -2448,6 +2455,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 3e64390..faecf04 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -469,6 +469,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 ")
@@ -1371,6 +1372,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)
@@ -1378,7 +1381,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)
@@ -10506,6 +10517,23 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
}
/*
+ * generate_opclass_name
+ * Compute the name to display for a opclass specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+char *
+generate_opclass_name(Oid opclass)
+{
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+ get_opclass_name(opclass, InvalidOid, &buf);
+
+ return &buf.data[1]; /* get_opclass_name() prepends space */
+}
+
+/*
* processIndirection - take care of array and subfield assignment
*
* We strip any top-level FieldStore or assignment SubscriptingRef nodes that
@@ -11183,6 +11211,62 @@ string_to_text(char *str)
}
/*
+ * Generate a C string representing a relation options from text[] datum.
+ */
+static void
+get_reloptions(StringInfo buf, Datum reloptions)
+{
+ Datum *options;
+ int noptions;
+ int i;
+
+ deconstruct_array(DatumGetArrayTypeP(reloptions),
+ TEXTOID, -1, false, 'i',
+ &options, NULL, &noptions);
+
+ for (i = 0; i < noptions; i++)
+ {
+ char *option = TextDatumGetCString(options[i]);
+ char *name;
+ char *separator;
+ char *value;
+
+ /*
+ * Each array element should have the form name=value. If the "="
+ * is missing for some reason, treat it like an empty value.
+ */
+ name = option;
+ separator = strchr(option, '=');
+ if (separator)
+ {
+ *separator = '\0';
+ value = separator + 1;
+ }
+ else
+ value = "";
+
+ if (i > 0)
+ appendStringInfoString(buf, ", ");
+ appendStringInfo(buf, "%s=", quote_identifier(name));
+
+ /*
+ * In general we need to quote the value; but to avoid unnecessary
+ * clutter, do not quote if it is an identifier that would not
+ * need quoting. (We could also allow numbers, but that is a bit
+ * trickier than it looks --- for example, are leading zeroes
+ * significant? We don't want to assume very much here about what
+ * custom reloptions might mean.)
+ */
+ if (quote_identifier(value) == value)
+ appendStringInfoString(buf, value);
+ else
+ simple_quote_literal(buf, value);
+
+ pfree(option);
+ }
+}
+
+/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
static char *
@@ -11202,56 +11286,9 @@ flatten_reloptions(Oid relid)
if (!isnull)
{
StringInfoData buf;
- Datum *options;
- int noptions;
- int i;
initStringInfo(&buf);
-
- deconstruct_array(DatumGetArrayTypeP(reloptions),
- TEXTOID, -1, false, 'i',
- &options, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *option = TextDatumGetCString(options[i]);
- char *name;
- char *separator;
- char *value;
-
- /*
- * Each array element should have the form name=value. If the "="
- * is missing for some reason, treat it like an empty value.
- */
- name = option;
- separator = strchr(option, '=');
- if (separator)
- {
- *separator = '\0';
- value = separator + 1;
- }
- else
- value = "";
-
- if (i > 0)
- appendStringInfoString(&buf, ", ");
- appendStringInfo(&buf, "%s=", quote_identifier(name));
-
- /*
- * In general we need to quote the value; but to avoid unnecessary
- * clutter, do not quote if it is an identifier that would not
- * need quoting. (We could also allow numbers, but that is a bit
- * trickier than it looks --- for example, are leading zeroes
- * significant? We don't want to assume very much here about what
- * custom reloptions might mean.)
- */
- if (quote_identifier(value) == value)
- appendStringInfoString(&buf, value);
- else
- simple_quote_literal(&buf, value);
-
- pfree(option);
- }
+ get_reloptions(&buf, reloptions);
result = buf.data;
}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 1710129..638d5ad 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -6260,6 +6260,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
Oid clause_op, Datum query,
GinQualCounts *counts)
{
+ FmgrInfo flinfo;
Oid extractProcOid;
Oid collation;
int strategy_op;
@@ -6309,15 +6310,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/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27602fa..8fbf5e3 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -908,6 +908,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;
+}
+
/* ---------- COLLATION CACHE ---------- */
/*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 585dcee..7f96935 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1438,7 +1438,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));
@@ -1502,6 +1502,10 @@ RelationInitIndexAccessInfo(Relation relation)
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
+#if 0
+ (void) RelationGetIndexAttOptions(relation, false);
+#endif
+
/*
* expressions, predicate, exclusion caches will be filled later
*/
@@ -1551,9 +1555,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));
}
}
@@ -1618,7 +1622,7 @@ LookupOpclassInfo(Oid operatorClassOid,
if (numSupport > 0)
opcentry->supportProcs = (RegProcedure *)
MemoryContextAllocZero(CacheMemoryContext,
- numSupport * sizeof(RegProcedure));
+ (numSupport + 1) * sizeof(RegProcedure));
else
opcentry->supportProcs = NULL;
}
@@ -1705,13 +1709,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);
@@ -3998,6 +4001,10 @@ load_critical_index(Oid indexoid, Oid heapoid)
ird->rd_refcnt = 1;
UnlockRelationOid(indexoid, AccessShareLock);
UnlockRelationOid(heapoid, AccessShareLock);
+
+#if 0
+ (void) RelationGetIndexAttOptions(ird, false);
+#endif
}
/*
@@ -5153,6 +5160,99 @@ GetRelationPublicationActions(Relation relation)
}
/*
+ * RelationGetIndexRawAttOptions -- get AM/opclass-specific options for the index
+ */
+Datum *
+RelationGetIndexRawAttOptions(Relation indexrel)
+{
+ Oid indexrelid = RelationGetRelid(indexrel);
+ int16 natts = RelationGetNumberOfAttributes(indexrel);
+ Datum *options = NULL;
+ int16 attnum;
+
+ for (attnum = 1; attnum <= natts; attnum++)
+ {
+ if (!OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
+ continue;
+
+ if (!options)
+ options = palloc0(sizeof(Datum) * natts);
+
+ options[attnum - 1] = get_attoptions(indexrelid, attnum);
+ }
+
+ return options;
+}
+
+static bytea **
+CopyIndexAttOptions(bytea **srcopts, int natts)
+{
+ bytea **opts = palloc(sizeof(*opts) * natts);
+
+ for (int i = 0; i < natts; i++)
+ {
+ bytea *opt = srcopts[i];
+
+ opts[i] = !opt ? NULL : (bytea *)
+ DatumGetPointer(datumCopy(PointerGetDatum(opt), false, -1));
+ }
+
+ return opts;
+}
+
+/*
+ * RelationGetIndexAttOptions
+ * get AM/opclass-specific options for an index parsed into a binary form
+ */
+bytea **
+RelationGetIndexAttOptions(Relation relation, bool copy)
+{
+ MemoryContext oldcxt;
+ bytea **opts = relation->rd_opcoptions;
+ Oid relid = RelationGetRelid(relation);
+ int natts = RelationGetNumberOfAttributes(relation); /* XXX IndexRelationGetNumberOfKeyAttributes */
+ int i;
+
+ /* Try to copy cached options. */
+ if (opts)
+ return copy ? CopyIndexAttOptions(opts, natts) : opts;
+
+ /* Get and parse opclass options. */
+ opts = palloc0(sizeof(*opts) * natts);
+
+ for (i = 0; i < natts; i++)
+ {
+ if (criticalRelcachesBuilt && relid != AttributeRelidNumIndexId)
+ {
+ Datum attoptions = get_attoptions(relid, i + 1);
+
+ opts[i] = index_opclass_options(relation, i + 1, attoptions, false);
+
+ if (attoptions != (Datum) 0)
+ pfree(DatumGetPointer(attoptions));
+ }
+ }
+
+ /* Copy parsed options to the cache. */
+ oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
+ relation->rd_opcoptions = CopyIndexAttOptions(opts, natts);
+ MemoryContextSwitchTo(oldcxt);
+
+ if (copy)
+ return opts;
+
+ for (i = 0; i < natts; i++)
+ {
+ if (opts[i])
+ pfree(opts[i]);
+ }
+
+ pfree(opts);
+
+ return relation->rd_opcoptions;
+}
+
+/*
* Routines to support ereport() reports of relation-related errors
*
* These could have been put into elog.c, but it seems like a module layering
@@ -5513,8 +5613,27 @@ load_relcache_init_file(bool shared)
rel->rd_indoption = indoption;
+#if 0
+ /* 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;
+ }
+ }
+#endif
+
/* 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));
}
@@ -5541,6 +5660,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);
}
/*
@@ -5568,6 +5688,7 @@ load_relcache_init_file(bool shared)
rel->rd_exclprocs = NULL;
rel->rd_exclstrats = NULL;
rel->rd_fdwroutine = NULL;
+ rel->rd_opcoptions = NULL;
/*
* Reset transient-state fields in the relcache entry
@@ -5814,7 +5935,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 */
@@ -5826,6 +5947,18 @@ write_relcache_init_file(bool shared)
write_item(rel->rd_indoption,
relform->relnatts * sizeof(int16),
fp);
+
+#if 0
+ 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);
+ }
+#endif
}
}
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 0484adb..e11146a 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"
@@ -1967,6 +1969,32 @@ get_fn_expr_variadic(FmgrInfo *flinfo)
return false;
}
+void
+set_fn_opclass_options(FmgrInfo *flinfo, bytea *options)
+{
+ flinfo->fn_expr = (Node *) makeConst(BYTEAOID, -1, InvalidOid, -1,
+ PointerGetDatum(options),
+ options == NULL, false);
+}
+
+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/genam.h b/src/include/access/genam.h
index 6c56717..778d17c 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -182,6 +182,9 @@ extern void index_store_float8_orderby_distances(IndexScanDesc scan,
double *distanceValues,
bool *distanceNulls,
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/reloptions.h b/src/include/access/reloptions.h
index 6d392e4..70238f9 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -37,6 +37,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),
@@ -109,6 +110,9 @@ typedef struct relopt_real
/* validation routines for strings */
typedef void (*validate_string_relopt) (const char *value);
+typedef Size (*fill_string_relopt) (const char *value, void *ptr);
+
+typedef void (*relopts_validator)(void *parsed_options, relopt_value *vals, int nvals);
typedef struct relopt_string
{
@@ -116,6 +120,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;
@@ -127,6 +132,13 @@ typedef struct
int offset; /* offset of field in result struct */
} relopt_parse_elt;
+typedef struct local_relopts
+{
+ List *options;
+ List *validators;
+ void *base;
+ Size base_size;
+} local_relopts;
/*
* These macros exist for the convenience of amoptions writers (but consider
@@ -255,6 +267,26 @@ extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
extern void add_string_reloption(bits32 kinds, const char *name, const char *desc,
const char *default_val, validate_string_relopt validator);
+extern void init_local_reloptions(local_relopts *opts, void *base, Size base_size);
+extern void extend_local_reloptions(local_relopts *opts, void *base, Size base_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,
+ bool *pval);
+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 *pval);
+extern void add_local_real_reloption(local_relopts *opts, const char *name,
+ const char *desc, double default_val,
+ double min_val, double max_val,
+ double *pval);
+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 *poffset);
+
extern Datum transformRelOptions(Datum oldOptions, List *defList,
const char *namspace, char *validnsps[],
bool acceptOidsOff, bool isReset);
@@ -263,12 +295,16 @@ extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
relopt_kind kind, int *numrelopts);
+extern relopt_value *parseLocalRelOptions(local_relopts *relopts, Datum options,
+ bool validate);
extern void *allocateReloptStruct(Size base, relopt_value *options,
int numoptions);
extern void fillRelOptions(void *rdopts, Size basesize,
relopt_value *options, int numoptions,
bool validate,
const relopt_parse_elt *elems, int nelems);
+extern void *parseAndFillLocalRelOptions(local_relopts *relopts, Datum options,
+ bool validate);
extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index eec71c2..866e94d 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -94,6 +94,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.h b/src/include/catalog/pg_amproc.h
index 62ca564..75cb340 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -67,4 +67,6 @@ CATALOG(pg_amproc,2603,AccessMethodProcedureRelationId)
*/
typedef FormData_pg_amproc *Form_pg_amproc;
+#define OPCLASS_OPTIONS_PROC 0 /* amprocnum for opclass options */
+
#endif /* PG_AMPROC_H */
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 29ae467..568a9c6 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -331,6 +331,8 @@ 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))
+#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)
@@ -699,6 +701,8 @@ 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 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 b593d22..967c818 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 94ded3c..aa266b2 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 23a06d7..a07c44d 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -806,6 +806,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 c8df5bf..2fa9965 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 char *get_collation_name(Oid colloid);
extern bool get_collation_isdeterministic(Oid colloid);
extern char *get_constraint_name(Oid conoid);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 91b3b1b..552d9eb 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -166,6 +166,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 2f2ace3..adc2b26 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"
@@ -49,6 +50,8 @@ extern Oid RelationGetPrimaryKeyIndex(Relation relation);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(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 d34cad2..e295052 100644
--- a/src/include/utils/ruleutils.h
+++ b/src/include/utils/ruleutils.h
@@ -34,6 +34,7 @@ extern List *set_deparse_context_planstate(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 8663f0c..40b3267 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -353,10 +353,11 @@ 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 3
-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 3
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 0 btint42cmp(int4, int2); -- invalid options parsing function
+ERROR: invalid opclass options parsing function
+HINT: opclass options parsing function must have signature '(internal) RETURNS void'
+ALTER OPERATOR FAMILY alt_opf4 USING btree ADD FUNCTION 4 btint42cmp(int4, int2); -- function number should be between 0 and 3
+ERROR: invalid function number 4, must be between 0 and 3
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;
@@ -496,6 +497,19 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
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 (#0) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 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 0 (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 0 (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 0 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 0 (int4, int4);
+ERROR: invalid function number 0, must be between 1 and 3
+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 acab8e0..b4023de 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -262,3 +262,8 @@ 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
+create index on btree_tall_tbl (id default(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 33c058f..1f53a9c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2054,7 +2054,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/input/create_function_1.source b/src/test/regress/input/create_function_1.source
index 223454a..412e339 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 5f43e8d..4d78fa1 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 826556e..3ade2d6 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -940,3 +940,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 ce9dbb1..31ad01c 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -298,8 +298,8 @@ 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 6 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 4 btint42cmp(int4, int2); -- function number should be between 0 and 3
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;
@@ -433,6 +433,15 @@ ALTER OPERATOR FAMILY alt_opf18 USING btree ADD
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 (#0) specifications.
+CREATE OPERATOR FAMILY alt_opf19 USING btree;
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 test_opclass_options_func(internal, text[], bool);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4, int2) btint42cmp(int4, int2);
+ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 0 (int4) test_opclass_options_func(internal); -- Ok
+ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 0 (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 48eaf4f..7290c86 100644
--- a/src/test/regress/sql/btree_index.sql
+++ b/src/test/regress/sql/btree_index.sql
@@ -140,3 +140,7 @@ 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));
+create index on btree_tall_tbl (id default(foo=1));
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 1227ef7..cee6a40 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1306,7 +1306,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
--
2.7.4
0002-Introduce-amattoptions-20190910.patchtext/x-patch; name=0002-Introduce-amattoptions-20190910.patchDownload
From c9acb5bf6033c1d18ff35c58000aa0fd62bdbf73 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 2/5] Introduce amattoptions
---
contrib/bloom/blutils.c | 1 +
src/backend/access/brin/brin.c | 1 +
src/backend/access/gin/ginutil.c | 1 +
src/backend/access/gist/gist.c | 1 +
src/backend/access/hash/hash.c | 1 +
src/backend/access/index/indexam.c | 16 +++++++++++-----
src/backend/access/nbtree/nbtree.c | 1 +
src/backend/access/spgist/spgutils.c | 1 +
src/backend/utils/cache/relcache.c | 3 ++-
src/include/access/amapi.h | 7 +++++++
10 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index cc16709..b62ba7f 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -131,6 +131,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = blcostestimate;
amroutine->amoptions = bloptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = blvalidate;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index ae7b729..e86f1f9 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -111,6 +111,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
amroutine->amoptions = brinoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = brinvalidate;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index cf9699a..1154239 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
amroutine->amoptions = ginoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = ginvalidate;
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 0cc8791..a1f96b1 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -85,6 +85,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
amroutine->amoptions = gistoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = gistproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = gistvalidate;
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 5cc30da..84fb4d6 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -84,6 +84,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
amroutine->amoptions = hashoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = NULL;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = hashvalidate;
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index bbed726..58b0bda 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -940,10 +940,10 @@ index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
bool validate)
{
Oid procid = index_getprocid(indrel, attnum, 0);
- FmgrInfo *procinfo;
local_relopts relopts;
+ amattoptions_function amattoptions = indrel->rd_indam->amattoptions;
- if (!OidIsValid(procid))
+ if (!OidIsValid(procid) && !amattoptions)
{
Oid opclass;
Datum indclassDatum;
@@ -969,11 +969,17 @@ index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
generate_opclass_name(opclass))));
}
- init_local_reloptions(&relopts, NULL, 0);
+ if (amattoptions)
+ amattoptions(&relopts, attnum);
+ else
+ init_local_reloptions(&relopts, NULL, 0);
- procinfo = index_getprocinfo(indrel, attnum, 0);
+ if (OidIsValid(procid))
+ {
+ FmgrInfo *procinfo = index_getprocinfo(indrel, attnum, 0);
- (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+ (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
+ }
return parseAndFillLocalRelOptions(&relopts, attoptions, validate);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 4cfd528..0741107 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -133,6 +133,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amoptions = btoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 45472db..0bf5734 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -66,6 +66,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
amroutine->amoptions = spgoptions;
+ amroutine->amattoptions = NULL;
amroutine->amproperty = spgproperty;
amroutine->ambuildphasename = NULL;
amroutine->amvalidate = spgvalidate;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 7f96935..d36b31b 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5172,7 +5172,8 @@ RelationGetIndexRawAttOptions(Relation indexrel)
for (attnum = 1; attnum <= natts; attnum++)
{
- if (!OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
+ if (!indexrel->rd_indam->amattoptions &&
+ !OidIsValid(index_getprocid(indexrel, attnum, OPCLASS_OPTIONS_PROC)))
continue;
if (!options)
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 6e3db06..4e79e81 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -25,6 +25,8 @@ struct IndexPath;
/* Likewise, this file shouldn't depend on execnodes.h. */
struct IndexInfo;
+struct local_relopts;
+
/*
* Properties for amproperty API. This list covers properties known to the
@@ -103,6 +105,10 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
typedef bytea *(*amoptions_function) (Datum reloptions,
bool validate);
+/* initialize per-attribute reloptions */
+typedef void (*amattoptions_function) (struct local_relopts *options,
+ int attno);
+
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -215,6 +221,7 @@ typedef struct IndexAmRoutine
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
amoptions_function amoptions;
+ amattoptions_function amattoptions; /* can be NULL */
amproperty_function amproperty; /* can be NULL */
ambuildphasename_function ambuildphasename; /* can be NULL */
amvalidate_function amvalidate;
--
2.7.4
0004-Use-opclass-parameters-in-GiST-tsvector_ops-20190910.patchtext/x-patch; name=0004-Use-opclass-parameters-in-GiST-tsvector_ops-20190910.patchDownload
From d0b53a9ab7dbd04206be08c03ae19ded15e7e921 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:39 +0300
Subject: [PATCH 4/5] Use opclass parameters in GiST tsvector_ops
---
doc/src/sgml/textsearch.sgml | 9 +-
src/backend/utils/adt/tsgistidx.c | 266 ++++++++++++++++++----------------
src/include/catalog/pg_amproc.dat | 3 +
src/include/catalog/pg_proc.dat | 3 +
src/test/regress/expected/tsearch.out | 176 ++++++++++++++++++++++
src/test/regress/sql/tsearch.sql | 45 ++++++
6 files changed, 379 insertions(+), 123 deletions(-)
diff --git a/doc/src/sgml/textsearch.sgml b/doc/src/sgml/textsearch.sgml
index 3f00ae3..2e881f7 100644
--- a/doc/src/sgml/textsearch.sgml
+++ b/doc/src/sgml/textsearch.sgml
@@ -3637,7 +3637,7 @@ SELECT plainto_tsquery('supernovae stars');
<tertiary>text search</tertiary>
</indexterm>
- <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable>);</literal>
+ <literal>CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING GIST (<replaceable>column</replaceable> [ { DEFAULT | tsvector_ops } (siglen = <replaceable>number</replaceable>) ] );</literal>
</term>
<listitem>
@@ -3645,6 +3645,8 @@ SELECT plainto_tsquery('supernovae stars');
Creates a GiST (Generalized Search Tree)-based index.
The <replaceable>column</replaceable> can be of <type>tsvector</type> or
<type>tsquery</type> type.
+ Optional integer parameter <literal>siglen</literal> determines
+ signature length in bytes (see below for details).
</para>
</listitem>
</varlistentry>
@@ -3668,7 +3670,10 @@ SELECT plainto_tsquery('supernovae stars');
to check the actual table row to eliminate such false matches.
(<productname>PostgreSQL</productname> does this automatically when needed.)
GiST indexes are lossy because each document is represented in the
- index by a fixed-length signature. The signature is generated by hashing
+ index by a fixed-length signature. Signature length in bytes is determined
+ by the value of the optional integer parameter <literal>siglen</literal>.
+ Default signature length (when <literal>siglen</literal> is not specied) is
+ 124 bytes, maximal length is 484 bytes. The signature is generated by hashing
each word into a single bit in an n-bit string, with all these bits OR-ed
together to produce an n-bit document signature. When two words hash to
the same bit position there will be a false match. If all words in
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 6ff71a4..85582cd 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -16,23 +16,29 @@
#include "access/gist.h"
#include "access/heaptoast.h"
+#include "access/reloptions.h"
#include "port/pg_bitutils.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
#include "utils/pg_crc.h"
-#define SIGLENINT 31 /* >121 => key will toast, so it will not work
- * !!! */
+#define SIGLEN_DEFAULT (31 * 4)
+#define SIGLEN_MAX (121 * 4) /* key will toast, so it will not work !!! */
-#define SIGLEN ( sizeof(int32) * SIGLENINT )
-#define SIGLENBIT (SIGLEN * BITS_PER_BYTE)
+#define SIGLENBIT(siglen) ((siglen) * BITS_PER_BYTE)
+
+/* tsvector_ops opclass options */
+typedef struct GistTsVectorOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int siglen; /* signature length */
+} GistTsVectorOptions;
-typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+#define LOOPBYTE(siglen) \
+ for (i = 0; i < siglen; i++)
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
#define GETBITBYTE(x,i) ( ((char)(x)) >> (i) & 0x01 )
@@ -40,8 +46,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))
@@ -65,13 +71,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)
@@ -102,9 +109,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);
@@ -148,36 +156,49 @@ uniqueint(int32 *a, int32 l)
}
static void
-makesign(BITVECP sign, SignTSVector *a)
+makesign(BITVECP sign, SignTSVector *a, int siglen)
{
int32 k,
len = ARRNELEM(a);
int32 *ptr = GETARR(a);
- MemSet((void *) sign, 0, sizeof(BITVEC));
+ MemSet((void *) sign, 0, siglen);
for (k = 0; k < len; k++)
- HASH(sign, ptr[k]);
+ HASH(sign, ptr[k], siglen);
}
+static SignTSVector *
+gtsvector_alloc(int flag, int len, BITVECP sign)
+{
+ int size = CALCGTSIZE(flag, len);
+ SignTSVector *res = palloc(size);
+
+ SET_VARSIZE(res, size);
+ res->flag = flag;
+
+ if ((flag & (SIGNKEY | ALLISTRUE)) == SIGNKEY && sign)
+ memcpy(GETSIGN(res), sign, len);
+
+ return res;
+}
+
+
Datum
gtsvector_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->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--)
@@ -208,13 +229,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;
}
@@ -226,22 +243,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,
@@ -315,12 +327,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
@@ -347,7 +361,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));
}
@@ -365,7 +379,7 @@ gtsvector_consistent(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, SignTSVector *add)
+unionkey(BITVECP sbase, SignTSVector *add, int siglen)
{
int32 i;
@@ -376,7 +390,9 @@ unionkey(BITVECP sbase, SignTSVector *add)
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ Assert(GETSIGLEN(add) == siglen);
+
+ LOOPBYTE(siglen)
sbase[i] |= sadd[i];
}
else
@@ -384,7 +400,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;
}
@@ -395,30 +411,24 @@ gtsvector_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
- int32 i,
- len;
- int32 flag = 0;
- SignTSVector *result;
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->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);
}
@@ -429,6 +439,7 @@ gtsvector_same(PG_FUNCTION_ARGS)
SignTSVector *a = (SignTSVector *) PG_GETARG_POINTER(0);
SignTSVector *b = (SignTSVector *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
if (ISSIGNKEY(a))
{ /* then b also ISSIGNKEY */
@@ -444,8 +455,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])
{
@@ -482,19 +495,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 */
@@ -506,17 +519,22 @@ hemdistsign(BITVECP a, BITVECP b)
static int
hemdist(SignTSVector *a, SignTSVector *b)
{
+ int siglena = GETSIGLEN(a);
+ int siglenb = GETSIGLEN(b);
+
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return SIGLENBIT(siglenb) - sizebitvec(GETSIGN(b), siglenb);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return SIGLENBIT(siglena) - sizebitvec(GETSIGN(a), siglena);
+
+ Assert(siglena == siglenb);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), siglena);
}
Datum
@@ -525,6 +543,7 @@ gtsvector_penalty(PG_FUNCTION_ARGS)
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); /* always ISSIGNKEY */
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
float *penalty = (float *) PG_GETARG_POINTER(2);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
SignTSVector *origval = (SignTSVector *) DatumGetPointer(origentry->key);
SignTSVector *newval = (SignTSVector *) DatumGetPointer(newentry->key);
BITVECP orig = GETSIGN(origval);
@@ -533,14 +552,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);
@@ -550,19 +577,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) )
@@ -586,19 +613,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
@@ -606,6 +633,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ int siglen = ((GistTsVectorOptions *) PG_GET_OPCLASS_OPTIONS())->siglen;
OffsetNumber k,
j;
SignTSVector *datum_l,
@@ -625,6 +653,7 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
BITVECP ptr;
int i;
CACHESIGN *cache;
+ char *cache_sign;
SPLITCOST *costvector;
maxoff = entryvec->n - 2;
@@ -633,16 +662,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;
@@ -664,44 +699,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);
@@ -727,36 +739,34 @@ gtsvector_picksplit(PG_FUNCTION_ARGS)
if (ISALLTRUE(datum_l) && cache[j].allistrue)
size_alpha = 0;
else
- size_alpha = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign)
- );
+ size_alpha = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_l) : GETSIGN(cache[j].sign), siglen);
}
else
- size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l));
+ size_alpha = hemdistsign(cache[j].sign, GETSIGN(datum_l), siglen);
if (ISALLTRUE(datum_r) || cache[j].allistrue)
{
if (ISALLTRUE(datum_r) && cache[j].allistrue)
size_beta = 0;
else
- size_beta = SIGLENBIT - sizebitvec(
- (cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign)
- );
+ size_beta = SIGLENBIT(siglen) -
+ sizebitvec((cache[j].allistrue) ? GETSIGN(datum_r) : GETSIGN(cache[j].sign), siglen);
}
else
- size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r));
+ size_beta = hemdistsign(cache[j].sign, GETSIGN(datum_r), siglen);
if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.1))
{
if (ISALLTRUE(datum_l) || cache[j].allistrue)
{
if (!ISALLTRUE(datum_l))
- MemSet((void *) GETSIGN(datum_l), 0xff, sizeof(BITVEC));
+ MemSet((void *) GETSIGN(datum_l), 0xff, siglen);
}
else
{
ptr = cache[j].sign;
- LOOPBYTE
+ LOOPBYTE(siglen)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -767,12 +777,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;
@@ -799,3 +809,17 @@ 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);
+ GistTsVectorOptions *options = NULL;
+
+ extend_local_reloptions(relopts, options, sizeof(*options));
+ add_local_int_reloption(relopts, "siglen", "signature length",
+ SIGLEN_DEFAULT, 1, SIGLEN_MAX,
+ &options->siglen);
+
+ PG_RETURN_VOID();
+}
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e70501..53a64ed 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -481,6 +481,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 => '0',
+ 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 e6645f1..ac4440b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8646,6 +8646,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/test/regress/expected/tsearch.out b/src/test/regress/expected/tsearch.out
index 7af2899..ff8ec8e 100644
--- a/src/test/regress/expected/tsearch.out
+++ b/src/test/regress/expected/tsearch.out
@@ -260,6 +260,182 @@ SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
508
(1 row)
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+ERROR: value 0 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+ERROR: value 485 out of bounds for option "siglen"
+DETAIL: Valid values are between "1" and "484".
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+ERROR: unrecognized parameter "foo"
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+ERROR: parameter "siglen" specified more than once
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a)
+ "wowidx2" gist (a tsvector_ops (siglen='1'))
+
+DROP INDEX wowidx;
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx2
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
+DROP INDEX wowidx2;
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+\d test_tsvector
+ Table "public.test_tsvector"
+ Column | Type | Collation | Nullable | Default
+--------+----------+-----------+----------+---------
+ t | text | | |
+ a | tsvector | | |
+Indexes:
+ "wowidx" gist (a tsvector_ops (siglen='484'))
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ QUERY PLAN
+-------------------------------------------------------------
+ Aggregate
+ -> Bitmap Heap Scan on test_tsvector
+ Recheck Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+ -> Bitmap Index Scan on wowidx
+ Index Cond: (a @@ '''wr'' | ''qh'''::tsquery)
+(5 rows)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+ count
+-------
+ 17
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+ count
+-------
+ 6
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+ count
+-------
+ 98
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+ count
+-------
+ 23
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+ count
+-------
+ 39
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+ count
+-------
+ 494
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+ count
+-------
+ 158
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+ count
+-------
+ 0
+(1 row)
+
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+ count
+-------
+ 508
+(1 row)
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
diff --git a/src/test/regress/sql/tsearch.sql b/src/test/regress/sql/tsearch.sql
index ece80b9..1bdbfd8 100644
--- a/src/test/regress/sql/tsearch.sql
+++ b/src/test/regress/sql/tsearch.sql
@@ -87,6 +87,51 @@ SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+-- Test siglen parameter of GiST tsvector_ops
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(foo=1));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=0));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=485));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100,foo='bar'));
+CREATE INDEX wowidx1 ON test_tsvector USING gist (a tsvector_ops(siglen=100, siglen = 200));
+
+CREATE INDEX wowidx2 ON test_tsvector USING gist (a tsvector_ops(siglen=1));
+
+\d test_tsvector
+
+DROP INDEX wowidx;
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
+DROP INDEX wowidx2;
+
+CREATE INDEX wowidx ON test_tsvector USING gist (a DEFAULT(siglen=484));
+
+\d test_tsvector
+
+EXPLAIN (costs off) SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)';
+SELECT count(*) FROM test_tsvector WHERE a @@ 'w:*|q:*';
+SELECT count(*) FROM test_tsvector WHERE a @@ any ('{wr,qh}');
+SELECT count(*) FROM test_tsvector WHERE a @@ 'no_such_lexeme';
+SELECT count(*) FROM test_tsvector WHERE a @@ '!no_such_lexeme';
+
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
--
2.7.4
0005-Remove-pg_index.indoption-20190910.patchtext/x-patch; name=0005-Remove-pg_index.indoption-20190910.patchDownload
From da324f0e2aae09f185ceca3473e8ca8eeae3c317 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 10 Sep 2019 03:35:40 +0300
Subject: [PATCH 5/5] Remove pg_index.indoption
---
doc/src/sgml/catalogs.sgml | 11 -------
src/backend/access/common/reloptions.c | 37 +++++++++++++++++++++
src/backend/access/nbtree/nbtree.c | 3 +-
src/backend/access/nbtree/nbtutils.c | 36 +++++++++++++--------
src/backend/catalog/index.c | 19 ++---------
src/backend/catalog/toasting.c | 6 +---
src/backend/commands/indexcmds.c | 51 ++++++++++++++---------------
src/backend/optimizer/util/plancat.c | 16 ++++++----
src/backend/parser/parse_utilcmd.c | 26 +--------------
src/backend/utils/adt/amutils.c | 45 ++++++++++++++------------
src/backend/utils/adt/ruleutils.c | 53 ++++++++++++++++++-------------
src/backend/utils/cache/relcache.c | 41 +++++-------------------
src/include/access/genam.h | 7 ++++
src/include/access/nbtree.h | 11 +++----
src/include/access/reloptions.h | 3 ++
src/include/catalog/index.h | 1 -
src/include/catalog/pg_index.h | 9 ++----
src/include/utils/rel.h | 1 -
src/test/regress/expected/btree_index.out | 4 +--
src/test/regress/expected/opr_sanity.out | 3 +-
src/test/regress/sql/opr_sanity.sql | 3 +-
21 files changed, 183 insertions(+), 203 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 5e71a2e..5b95131 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3916,17 +3916,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</row>
<row>
- <entry><structfield>indoption</structfield></entry>
- <entry><type>int2vector</type></entry>
- <entry></entry>
- <entry>
- This is an array of <structfield>indnkeyatts</structfield> values that
- store per-column flag bits. The meaning of the bits is defined by
- the index's access method.
- </entry>
- </row>
-
- <row>
<entry><structfield>indexprs</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 3ad1324..5d7e704 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1888,3 +1888,40 @@ AlterTableGetRelOptionsLockLevel(List *defList)
return lockmode;
}
+
+static void
+ordattoptions_validator(void *parsed_options, relopt_value *vals, int nvals)
+{
+ OrderedAttOptions *ordopts = parsed_options;
+
+ for (int i = 0; i < nvals; i++)
+ if (!strcmp(vals[i].gen->name, "nulls_first") &&
+ !vals[i].isset)
+ ordopts->nulls_first = ordopts->desc;
+}
+
+void
+ordered_index_attoptions(local_relopts *relopts, int attno)
+{
+ OrderedAttOptions *attopts = NULL;
+
+ init_local_reloptions(relopts, attopts, sizeof(*attopts));
+ add_local_bool_reloption(relopts, INDOPTION_DESC,
+ "Descending ordering",
+ false, &attopts->desc);
+ add_local_bool_reloption(relopts, INDOPTION_NULLS_FIRST,
+ "NULLs sorted first",
+ false, &attopts->nulls_first);
+ register_reloptions_validator(relopts, ordattoptions_validator);
+}
+
+OrderedAttOptions *
+get_ordered_attoptions(Datum attoptions)
+{
+ local_relopts relopts;
+
+ ordered_index_attoptions(&relopts, 0);
+
+ return (OrderedAttOptions *)
+ parseAndFillLocalRelOptions(&relopts, attoptions, false);
+}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 0741107..d4ed36d 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -20,6 +20,7 @@
#include "access/nbtree.h"
#include "access/nbtxlog.h"
+#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/xlog.h"
#include "commands/progress.h"
@@ -133,7 +134,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
amroutine->amoptions = btoptions;
- amroutine->amattoptions = NULL;
+ amroutine->amattoptions = ordered_index_attoptions;
amroutine->amproperty = btproperty;
amroutine->ambuildphasename = btbuildphasename;
amroutine->amvalidate = btvalidate;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 4c7b2d0..8d093de 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -46,7 +46,7 @@ static int _bt_compare_array_elements(const void *a, const void *b, void *arg);
static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
ScanKey leftarg, ScanKey rightarg,
bool *result);
-static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption);
+static bool _bt_fix_scankey_strategy(ScanKey skey, bytea **attoptions);
static void _bt_mark_scankey_required(ScanKey skey);
static bool _bt_check_rowcompare(ScanKey skey,
IndexTuple tuple, int tupnatts, TupleDesc tupdesc,
@@ -54,6 +54,9 @@ static bool _bt_check_rowcompare(ScanKey skey,
static int _bt_keep_natts(Relation rel, IndexTuple lastleft,
IndexTuple firstright, BTScanInsert itup_key);
+#define _bt_scankey_ordering_flags(ordopts) \
+ ((ordopts->desc ? SK_BT_DESC : 0) | \
+ (ordopts->nulls_first ? SK_BT_NULLS_FIRST : 0))
/*
* _bt_mkscankey
@@ -88,13 +91,13 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
ScanKey skey;
TupleDesc itupdesc;
int indnkeyatts;
- int16 *indoption;
+ bytea **attoptions;
int tupnatts;
int i;
itupdesc = RelationGetDescr(rel);
indnkeyatts = IndexRelationGetNumberOfKeyAttributes(rel);
- indoption = rel->rd_indoption;
+ attoptions = RelationGetIndexAttOptions(rel, false);
tupnatts = itup ? BTreeTupleGetNAtts(itup, rel) : 0;
Assert(tupnatts <= IndexRelationGetNumberOfAttributes(rel));
@@ -117,6 +120,7 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
for (i = 0; i < indnkeyatts; i++)
{
FmgrInfo *procinfo;
+ OrderedAttOptions *ordopts = (OrderedAttOptions *) attoptions[i];
Datum arg;
bool null;
int flags;
@@ -139,7 +143,9 @@ _bt_mkscankey(Relation rel, IndexTuple itup)
arg = (Datum) 0;
null = true;
}
- flags = (null ? SK_ISNULL : 0) | (indoption[i] << SK_BT_INDOPTION_SHIFT);
+
+ flags = (null ? SK_ISNULL : 0) | _bt_scankey_ordering_flags(ordopts);
+
ScanKeyEntryInitializeWithInfo(&skey[i],
flags,
(AttrNumber) (i + 1),
@@ -192,7 +198,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
- int16 *indoption = scan->indexRelation->rd_indoption;
+ bytea **attopts = RelationGetIndexAttOptions(scan->indexRelation, false);
int numArrayKeys;
ScanKey cur;
int i;
@@ -335,7 +341,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan)
* successive primitive indexscans produce data in index order.
*/
num_elems = _bt_sort_array_elements(scan, cur,
- (indoption[cur->sk_attno - 1] & INDOPTION_DESC) != 0,
+ ((OrderedAttOptions *) attopts[cur->sk_attno - 1])->desc,
elem_values, num_nonnulls);
/*
@@ -745,7 +751,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int numberOfKeys = scan->numberOfKeys;
- int16 *indoption = scan->indexRelation->rd_indoption;
+ bytea **attoptions = RelationGetIndexAttOptions(scan->indexRelation, false);
int new_numberOfKeys;
int numberOfEqualCols;
ScanKey inkeys;
@@ -782,7 +788,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
if (numberOfKeys == 1)
{
/* Apply indoption to scankey (might change sk_strategy!) */
- if (!_bt_fix_scankey_strategy(cur, indoption))
+ if (!_bt_fix_scankey_strategy(cur, attoptions))
so->qual_ok = false;
memcpy(outkeys, cur, sizeof(ScanKeyData));
so->numberOfKeys = 1;
@@ -817,7 +823,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
if (i < numberOfKeys)
{
/* Apply indoption to scankey (might change sk_strategy!) */
- if (!_bt_fix_scankey_strategy(cur, indoption))
+ if (!_bt_fix_scankey_strategy(cur, attoptions))
{
/* NULL can't be matched, so give up */
so->qual_ok = false;
@@ -1195,11 +1201,10 @@ _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
* not going to change while the scankey survives.
*/
static bool
-_bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
+_bt_fix_scankey_strategy(ScanKey skey, bytea **attopts)
{
- int addflags;
-
- addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
+ OrderedAttOptions *ordopts = (OrderedAttOptions *) attopts[skey->sk_attno - 1];
+ int addflags = _bt_scankey_ordering_flags(ordopts);
/*
* We treat all btree operators as strict (even if they're not so marked
@@ -1268,7 +1273,10 @@ _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
for (;;)
{
Assert(subkey->sk_flags & SK_ROW_MEMBER);
- addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
+
+ ordopts = (OrderedAttOptions *) attopts[subkey->sk_attno - 1];
+ addflags = _bt_scankey_ordering_flags(ordopts);
+
if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC))
subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy);
subkey->sk_flags |= addflags;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index cb7f0ba..60ec7ab 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -114,7 +114,6 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
IndexInfo *indexInfo,
Oid *collationOids,
Oid *classOids,
- int16 *coloptions,
bool primary,
bool isexclusion,
bool immediate,
@@ -535,7 +534,6 @@ UpdateIndexRelation(Oid indexoid,
IndexInfo *indexInfo,
Oid *collationOids,
Oid *classOids,
- int16 *coloptions,
bool primary,
bool isexclusion,
bool immediate,
@@ -545,7 +543,6 @@ UpdateIndexRelation(Oid indexoid,
int2vector *indkey;
oidvector *indcollation;
oidvector *indclass;
- int2vector *indoption;
Datum exprsDatum;
Datum predDatum;
Datum values[Natts_pg_index];
@@ -555,7 +552,7 @@ UpdateIndexRelation(Oid indexoid,
int i;
/*
- * Copy the index key, opclass, and indoption info into arrays (should we
+ * Copy the index key and opclass info into arrays (should we
* make the caller pass them like this to start with?)
*/
indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs);
@@ -563,7 +560,6 @@ UpdateIndexRelation(Oid indexoid,
indkey->values[i] = indexInfo->ii_IndexAttrNumbers[i];
indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexKeyAttrs);
indclass = buildoidvector(classOids, indexInfo->ii_NumIndexKeyAttrs);
- indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexKeyAttrs);
/*
* Convert the index expressions (if any) to a text datum
@@ -622,7 +618,6 @@ UpdateIndexRelation(Oid indexoid,
values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
- values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
values[Anum_pg_index_indexprs - 1] = exprsDatum;
if (exprsDatum == (Datum) 0)
nulls[Anum_pg_index_indexprs - 1] = true;
@@ -665,7 +660,6 @@ UpdateIndexRelation(Oid indexoid,
* tableSpaceId: OID of tablespace to use
* collationObjectId: array of collation OIDs, one per index column
* classObjectId: array of index opclass OIDs, one per index column
- * coloptions: array of per-index-column indoption settings
* reloptions: AM-specific options
* flags: bitmask that can include any combination of these bits:
* INDEX_CREATE_IS_PRIMARY
@@ -705,7 +699,6 @@ index_create(Relation heapRelation,
Oid tableSpaceId,
Oid *collationObjectId,
Oid *classObjectId,
- int16 *coloptions,
Datum reloptions,
bits16 flags,
bits16 constr_flags,
@@ -950,7 +943,7 @@ index_create(Relation heapRelation,
*/
UpdateIndexRelation(indexRelationId, heapRelationId, parentIndexRelid,
indexInfo,
- collationObjectId, classObjectId, coloptions,
+ collationObjectId, classObjectId,
isprimary, is_exclusion,
(constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) == 0,
!concurrent && !invalid,
@@ -1215,10 +1208,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
HeapTuple indexTuple,
classTuple;
Datum indclassDatum,
- colOptionDatum,
optionDatum;
oidvector *indclass;
- int2vector *indcoloptions;
bool isnull;
List *indexColNames = NIL;
List *indexExprs = NIL;
@@ -1247,11 +1238,6 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
Assert(!isnull);
indclass = (oidvector *) DatumGetPointer(indclassDatum);
- colOptionDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
- Anum_pg_index_indoption, &isnull);
- Assert(!isnull);
- indcoloptions = (int2vector *) DatumGetPointer(colOptionDatum);
-
/* Fetch options of index if any */
classTuple = SearchSysCache1(RELOID, oldIndexId);
if (!HeapTupleIsValid(classTuple))
@@ -1339,7 +1325,6 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char
indexRelation->rd_rel->reltablespace,
indexRelation->rd_indcollation,
indclass->values,
- indcoloptions->values,
optionDatum,
INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT,
0,
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 7290731..5361955 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -142,7 +142,6 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
IndexInfo *indexInfo;
Oid collationObjectId[2];
Oid classObjectId[2];
- int16 coloptions[2];
ObjectAddress baseobject,
toastobject;
@@ -320,16 +319,13 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
classObjectId[0] = OID_BTREE_OPS_OID;
classObjectId[1] = INT4_BTREE_OPS_OID;
- coloptions[0] = 0;
- coloptions[1] = 0;
-
index_create(toast_rel, toast_idxname, toastIndexOid, InvalidOid,
InvalidOid, InvalidOid,
indexInfo,
list_make2("chunk_id", "chunk_seq"),
BTREE_AM_OID,
rel->rd_rel->reltablespace,
- collationObjectId, classObjectId, coloptions, (Datum) 0,
+ collationObjectId, classObjectId, (Datum) 0,
INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL);
table_close(toast_rel, NoLock);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 04148e5..1e08100 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -73,7 +73,6 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *typeOidP,
Oid *collationOidP,
Oid *classOidP,
- int16 *colOptionP,
List *attList,
List *exclusionOpNames,
Oid relId,
@@ -155,7 +154,6 @@ CheckIndexCompatible(Oid oldId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- int16 *coloptions;
IndexInfo *indexInfo;
int numberOfAttributes;
int old_natts;
@@ -208,11 +206,9 @@ CheckIndexCompatible(Oid oldId,
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
- coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
ComputeIndexAttrs(indexInfo,
typeObjectId, collationObjectId, classObjectId,
- coloptions, attributeList,
- exclusionOpNames, relationId,
+ attributeList, exclusionOpNames, relationId,
accessMethodName, accessMethodId,
amcanorder, isconstraint);
@@ -504,7 +500,6 @@ DefineIndex(Oid relationId,
amoptions_function amoptions;
bool partitioned;
Datum reloptions;
- int16 *coloptions;
IndexInfo *indexInfo;
bits16 flags;
bits16 constr_flags;
@@ -834,11 +829,9 @@ DefineIndex(Oid relationId,
typeObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
- coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
ComputeIndexAttrs(indexInfo,
typeObjectId, collationObjectId, classObjectId,
- coloptions, allIndexParams,
- stmt->excludeOpNames, relationId,
+ allIndexParams, stmt->excludeOpNames, relationId,
accessMethodName, accessMethodId,
amcanorder, stmt->isconstraint);
@@ -1036,7 +1029,7 @@ DefineIndex(Oid relationId,
stmt->oldNode, indexInfo, indexColNames,
accessMethodId, tablespaceId,
collationObjectId, classObjectId,
- coloptions, reloptions,
+ reloptions,
flags, constr_flags,
allowSystemTableMods, !check_rights,
&createdConstraintId);
@@ -1566,7 +1559,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *typeOidP,
Oid *collationOidP,
Oid *classOidP,
- int16 *colOptionP,
List *attList, /* list of IndexElem's */
List *exclusionOpNames,
Oid relId,
@@ -1714,7 +1706,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
errmsg("including column does not support NULLS FIRST/LAST options")));
classOidP[attn] = InvalidOid;
- colOptionP[attn] = 0;
collationOidP[attn] = InvalidOid;
attn++;
@@ -1829,22 +1820,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
* zero for any un-ordered index, while ordered indexes have DESC and
* NULLS FIRST/LAST options.
*/
- colOptionP[attn] = 0;
- if (amcanorder)
- {
- /* default ordering is ASC */
- if (attribute->ordering == SORTBY_DESC)
- colOptionP[attn] |= INDOPTION_DESC;
- /* default null ordering is LAST for ASC, FIRST for DESC */
- if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
- {
- if (attribute->ordering == SORTBY_DESC)
- colOptionP[attn] |= INDOPTION_NULLS_FIRST;
- }
- else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
- colOptionP[attn] |= INDOPTION_NULLS_FIRST;
- }
- else
+ if (!amcanorder)
{
/* index AM does not support ordering */
if (attribute->ordering != SORTBY_DEFAULT)
@@ -1859,6 +1835,25 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName)));
}
+ if (attribute->ordering == SORTBY_DESC)
+ {
+ attribute->opclassopts =
+ lappend(attribute->opclassopts,
+ makeDefElem(INDOPTION_DESC, NULL, -1));
+
+ if (attribute->nulls_ordering == SORTBY_NULLS_LAST)
+ attribute->opclassopts =
+ lappend(attribute->opclassopts,
+ makeDefElem(INDOPTION_NULLS_FIRST,
+ (Node *) makeString("false"), -1));
+ }
+ else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
+ {
+ attribute->opclassopts =
+ lappend(attribute->opclassopts,
+ makeDefElem(INDOPTION_NULLS_FIRST, NULL, -1));
+ }
+
/* Set up the per-column opclass options (attoptions field). */
if (attribute->opclassopts)
{
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index de9b6f4..6845079 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -299,10 +299,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
for (i = 0; i < nkeycolumns; i++)
{
- int16 opt = indexRelation->rd_indoption[i];
+ OrderedAttOptions *opt = (OrderedAttOptions *) info->opclassoptions[i];
- info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
- info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
+ Assert(opt);
+
+ info->reverse_sort[i] = opt->desc;
+ info->nulls_first[i] = opt->nulls_first;
}
}
else if (amroutine->amcanorder)
@@ -327,14 +329,16 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
for (i = 0; i < nkeycolumns; i++)
{
- int16 opt = indexRelation->rd_indoption[i];
+ OrderedAttOptions *opt = (OrderedAttOptions *) info->opclassoptions[i];
Oid ltopr;
Oid btopfamily;
Oid btopcintype;
int16 btstrategy;
- info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
- info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
+ Assert(opt);
+
+ info->reverse_sort[i] = opt->desc;
+ info->nulls_first[i] = opt->nulls_first;
ltopr = get_opfamily_member(info->opfamily[i],
info->opcintype[i],
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 5872376..8badc74 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1523,7 +1523,6 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
AttrNumber attnum = idxrec->indkey.values[keyno];
Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(source_idx),
keyno);
- int16 opt = source_idx->rd_indoption[keyno];
iparam = makeNode(IndexElem);
@@ -1583,28 +1582,6 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
- /* Adjust options if necessary */
- if (source_idx->rd_indam->amcanorder)
- {
- /*
- * If it supports sort ordering, copy DESC and NULLS opts. Don't
- * set non-default settings unnecessarily, though, so as to
- * improve the chance of recognizing equivalence to constraint
- * indexes.
- */
- if (opt & INDOPTION_DESC)
- {
- iparam->ordering = SORTBY_DESC;
- if ((opt & INDOPTION_NULLS_FIRST) == 0)
- iparam->nulls_ordering = SORTBY_NULLS_LAST;
- }
- else
- {
- if (opt & INDOPTION_NULLS_FIRST)
- iparam->nulls_ordering = SORTBY_NULLS_FIRST;
- }
- }
-
index->indexParams = lappend(index->indexParams, iparam);
}
@@ -2161,8 +2138,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
defopclass = GetDefaultOpClass(attform->atttypid,
index_rel->rd_rel->relam);
if (indclass->values[i] != defopclass ||
- attoptions != (Datum) 0 ||
- index_rel->rd_indoption[i] != 0)
+ attoptions != (Datum) 0)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("index \"%s\" column number %d does not have default sorting behavior", index_name, i + 1),
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index e81d6cc..932f60c 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -15,9 +15,11 @@
#include "access/amapi.h"
#include "access/htup_details.h"
+#include "access/reloptions.h"
#include "catalog/pg_class.h"
#include "catalog/pg_index.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -114,14 +116,11 @@ lookup_prop_name(const char *name)
* otherwise sets *res to the boolean value to return.
*/
static bool
-test_indoption(HeapTuple tuple, int attno, bool guard,
- int16 iopt_mask, int16 iopt_expect,
- bool *res)
+test_indoption(Oid indexrelid, int attno, bool guard,
+ int offset, bool expect, bool *res)
{
- Datum datum;
- bool isnull;
- int2vector *indoption;
- int16 indoption_val;
+ OrderedAttOptions *ordopts;
+ Datum relopts;
if (!guard)
{
@@ -129,14 +128,14 @@ test_indoption(HeapTuple tuple, int attno, bool guard,
return true;
}
- datum = SysCacheGetAttr(INDEXRELID, tuple,
- Anum_pg_index_indoption, &isnull);
- Assert(!isnull);
+ relopts = get_attoptions(indexrelid, attno);
+ ordopts = get_ordered_attoptions(relopts);
- indoption = ((int2vector *) DatumGetPointer(datum));
- indoption_val = indoption->values[attno - 1];
+ *res = *((bool *)((char *) ordopts + offset)) == expect;
- *res = (indoption_val & iopt_mask) == iopt_expect;
+ pfree(ordopts);
+ if (relopts != (Datum) 0)
+ pfree(DatumGetPointer(relopts));
return true;
}
@@ -249,29 +248,33 @@ indexam_property(FunctionCallInfo fcinfo,
{
case AMPROP_ASC:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_DESC, 0, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, desc), false,
+ &res))
isnull = false;
break;
case AMPROP_DESC:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_DESC, INDOPTION_DESC, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, desc), true,
+ &res))
isnull = false;
break;
case AMPROP_NULLS_FIRST:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_NULLS_FIRST, INDOPTION_NULLS_FIRST, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, nulls_first),
+ true, &res))
isnull = false;
break;
case AMPROP_NULLS_LAST:
if (iskey &&
- test_indoption(tuple, attno, routine->amcanorder,
- INDOPTION_NULLS_FIRST, 0, &res))
+ test_indoption(index_oid, attno, routine->amcanorder,
+ offsetof(OrderedAttOptions, nulls_first),
+ false, &res))
isnull = false;
break;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index faecf04..a649e4b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -22,6 +22,7 @@
#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/relation.h"
+#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/dependency.h"
@@ -469,7 +470,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);
+static int get_reloptions(StringInfo buf, Datum reloptions, bool skip_ord_opts);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -1194,11 +1195,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
int keyno;
Datum indcollDatum;
Datum indclassDatum;
- Datum indoptionDatum;
bool isnull;
oidvector *indcollation;
oidvector *indclass;
- int2vector *indoption;
StringInfoData buf;
char *str;
char *sep;
@@ -1218,7 +1217,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
indrelid = idxrec->indrelid;
Assert(indexrelid == idxrec->indexrelid);
- /* Must get indcollation, indclass, and indoption the hard way */
+ /* Must get indcollation and indclass the hard way */
indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indcollation, &isnull);
Assert(!isnull);
@@ -1229,11 +1228,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Assert(!isnull);
indclass = (oidvector *) DatumGetPointer(indclassDatum);
- indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
- Anum_pg_index_indoption, &isnull);
- Assert(!isnull);
- indoption = (int2vector *) DatumGetPointer(indoptionDatum);
-
/*
* Fetch the pg_class tuple of the index relation
*/
@@ -1370,7 +1364,6 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
if (!attrsOnly && keyno < idxrec->indnkeyatts &&
(!colno || colno == keyno + 1))
{
- int16 opt = indoption->values[keyno];
Oid indcoll = indcollation->values[keyno];
Datum attoptions = get_attoptions(indexrelid, keyno + 1);
bool has_options = attoptions != (Datum) 0;
@@ -1384,27 +1377,32 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
get_opclass_name(indclass->values[keyno],
has_options ? InvalidOid : keycoltype, &buf);
- if (has_options)
+ if (has_options) /* FIXME default opclass */
{
appendStringInfoString(&buf, " (");
- get_reloptions(&buf, attoptions);
- appendStringInfoChar(&buf, ')');
+ if (get_reloptions(&buf, attoptions, amroutine->amcanorder) > 0)
+ appendStringInfoChar(&buf, ')');
+ else
+ buf.len -= 2;
}
/* Add options if relevant */
- if (amroutine->amcanorder)
+ if (amroutine->amcanorder && has_options)
{
+ OrderedAttOptions *ordopts =
+ get_ordered_attoptions(attoptions);
+
/* if it supports sort ordering, report DESC and NULLS opts */
- if (opt & INDOPTION_DESC)
+ if (ordopts->desc)
{
appendStringInfoString(&buf, " DESC");
/* NULLS FIRST is the default in this case */
- if (!(opt & INDOPTION_NULLS_FIRST))
+ if (!ordopts->nulls_first)
appendStringInfoString(&buf, " NULLS LAST");
}
else
{
- if (opt & INDOPTION_NULLS_FIRST)
+ if (ordopts->nulls_first)
appendStringInfoString(&buf, " NULLS FIRST");
}
}
@@ -11213,18 +11211,19 @@ string_to_text(char *str)
/*
* Generate a C string representing a relation options from text[] datum.
*/
-static void
-get_reloptions(StringInfo buf, Datum reloptions)
+static int
+get_reloptions(StringInfo buf, Datum reloptions, bool skip_ord_opts)
{
Datum *options;
int noptions;
int i;
+ int j;
deconstruct_array(DatumGetArrayTypeP(reloptions),
TEXTOID, -1, false, 'i',
&options, NULL, &noptions);
- for (i = 0; i < noptions; i++)
+ for (i = j = 0; i < noptions; i++)
{
char *option = TextDatumGetCString(options[i]);
char *name;
@@ -11245,7 +11244,15 @@ get_reloptions(StringInfo buf, Datum reloptions)
else
value = "";
- if (i > 0)
+ if (skip_ord_opts &&
+ (!strcmp(name, INDOPTION_DESC) ||
+ !strcmp(name, INDOPTION_NULLS_FIRST)))
+ {
+ pfree(option);
+ continue;
+ }
+
+ if (j++ > 0)
appendStringInfoString(buf, ", ");
appendStringInfo(buf, "%s=", quote_identifier(name));
@@ -11264,6 +11271,8 @@ get_reloptions(StringInfo buf, Datum reloptions)
pfree(option);
}
+
+ return j;
}
/*
@@ -11288,7 +11297,7 @@ flatten_reloptions(Oid relid)
StringInfoData buf;
initStringInfo(&buf);
- get_reloptions(&buf, reloptions);
+ get_reloptions(&buf, reloptions, false);
result = buf.data;
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d36b31b..ee32087 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1365,11 +1365,9 @@ RelationInitIndexAccessInfo(Relation relation)
Form_pg_am aform;
Datum indcollDatum;
Datum indclassDatum;
- Datum indoptionDatum;
bool isnull;
oidvector *indcoll;
oidvector *indclass;
- int2vector *indoption;
MemoryContext indexcxt;
MemoryContext oldcontext;
int indnatts;
@@ -1454,9 +1452,6 @@ RelationInitIndexAccessInfo(Relation relation)
relation->rd_indcollation = (Oid *)
MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(Oid));
- relation->rd_indoption = (int16 *)
- MemoryContextAllocZero(indexcxt, indnkeyatts * sizeof(int16));
-
/*
* indcollation cannot be referenced directly through the C struct,
* because it comes after the variable-width indkey field. Must extract
@@ -1491,17 +1486,6 @@ RelationInitIndexAccessInfo(Relation relation)
relation->rd_opfamily, relation->rd_opcintype,
amsupport, indnkeyatts);
- /*
- * Similarly extract indoption and copy it to the cache entry
- */
- indoptionDatum = fastgetattr(relation->rd_indextuple,
- Anum_pg_index_indoption,
- GetPgIndexDescriptor(),
- &isnull);
- Assert(!isnull);
- indoption = (int2vector *) DatumGetPointer(indoptionDatum);
- memcpy(relation->rd_indoption, indoption->values, indnkeyatts * sizeof(int16));
-
#if 0
(void) RelationGetIndexAttOptions(relation, false);
#endif
@@ -5232,6 +5216,14 @@ RelationGetIndexAttOptions(Relation relation, bool copy)
if (attoptions != (Datum) 0)
pfree(DatumGetPointer(attoptions));
}
+ else
+ {
+ OrderedAttOptions *opt = palloc0(sizeof(*opt));
+
+ SET_VARSIZE(opt, sizeof(*opt));
+
+ opts[i] = (bytea *) opt;
+ }
}
/* Copy parsed options to the cache. */
@@ -5527,7 +5519,6 @@ load_relcache_init_file(bool shared)
Oid *opcintype;
RegProcedure *support;
int nsupport;
- int16 *indoption;
Oid *indcollation;
/* Count nailed indexes to ensure we have 'em all */
@@ -5604,16 +5595,6 @@ load_relcache_init_file(bool shared)
rel->rd_indcollation = indcollation;
- /* finally, read the vector of indoption values */
- if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
- goto read_failed;
-
- indoption = (int16 *) MemoryContextAlloc(indexcxt, len);
- if (fread(indoption, 1, len, fp) != len)
- goto read_failed;
-
- rel->rd_indoption = indoption;
-
#if 0
/* finally, read the vector of opcoptions values */
rel->rd_opcoptions = (bytea **)
@@ -5659,7 +5640,6 @@ load_relcache_init_file(bool shared)
Assert(rel->rd_opcintype == NULL);
Assert(rel->rd_support == NULL);
Assert(rel->rd_supportinfo == NULL);
- Assert(rel->rd_indoption == NULL);
Assert(rel->rd_indcollation == NULL);
Assert(rel->rd_opcoptions == NULL);
}
@@ -5944,11 +5924,6 @@ write_relcache_init_file(bool shared)
relform->relnatts * sizeof(Oid),
fp);
- /* finally, write the vector of indoption values */
- write_item(rel->rd_indoption,
- relform->relnatts * sizeof(int16),
- fp);
-
#if 0
Assert(rel->rd_opcoptions);
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index 778d17c..322577d 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -117,6 +117,13 @@ typedef enum IndexUniqueCheck
UNIQUE_CHECK_EXISTING /* Check if existing tuple is unique */
} IndexUniqueCheck;
+/* Common attribute options for ordered indexes */
+typedef struct OrderedAttOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ bool desc; /* DESCending ordering */
+ bool nulls_first; /* NULLS FIRST/LAST */
+} OrderedAttOptions;
/*
* generalized index_ interface routines (in indexam.c)
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 52eafe6..6901d6e 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -672,13 +672,12 @@ typedef BTScanOpaqueData *BTScanOpaque;
/*
* We use some private sk_flags bits in preprocessed scan keys. We're allowed
* to use bits 16-31 (see skey.h). The uppermost bits are copied from the
- * index's indoption[] array entry for the index attribute.
+ * index's attoptions for the index attribute.
*/
-#define SK_BT_REQFWD 0x00010000 /* required to continue forward scan */
-#define SK_BT_REQBKWD 0x00020000 /* required to continue backward scan */
-#define SK_BT_INDOPTION_SHIFT 24 /* must clear the above bits */
-#define SK_BT_DESC (INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
-#define SK_BT_NULLS_FIRST (INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
+#define SK_BT_REQFWD 0x00010000 /* required to continue forward scan */
+#define SK_BT_REQBKWD 0x00020000 /* required to continue backward scan */
+#define SK_BT_DESC 0x00040000
+#define SK_BT_NULLS_FIRST 0x00080000
/*
* Constant definition for progress reporting. Phase numbers must match
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 70238f9..5592da5 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -316,4 +316,7 @@ extern bytea *attribute_reloptions(Datum reloptions, bool validate);
extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+extern void ordered_index_attoptions(local_relopts *relopts, int attno);
+extern OrderedAttOptions *get_ordered_attoptions(Datum attoptions);
+
#endif /* RELOPTIONS_H */
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 1113d25..b306882 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -64,7 +64,6 @@ extern Oid index_create(Relation heapRelation,
Oid tableSpaceId,
Oid *collationObjectId,
Oid *classObjectId,
- int16 *coloptions,
Datum reloptions,
bits16 flags,
bits16 constr_flags,
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 2438374..28cd920 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -49,7 +49,6 @@ CATALOG(pg_index,2610,IndexRelationId) BKI_SCHEMA_MACRO
#ifdef CATALOG_VARLEN
oidvector indcollation; /* collation identifiers */
oidvector indclass; /* opclass identifiers */
- int2vector indoption; /* per-column flags (AM-specific meanings) */
pg_node_tree indexprs; /* expression trees for index attributes that
* are not simple column references; one for
* each zero entry in indkey[] */
@@ -68,12 +67,10 @@ typedef FormData_pg_index *Form_pg_index;
#ifdef EXPOSE_TO_CLIENT_CODE
/*
- * Index AMs that support ordered scans must support these two indoption
- * bits. Otherwise, the content of the per-column indoption fields is
- * open for future definition.
+ * Index AMs that support ordered scans must support these two reloptions.
*/
-#define INDOPTION_DESC 0x0001 /* values are in reverse order */
-#define INDOPTION_NULLS_FIRST 0x0002 /* NULLs are first instead of last */
+#define INDOPTION_DESC "desc" /* values are in reverse order */
+#define INDOPTION_NULLS_FIRST "nulls_first" /* NULLs are first instead of last */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 552d9eb..54b3642 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -159,7 +159,6 @@ typedef struct RelationData
Oid *rd_opcintype; /* OIDs of opclass declared input data types */
RegProcedure *rd_support; /* OIDs of support procedures */
struct FmgrInfo *rd_supportinfo; /* lookup info for support procedures */
- int16 *rd_indoption; /* per-column AM-specific flags */
List *rd_indexprs; /* index expression trees, if any */
List *rd_indpred; /* index predicate tree, if any */
Oid *rd_exclops; /* OIDs of exclusion operators, if any */
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index b4023de..d80d9cd 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -264,6 +264,6 @@ VACUUM delete_test_table;
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
+ERROR: unrecognized parameter "foo"
create index on btree_tall_tbl (id default(foo=1));
-ERROR: operator class int4_ops has no options
+ERROR: unrecognized parameter "foo"
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1f53a9c..a8329b6 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -2096,8 +2096,7 @@ SELECT p1.indexrelid, p1.indrelid
FROM pg_index as p1
WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
- array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
- array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
+ array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1;
indexrelid | indrelid
------------+----------
(0 rows)
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index cee6a40..7dd42a3 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1341,8 +1341,7 @@ SELECT p1.indexrelid, p1.indrelid
FROM pg_index as p1
WHERE array_lower(indkey, 1) != 0 OR array_upper(indkey, 1) != indnatts-1 OR
array_lower(indclass, 1) != 0 OR array_upper(indclass, 1) != indnatts-1 OR
- array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1 OR
- array_lower(indoption, 1) != 0 OR array_upper(indoption, 1) != indnatts-1;
+ array_lower(indcollation, 1) != 0 OR array_upper(indcollation, 1) != indnatts-1;
-- Check that opclasses and collations match the underlying columns.
-- (As written, this test ignores expression indexes.)
--
2.7.4
On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:
On 04.09.2019 1:02, Alvaro Herrera wrote:
On 2019-Jun-11, Tomas Vondra wrote:
1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.API was simplified in the new version of the patches (see below).
2) The 0001 part does this in index_opclass_options_generic:
get_opclass_name(opclass, InvalidOid, &str);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.I decided to add new function generate_opclass_name() like existing
generate_collation_name(), and to reuse static get_opclass_name().Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.New version of rebased patches is attached:
1. Opclass parameters infrastructure.
API was completely refactored since the previous version:
- API was generalized for all AMs. Previously, each AM should implement
opclass options parsing/passing in its own way using its own support
�function numbers.
Now each AMs uses 0th support function (discussable). Binary bytea values
of parsed options are passed to support functions using special expression
node initialized in FmgrInfo.fn_expr (see macro PG_GET_OPCLASS_OPTIONS(),
get_fn_opclass_options(), set_fn_opclass_options).
I very much doubt these changes are in the right direction. Firstly, using
0 as procnum is weird - AFAICS you picked 0 because after moving it from
individual AMs to pg_amproc.h it's hard to guarantee the procnum does not
clash with other am procs. But ISTM that's more a hint that the move to
pg_amproc.h itself was a bad idea. I suggest we undo that move, instead of
trying to fix the symptoms. That is, each AM should have a custom procnum.
Also, looking at fn_expr definition in FmgrInfo, I see this
fmNodePtr fn_expr; /* expression parse tree for call, or NULL */
it seems like a rather bad idea to reuse that to pass options when it's
clearly not meant for that purpose.
- Introduced new structure local_relopts (needs a better name, of course)
with a set of functions for opclass/AM options definition. The parsing
was moved into index_opclass_option(). That was necessary for mixing of
�AM- and opclass-specific options. Opclasses now extend the structure with
AM's options adding their own options. See patch #4 for an example.
OK. No opinion on this change yet.
2. New AM method amattoptions().
amattoptions() is used to specify per-column AM-specific options.
The example is signature length for bloom indexes (patch #3).
I'm somewhat confused how am I supposed to use this, considering the patch
set only defines this for the contrib/bloom index AM. So let's say I want
to create a custom BRIN opclass with per-attribute options (like the two
BRIN opclasses I work on in the other thread). Clearly, I can't tweak the
IndexAmRoutine from the extension. ISTM the patch series should modify all
existing index AMs to have a valid amattoptions() implementation, calling
the new amproc if defined.
Or what is the correct way to define custom opclass for existing index AM
(e.g. BRIN) with attribute options?
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Wed, Sep 11, 2019 at 12:03:58AM +0200, Tomas Vondra wrote:
On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:
On 04.09.2019 1:02, Alvaro Herrera wrote:
On 2019-Jun-11, Tomas Vondra wrote:
1) We need a better infrastructure to parse opclass parameters. For
example the gtsvector_options does this:I think this is part of what Nikolay's patch series was supposed to
address. But that one has been going way too slow. I agree we need
something better.API was simplified in the new version of the patches (see below).
2) The 0001 part does this in index_opclass_options_generic:
get_opclass_name(opclass, InvalidOid, &str);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("operator class \"%s\" has no options",
opclassname.data)));But that's a bit broken, because get_opclass_name() appends the opclass
name to 'str', but with a space at the beginning.Yeah, I think just exporting get_opclass_name from ruleutils.c is a bad
idea. Sounds like we need a (very small) new function in lsyscache.c
that does the job of extracting the opclass name, and then the ruleutils
function can call that one to avoid duplicated code.I decided to add new function generate_opclass_name() like existing
generate_collation_name(), and to reuse static get_opclass_name().Anyway, this patchset doesn't apply anymore. Somebody (maybe its
author this time?) please rebase.New version of rebased patches is attached:
1. Opclass parameters infrastructure.
API was completely refactored since the previous version:
- API was generalized for all AMs. Previously, each AM should implement
opclass options parsing/passing in its own way using its own support
�function numbers.
Now each AMs uses 0th support function (discussable). Binary bytea values
of parsed options are passed to support functions using special expression
node initialized in FmgrInfo.fn_expr (see macro PG_GET_OPCLASS_OPTIONS(),
get_fn_opclass_options(), set_fn_opclass_options).I very much doubt these changes are in the right direction. Firstly, using
0 as procnum is weird - AFAICS you picked 0 because after moving it from
individual AMs to pg_amproc.h it's hard to guarantee the procnum does not
clash with other am procs. But ISTM that's more a hint that the move to
pg_amproc.h itself was a bad idea. I suggest we undo that move, instead of
trying to fix the symptoms. That is, each AM should have a custom procnum.Also, looking at fn_expr definition in FmgrInfo, I see this
fmNodePtr fn_expr; /* expression parse tree for call, or NULL */
it seems like a rather bad idea to reuse that to pass options when it's
clearly not meant for that purpose.
BTW, is there a place where we actually verify the signature of the new am
proc? Because I only see code like this:
+ case OPCLASS_OPTIONS_PROC:
+ ok = true;
+ break;
in all "validate" functions.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 11.09.2019 1:14, Tomas Vondra wrote:
BTW, is there a place where we actually verify the signature of the
new am
proc? Because I only see code like this:+ case OPCLASS_OPTIONS_PROC: + ok = true; + break;in all "validate" functions.
See assignProcTypes() at src/backend/commands/opclasscmds.c
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On 11.09.2019 1:03, Tomas Vondra wrote:
On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:
2. New AM method amattoptions().
amattoptions() is used to specify per-column AM-specific options.
The example is signature length for bloom indexes (patch #3).I'm somewhat confused how am I supposed to use this, considering the
patch
set only defines this for the contrib/bloom index AM. So let's say I want
to create a custom BRIN opclass with per-attribute options (like the two
BRIN opclasses I work on in the other thread). Clearly, I can't tweak the
IndexAmRoutine from the extension. ISTM the patch series should modify
all
existing index AMs to have a valid amattoptions() implementation, calling
the new amproc if defined.Or what is the correct way to define custom opclass for existing index AM
(e.g. BRIN) with attribute options?
Per-attribute opclass options are implemented independently from per-attribute
AM options. amattoptions() is optional and needs to be defined only if AM has
per-attribute options. amproc #0 is called regardless of whether amattoptions
is defined or not. That was the main reason why uniform procnum 0 was picked.
You should simply define function like that and use it as amproc #0:
Datum
brin_bloom_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
BloomOptions *blopts = NULL;
extend_local_reloptions(relopts, blopts, sizeof(*blopts));
add_local_real_reloption(relopts, "n_distinct_per_range", "desc",
-0.1, -1.0, INT_MAX, &blopts->nDistinctPerRange);
add_local_real_reloption(relopts, "false_positive_rate", "desc",
0.01, 0.001, 1.0, &blopts->falsePositiveRate);
PG_RETURN_VOID();
}
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On Wed, Sep 11, 2019 at 01:44:28AM +0300, Nikita Glukhov wrote:
On 11.09.2019 1:03, Tomas Vondra wrote:
On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:
2. New AM method amattoptions().
� amattoptions() is used to specify per-column AM-specific options.
� The example is signature length for bloom indexes (patch #3).I'm somewhat confused how am I supposed to use this, considering the
patch
set only defines this for the contrib/bloom index AM. So let's say I want
to create a custom BRIN opclass with per-attribute options (like the two
BRIN opclasses I work on in the other thread). Clearly, I can't tweak the
IndexAmRoutine from the extension. ISTM the patch series should
modify all
existing index AMs to have a valid amattoptions() implementation, calling
the new amproc if defined.Or what is the correct way to define custom opclass for existing index AM
(e.g. BRIN) with attribute options?Per-attribute opclass options are implemented independently from per-attribute
AM options. amattoptions() is optional and needs to be defined only if AM has
per-attribute options.
OK, thanks for the explanation - so the per-attribute opclass options will
work even when the AM does not have amattoptions() defined. Is there any
practical reason why not to just define everything as opclass options and
get rid of the amattoptions() entirely?
amproc #0 is called regardless of whether amattoptions
is defined or not. That was the main reason why uniform procnum 0 was
picked.
I still think using procnum 0 and passing the data through fn_expr are not
the right solution. Firstly, traditionally the amprocs are either required
or optional, with required procs having low procnums and optional starting
at 11 or so. The 0 breaks this, because it's optional but it contradicts
the procnum rule. Also, what happens if we need to add another optional
amproc defined for all AMs? Surely we won't use -1.
IMHO we should keep AM-specific procnum and pass it somehow to the AM
machinery.
FWIW there seems to be a bug in identify_opfamily_groups() which does
this:
/* Ignore strategy numbers outside supported range */
if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;
but then identify_opfamily_groups() computes allfuncs without any such
restriction, i.e. it includes procnum 0. And then it fails on this check
if (thisgroup->functionset != allfuncs) {...}
None of the built-in brin opclasses defines the new amproc, so the code
does not hit this issue. I only noticed this with the opclasses added in
the other thread.
As for the fn_expr, I still think this seems like a misuse of a field
which was intended for something else. I wonder if it might be breaking
some exising code - either in code or in some extension. It seems quite
possible.
It just seems like we're inventing a new way to novel way to pass data
into a function while we already have parameters for that purpose. Adding
parameters may require care so as not to break existing opclasses, but it
seems like the right approach.
You should simply define function like that and use it as amproc #0:
Datum
brin_bloom_options(PG_FUNCTION_ARGS)
{
local_relopts *relopts = (local_relopts *) PG_GETARG_POINTER(0);
BloomOptions *blopts = NULL;extend_local_reloptions(relopts, blopts, sizeof(*blopts));
add_local_real_reloption(relopts, "n_distinct_per_range", "desc",
-0.1, -1.0, INT_MAX, &blopts->nDistinctPerRange);add_local_real_reloption(relopts, "false_positive_rate", "desc",
0.01, 0.001, 1.0, &blopts->falsePositiveRate);PG_RETURN_VOID();
}
OK, this did the trick. Thanks. I don't have a clear opinion on the API,
but it certainly looks like an improvement.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Thu, Sep 12, 2019 at 02:16:34AM +0200, Tomas Vondra wrote:
I still think using procnum 0 and passing the data through fn_expr are not
the right solution. Firstly, traditionally the amprocs are either required
or optional, with required procs having low procnums and optional starting
at 11 or so. The 0 breaks this, because it's optional but it contradicts
the procnum rule. Also, what happens if we need to add another optional
amproc defined for all AMs? Surely we won't use -1.IMHO we should keep AM-specific procnum and pass it somehow to the AM
machinery.
The latest review has not been addressed, and this was 7 weeks ago.
So I am marking the patch as returned with feedback.
--
Michael
Attached new version of the patches.
On 12.09.2019 3:16, Tomas Vondra wrote:
On Wed, Sep 11, 2019 at 01:44:28AM +0300, Nikita Glukhov wrote:
On 11.09.2019 1:03, Tomas Vondra wrote:
On Tue, Sep 10, 2019 at 04:30:41AM +0300, Nikita Glukhov wrote:
2. New AM method amattoptions().
amattoptions() is used to specify per-column AM-specific options.
The example is signature length for bloom indexes (patch #3).I'm somewhat confused how am I supposed to use this, considering the
patch
set only defines this for the contrib/bloom index AM. So let's say I
want
to create a custom BRIN opclass with per-attribute options (like the
two
BRIN opclasses I work on in the other thread). Clearly, I can't
tweak the
IndexAmRoutine from the extension. ISTM the patch series should
modify all
existing index AMs to have a valid amattoptions() implementation,
calling
the new amproc if defined.Or what is the correct way to define custom opclass for existing
index AM
(e.g. BRIN) with attribute options?Per-attribute opclass options are implemented independently from
per-attribute
AM options. amattoptions() is optional and needs to be defined only
if AM has
per-attribute options.OK, thanks for the explanation - so the per-attribute opclass options
will
work even when the AM does not have amattoptions() defined. Is there any
practical reason why not to just define everything as opclass options and
get rid of the amattoptions() entirely?
The reason is that it would be no need to duplicate AM-specific
per-attribute
options in each opclass. For example, contrib/bloom AM has per-column
options
(signature length), but its opclasses have no options now.
amproc #0 is called regardless of whether amattoptions
is defined or not. That was the main reason why uniform procnum 0 was
picked.I still think using procnum 0 and passing the data through fn_expr are
not
the right solution. Firstly, traditionally the amprocs are either
required
or optional, with required procs having low procnums and optional
starting
at 11 or so. The 0 breaks this, because it's optional but it contradicts
the procnum rule. Also, what happens if we need to add another optional
amproc defined for all AMs? Surely we won't use -1.IMHO we should keep AM-specific procnum and pass it somehow to the AM
machinery.
As you suggested, I introduced AM-specific procnum
IndexAmRoutine.attoptsprocnum, that has different values for each AM.
This was not a problem, because there are only a few AMs now.
FWIW there seems to be a bug in identify_opfamily_groups() which does
this:
/* Ignore strategy numbers outside supported range */
if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64)
thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy;but then identify_opfamily_groups() computes allfuncs without any such
restriction, i.e. it includes procnum 0. And then it fails on this checkif (thisgroup->functionset != allfuncs) {...}
None of the built-in brin opclasses defines the new amproc, so the code
does not hit this issue. I only noticed this with the opclasses added in
the other thread.
This really seems to be bug in previous patches, but now procnum can't be 0.
As for the fn_expr, I still think this seems like a misuse of a field
which was intended for something else. I wonder if it might be breaking
some exising code - either in code or in some extension. It seems quite
possible.It just seems like we're inventing a new way to novel way to pass data
into a function while we already have parameters for that purpose. Adding
parameters may require care so as not to break existing opclasses, but it
seems like the right approach.
fn_expr is used to pass metainformation about user function calls. It is
not
used In AM method calls, so it seems safe to reuse it. Opclass parameters
can be considered here as some kind metainfo about AM calls.
Of course, we could add special parameter to each support function of
each AM.
But it looks too complicated, and every external AM needs to do that to
enable
opclass parameters. So I think it is desirable to implement a more general
solution.
--
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0008-Use-opclass-parameters-in-contrib_hstore-20200229.patch.gzapplication/gzip; name=0008-Use-opclass-parameters-in-contrib_hstore-20200229.patch.gzDownload
���Y^ 0008-Use-opclass-parameters-in-contrib_hstore-20200229.patch �;kw�����WTr�N�%�$0��qC�x�������� [ " '�I��oU?�B��ggw��A������n�{��C�6&�c�n��]m���11�zu���ZchV�j�� W�z����k�?��n�#���q����swu?��xQ��+��f�������U��i�����g�����"S����|m���U]/�V�;{����[���7�
�w�
l��#�
XZ�5�C��Y��[��3�����b�\.e�>o����������|��k���\6*u���_\6LP�M���� ��N �L� ��bJ��B��)g>E{������+���Y��h= b;���ZL��F�D��:�"P����m��
�����*�{c]��j���X;� ��S'k���z��bl�fu�Ri6���W���X�(�'0�ye�������c�����>��_��N����aD���oE��b�Y���Z�5D��8`�95���Q���M�#��n.Ik��U����L$�m� �*}l�u{�T�?��+��y�$~]C74��7o����NN��_T��<_�:�3�`�;�� o���1�%�k�[���!Dc����e�}���~�G3�����>!v��F�����WH���z�z����$�
|��rB����M��o;g}�1��-�39R�-��v�������V^]u��^��>tZW�W���UT/[����y�������z{����M���} �������^�oZ��
�o]]\~
2�e ����9k����tc�U8\)��L�`�Q�`�_���O����R��c��0�t��'�/�a�����������o���C�������_�-�d������A:�)v�7��~���rW(���������X+7<`:Bt�mU���xU$O[��`����<F��:������]o��]���$��h��y�2d�� %�-U�����T�c�8���]��� ���fb��iU���Y�����P��^R<�;����}�����S6v�?1$,V�L/I����������6�n\�����?�tR�z��P��P ������(��v9PQ��
zY�,��k/G�b�||�`f[ck����bDZ�f���e�����f!�B!p�8��x|XX�
�������1�9]?����R(� :SD�U��Yph������0�5�e���8�lw.:}����0M89����.�lX��7<B�z__�xA�3X
R���M(���b��,$
��'��0���k�o�^�J��j����{���������XQ�T�:J�D�� "�eiX�����}���QR�{�������.��k����2������G�1Gr��j�Q C��Q��~���'����?��M�C�|k��&f�h���1��1�:K�6�`�,��-���P�����}n�|/x�2��6��MsP���"$ZR��@e�������y��c���kMP�
����T#g;��!��d�����O�o��(l6����Xz8n��'