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 ****
  
   <para>
    There are five user-defined methods that an index operator class for
!   <acronym>SP-GiST</acronym> must provide.  All five follow the convention
!   of accepting two <type>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 <type>void</>, since
!   all their results appear in the output struct; but
    <function>leaf_consistent</> additionally returns a <type>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.
   </para>
  
   <para>
!   The five user-defined methods are:
   </para>
  
   <variablelist>
--- 240,260 ----
  
   <para>
    There are five user-defined methods that an index operator class for
!   <acronym>SP-GiST</acronym> must provide and one optional. All five mandatory
!   methos follow the convention of accepting two <type>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 
!   <type>void</>, since all their results appear in the output struct; but
    <function>leaf_consistent</> additionally returns a <type>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 <function>compress</> accepts
!   datum to be indexed and returns values which actually will be indexed.  
   </para>
  
   <para>
!   The five mandatory user-defined methods are:
   </para>
  
   <variablelist>
*************** 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 &gt; 1 page */
  } spgConfigOut;
*************** typedef struct spgConfigOut
*** 303,309 ****
        <structfield>longValuesOK</> should be set true only when the
        <structfield>attType</> is of variable length and the operator
        class is capable of segmenting long values by repeated suffixing
!       (see <xref linkend="spgist-limits">).
       </para>
       </listitem>
      </varlistentry>
--- 305,319 ----
        <structfield>longValuesOK</> should be set true only when the
        <structfield>attType</> is of variable length and the operator
        class is capable of segmenting long values by repeated suffixing
!       (see <xref linkend="spgist-limits">). <structfield>leafType</>
!       usually has the same value as <structfield>attType</> but if
!       it's different then optional method  <function>compress</>
!       should be provided. Method  <function>compress</> is responsible
!       for transformation from <structfield>attType</> to 
!       <structfield>leafType</>. In this case all other function should
!       accept <structfield>leafType</> values. Note: both consistent
!       functions will get <structfield>scankeys</> unchanged, without
!       <function>compress</> transformation.
       </para>
       </listitem>
      </varlistentry>
*************** typedef struct spgInnerConsistentOut
*** 624,630 ****
         <structfield>reconstructedValue</> is the value reconstructed for the
         parent tuple; it is <literal>(Datum) 0</> at the root level or if the
         <function>inner_consistent</> function did not provide a value at the
!        parent level.
         <structfield>traversalValue</> is a pointer to any traverse data
         passed down from the previous call of <function>inner_consistent</>
         on the parent index tuple, or NULL at the root level.
--- 634,641 ----
         <structfield>reconstructedValue</> is the value reconstructed for the
         parent tuple; it is <literal>(Datum) 0</> at the root level or if the
         <function>inner_consistent</> function did not provide a value at the
!        parent level. <structfield>reconstructedValue</> should be always a
!        <structname>spgConfigOut</>.<structfield>leafType</> type. 
         <structfield>traversalValue</> is a pointer to any traverse data
         passed down from the previous call of <function>inner_consistent</>
         on the parent index tuple, or NULL at the root level.
*************** typedef struct spgLeafConsistentOut
*** 730,736 ****
         <structfield>reconstructedValue</> is the value reconstructed for the
         parent tuple; it is <literal>(Datum) 0</> at the root level or if the
         <function>inner_consistent</> function did not provide a value at the
!        parent level.
         <structfield>traversalValue</> is a pointer to any traverse data
         passed down from the previous call of <function>inner_consistent</>
         on the parent index tuple, or NULL at the root level.
--- 741,748 ----
         <structfield>reconstructedValue</> is the value reconstructed for the
         parent tuple; it is <literal>(Datum) 0</> at the root level or if the
         <function>inner_consistent</> function did not provide a value at the
!        parent level. <structfield>reconstructedValue</> should be always a
!        <structname>spgConfigOut</>.<structfield>leafType</> type. 
         <structfield>traversalValue</> is a pointer to any traverse data
         passed down from the previous call of <function>inner_consistent</>
         on the parent index tuple, or NULL at the root level.
*************** typedef struct spgLeafConsistentOut
*** 757,762 ****
--- 769,792 ----
      </varlistentry>
     </variablelist>
  
+  <para>
+   The optional user-defined method is:
+  </para>
+ 
+  <variablelist>
+     <varlistentry>
+      <term><function>Datum compress(Datum in)</></term>
+      <listitem>
+       <para>
+        Converts the data item into a format suitable for physical storage in 
+        an index page. It accepts <structname>spgConfigIn</>.<structfield>attType</>
+        value and return <structname>spgConfigOut</>.<structfield>leafType</>
+        value. Output value should not be toasted.
+       </para>
+      </listitem>
+     </varlistentry>
+   </variablelist>
+ 
    <para>
     All the SP-GiST support methods are normally called in a short-lived
     memory context; that is, <varname>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)))
  
