diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml
new file mode 100644
index cd4a8d0..dcdc297
*** a/doc/src/sgml/spgist.sgml
--- b/doc/src/sgml/spgist.sgml
***************
*** 240,259 ****
There are five user-defined methods that an index operator class for
! SP-GiST must provide. All five follow the convention
! of accepting two internal> arguments, the first of which is a
! pointer to a C struct containing input values for the support method,
! while the second argument is a pointer to a C struct where output values
! must be placed. Four of the methods just return void>, since
! all their results appear in the output struct; but
leaf_consistent> additionally returns a boolean> result.
The methods must not modify any fields of their input structs. In all
cases, the output struct is initialized to zeroes before calling the
! user-defined method.
! The five user-defined methods are:
--- 240,260 ----
There are five user-defined methods that an index operator class for
! SP-GiST must provide and one optional. All five mandatory
! methos follow the convention of accepting two internal> arguments,
! the first of which is a pointer to a C struct containing input values for
! the support method, while the second argument is a pointer to a C struct
! where output values must be placed. Four of the methods just return
! void>, since all their results appear in the output struct; but
leaf_consistent> additionally returns a boolean> result.
The methods must not modify any fields of their input structs. In all
cases, the output struct is initialized to zeroes before calling the
! user-defined method. Optional method compress> accepts
! datum to be indexed and returns values which actually will be indexed.
! The five mandatory user-defined methods are:
*************** typedef struct spgConfigOut
*** 283,288 ****
--- 284,290 ----
{
Oid prefixType; /* Data type of inner-tuple prefixes */
Oid labelType; /* Data type of inner-tuple node labels */
+ Oid leafType; /* Data type of leaf */
bool canReturnData; /* Opclass can reconstruct original data */
bool longValuesOK; /* Opclass can cope with values > 1 page */
} spgConfigOut;
*************** typedef struct spgConfigOut
*** 303,309 ****
longValuesOK> should be set true only when the
attType> is of variable length and the operator
class is capable of segmenting long values by repeated suffixing
! (see ).
--- 305,319 ----
longValuesOK> should be set true only when the
attType> is of variable length and the operator
class is capable of segmenting long values by repeated suffixing
! (see ). leafType>
! usually has the same value as attType> but if
! it's different then optional method compress>
! should be provided. Method compress> is responsible
! for transformation from attType> to
! leafType>. In this case all other function should
! accept leafType> values. Note: both consistent
! functions will get scankeys> unchanged, without
! compress> transformation.
*************** typedef struct spgInnerConsistentOut
*** 624,630 ****
reconstructedValue> is the value reconstructed for the
parent tuple; it is (Datum) 0> at the root level or if the
inner_consistent> function did not provide a value at the
! parent level.
traversalValue> is a pointer to any traverse data
passed down from the previous call of inner_consistent>
on the parent index tuple, or NULL at the root level.
--- 634,641 ----
reconstructedValue> is the value reconstructed for the
parent tuple; it is (Datum) 0> at the root level or if the
inner_consistent> function did not provide a value at the
! parent level. reconstructedValue> should be always a
! spgConfigOut>.leafType> type.
traversalValue> is a pointer to any traverse data
passed down from the previous call of inner_consistent>
on the parent index tuple, or NULL at the root level.
*************** typedef struct spgLeafConsistentOut
*** 730,736 ****
reconstructedValue> is the value reconstructed for the
parent tuple; it is (Datum) 0> at the root level or if the
inner_consistent> function did not provide a value at the
! parent level.
traversalValue> is a pointer to any traverse data
passed down from the previous call of inner_consistent>
on the parent index tuple, or NULL at the root level.
--- 741,748 ----
reconstructedValue> is the value reconstructed for the
parent tuple; it is (Datum) 0> at the root level or if the
inner_consistent> function did not provide a value at the
! parent level. reconstructedValue> should be always a
! spgConfigOut>.leafType> type.
traversalValue> is a pointer to any traverse data
passed down from the previous call of inner_consistent>
on the parent index tuple, or NULL at the root level.
*************** typedef struct spgLeafConsistentOut
*** 757,762 ****
--- 769,792 ----
+
+ The optional user-defined method is:
+
+
+
+
+ Datum compress(Datum in)>
+
+
+ Converts the data item into a format suitable for physical storage in
+ an index page. It accepts spgConfigIn>.attType>
+ value and return spgConfigOut>.leafType>
+ value. Output value should not be toasted.
+
+
+
+
+
All the SP-GiST support methods are normally called in a short-lived
memory context; that is, CurrentMemoryContext> will be reset
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
new file mode 100644
index b0702a7..68c3f45
*** a/src/backend/access/spgist/spgdoinsert.c
--- b/src/backend/access/spgist/spgdoinsert.c
*************** spgdoinsert(Relation index, SpGistState
*** 1899,1919 ****
FmgrInfo *procinfo = NULL;
/*
! * Look up FmgrInfo of the user-defined choose function once, to save
! * cycles in the loop below.
*/
if (!isnull)
! procinfo = index_getprocinfo(index, 1, SPGIST_CHOOSE_PROC);
/*
! * Since we don't use index_form_tuple in this AM, we have to make sure
! * value to be inserted is not toasted; FormIndexDatum doesn't guarantee
! * that.
*/
! if (!isnull && state->attType.attlen == -1)
! datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
!
! leafDatum = datum;
/*
* Compute space needed for a leaf tuple containing the given datum.
--- 1899,1939 ----
FmgrInfo *procinfo = NULL;
/*
! * Prepare the leaf datum to insert.
! *
! * If there is an optional "compress" method, call it to form the leaf
! * datum from the input datum. Otherwise we will store the input datum as
! * is. (We have to detoast it, though. We assume the "compress" method to
! * return an untoasted value.)
*/
if (!isnull)
! {
! if (OidIsValid(index_getprocid(index, 1, SPGIST_COMPRESS_PROC)))
! {
! procinfo = index_getprocinfo(index, 1, SPGIST_COMPRESS_PROC);
! leafDatum = FunctionCall1Coll(procinfo,
! index->rd_indcollation[0],
! datum);
! }
! else
! {
! Assert(state->attLeafType.type == state->attType.type);
!
! if (state->attType.attlen == -1)
! leafDatum = PointerGetDatum(PG_DETOAST_DATUM(datum));
! else
! leafDatum = datum;
! }
! }
! else
! leafDatum = (Datum) 0;
/*
! * Look up FmgrInfo of the user-defined choose function once, to save
! * cycles in the loop below.
*/
! if (!isnull)
! procinfo = index_getprocinfo(index, 1, SPGIST_CHOOSE_PROC);
/*
* Compute space needed for a leaf tuple containing the given datum.
*************** spgdoinsert(Relation index, SpGistState
*** 1923,1929 ****
*/
if (!isnull)
leafSize = SGLTHDRSZ + sizeof(ItemIdData) +
! SpGistGetTypeSize(&state->attType, leafDatum);
else
leafSize = SGDTSIZE + sizeof(ItemIdData);
--- 1943,1949 ----
*/
if (!isnull)
leafSize = SGLTHDRSZ + sizeof(ItemIdData) +
! SpGistGetTypeSize(&state->attLeafType, leafDatum);
else
leafSize = SGDTSIZE + sizeof(ItemIdData);
*************** spgdoinsert(Relation index, SpGistState
*** 2138,2144 ****
{
leafDatum = out.result.matchNode.restDatum;
leafSize = SGLTHDRSZ + sizeof(ItemIdData) +
! SpGistGetTypeSize(&state->attType, leafDatum);
}
/*
--- 2158,2164 ----
{
leafDatum = out.result.matchNode.restDatum;
leafSize = SGLTHDRSZ + sizeof(ItemIdData) +
! SpGistGetTypeSize(&state->attLeafType, leafDatum);
}
/*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
new file mode 100644
index 22f64b0..8a1311d
*** a/src/backend/access/spgist/spgutils.c
--- b/src/backend/access/spgist/spgutils.c
*************** spgGetCache(Relation index)
*** 124,130 ****
PointerGetDatum(&cache->config));
/* Get the information we need about each relevant datatype */
! fillTypeDesc(&cache->attType, atttype);
fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
fillTypeDesc(&cache->attLabelType, cache->config.labelType);
--- 124,146 ----
PointerGetDatum(&cache->config));
/* Get the information we need about each relevant datatype */
! if (OidIsValid(cache->config.leafType) &&
! cache->config.leafType != atttype)
! {
! if (!OidIsValid(index_getprocid(index, 1, SPGIST_COMPRESS_PROC)))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("compress method must not defined when leaf type is different from input type")));
!
! fillTypeDesc(&cache->attType, atttype);
! fillTypeDesc(&cache->attLeafType, cache->config.leafType);
! }
! else
! {
! fillTypeDesc(&cache->attType, atttype);
! cache->attLeafType = cache->attType;
! }
!
fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
fillTypeDesc(&cache->attLabelType, cache->config.labelType);
*************** initSpGistState(SpGistState *state, Rela
*** 164,169 ****
--- 180,186 ----
state->config = cache->config;
state->attType = cache->attType;
+ state->attLeafType = cache->attLeafType;
state->attPrefixType = cache->attPrefixType;
state->attLabelType = cache->attLabelType;
*************** spgFormLeafTuple(SpGistState *state, Ite
*** 598,604 ****
/* compute space needed (note result is already maxaligned) */
size = SGLTHDRSZ;
if (!isnull)
! size += SpGistGetTypeSize(&state->attType, datum);
/*
* Ensure that we can replace the tuple with a dead tuple later. This
--- 615,621 ----
/* compute space needed (note result is already maxaligned) */
size = SGLTHDRSZ;
if (!isnull)
! size += SpGistGetTypeSize(&state->attLeafType, datum);
/*
* Ensure that we can replace the tuple with a dead tuple later. This
*************** spgFormLeafTuple(SpGistState *state, Ite
*** 614,620 ****
tup->nextOffset = InvalidOffsetNumber;
tup->heapPtr = *heapPtr;
if (!isnull)
! memcpyDatum(SGLTDATAPTR(tup), &state->attType, datum);
return tup;
}
--- 631,637 ----
tup->nextOffset = InvalidOffsetNumber;
tup->heapPtr = *heapPtr;
if (!isnull)
! memcpyDatum(SGLTDATAPTR(tup), &state->attLeafType, datum);
return tup;
}
diff --git a/src/backend/access/spgist/spgvalidate.c b/src/backend/access/spgist/spgvalidate.c
new file mode 100644
index 157cf2a..514da47
*** a/src/backend/access/spgist/spgvalidate.c
--- b/src/backend/access/spgist/spgvalidate.c
*************** spgvalidate(Oid opclassoid)
*** 52,57 ****
--- 52,61 ----
OpFamilyOpFuncGroup *opclassgroup;
int i;
ListCell *lc;
+ spgConfigIn configIn;
+ spgConfigOut configOut;
+ Oid configOutLefttype = InvalidOid;
+ Oid configOutRighttype = InvalidOid;
/* Fetch opclass information */
classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
*************** spgvalidate(Oid opclassoid)
*** 100,105 ****
--- 104,118 ----
switch (procform->amprocnum)
{
case SPGIST_CONFIG_PROC:
+ ok = check_amproc_signature(procform->amproc, VOIDOID, true,
+ 2, 2, INTERNALOID, INTERNALOID);
+ configIn.attType = procform->amproclefttype;
+ OidFunctionCall2(procform->amproc,
+ PointerGetDatum(&configIn),
+ PointerGetDatum(&configOut));
+ configOutLefttype = procform->amproclefttype;
+ configOutRighttype = procform->amprocrighttype;
+ break;
case SPGIST_CHOOSE_PROC:
case SPGIST_PICKSPLIT_PROC:
case SPGIST_INNER_CONSISTENT_PROC:
*************** spgvalidate(Oid opclassoid)
*** 110,115 ****
--- 123,137 ----
ok = check_amproc_signature(procform->amproc, BOOLOID, true,
2, 2, INTERNALOID, INTERNALOID);
break;
+ case SPGIST_COMPRESS_PROC:
+ if (configOutLefttype != procform->amproclefttype ||
+ configOutRighttype != procform->amprocrighttype)
+ ok = false;
+ else
+ ok = check_amproc_signature(procform->amproc,
+ configOut.leafType, true,
+ 1, 1, procform->amproclefttype);
+ break;
default:
ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
*************** spgvalidate(Oid opclassoid)
*** 212,218 ****
if (thisgroup->lefttype != thisgroup->righttype)
continue;
! for (i = 1; i <= SPGISTNProc; i++)
{
if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
--- 234,240 ----
if (thisgroup->lefttype != thisgroup->righttype)
continue;
! for (i = 1; i <= SPGISTNRequiredProc; i++)
{
if ((thisgroup->functionset & (((uint64) 1) << i)) != 0)
continue; /* got it */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
new file mode 100644
index d1bc396..a477278
*** a/src/include/access/spgist.h
--- b/src/include/access/spgist.h
***************
*** 30,36 ****
#define SPGIST_PICKSPLIT_PROC 3
#define SPGIST_INNER_CONSISTENT_PROC 4
#define SPGIST_LEAF_CONSISTENT_PROC 5
! #define SPGISTNProc 5
/*
* Argument structs for spg_config method
--- 30,38 ----
#define SPGIST_PICKSPLIT_PROC 3
#define SPGIST_INNER_CONSISTENT_PROC 4
#define SPGIST_LEAF_CONSISTENT_PROC 5
! #define SPGIST_COMPRESS_PROC 6
! #define SPGISTNRequiredProc 5
! #define SPGISTNProc 6
/*
* Argument structs for spg_config method
*************** typedef struct spgConfigOut
*** 44,49 ****
--- 46,52 ----
{
Oid prefixType; /* Data type of inner-tuple prefixes */
Oid labelType; /* Data type of inner-tuple node labels */
+ Oid leafType; /* Data type of leaf (type of SPGIST_COMPRESS_PROC output) */
bool canReturnData; /* Opclass can reconstruct original data */
bool longValuesOK; /* Opclass can cope with values > 1 page */
} spgConfigOut;
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
new file mode 100644
index 1c4b321..69dc2ba
*** a/src/include/access/spgist_private.h
--- b/src/include/access/spgist_private.h
*************** typedef struct SpGistState
*** 119,125 ****
{
spgConfigOut config; /* filled in by opclass config method */
! SpGistTypeDesc attType; /* type of input data and leaf values */
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
SpGistTypeDesc attLabelType; /* type of node label values */
--- 119,126 ----
{
spgConfigOut config; /* filled in by opclass config method */
! SpGistTypeDesc attType; /* type of values to be indexed/restored */
! SpGistTypeDesc attLeafType; /* type of leaf values */
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
SpGistTypeDesc attLabelType; /* type of node label values */
*************** typedef struct SpGistCache
*** 178,184 ****
{
spgConfigOut config; /* filled in by opclass config method */
! SpGistTypeDesc attType; /* type of input data and leaf values */
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
SpGistTypeDesc attLabelType; /* type of node label values */
--- 179,186 ----
{
spgConfigOut config; /* filled in by opclass config method */
! SpGistTypeDesc attType; /* type of values to be indexed/restored */
! SpGistTypeDesc attLeafType; /* type of leaf values */
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
SpGistTypeDesc attLabelType; /* type of node label values */
*************** typedef SpGistLeafTupleData *SpGistLeafT
*** 300,306 ****
#define SGLTHDRSZ MAXALIGN(sizeof(SpGistLeafTupleData))
#define SGLTDATAPTR(x) (((char *) (x)) + SGLTHDRSZ)
! #define SGLTDATUM(x, s) ((s)->attType.attbyval ? \
*(Datum *) SGLTDATAPTR(x) : \
PointerGetDatum(SGLTDATAPTR(x)))
--- 302,308 ----
#define SGLTHDRSZ MAXALIGN(sizeof(SpGistLeafTupleData))
#define SGLTDATAPTR(x) (((char *) (x)) + SGLTHDRSZ)
! #define SGLTDATUM(x, s) ((s)->attLeafType.attbyval ? \
*(Datum *) SGLTDATAPTR(x) : \
PointerGetDatum(SGLTDATAPTR(x)))