</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),