</para>
 
  <para>
-   There are seven methods that an index operator class for
-   <acronym>GiST</acronym> must provide, and two that are optional.
+   There are five methods that an index operator class for
+   <acronym>GiST</acronym> must provide, and four that are optional.
    Correctness of the index is ensured
    by proper implementation of the <function>same</>, <function>consistent</>
    and <function>union</> methods, while efficiency (size and speed) of the
    index will depend on the <function>penalty</> and <function>picksplit</>
    methods.
-   The remaining two basic methods are <function>compress</> and
+   Two optional methods are <function>compress</> and
    <function>decompress</>, which allow an index to have internal tree data of
    a different type than the data it indexes. The leaves are to be of the
    indexed data type, while the other tree nodes can be of any C struct (but
    The optional eighth method is <function>distance</>, which is needed
    if the operator class wishes to support ordered scans (nearest-neighbor
    searches). The optional ninth method <function>fetch</> is needed if the
-   operator class wishes to support index-only scans.
+   operator class wishes to support index-only scans, except when the
+   <function>compress</> method is omitted.
  </para>
 
  <variablelist>
      <term><function>compress</></term>
      <listitem>
       <para>
-       Converts the data item into a format suitable for physical storage in
+       Converts a data item into a format suitable for physical storage in
        an index page.
+       If the <function>compress</> method is omitted, data items are stored
+       in the index without modification.
       </para>
 
       <para>
      <term><function>decompress</></term>
      <listitem>
       <para>
-       The reverse of the <function>compress</function> method.  Converts the
-       index representation of the data item into a format that can be
-       manipulated by the other GiST methods in the operator class.
+       Converts the stored representation of a data item into a format that
+       can be manipulated by the other GiST methods in the operator class.
+       If the <function>decompress</> method is omitted, it is assumed that
+       the other GiST methods can work directly on the stored data format.
+       (<function>decompress</> is not necessarily the reverse of
+       the <function>compress</function> method; in particular,
+       if <function>compress</function> is lossy then it's impossible
+       for <function>decompress</> to exactly reconstruct the original
+       data.  <function>decompress</> is not necessarily equivalent
+       to <function>fetch</>, either, since the other GiST methods might not
+       require full reconstruction of the data.)
       </para>
 
       <para>
 </programlisting>
 
         The above skeleton is suitable for the case where no decompression
-        is needed.
+        is needed.  (But, of course, omitting the method altogether is even
+        easier, and is recommended in such cases.)
       </para>
      </listitem>
     </varlistentry>
         struct, whose <structfield>key</> field contains the same datum in its
         original, uncompressed form. If the opclass's compress function does
         nothing for leaf entries, the <function>fetch</> method can return the
-        argument as-is.
+        argument as-is.  Or, if the opclass does not have a compress function,
+        the <function>fetch</> method can be omitted as well, since it would
+        necessarily be a no-op.
        </para>
 
        <para>
 
        fmgr_info_copy(&(giststate->unionFn[i]),
                       index_getprocinfo(index, i + 1, GIST_UNION_PROC),
                       scanCxt);
-       fmgr_info_copy(&(giststate->compressFn[i]),
-                      index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
-                      scanCxt);
-       fmgr_info_copy(&(giststate->decompressFn[i]),
-                      index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
-                      scanCxt);
+
+       /* opclasses are not required to provide a Compress method */
+       if (OidIsValid(index_getprocid(index, i + 1, GIST_COMPRESS_PROC)))
+           fmgr_info_copy(&(giststate->compressFn[i]),
+                          index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
+                          scanCxt);
+       else
+           giststate->compressFn[i].fn_oid = InvalidOid;
+
+       /* opclasses are not required to provide a Decompress method */
+       if (OidIsValid(index_getprocid(index, i + 1, GIST_DECOMPRESS_PROC)))
+           fmgr_info_copy(&(giststate->decompressFn[i]),
+                          index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
+                          scanCxt);
+       else
+           giststate->decompressFn[i].fn_oid = InvalidOid;
+
        fmgr_info_copy(&(giststate->penaltyFn[i]),
                       index_getprocinfo(index, i + 1, GIST_PENALTY_PROC),
                       scanCxt);
        fmgr_info_copy(&(giststate->equalFn[i]),
                       index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
                       scanCxt);
+
        /* opclasses are not required to provide a Distance method */
        if (OidIsValid(index_getprocid(index, i + 1, GIST_DISTANCE_PROC)))
            fmgr_info_copy(&(giststate->distanceFn[i]),
 
  * Can we do index-only scans on the given index column?
  *
  * Opclasses that implement a fetch function support index-only scans.
+ * Opclasses without compression functions also support index-only scans.
  */
 bool
 gistcanreturn(Relation index, int attno)
 {
-   if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
+   if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
+       !OidIsValid(index_getprocid(index, attno, GIST_COMPRESS_PROC)))
        return true;
    else
        return false;
 
        GISTENTRY  *dep;
 
        gistentryinit(*e, k, r, pg, o, l);
+
+       /* there may not be a decompress function in opclass */
+       if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
+           return;
+
        dep = (GISTENTRY *)
            DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
                                              giststate->supportCollation[nkey],
 
            gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
                          isleaf);
-           cep = (GISTENTRY *)
-               DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
-                                                 giststate->supportCollation[i],
-                                                 PointerGetDatum(¢ry)));
+           /* there may not be a compress function in opclass */
+           if (OidIsValid(giststate->compressFn[i].fn_oid))
+               cep = (GISTENTRY *)
+                   DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+                                                     giststate->supportCollation[i],
+                                                     PointerGetDatum(¢ry)));
+           else
+               cep = ¢ry;
            compatt[i] = cep->key;
        }
    }
            else
                fetchatt[i] = (Datum) 0;
        }
+       else if (giststate->compressFn[i].fn_oid == InvalidOid)
+       {
+           /*
+            * If opclass does not provide compress method that could change
+            * original value, att is necessarily stored in original form.
+            */
+           if (!isnull[i])
+               fetchatt[i] = datum;
+           else
+               fetchatt[i] = (Datum) 0;
+       }
        else
        {
            /*
                                 ObjectIdGetDatum(opcintype),
                                 ObjectIdGetDatum(opcintype),
                                 Int16GetDatum(procno));
+
+   /*
+    * Special case: even without a fetch function, AMPROP_RETURNABLE is true
+    * if the opclass has no compress function.
+    */
+   if (prop == AMPROP_RETURNABLE && !*res)
+   {
+       *res = !SearchSysCacheExists4(AMPROCNUM,
+                                     ObjectIdGetDatum(opfamily),
+                                     ObjectIdGetDatum(opcintype),
+                                     ObjectIdGetDatum(opcintype),
+                                     Int16GetDatum(GIST_COMPRESS_PROC));
+   }
+
    return true;
 }
 
 
        if (opclassgroup &&
            (opclassgroup->functionset & (((uint64) 1) << i)) != 0)
            continue;           /* got it */
-       if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC)
+       if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
+           i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
            continue;           /* optional methods */
        ereport(INFO,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),