]> granicus.if.org Git - postgresql/commitdiff
Allow no-op GiST support functions to be omitted.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 20 Sep 2017 03:32:27 +0000 (23:32 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 20 Sep 2017 03:32:59 +0000 (23:32 -0400)
There are common use-cases in which the compress and/or decompress
functions can be omitted, with the result being that we make no
data transformation when storing or retrieving index values.
Previously, you had to provide a no-op function anyway, but this
patch allows such opclass support functions to be omitted.

Furthermore, if the compress function is omitted, then the core code
knows that the stored representation is the same as the original data.
This means we can allow index-only scans without requiring a fetch
function to be provided either.  Previously you had to provide a
no-op fetch function if you wanted IOS to work.

This reportedly provides a small performance benefit in such cases,
but IMO the real reason for doing it is just to reduce the amount of
useless boilerplate code that has to be written for GiST opclasses.

Andrey Borodin, reviewed by Dmitriy Sarafannikov

Discussion: https://postgr.es/m/CAJEAwVELVx9gYscpE=Be6iJxvdW5unZ_LkcAaVNSeOwvdwtD=A@mail.gmail.com

doc/src/sgml/gist.sgml
src/backend/access/gist/gist.c
src/backend/access/gist/gistget.c
src/backend/access/gist/gistutil.c
src/backend/access/gist/gistvalidate.c

index b3cc347e5ccc732e44d4ec549b1922f72fd4cb4d..1648eb3672f17aa2a2f5909cbe42f76ed0386f62 100644 (file)
@@ -267,14 +267,14 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
   </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
@@ -285,7 +285,8 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
    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>
@@ -468,8 +469,10 @@ my_union(PG_FUNCTION_ARGS)
      <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>
@@ -527,9 +530,17 @@ my_compress(PG_FUNCTION_ARGS)
      <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>
@@ -555,7 +566,8 @@ my_decompress(PG_FUNCTION_ARGS)
 </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>
@@ -883,7 +895,9 @@ LANGUAGE C STRICT;
         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>
index 565525bbdfceb95407137fd962f3d58bcf8fdf38..aec174cd00609968103c2ea24b036da76f4c1302 100644 (file)
@@ -1453,12 +1453,23 @@ initGISTstate(Relation index)
                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);
@@ -1468,6 +1479,7 @@ initGISTstate(Relation index)
                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]),
index 760ea0c997e4e73c134ad79b7fddfc14c8b6d52a..06dac0bb53d60776ed39112deec6cad7e03af8ef 100644 (file)
@@ -801,11 +801,13 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
  * 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;
index b6ccc1a66a132cdf4e8d52f89015823a7576771c..26d89f79ae6c573b5b1381041683f53cbc8bbf84 100644 (file)
@@ -550,6 +550,11 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
                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],
@@ -585,10 +590,14 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
 
                        gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
                                                  isleaf);
-                       cep = (GISTENTRY *)
-                               DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
-                                                                                                 giststate->supportCollation[i],
-                                                                                                 PointerGetDatum(&centry)));
+                       /* 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(&centry)));
+                       else
+                               cep = &centry;
                        compatt[i] = cep->key;
                }
        }
@@ -648,6 +657,17 @@ gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
                        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
                {
                        /*
@@ -934,6 +954,20 @@ gistproperty(Oid index_oid, int attno,
                                                                 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;
 }
 
index 42254c5f153fba605b49b35926116f4ee9a81795..42f91ac0c960c9ae431b6bebc768d1d307b769a6 100644 (file)
@@ -258,7 +258,8 @@ gistvalidate(Oid opclassoid)
                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),