Hi!
There was an interesting idea. Now it is possible to add in runtime options
for relation, when you are creating a new extension with new access method.
But if you add a custom operator class, you have no tool to tune it's
behavior. But in some cases it would be nice to have such tool. For example in
intarray extension for gist__intbig_ops there is an option SIGLENINT that
defines the length of the signature that will be used while creating index. Now
it is #defined in the code, but theoretically one can tune index
size/performance by manually setting SIGLENINT value that is more suitable for
his data.
Moreover the authors of bloom extension have to create workaround for
customizing each column behavior: they sets number of bits per column by
setting reloptions col1 — col16. This is not handy. I would seven say a little
bit ugly.
So adding options for opclass seems to be really good idea.
As far as in postgres index is a relation, index column is an attribute in
this relation and each column might have only one opclass, this brings us to
conclusion that when we as speaking about options of an opclass in the index
column, then we can actually speak about an options of an attribute.
If we look into pg_attribute table, we will see, that there is already
attoptions attribute there. So we can use it. Just add a way to add them in
runtime and teach opclass to use it.
The general idea is the following:
Each access method should have amattoptions function, that works similar to
amoptions function (parses and validate text[] of attoptions into internal
data structure) but it takes an additional argument -- the number of an
attribute we are processing.
amattoptions do this job by calling a specific support function of an opclass,
that knows how to parse it (and do it in a similar way as amoptions function
of an access method)
the result of amattoptions should be cached as attoptions are cached now or
similar way. (In current version of prototype I do it wrong, but should change
it later)
All the support functions of the access method should accept parsed attoptions
in their argument lists, and use it if necessary. In the prototype I've
implemented if for gist opclass, and added attoptions as a last argument of
all support functions. So it is even backward compatible as all funtions I
have not touched just ignores this last argument.
The implementation of an access method should get attoptions from cache, if
they are there, or from am->amattoptions if not (in the prototype it is done
using get_index_attribute_options function) and pass it to the support
functions.
In the prototype I've added a member to GISTSTATE structure for
storing parsed attoptions, and fill in initGISTstate and used it all over the
code. In other implementations solutions might be different.
So... The prototype I wrote is in attachment. It is really dirty. Some things
are not done yet. Some things should be changed. But never the less work, and
it shows what I am going to do better than all explanations.
To see how it works you should do the following:
# create extension intarray ;
# create table test (i int[]);
# create table test2 (i int[]);
# create index ON test USING GIST (i USING gist__intbig_ops WITH OPTIONS
(sig_len_int=22) );
# create index ON test2 USING GIST (i USING gist__intbig_ops WITH OPTIONS
(sig_len_int=120) );
# select attoptions from pg_attribute where attrelid = 'test_i_idx'::regclass
OR attrelid = 'test2_i_idx'::regclass;
attoptions
-------------------
{sig_len_int=120}
{sig_len_int=22}
(2 rows)
and if you uncomment development warning output from the code you will see
that these values are really used through the code.
Speaking of the syntax, I've added WITH OPTIONS keywords for specifying
attoptions, not just WITH, that most of us would expect. I did it because
index_elem, the node that is used for definition of an index column is used not
only in CREATE INDEX expression, but also in CREATE TABLE CONSTRAINT EXCLUDE.
Because CREATE TABLE CONSTRAINT EXCLUDE actually creates an index, and one can
specify there full specification of index element there. But right after this
you should write "WITH operator" clause.
So if I will try to use WITH keyword for attoptions, a syntax parser will be
confused, as it cat not distinguish one WITH keyword from another. So decided
to use WITH OTPTIONS keywords for attoptions.
There might be some tricks that will allow to use WITH in both places but I
think "WITH OPTIONS" might make SQL code more readable, because an expression
with two "WITH" in a row will be hard to read for human too ;-)
So we come to the last part of this message, that is most important for me for
the moment:
One of the issues that I've solved while writing this patch, was the
following: attoptions and reloptions are should use almost the same code for
options parsing. For reloptions this code is written, it is good, but you
can't reuse it for attoptions as it is binded to relopt_kind and quite
centralized.
So first step to implement attoptions for indexes, will be rewriting reoptions
code to get rid of relopt_kind, make all access methods has its own option
descriptor catalogs and use reference to this catalog instead of relopt_kind.
This should be a separate patch and I think I will start another thread for
it. I will write another letter for relopt_kind remove issue...
--
Nikolay Shaplov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 05dbe87..f439403 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -36,8 +36,8 @@
PG_FUNCTION_INFO_V1(blhandler);
-/* Kind of relation optioms for bloom index */
-static relopt_kind bl_relopt_kind;
+/* Catalog of relation options for bloom index */
+static options_catalog bl_relopt_catalog;
static int32 myRand(void);
static void mySrand(uint32 seed);
@@ -51,15 +51,18 @@ _PG_init(void)
int i;
char buf[16];
- bl_relopt_kind = add_reloption_kind();
+ bl_relopt_catalog.definitions = NULL;
+ bl_relopt_catalog.num = 0;
+ bl_relopt_catalog.max = 0;
+ bl_relopt_catalog.need_initialization = 1;
- add_int_reloption(bl_relopt_kind, "length",
+ add_int_reloption(0, &bl_relopt_catalog, "length",
"Length of signature in uint16 type", 5, 1, 256);
for (i = 0; i < INDEX_MAX_KEYS; i++)
{
snprintf(buf, 16, "col%d", i + 1);
- add_int_reloption(bl_relopt_kind, buf,
+ add_int_reloption(0, &bl_relopt_catalog, buf,
"Number of bits for corresponding column", 2, 1, 2048);
}
}
@@ -104,6 +107,7 @@ blhandler(PG_FUNCTION_ARGS)
amroutine->amcostestimate = blcostestimate;
amroutine->amoptions = bloptions;
amroutine->amvalidate = blvalidate;
+ amroutine->amattoptions = NULL;
PG_RETURN_POINTER(amroutine);
}
@@ -457,7 +461,7 @@ bloptions(Datum reloptions, bool validate)
tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]);
}
- options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
+ options = parseRelOptions(reloptions, validate, &bl_relopt_catalog, 0, &numoptions);
rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
validate, tab, INDEX_MAX_KEYS + 1);
diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h
index d524f0f..a05350f 100644
--- a/contrib/intarray/_int.h
+++ b/contrib/intarray/_int.h
@@ -47,15 +47,24 @@
/* bigint defines */
-#define SIGLENINT 63 /* >122 => key will toast, so very slow!!! */
-#define SIGLEN ( sizeof(int)*SIGLENINT )
-#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
+//#define SIGLENINT 63 /* >122 => key will toast, so very slow!!! */
+//#define SIGLEN ( sizeof(int)*SIGLENINT )
+//#define SIGLENBIT (SIGLEN*BITS_PER_BYTE)
-typedef char BITVEC[SIGLEN];
+
+#define SIGLEN_BYTES(i) (sizeof(int)*i) /* array signature length in bytes */
+#define SIGLEN_BITS(i) \
+ (SIGLEN_BYTES(i)*BITS_PER_BYTE) /* array signature length in bits */
+
+
+//typedef char BITVEC[SIGLEN];
typedef char *BITVECP;
-#define LOOPBYTE \
- for(i=0;i<SIGLEN;i++)
+//#define LOOPBYTE \
+// for(i=0;i<SIGLEN;i++)
+
+#define LOOPBYTE_NEW(siglenint) \
+ for(i=0;i<SIGLEN_BYTES(siglenint);i++)
/* beware of multiple evaluation of arguments to these macros! */
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITS_PER_BYTE ) ) )
@@ -63,8 +72,12 @@ 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) (((unsigned int)(val)) % SIGLENBIT)
+#define HASHVAL_NEW(val, siglenint) (((unsigned int)(val)) % (unsigned int)(SIGLEN_BITS(siglenint)))
+
+//#define HASH(sign, val) SETBIT((sign), HASHVAL(val))
+#define HASH_NEW(sign, val, siglenint) SETBIT((sign), HASHVAL_NEW(val, siglenint))
/*
* type of index key
@@ -76,12 +89,23 @@ typedef struct
char data[FLEXIBLE_ARRAY_MEMBER];
} GISTTYPE;
+/* intarray opclass options */
+typedef struct IntArrayOpclassOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ int sig_len_int; /* FIXME comment */
+} IntArrayOpclassOptions;
+
+
#define ALLISTRUE 0x04
#define ISALLTRUE(x) ( ((GISTTYPE*)x)->flag & ALLISTRUE )
#define GTHDRSIZE (VARHDRSZ + sizeof(int32))
-#define CALCGTSIZE(flag) ( GTHDRSIZE+(((flag) & ALLISTRUE) ? 0 : SIGLEN) )
+
+#define GT_EMPTY_SIZE GTHDRSIZE
+
+#define GT_SIZE(siglenint) (GTHDRSIZE + SIGLEN_BYTES(siglenint))
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
@@ -94,6 +118,7 @@ typedef void (*formfloat) (ArrayType *, float *);
/*
* useful functions
*/
+extern void _PG_init(void);
bool isort(int32 *a, int len);
ArrayType *new_intArrayType(int num);
ArrayType *copy_intArrayType(ArrayType *a);
@@ -109,7 +134,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, void *options);
/*****************************************************************************
@@ -155,7 +180,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, bool calcnot, void *options);
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 5d9e676..02d09f6 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;
@@ -254,43 +254,48 @@ checkcondition_arr(void *checkval, ITEM *item)
return false;
}
+
static bool
-checkcondition_bit(void *checkval, ITEM *item)
+checkcondition_bit(void *checkval, ITEM *item, void *options)
{
- return GETBIT(checkval, HASHVAL(item->val));
+ IntArrayOpclassOptions* attoptions;
+
+ attoptions = (IntArrayOpclassOptions*) options;
+ return GETBIT(checkval, HASHVAL_NEW(item->val, attoptions->sig_len_int));
}
+
/*
* 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, bool calcnot, void *options,
+ 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, calcnot, options, 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, calcnot, options, chkcond))
+ return execute(curitem - 1, checkval, calcnot, options, chkcond);
else
return false;
}
else
{ /* |-operator */
- if (execute(curitem + curitem->left, checkval, calcnot, chkcond))
+ if (execute(curitem + curitem->left, checkval, calcnot, options, chkcond))
return true;
else
- return execute(curitem - 1, checkval, calcnot, chkcond);
+ return execute(curitem - 1, checkval, calcnot, options, chkcond);
}
}
@@ -298,10 +303,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, bool calcnot, void *options)
{
return execute(GETQUERY(query) + query->size - 1,
- (void *) sign, calcnot,
+ (void *) sign, calcnot, options,
checkcondition_bit);
}
@@ -315,7 +320,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, calcnot, NULL,
checkcondition_arr);
}
@@ -326,7 +331,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 +362,7 @@ gin_bool_consistent(QUERYTYPE *query, bool *check)
}
return execute(GETQUERY(query) + query->size - 1,
- (void *) &gcv, true,
+ (void *) &gcv, true, NULL,
checkcondition_gin);
}
@@ -430,7 +435,7 @@ boolop(PG_FUNCTION_ARGS)
chkval.arre = chkval.arrb + ARRNELEMS(val);
result = execute(GETQUERY(query) + query->size - 1,
&chkval, true,
- checkcondition_arr);
+ NULL, checkcondition_arr);
pfree(val);
PG_FREE_IF_COPY(query, 1);
diff --git a/contrib/intarray/_int_tool.c b/contrib/intarray/_int_tool.c
index 3c52912..52085e4 100644
--- a/contrib/intarray/_int_tool.c
+++ b/contrib/intarray/_int_tool.c
@@ -312,8 +312,9 @@ _int_unique(ArrayType *r)
return resize_intArrayType(r, dr + 1 - ARRPTR(r));
}
+#if 0
void
-gensign(BITVEC sign, int *a, int len)
+gensign(BITVEC sign, int *a, int len, int siglenint)
{
int i;
@@ -324,6 +325,7 @@ gensign(BITVEC sign, int *a, int len)
a++;
}
}
+#endif
int32
intarray_match_first(ArrayType *a, int32 elem)
diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c
index 6dae7c91..af4c16b 100644
--- a/contrib/intarray/_intbig_gist.c
+++ b/contrib/intarray/_intbig_gist.c
@@ -5,6 +5,7 @@
#include "access/gist.h"
#include "access/stratnum.h"
+#include "access/reloptions.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_attoptions);
/* Number of one-bits in an unsigned byte */
static const uint8 number_of_ones[256] = {
@@ -43,6 +45,30 @@ static const uint8 number_of_ones[256] = {
PG_FUNCTION_INFO_V1(_intbig_in);
PG_FUNCTION_INFO_V1(_intbig_out);
+/* Catalog of relation options for bloom index */
+static options_catalog intbig_gist_attopt_catalog;
+
+
+void
+_PG_init(void)
+{
+ intbig_gist_attopt_catalog.definitions = NULL;
+ intbig_gist_attopt_catalog.num = 0;
+ intbig_gist_attopt_catalog.max = 0;
+ intbig_gist_attopt_catalog.need_initialization = 1;
+
+ add_int_reloption(0, &intbig_gist_attopt_catalog, "sig_len_int",
+ "FIXME description", /*63*/ 120, 10, 122); //FIXME limits
+
+// elog(WARNING, "_intbig_gist.c _PG_init");
+}
+
+BITVECP
+new_bitvec(int siglenint)
+{
+ return (BITVECP) palloc0(SIGLEN_BYTES(siglenint));
+}
+
Datum
_intbig_in(PG_FUNCTION_ARGS)
{
@@ -66,7 +92,7 @@ _intbig_out(PG_FUNCTION_ARGS)
** intbig functions
*********************************************************************/
static bool
-_intbig_overlap(GISTTYPE *a, ArrayType *b)
+_intbig_overlap(GISTTYPE *a, ArrayType *b, int siglenint)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -75,7 +101,8 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (GETBIT(GETSIGN(a), HASHVAL_NEW(*ptr,siglenint)))
+
return true;
ptr++;
}
@@ -84,7 +111,7 @@ _intbig_overlap(GISTTYPE *a, ArrayType *b)
}
static bool
-_intbig_contains(GISTTYPE *a, ArrayType *b)
+_intbig_contains(GISTTYPE *a, ArrayType *b, int siglenint)
{
int num = ARRNELEMS(b);
int32 *ptr = ARRPTR(b);
@@ -93,7 +120,9 @@ _intbig_contains(GISTTYPE *a, ArrayType *b)
while (num--)
{
- if (!GETBIT(GETSIGN(a), HASHVAL(*ptr)))
+ if (!GETBIT(GETSIGN(a), HASHVAL_NEW(*ptr, siglenint)))
+
+
return false;
ptr++;
}
@@ -107,6 +136,10 @@ 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);
+ IntArrayOpclassOptions *attoptions =
+ (IntArrayOpclassOptions *) PG_GETARG_POINTER(3);
+
+//elog(WARNING, "g_intbig_same!!; sig_len_int=%i", attoptions->sig_len_int);
if (ISALLTRUE(a) && ISALLTRUE(b))
*result = true;
@@ -121,7 +154,8 @@ g_intbig_same(PG_FUNCTION_ARGS)
sb = GETSIGN(b);
*result = true;
- LOOPBYTE
+// for(i=0;i<SIGLEN;i++)
+ LOOPBYTE_NEW(attoptions->sig_len_int)
{
if (sa[i] != sb[i])
{
@@ -137,6 +171,10 @@ Datum
g_intbig_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ IntArrayOpclassOptions *attoptions =
+ (IntArrayOpclassOptions *) PG_GETARG_POINTER(1);
+
+//elog(WARNING, "g_intbig_compress!!; sig_len_int=%i", attoptions->sig_len_int);
if (entry->leafkey)
{
@@ -144,8 +182,7 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ArrayType *in = DatumGetArrayTypeP(entry->key);
int32 *ptr;
int num;
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
-
+ GISTTYPE *res = (GISTTYPE *) palloc0(GT_SIZE(attoptions->sig_len_int));
CHECKARRVALID(in);
if (ARRISEMPTY(in))
{
@@ -157,11 +194,11 @@ g_intbig_compress(PG_FUNCTION_ARGS)
ptr = ARRPTR(in);
num = ARRNELEMS(in);
}
- SET_VARSIZE(res, CALCGTSIZE(0));
+ SET_VARSIZE(res, GT_SIZE(attoptions->sig_len_int));
while (num--)
{
- HASH(GETSIGN(res), *ptr);
+ HASH_NEW(GETSIGN(res), *ptr, attoptions->sig_len_int);
ptr++;
}
@@ -182,14 +219,14 @@ g_intbig_compress(PG_FUNCTION_ARGS)
BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
GISTTYPE *res;
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
{
if ((sign[i] & 0xff) != 0xff)
PG_RETURN_POINTER(entry);
}
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
+ res = (GISTTYPE *) palloc(GT_EMPTY_SIZE);
+ SET_VARSIZE(res, GT_EMPTY_SIZE);
res->flag = ALLISTRUE;
retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
@@ -205,24 +242,22 @@ g_intbig_compress(PG_FUNCTION_ARGS)
static int32
-sizebitvec(BITVECP sign)
+sizebitvec(BITVECP sign, IntArrayOpclassOptions *attoptions)
{
int32 size = 0,
i;
-
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
size += number_of_ones[(unsigned char) sign[i]];
return size;
}
static int
-hemdistsign(BITVECP a, BITVECP b)
+hemdistsign(BITVECP a, BITVECP b, IntArrayOpclassOptions *attoptions)
{
int i,
diff,
dist = 0;
-
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
{
diff = (unsigned char) (a[i] ^ b[i]);
dist += number_of_ones[diff];
@@ -231,19 +266,20 @@ hemdistsign(BITVECP a, BITVECP b)
}
static int
-hemdist(GISTTYPE *a, GISTTYPE *b)
+hemdist(GISTTYPE *a, GISTTYPE *b, IntArrayOpclassOptions *attoptions)
{
+ int siglen_bits = SIGLEN_BITS(attoptions->sig_len_int);
if (ISALLTRUE(a))
{
if (ISALLTRUE(b))
return 0;
else
- return SIGLENBIT - sizebitvec(GETSIGN(b));
+ return siglen_bits - sizebitvec(GETSIGN(b), attoptions);
}
else if (ISALLTRUE(b))
- return SIGLENBIT - sizebitvec(GETSIGN(a));
+ return siglen_bits - sizebitvec(GETSIGN(a), attoptions);
- return hemdistsign(GETSIGN(a), GETSIGN(b));
+ return hemdistsign(GETSIGN(a), GETSIGN(b), attoptions);
}
Datum
@@ -253,14 +289,14 @@ g_intbig_decompress(PG_FUNCTION_ARGS)
}
static int32
-unionkey(BITVECP sbase, GISTTYPE *add)
+unionkey(BITVECP sbase, GISTTYPE *add, IntArrayOpclassOptions *attoptions)
{
int32 i;
BITVECP sadd = GETSIGN(add);
if (ISALLTRUE(add))
return 1;
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
sbase[i] |= sadd[i];
return 0;
}
@@ -270,28 +306,40 @@ g_intbig_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int *size = (int *) PG_GETARG_POINTER(1);
- BITVEC base;
+ IntArrayOpclassOptions *attoptions =
+ (IntArrayOpclassOptions *) PG_GETARG_POINTER(2);
+ BITVECP base;
int32 i,
len;
int32 flag = 0;
GISTTYPE *result;
+/*
+if (attoptions->sig_len_int == 63)
+{
+elog(WARNING, "g_intbig_compress!!; Right! sig_len_int=%i", attoptions->sig_len_int);
+} else
+{
+elog(WARNING, "g_intbig_compress!!; Wrong!!!!! sig_len_int=%i", attoptions->sig_len_int);
- MemSet((void *) base, 0, sizeof(BITVEC));
+}*/
+// MemSet((void *) base, 0, SIGLEN_BYTES(attoptions->sig_len_int)); // may be here will be better GT_SIZE(attoptions->sig_len_int)-GT_EMPTY_SIZE the same value but may be more clear
+ base = new_bitvec(attoptions->sig_len_int);
for (i = 0; i < entryvec->n; i++)
{
- if (unionkey(base, GETENTRY(entryvec, i)))
+ if (unionkey(base, GETENTRY(entryvec, i), attoptions))
{
flag = ALLISTRUE;
break;
}
}
- len = CALCGTSIZE(flag);
+ len = ((flag) & ALLISTRUE) ? GT_EMPTY_SIZE : GT_SIZE(attoptions->sig_len_int);
+
result = (GISTTYPE *) palloc(len);
SET_VARSIZE(result, len);
result->flag = flag;
if (!ISALLTRUE(result))
- memcpy((void *) GETSIGN(result), (void *) base, sizeof(BITVEC));
+ memcpy((void *) GETSIGN(result), (void *) base, SIGLEN_BYTES(attoptions->sig_len_int));
*size = len;
PG_RETURN_POINTER(result);
@@ -303,10 +351,14 @@ g_intbig_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);
+ IntArrayOpclassOptions *attoptions =
+ (IntArrayOpclassOptions *) PG_GETARG_POINTER(3);
GISTTYPE *origval = (GISTTYPE *) DatumGetPointer(origentry->key);
GISTTYPE *newval = (GISTTYPE *) DatumGetPointer(newentry->key);
- *penalty = hemdist(origval, newval);
+//elog(WARNING, "g_intbig_penalty!!; sig_len_int=%i", attoptions->sig_len_int);
+
+ *penalty = hemdist(origval, newval, attoptions);
PG_RETURN_POINTER(penalty);
}
@@ -329,6 +381,9 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ IntArrayOpclassOptions *attoptions =
+ (IntArrayOpclassOptions *) PG_GETARG_POINTER(2);
+
OffsetNumber k,
j;
GISTTYPE *datum_l,
@@ -350,6 +405,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
SPLITCOST *costvector;
GISTTYPE *_k,
*_j;
+ int siglen;
+
+//elog(WARNING, "g_intbig_picksplit!!");
+//elog(WARNING, "sig_len_int=%i", attoptions->sig_len_int);
maxoff = entryvec->n - 2;
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
@@ -361,7 +420,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),attoptions);
if (size_waste > waste)
{
waste = size_waste;
@@ -381,7 +440,7 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
seed_1 = 1;
seed_2 = 2;
}
-
+ siglen = SIGLEN_BYTES(attoptions->sig_len_int);
/* form initial .. */
if (ISALLTRUE(GETENTRY(entryvec, seed_1)))
{
@@ -391,10 +450,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
}
else
{
- datum_l = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_l, GTHDRSIZE + SIGLEN);
+ 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));
+ memcpy((void *) GETSIGN(datum_l), (void *) GETSIGN(GETENTRY(entryvec, seed_1)), SIGLEN_BYTES(attoptions->sig_len_int));
}
if (ISALLTRUE(GETENTRY(entryvec, seed_2)))
{
@@ -404,10 +463,10 @@ g_intbig_picksplit(PG_FUNCTION_ARGS)
}
else
{
- datum_r = (GISTTYPE *) palloc(GTHDRSIZE + SIGLEN);
- SET_VARSIZE(datum_r, GTHDRSIZE + SIGLEN);
+ 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));
+ memcpy((void *) GETSIGN(datum_r), (void *) GETSIGN(GETENTRY(entryvec, seed_2)), SIGLEN_BYTES(attoptions->sig_len_int));
}
maxoff = OffsetNumberNext(maxoff);
@@ -417,8 +476,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, attoptions);
+ size_beta = hemdist(datum_r, _j, attoptions);
costvector[j - 1].cost = Abs(size_alpha - size_beta);
}
qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost);
@@ -442,20 +501,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, attoptions);
+ size_beta = hemdist(datum_r, _j, attoptions);
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_BYTES(attoptions->sig_len_int));
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
union_l[i] |= ptr[i];
}
*left++ = j;
@@ -466,12 +525,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_BYTES(attoptions->sig_len_int));
}
else
{
ptr = GETSIGN(_j);
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
union_r[i] |= ptr[i];
}
*right++ = j;
@@ -497,8 +556,13 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ IntArrayOpclassOptions *attoptions =
+ (IntArrayOpclassOptions *) PG_GETARG_POINTER(5);
+
bool retval;
+//elog(WARNING, "g_intbig_consistent!!; sig_len_int=%i", attoptions->sig_len_int);
+
/* All cases served by this function are inexact */
*recheck = true;
@@ -509,7 +573,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
{
retval = signconsistent((QUERYTYPE *) query,
GETSIGN(DatumGetPointer(entry->key)),
- false);
+ false, (void*)attoptions);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
}
@@ -519,7 +583,7 @@ 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, attoptions->sig_len_int);
break;
case RTSameStrategyNumber:
if (GIST_LEAF(entry))
@@ -527,22 +591,22 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
+ BITVECP qp;
BITVECP dq,
de;
- memset(qp, 0, sizeof(BITVEC));
-
+// memset(qp, 0, SIGLEN_BYTES(attoptions->sig_len_int));
+ qp = new_bitvec(attoptions->sig_len_int);
while (num--)
{
- HASH(qp, *ptr);
+ HASH_NEW(qp, *ptr, attoptions->sig_len_int);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
{
if (de[i] != dq[i])
{
@@ -553,11 +617,11 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
else
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query, attoptions->sig_len_int);
break;
case RTContainsStrategyNumber:
case RTOldContainsStrategyNumber:
- retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_contains((GISTTYPE *) DatumGetPointer(entry->key), query, attoptions->sig_len_int);
break;
case RTContainedByStrategyNumber:
case RTOldContainedByStrategyNumber:
@@ -566,22 +630,22 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
int i,
num = ARRNELEMS(query);
int32 *ptr = ARRPTR(query);
- BITVEC qp;
+ BITVECP qp;
BITVECP dq,
de;
- memset(qp, 0, sizeof(BITVEC));
-
+// memset(qp, 0, SIGLEN_BYTES(attoptions->sig_len_int));
+ qp = new_bitvec(attoptions->sig_len_int);
while (num--)
{
- HASH(qp, *ptr);
+ HASH_NEW(qp, *ptr, attoptions->sig_len_int);
ptr++;
}
de = GETSIGN((GISTTYPE *) DatumGetPointer(entry->key));
dq = qp;
retval = true;
- LOOPBYTE
+ LOOPBYTE_NEW(attoptions->sig_len_int)
{
if (de[i] & ~dq[i])
{
@@ -591,7 +655,7 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
}
}
else
- retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query);
+ retval = _intbig_overlap((GISTTYPE *) DatumGetPointer(entry->key), query, attoptions->sig_len_int);
break;
default:
retval = FALSE;
@@ -599,3 +663,46 @@ g_intbig_consistent(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(retval);
}
+
+Datum
+g_intbig_attoptions(PG_FUNCTION_ARGS)
+{
+ relopt_value *options;
+ int numoptions;
+ IntArrayOpclassOptions *attopts;
+ MemoryContext oldcxt;
+ Datum raw_options = PG_GETARG_DATUM(0);
+ bool validate = PG_GETARG_BOOL(1);
+ relopt_parse_elt tab[] =
+ {{"sig_len_int", RELOPT_TYPE_INT, offsetof(IntArrayOpclassOptions, sig_len_int)}};
+
+
+ options = parseRelOptions(raw_options, validate, &intbig_gist_attopt_catalog, 0, &numoptions);
+ attopts = allocateReloptStruct(sizeof(IntArrayOpclassOptions), options, numoptions);
+
+ fillRelOptions((void *) attopts, sizeof(IntArrayOpclassOptions), options, numoptions,
+ validate, tab, 1);
+
+// elog(WARNING, "************* %i", attopts->sig_len_int);
+
+// oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+// attopts = palloc0(sizeof(IntArrayOpclassOptions)); //FIXME do something else with this palloc
+// MemoryContextSwitchTo(oldcxt);
+
+// attopts->sig_len_int = 10;
+
+// attopts->sig_len_int = 63;
+// attopts->sig_len_int = 77;
+// attopts->sig_len_int = 120;
+
+
+
+
+// char s[] = "Awe postgres!";
+// char * copy;
+// elog(WARNING, "g_int_getattopdesc!!");
+// copy = palloc(sizeof(s));
+// memcpy(copy,s,sizeof(s));
+ PG_RETURN_POINTER(attopts);
+}
+
diff --git a/contrib/intarray/intarray--1.1.sql b/contrib/intarray/intarray--1.1.sql
index 6ee0d5a..23be554 100644
--- a/contrib/intarray/intarray--1.1.sql
+++ b/contrib/intarray/intarray--1.1.sql
@@ -394,6 +394,7 @@ AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
+
-- Create the operator class for indexing
CREATE OPERATOR CLASS gist__int_ops
@@ -413,7 +414,6 @@ DEFAULT FOR TYPE _int4 USING gist AS
FUNCTION 6 g_int_picksplit (internal, internal),
FUNCTION 7 g_int_same (_int4, _int4, internal);
-
---------------------------------------------
-- intbig
---------------------------------------------
@@ -435,12 +435,12 @@ CREATE TYPE intbig_gkey (
OUTPUT = _intbig_out
);
-CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal)
+CREATE FUNCTION g_intbig_consistent(internal,_int4,smallint,oid,internal,internal)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
-CREATE FUNCTION g_intbig_compress(internal)
+CREATE FUNCTION g_intbig_compress(internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
@@ -450,26 +450,31 @@ RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
-CREATE FUNCTION g_intbig_penalty(internal,internal,internal)
+CREATE FUNCTION g_intbig_penalty(internal, internal, internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
-CREATE FUNCTION g_intbig_picksplit(internal, internal)
+CREATE FUNCTION g_intbig_picksplit(internal, internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
-CREATE FUNCTION g_intbig_union(internal, internal)
+CREATE FUNCTION g_intbig_union(internal, internal, internal)
RETURNS intbig_gkey
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
-CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal)
+CREATE FUNCTION g_intbig_same(intbig_gkey, intbig_gkey, internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
+CREATE FUNCTION g_intbig_attoptions(internal, boolean)
+RETURNS internal
+AS 'MODULE_PATHNAME', 'g_intbig_attoptions'
+LANGUAGE C IMMUTABLE STRICT;
+
-- register the opclass for indexing (not as default)
CREATE OPERATOR CLASS gist__intbig_ops
@@ -482,13 +487,15 @@ AS
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 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),
- 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),
+ 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_attoptions(internal, boolean),
+
STORAGE intbig_gkey;
--GIN
@@ -503,6 +510,8 @@ RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
+
+
CREATE OPERATOR CLASS gin__int_ops
FOR TYPE _int4 USING gin
AS
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 89bad05..797f367 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -110,6 +110,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = brinendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amattoptions = NULL;
PG_RETURN_POINTER(amroutine);
}
@@ -761,7 +762,7 @@ brinoptions(Datum reloptions, bool validate)
{"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pagesPerRange)}
};
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN,
+ options = parseRelOptions(reloptions, validate, NULL, RELOPT_KIND_BRIN,
&numoptions);
/* if none set, we're done */
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 7448c7f..a665130 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -378,6 +378,7 @@ static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
static int num_custom_options = 0;
static relopt_gen **custom_options = NULL;
static bool need_initialization = true;
+static int max_custom_options = 0;
static void initialize_reloptions(void);
static void parse_one_reloption(relopt_value *option, char *text_str,
@@ -496,32 +497,31 @@ add_reloption_kind(void)
* main parser table.
*/
static void
-add_reloption(relopt_gen *newoption)
+add_reloption(relopt_gen *newoption, options_catalog * catalog)
{
- static int max_custom_options = 0;
-
- if (num_custom_options >= max_custom_options)
+ if (catalog->num + 1 >= catalog->max)
{
MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
- if (max_custom_options == 0)
+ if (catalog->max == 0)
{
- max_custom_options = 8;
- custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
+ catalog->max = 8;
+ catalog->definitions = palloc(catalog->max * sizeof(relopt_gen *));
}
else
{
- max_custom_options *= 2;
- custom_options = repalloc(custom_options,
- max_custom_options * sizeof(relopt_gen *));
+ catalog->max *= 2;
+ catalog->definitions = repalloc(catalog->definitions,
+ catalog->max * sizeof(relopt_gen *));
}
MemoryContextSwitchTo(oldcxt);
}
- custom_options[num_custom_options++] = newoption;
-
- need_initialization = true;
+ catalog->definitions[catalog->num] = newoption;
+ catalog->definitions[catalog->num + 1] = NULL;
+ catalog->num++;
+ catalog->need_initialization = true;
}
/*
@@ -578,15 +578,33 @@ allocate_reloption(bits32 kinds, int type, char *name, char *desc)
* Add a new boolean reloption
*/
void
-add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
+add_bool_reloption(bits32 kinds, options_catalog * catalog,
+ char *name, char *desc, bool default_val)
{
relopt_bool *newoption;
newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
name, desc);
newoption->default_val = default_val;
-
- add_reloption((relopt_gen *) newoption);
+
+ if (catalog)
+ {
+ add_reloption((relopt_gen *) newoption, catalog);
+ } else
+ {
+ options_catalog static_catalog;
+ static_catalog.num = num_custom_options;
+ static_catalog.max = max_custom_options;
+ static_catalog.need_initialization = need_initialization;
+ static_catalog.definitions = custom_options;
+
+ add_reloption((relopt_gen *) newoption, &static_catalog);
+
+ num_custom_options = static_catalog.num;
+ max_custom_options = static_catalog.max;
+ need_initialization = static_catalog.need_initialization;
+ custom_options = static_catalog.definitions;
+ }
}
/*
@@ -594,8 +612,8 @@ add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
* Add a new integer reloption
*/
void
-add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
- int min_val, int max_val)
+add_int_reloption(bits32 kinds, options_catalog * catalog,
+ char *name, char *desc, int default_val, int min_val, int max_val)
{
relopt_int *newoption;
@@ -605,7 +623,24 @@ add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
newoption->min = min_val;
newoption->max = max_val;
- add_reloption((relopt_gen *) newoption);
+ if (catalog)
+ {
+ add_reloption((relopt_gen *) newoption, catalog);
+ } else
+ {
+ options_catalog static_catalog;
+ static_catalog.num = num_custom_options;
+ static_catalog.max = max_custom_options;
+ static_catalog.need_initialization = need_initialization;
+ static_catalog.definitions = custom_options;
+
+ add_reloption((relopt_gen *) newoption, &static_catalog);
+
+ num_custom_options = static_catalog.num;
+ max_custom_options = static_catalog.max;
+ need_initialization = static_catalog.need_initialization;
+ custom_options = static_catalog.definitions;
+ }
}
/*
@@ -613,8 +648,9 @@ add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
* Add a new float reloption
*/
void
-add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
- double min_val, double max_val)
+add_real_reloption(bits32 kinds, options_catalog * catalog,
+ char *name, char *desc, double default_val,
+ double min_val, double max_val)
{
relopt_real *newoption;
@@ -624,7 +660,24 @@ add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
newoption->min = min_val;
newoption->max = max_val;
- add_reloption((relopt_gen *) newoption);
+ if (catalog)
+ {
+ add_reloption((relopt_gen *) newoption, catalog);
+ } else
+ {
+ options_catalog static_catalog;
+ static_catalog.num = num_custom_options;
+ static_catalog.max = max_custom_options;
+ static_catalog.need_initialization = need_initialization;
+ static_catalog.definitions = custom_options;
+
+ add_reloption((relopt_gen *) newoption, &static_catalog);
+
+ num_custom_options = static_catalog.num;
+ max_custom_options = static_catalog.max;
+ need_initialization = static_catalog.need_initialization;
+ custom_options = static_catalog.definitions;
+ }
}
/*
@@ -637,8 +690,8 @@ add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
* the validation.
*/
void
-add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
- validate_string_relopt validator)
+add_string_reloption(bits32 kinds, options_catalog * catalog, char *name,
+ char *desc, char *default_val, validate_string_relopt validator)
{
relopt_string *newoption;
@@ -663,7 +716,24 @@ add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
newoption->default_isnull = true;
}
- add_reloption((relopt_gen *) newoption);
+ if (catalog)
+ {
+ add_reloption((relopt_gen *) newoption, catalog);
+ } else
+ {
+ options_catalog static_catalog;
+ static_catalog.num = num_custom_options;
+ static_catalog.max = max_custom_options;
+ static_catalog.need_initialization = need_initialization;
+ static_catalog.definitions = custom_options;
+
+ add_reloption((relopt_gen *) newoption, &static_catalog);
+
+ num_custom_options = static_catalog.num;
+ max_custom_options = static_catalog.max;
+ need_initialization = static_catalog.need_initialization;
+ custom_options = static_catalog.definitions;
+ }
}
/*
@@ -714,13 +784,13 @@ transformRelOptions(Datum oldOptions, List *defList, char *namspace,
deconstruct_array(array, TEXTOID, -1, false, 'i',
&oldoptions, NULL, &noldoptions);
-
for (i = 0; i < noldoptions; i++)
{
text *oldoption = DatumGetTextP(oldoptions[i]);
char *text_str = VARDATA(oldoption);
int text_len = VARSIZE(oldoption) - VARHDRSZ;
+//elog(WARNING, "parsing option %s",text_str);
/* Search for a match in defList */
foreach(cell, defList)
{
@@ -832,6 +902,8 @@ transformRelOptions(Datum oldOptions, List *defList, char *namspace,
t = (text *) palloc(len + 1);
SET_VARSIZE(t, len);
sprintf(VARDATA(t), "%s=%s", def->defname, value);
+
+//elog(WARNING, "parsing option %s=%s", def->defname, value);
astate = accumArrayResult(astate, PointerGetDatum(t),
false, TEXTOID,
@@ -964,21 +1036,31 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
* be freed by the caller.
*/
relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
+parseRelOptions(Datum options, bool validate, options_catalog * catalog,
+ relopt_kind kind, int *numrelopts)
{
relopt_value *reloptions;
int numoptions = 0;
int i;
int j;
+ options_catalog static_catalog;
if (need_initialization)
initialize_reloptions();
+ if (! catalog)
+ {
+ static_catalog.definitions = relOpts;
+ catalog = &static_catalog;
+ } else
+ {
+ Assert(kind == 0);
+ }
+
/* Build a list of expected options, based on kind */
- for (i = 0; relOpts[i]; i++)
- if (relOpts[i]->kinds & kind)
+ for (i = 0; catalog->definitions[i]; i++)
+ if (! kind || (catalog->definitions[i]->kinds & kind))
numoptions++;
if (numoptions == 0)
@@ -989,11 +1071,11 @@ parseRelOptions(Datum options, bool validate, relopt_kind kind,
reloptions = palloc(numoptions * sizeof(relopt_value));
- for (i = 0, j = 0; relOpts[i]; i++)
+ for (i = 0, j = 0; catalog->definitions[i]; i++)
{
- if (relOpts[i]->kinds & kind)
+ if (! kind || (catalog->definitions[i]->kinds & kind))
{
- reloptions[j].gen = relOpts[i];
+ reloptions[j].gen = catalog->definitions[i];
reloptions[j].isset = false;
j++;
}
@@ -1305,7 +1387,7 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
offsetof(StdRdOptions, parallel_degree)}
};
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
+ options = parseRelOptions(reloptions, validate, NULL, kind, &numoptions);
/* if none set, we're done */
if (numoptions == 0)
@@ -1337,7 +1419,7 @@ view_reloptions(Datum reloptions, bool validate)
offsetof(ViewOptions, check_option_offset)}
};
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
+ options = parseRelOptions(reloptions, validate, NULL, RELOPT_KIND_VIEW, &numoptions);
/* if none set, we're done */
if (numoptions == 0)
@@ -1417,7 +1499,7 @@ attribute_reloptions(Datum reloptions, bool validate)
{"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
};
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
+ options = parseRelOptions(reloptions, validate, NULL, RELOPT_KIND_ATTRIBUTE,
&numoptions);
/* if none set, we're done */
@@ -1449,7 +1531,7 @@ tablespace_reloptions(Datum reloptions, bool validate)
{"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
};
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
+ options = parseRelOptions(reloptions, validate, NULL, RELOPT_KIND_TABLESPACE,
&numoptions);
/* if none set, we're done */
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index a2450f4..051c90e 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -65,6 +65,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amattoptions = NULL;
PG_RETURN_POINTER(amroutine);
}
@@ -573,7 +574,7 @@ ginoptions(Datum reloptions, bool validate)
pendingListCleanupSize)}
};
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
+ options = parseRelOptions(reloptions, validate, NULL, RELOPT_KIND_GIN,
&numoptions);
/* if none set, we're done */
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index a290887..4a76e2d 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -87,6 +87,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = gistendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amattoptions = gistattoptions;
PG_RETURN_POINTER(amroutine);
}
@@ -1421,6 +1422,7 @@ initGISTstate(Relation index)
giststate->scanCxt = scanCxt;
giststate->tempCxt = scanCxt; /* caller must change this if needed */
giststate->tupdesc = index->rd_att;
+ giststate->attoptions = get_index_attribute_options(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 affd635..f73bbac 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -193,11 +193,13 @@ gistindex_keytest(IndexScanDesc scan,
Datum test;
bool recheck;
GISTENTRY de;
+ bytea* attoptions;
gistdentryinit(giststate, key->sk_attno - 1, &de,
datum, r, page, offset,
FALSE, isNull);
+ attoptions = giststate->attoptions[key->sk_attno - 1];
/*
* Call the Consistent function to evaluate the test. The
* arguments are the index datum (as a GISTENTRY*), the comparison
@@ -213,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(attoptions));
if (!DatumGetBool(test))
return false;
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index d394969..93bd937 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->attoptions[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->attoptions[attno]));
}
/*
@@ -425,15 +427,16 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
sv->spl_rdatum_exists = (v->spl_risnull[attno]) ? false : true;
sv->spl_ldatum = v->spl_lattr[attno];
sv->spl_rdatum = v->spl_rattr[attno];
-
+
/*
* 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->attoptions[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 fac166d..60fe6de 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -21,6 +21,13 @@
#include "storage/lmgr.h"
#include "utils/builtins.h"
+#include "access/htup_details.h"
+#include "utils/syscache.h"
+#include "utils/catcache.h"
+
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_opfamily.h"
+#include "catalog/pg_amproc.h"
/*
* Write itup vector to page, has no control of free space.
@@ -195,10 +202,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->attoptions[i]));
isnull[i] = FALSE;
}
@@ -264,10 +272,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->attoptions[attno]));
}
}
@@ -276,10 +285,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->attoptions[attno]));
return result;
}
@@ -582,9 +592,10 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
isleaf);
cep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+ DatumGetPointer(FunctionCall2Coll(&giststate->compressFn[i],
giststate->supportCollation[i],
- PointerGetDatum(¢ry)));
+ PointerGetDatum(¢ry),
+ PointerGetDatum(giststate->attoptions[i])));
compatt[i] = cep->key;
}
}
@@ -670,11 +681,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->attoptions[attno]));
/* disallow negative or NaN penalty */
if (isnan(penalty) || penalty < 0.0)
penalty = 0.0;
@@ -819,7 +831,7 @@ gistoptions(Datum reloptions, bool validate)
{"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
};
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
+ options = parseRelOptions(reloptions, validate, NULL, RELOPT_KIND_GIST,
&numoptions);
/* if none set, we're done */
@@ -864,3 +876,78 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+bytea *
+gistattoptions(Oid opfamilyoid, Oid opcintype, Datum reloptions, bool validate)
+{
+ bool result = true;
+ HeapTuple familytup;
+ Form_pg_opfamily familyform;
+ char *opfamilyname;
+ CatCList *proclist,
+ *oprlist;
+ bytea *res = NULL;
+
+ bool ok = false;
+
+ int i;
+
+ /* Fetch opfamily information */
+ familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
+ if (!HeapTupleIsValid(familytup))
+ elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
+ familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
+
+ opfamilyname = NameStr(familyform->opfname);
+
+ /* Fetch all operators and support functions of the opfamily */
+ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+
+ for (i = 0; i < proclist->n_members; i++)
+ {
+ HeapTuple proctup = &proclist->members[i]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ Oid tmp_oid = procform->amproc; // FIXME rename var;
+ /*
+ * All GiST support functions should be registered with matching
+ * left/right types
+ */
+ if (procform->amproclefttype != procform->amprocrighttype)
+ {
+ ereport(INFO,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gist opfamily %s contains support procedure %s with cross-type registration",
+ opfamilyname,
+ format_procedure(procform->amproc))));
+ result = false;
+ }
+
+ /*
+ * We can't check signatures except within the specific opclass, since
+ * we need to know the associated opckeytype in many cases.
+ */
+ if (procform->amproclefttype != opcintype)
+ continue;
+
+ switch (procform->amprocnum)
+ {
+ case GIST_GET_ATTOPT_DEST_PROC:
+// elog(WARNING, "2Proper function attopdesc for opclass found %i",(unsigned int) tmp_oid);
+
+ res = DatumGetPointer(OidFunctionCall2(tmp_oid, reloptions, validate));
+
+// elog(WARNING, "Got result:'%i'",*((int *)res +1));
+
+ ok = true;
+ break;
+ }
+ }
+// if (! ok) elog(WARNING, "2Proper function attopdesc for opclass NOT FOUND");
+
+ ReleaseCatCacheList(proclist);
+ ReleaseCatCacheList(oprlist);
+ ReleaseSysCache(familytup);
+ return res;
+}
\ No newline at end of file
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 49a6c81..42a9460 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -82,6 +82,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = hashendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amattoptions = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 013394c..2255a79 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -115,6 +115,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amattoptions = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index bc679bf..d5d4132 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -66,6 +66,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amendscan = spgendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amattoptions = NULL;
PG_RETURN_POINTER(amroutine);
}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..e95d0ef 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -600,7 +600,8 @@ CheckAttributeType(const char *attname,
void
InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
- CatalogIndexState indstate)
+ CatalogIndexState indstate,
+ Datum attoptions)
{
Datum values[Natts_pg_attribute];
bool nulls[Natts_pg_attribute];
@@ -628,10 +629,14 @@ 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);
+ if (attoptions != (Datum) 0)
+ values[Anum_pg_attribute_attoptions - 1] = attoptions;
+ else
+ nulls[Anum_pg_attribute_attoptions - 1] = true;
/* 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] = true;
nulls[Anum_pg_attribute_attfdwoptions - 1] = true;
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
@@ -689,7 +694,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
attr->attstattarget = -1;
attr->attcacheoff = -1;
- InsertPgAttributeTuple(rel, attr, indstate);
+ InsertPgAttributeTuple(rel, attr, indstate, (Datum) 0);
/* Add dependency info */
myself.classId = RelationRelationId;
@@ -739,7 +744,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
attStruct.attinhcount = oidinhcount;
}
- InsertPgAttributeTuple(rel, &attStruct, indstate);
+ InsertPgAttributeTuple(rel, &attStruct, indstate, (Datum) 0);
}
}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 31a1438..93680aa 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -96,7 +96,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 *attoptions);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
IndexInfo *indexInfo,
Oid *collationOids,
@@ -486,7 +487,7 @@ InitializeAttributeOids(Relation indexRelation,
* ----------------------------------------------------------------
*/
static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Datum *attoptions)
{
Relation pg_attribute;
CatalogIndexState indstate;
@@ -514,7 +515,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
Assert(indexTupDesc->attrs[i]->attnum == i + 1);
Assert(indexTupDesc->attrs[i]->attcacheoff == -1);
- InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate);
+ InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate,
+ attoptions ? attoptions[i]: (Datum) 0);
}
CatalogCloseIndexes(indstate);
@@ -864,6 +866,7 @@ index_create(Relation heapRelation,
indexRelation->rd_rel->relowner = heapRelation->rd_rel->relowner;
indexRelation->rd_rel->relam = accessMethodObjectId;
indexRelation->rd_rel->relhasoids = false;
+ indexRelation->rd_attoptions = NULL;
/*
* store index's pg_class entry
@@ -887,7 +890,7 @@ index_create(Relation heapRelation,
/*
* append ATTRIBUTE tuples for the index
*/
- AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+ AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, indexInfo->ii_AttOptions);
/* ----------------
* update pg_index
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..8ec4ecc 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -308,6 +308,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = NIL;
indexInfo->ii_PredicateState = NIL;
+ indexInfo->ii_AttOptions = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index cf8c816..42cddd3 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -525,7 +525,8 @@ do_analyze_rel(Relation onerel, int options, VacuumParams *params,
* If the appropriate flavor of the n_distinct option is
* specified, override with the corresponding value.
*/
- aopt = get_attribute_options(onerel->rd_id, stats->attr->attnum);
+ aopt = get_attribute_options(onerel->rd_rel->relkind, onerel->rd_id,
+ stats->attr->attnum);
if (aopt != NULL)
{
float8 n_distinct;
@@ -811,7 +812,7 @@ compute_index_stats(Relation onerel, double totalrows,
{
VacAttrStats *stats = thisdata->vacattrstats[i];
AttributeOpts *aopt =
- get_attribute_options(stats->attr->attrelid,
+ get_attribute_options(RELKIND_RELATION, stats->attr->attrelid,
stats->attr->attnum);
stats->exprvals = exprvals + i;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..b789d7b 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -67,6 +67,7 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
List *exclusionOpNames,
Oid relId,
char *accessMethodName, Oid accessMethodId,
+ IndexAmRoutine *amRoutine,
bool amcanorder,
bool isconstraint);
static Oid GetIndexOpClass(List *opclass, Oid attrType,
@@ -193,6 +194,7 @@ CheckIndexCompatible(Oid oldId,
coloptions, attributeList,
exclusionOpNames, relationId,
accessMethodName, accessMethodId,
+ amRoutine,
amcanorder, isconstraint);
@@ -523,7 +525,6 @@ DefineIndex(Oid relationId,
amcanorder = amRoutine->amcanorder;
amoptions = amRoutine->amoptions;
- pfree(amRoutine);
ReleaseSysCache(tuple);
/*
@@ -568,7 +569,9 @@ DefineIndex(Oid relationId,
coloptions, stmt->indexParams,
stmt->excludeOpNames, relationId,
accessMethodName, accessMethodId,
+ amRoutine,
amcanorder, stmt->isconstraint);
+ pfree(amRoutine);
/*
* Extra checks when creating a PRIMARY KEY index.
@@ -997,6 +1000,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Oid relId,
char *accessMethodName,
Oid accessMethodId,
+ IndexAmRoutine *amRoutine,
bool amcanorder,
bool isconstraint)
{
@@ -1017,7 +1021,8 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
}
else
nextExclOp = NULL;
-
+
+ indexInfo->ii_AttOptions = (Datum*) palloc0(sizeof(Datum) * list_length(attList)); //FIXME list_length(attList) to ncols
/*
* process attributeList
*/
@@ -1249,6 +1254,46 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName)));
}
+if (amRoutine->amattoptions)
+{
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid opfamilyoid;
+ Oid opcintype;
+ Datum attoptions;
+
+
+ attoptions = transformRelOptions((Datum) 0, attribute->options,
+ NULL, NULL, false, false);
+
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(classOidP[attn]));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", classOidP[attn]);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ opfamilyoid = classform->opcfamily;
+ opcintype = classform->opcintype;
+
+
+ amRoutine->amattoptions(opfamilyoid, opcintype, attoptions, true); /* Do validate */
+ indexInfo->ii_AttOptions[attn] = attoptions;
+
+ //
+ if(indexInfo->ii_AttOptions[attn])
+ {
+// elog(WARNING, "Have amattoptions: %i",*((int *)indexInfo->ii_AttOptions[attn] +1));
+ } else
+ {
+// elog(WARNING, "Have amattoptions, but there is no options here");
+ }
+ ReleaseSysCache(classtup);
+} else
+{
+ indexInfo->ii_AttOptions[attn] = (Datum) 0;
+// elog(WARNING, "No amattoptions");
+// FIXME add ERROR if no attoptions is supporsed to be here...
+}
+
attn++;
}
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..2d2456e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4858,7 +4858,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
ReleaseSysCache(typeTuple);
- InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+ InsertPgAttributeTuple(attrdesc, &attribute, NULL, (Datum)0);
heap_close(attrdesc, RowExclusiveLock);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 20e38f0..4b2edbe 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2591,6 +2591,7 @@ _copyIndexElem(const IndexElem *from)
COPY_NODE_FIELD(opclass);
COPY_SCALAR_FIELD(ordering);
COPY_SCALAR_FIELD(nulls_ordering);
+ COPY_NODE_FIELD(options);
return newnode;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 18ec5f0..56b3d86 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -347,7 +347,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <list> stmtblock stmtmulti
OptTableElementList TableElementList OptInherit definition
OptTypedTableElementList TypedTableElementList
- reloptions opt_reloptions
+ reloptions opt_reloptions opt_attoptions
OptWith distinct_clause opt_all_clause opt_definition func_args func_args_list
func_args_with_defaults func_args_with_defaults_list
aggr_args aggr_args_list
@@ -2973,7 +2973,7 @@ columnDef: ColId Typename create_generic_options ColQualList
}
;
-columnOptions: ColId WITH OPTIONS ColQualList
+columnOptions: ColId WITH_LA OPTIONS ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
@@ -6707,7 +6707,8 @@ index_params: index_elem { $$ = list_make1($1); }
* 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 opt_collate opt_class opt_attoptions opt_asc_desc opt_nulls_order
{
$$ = makeNode(IndexElem);
$$->name = $1;
@@ -6715,10 +6716,11 @@ index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
$$->indexcolname = NULL;
$$->collation = $2;
$$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
+ $$->options = $4;
+ $$->ordering = $5;
+ $$->nulls_ordering = $6;
}
- | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order
+ | func_expr_windowless opt_collate opt_class opt_attoptions opt_asc_desc opt_nulls_order
{
$$ = makeNode(IndexElem);
$$->name = NULL;
@@ -6726,10 +6728,11 @@ index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
$$->indexcolname = NULL;
$$->collation = $2;
$$->opclass = $3;
- $$->ordering = $4;
- $$->nulls_ordering = $5;
+ $$->options = $4;
+ $$->ordering = $5;
+ $$->nulls_ordering = $6;
}
- | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' opt_collate opt_class opt_attoptions opt_asc_desc opt_nulls_order
{
$$ = makeNode(IndexElem);
$$->name = NULL;
@@ -6737,8 +6740,9 @@ index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order
$$->indexcolname = NULL;
$$->collation = $4;
$$->opclass = $5;
- $$->ordering = $6;
- $$->nulls_ordering = $7;
+ $$->options = $6;
+ $$->ordering = $7;
+ $$->nulls_ordering = $8;
}
;
@@ -6751,6 +6755,10 @@ opt_class: any_name { $$ = $1; }
| /*EMPTY*/ { $$ = NIL; }
;
+opt_attoptions: WITH_LA OPTIONS '(' reloption_list ')' { $$ = $4; }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
+
opt_asc_desc: ASC { $$ = SORTBY_ASC; }
| DESC { $$ = SORTBY_DESC; }
| /*EMPTY*/ { $$ = SORTBY_DEFAULT; }
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index 61d24e1..7a14e1e 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -185,6 +185,7 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
{
case TIME:
case ORDINALITY:
+ case OPTIONS:
cur_token = WITH_LA;
break;
}
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 54eb864..ae97b9d 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -22,7 +22,8 @@
#include "utils/hsearch.h"
#include "utils/inval.h"
#include "utils/syscache.h"
-
+#include "utils/rel.h"
+#include "utils/memutils.h"
/* Hash table for informations about each attribute's options */
static HTAB *AttoptCacheHash = NULL;
@@ -101,7 +102,7 @@ InitializeAttoptCache(void)
* Fetch attribute options for a specified table OID.
*/
AttributeOpts *
-get_attribute_options(Oid attrelid, int attnum)
+get_attribute_options(char relkind, Oid attrelid, int attnum)
{
AttoptCacheKey key;
AttoptCacheEntry *attopt;
@@ -177,3 +178,87 @@ get_attribute_options(Oid attrelid, int attnum)
memcpy(result, attopt->opts, VARSIZE(attopt->opts));
return result;
}
+
+bytea **
+get_index_attribute_options(Relation index)
+{
+ int natts;
+ int i;
+ IndexAmRoutine* amRoutine;
+ MemoryContext oldcxt;
+
+// elog(WARNING, "get_index_attribute_options");
+
+ if (index->rd_attoptions)
+ {
+// elog(WARNING, "hot get");
+
+ if (index->rd_attoptions[0])
+ {
+// elog(WARNING, "%i ",*((int *) index->rd_attoptions[i] + 1) );
+ }
+ }
+
+ if (index->rd_attoptions)
+ return index->rd_attoptions;
+ natts = index->rd_att->natts;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ index->rd_attoptions = palloc(sizeof (bytea **)*natts); // FIXME palloc is wrong here; FIXME free this memory somewhere
+ MemoryContextSwitchTo(oldcxt);
+
+ amRoutine = GetIndexAmRoutine(index->rd_amhandler);
+
+ for(i=0;i<natts;i++)
+ {
+ if (amRoutine->amattoptions)
+ {
+ HeapTuple tp;
+ Datum raw_attoptions = (Datum) 0;
+ Datum parsed_attoptions;
+
+ tp = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(index->rd_id),
+ Int16GetDatum(i + 1));
+ if (HeapTupleIsValid(tp))
+ {
+ bool isNull;
+
+ raw_attoptions = SysCacheGetAttr(ATTNUM,
+ tp,
+ Anum_pg_attribute_attoptions,
+ &isNull);
+ if (isNull)
+ raw_attoptions = (Datum) 0;
+// if (!isNull) elog(WARNING, "Non null attoptions!");
+ ReleaseSysCache(tp);
+ }
+ parsed_attoptions = amRoutine->amattoptions(
+ index->rd_opfamily[i], index->rd_opcintype[i],
+ raw_attoptions, false);
+
+ if (parsed_attoptions)
+ {
+ Datum result;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+ result = palloc(VARSIZE(parsed_attoptions));
+ MemoryContextSwitchTo(oldcxt);
+
+ memcpy(result, parsed_attoptions, VARSIZE(parsed_attoptions));
+
+ index->rd_attoptions[i] = result;
+ } else
+ {
+ index->rd_attoptions[i] = NULL;
+ }
+
+// elog(WARNING, "coldget %i ",*((int *) index->rd_attoptions[i] + 1) );
+
+ } else
+ {
+ index->rd_attoptions[i] = NULL;
+ }
+ }
+ return index->rd_attoptions;
+}
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
index 35f1061..13804fa 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -104,6 +104,10 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+//typedef char *(*amgetattopdesc_function) (Oid opclassoid);
+
+typedef bytea *(*amattoptions_function) (Oid opfamilyoid, Oid opcintype,
+ Datum reloptions, bool validate);
/*
* API struct for an index AM. Note this must be stored in a single palloc'd
@@ -162,6 +166,8 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+// amgetattopdesc_function amgetattopdesc; /* can be NULL */
+ amattoptions_function amattoptions; /* can be NULL */
} IndexAmRoutine;
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index 4343d6f..d8b5ac8 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_GET_ATTOPT_DEST_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 f9732ba..6989afd 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -95,6 +95,8 @@ typedef struct GISTSTATE
/* Collations to pass to the support functions */
Oid supportCollation[INDEX_MAX_KEYS];
+
+ bytea **attoptions; /* FIXME add comment */
} GISTSTATE;
@@ -440,6 +442,8 @@ extern void gistdoinsert(Relation r,
Size freespace,
GISTSTATE *GISTstate);
+
+
/* A List of these is returned from gistplacetopage() in *splitinfo */
typedef struct
{
@@ -540,6 +544,10 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
extern XLogRecPtr gistGetFakeLSN(Relation rel);
+// extern char *gistgetattopdesc(Oid opclassoid);
+extern bytea *gistattoptions(Oid opfamilyoid, Oid opcintype,
+ Datum reloptions, bool validate);
+
/* gistvacuum.c */
extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats,
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 469ac67..216f0c7 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -126,6 +126,13 @@ typedef struct
int offset; /* offset of field in result struct */
} relopt_parse_elt;
+typedef struct
+{
+ relopt_gen **definitions;
+ int num;
+ int max;
+ bool need_initialization;
+} options_catalog;
/*
* These macros exist for the convenience of amoptions writers (but consider
@@ -245,14 +252,16 @@ typedef struct
extern relopt_kind add_reloption_kind(void);
-extern void add_bool_reloption(bits32 kinds, char *name, char *desc,
- bool default_val);
-extern void add_int_reloption(bits32 kinds, char *name, char *desc,
- int default_val, int min_val, int max_val);
-extern void add_real_reloption(bits32 kinds, char *name, char *desc,
- double default_val, double min_val, double max_val);
-extern void add_string_reloption(bits32 kinds, char *name, char *desc,
- char *default_val, validate_string_relopt validator);
+extern void add_bool_reloption(bits32 kinds, options_catalog * catalog,
+ char *name, char *desc, bool default_val);
+extern void add_int_reloption(bits32 kinds, options_catalog * catalog,
+ char *name, char *desc, int default_val, int min_val, int max_val);
+extern void add_real_reloption(bits32 kinds, options_catalog * catalog,
+ char *name, char *desc, double default_val,
+ double min_val, double max_val);
+extern void add_string_reloption(bits32 kinds, options_catalog * catalog,
+ char *name, char *desc, char *default_val,
+ validate_string_relopt validator);
extern Datum transformRelOptions(Datum oldOptions, List *defList,
char *namspace, char *validnsps[],
@@ -261,7 +270,7 @@ extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
- relopt_kind kind, int *numrelopts);
+ options_catalog * catalog, relopt_kind kind, int *numrelopts);
extern void *allocateReloptStruct(Size base, relopt_value *options,
int numoptions);
extern void fillRelOptions(void *rdopts, Size basesize,
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..a3654a2 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -87,7 +87,8 @@ extern List *heap_truncate_find_FKs(List *relationIds);
extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
- CatalogIndexState indstate);
+ CatalogIndexState indstate,
+ Datum attoptions);
extern void InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index ee97c5d..1540748 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 */
+
#endif
} FormData_pg_index;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index ee4e189..6af4b39 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -40,6 +40,7 @@
* ExpressionsState exec state for expressions, or NIL if none
* Predicate partial-index predicate, or NIL if none
* PredicateState exec state for predicate, or NIL if none
+ * AttOptions FIXME
* ExclusionOps Per-column exclusion operators, or NULL if none
* ExclusionProcs Underlying function OIDs for ExclusionOps
* ExclusionStrats Opclass strategy numbers for ExclusionOps
@@ -64,6 +65,7 @@ typedef struct IndexInfo
List *ii_ExpressionsState; /* list of ExprState */
List *ii_Predicate; /* list of Expr */
List *ii_PredicateState; /* list of ExprState */
+ Datum *ii_AttOptions; /* array of attrubute options */
Oid *ii_ExclusionOps; /* array with one entry per column */
Oid *ii_ExclusionProcs; /* array with one entry per column */
uint16 *ii_ExclusionStrats; /* array with one entry per column */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 714cf15..fe5c68b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -638,6 +638,7 @@ typedef struct IndexElem
List *opclass; /* name of desired opclass; NIL = default */
SortByDir ordering; /* ASC/DESC/default */
SortByNulls nulls_ordering; /* FIRST/LAST/default */
+ List *options; /* WITH OPTIONS clause options: a list of DefElem */
} IndexElem;
/*
diff --git a/src/include/utils/attoptcache.h b/src/include/utils/attoptcache.h
index cf8ead0..4411238 100644
--- a/src/include/utils/attoptcache.h
+++ b/src/include/utils/attoptcache.h
@@ -23,6 +23,7 @@ typedef struct AttributeOpts
float8 n_distinct_inherited;
} AttributeOpts;
-AttributeOpts *get_attribute_options(Oid spcid, int attnum);
+AttributeOpts *get_attribute_options(char relkind, Oid spcid, int attnum);
+bytea **get_index_attribute_options(Relation index);
#endif /* ATTOPTCACHE_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index a0ba417..314a267 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -146,6 +146,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_attoptions; /* FIXME */
/*
* foreign-table support
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers