Skip to content

Commit d3a4f89

Browse files
committed
Allow no-op GiST support functions to be omitted.
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
1 parent 896537f commit d3a4f89

File tree

5 files changed

+85
-22
lines changed

5 files changed

+85
-22
lines changed

doc/src/sgml/gist.sgml

+24-10
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,14 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
267267
</para>
268268

269269
<para>
270-
There are seven methods that an index operator class for
271-
<acronym>GiST</acronym> must provide, and two that are optional.
270+
There are five methods that an index operator class for
271+
<acronym>GiST</acronym> must provide, and four that are optional.
272272
Correctness of the index is ensured
273273
by proper implementation of the <function>same</>, <function>consistent</>
274274
and <function>union</> methods, while efficiency (size and speed) of the
275275
index will depend on the <function>penalty</> and <function>picksplit</>
276276
methods.
277-
The remaining two basic methods are <function>compress</> and
277+
Two optional methods are <function>compress</> and
278278
<function>decompress</>, which allow an index to have internal tree data of
279279
a different type than the data it indexes. The leaves are to be of the
280280
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);
285285
The optional eighth method is <function>distance</>, which is needed
286286
if the operator class wishes to support ordered scans (nearest-neighbor
287287
searches). The optional ninth method <function>fetch</> is needed if the
288-
operator class wishes to support index-only scans.
288+
operator class wishes to support index-only scans, except when the
289+
<function>compress</> method is omitted.
289290
</para>
290291

291292
<variablelist>
@@ -468,8 +469,10 @@ my_union(PG_FUNCTION_ARGS)
468469
<term><function>compress</></term>
469470
<listitem>
470471
<para>
471-
Converts the data item into a format suitable for physical storage in
472+
Converts a data item into a format suitable for physical storage in
472473
an index page.
474+
If the <function>compress</> method is omitted, data items are stored
475+
in the index without modification.
473476
</para>
474477

475478
<para>
@@ -527,9 +530,17 @@ my_compress(PG_FUNCTION_ARGS)
527530
<term><function>decompress</></term>
528531
<listitem>
529532
<para>
530-
The reverse of the <function>compress</function> method. Converts the
531-
index representation of the data item into a format that can be
532-
manipulated by the other GiST methods in the operator class.
533+
Converts the stored representation of a data item into a format that
534+
can be manipulated by the other GiST methods in the operator class.
535+
If the <function>decompress</> method is omitted, it is assumed that
536+
the other GiST methods can work directly on the stored data format.
537+
(<function>decompress</> is not necessarily the reverse of
538+
the <function>compress</function> method; in particular,
539+
if <function>compress</function> is lossy then it's impossible
540+
for <function>decompress</> to exactly reconstruct the original
541+
data. <function>decompress</> is not necessarily equivalent
542+
to <function>fetch</>, either, since the other GiST methods might not
543+
require full reconstruction of the data.)
533544
</para>
534545

535546
<para>
@@ -555,7 +566,8 @@ my_decompress(PG_FUNCTION_ARGS)
555566
</programlisting>
556567

557568
The above skeleton is suitable for the case where no decompression
558-
is needed.
569+
is needed. (But, of course, omitting the method altogether is even
570+
easier, and is recommended in such cases.)
559571
</para>
560572
</listitem>
561573
</varlistentry>
@@ -883,7 +895,9 @@ LANGUAGE C STRICT;
883895
struct, whose <structfield>key</> field contains the same datum in its
884896
original, uncompressed form. If the opclass's compress function does
885897
nothing for leaf entries, the <function>fetch</> method can return the
886-
argument as-is.
898+
argument as-is. Or, if the opclass does not have a compress function,
899+
the <function>fetch</> method can be omitted as well, since it would
900+
necessarily be a no-op.
887901
</para>
888902

889903
<para>

src/backend/access/gist/gist.c

+18-6
Original file line numberDiff line numberDiff line change
@@ -1453,12 +1453,23 @@ initGISTstate(Relation index)
14531453
fmgr_info_copy(&(giststate->unionFn[i]),
14541454
index_getprocinfo(index, i + 1, GIST_UNION_PROC),
14551455
scanCxt);
1456-
fmgr_info_copy(&(giststate->compressFn[i]),
1457-
index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
1458-
scanCxt);
1459-
fmgr_info_copy(&(giststate->decompressFn[i]),
1460-
index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
1461-
scanCxt);
1456+
1457+
/* opclasses are not required to provide a Compress method */
1458+
if (OidIsValid(index_getprocid(index, i + 1, GIST_COMPRESS_PROC)))
1459+
fmgr_info_copy(&(giststate->compressFn[i]),
1460+
index_getprocinfo(index, i + 1, GIST_COMPRESS_PROC),
1461+
scanCxt);
1462+
else
1463+
giststate->compressFn[i].fn_oid = InvalidOid;
1464+
1465+
/* opclasses are not required to provide a Decompress method */
1466+
if (OidIsValid(index_getprocid(index, i + 1, GIST_DECOMPRESS_PROC)))
1467+
fmgr_info_copy(&(giststate->decompressFn[i]),
1468+
index_getprocinfo(index, i + 1, GIST_DECOMPRESS_PROC),
1469+
scanCxt);
1470+
else
1471+
giststate->decompressFn[i].fn_oid = InvalidOid;
1472+
14621473
fmgr_info_copy(&(giststate->penaltyFn[i]),
14631474
index_getprocinfo(index, i + 1, GIST_PENALTY_PROC),
14641475
scanCxt);
@@ -1468,6 +1479,7 @@ initGISTstate(Relation index)
14681479
fmgr_info_copy(&(giststate->equalFn[i]),
14691480
index_getprocinfo(index, i + 1, GIST_EQUAL_PROC),
14701481
scanCxt);
1482+
14711483
/* opclasses are not required to provide a Distance method */
14721484
if (OidIsValid(index_getprocid(index, i + 1, GIST_DISTANCE_PROC)))
14731485
fmgr_info_copy(&(giststate->distanceFn[i]),

src/backend/access/gist/gistget.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -801,11 +801,13 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
801801
* Can we do index-only scans on the given index column?
802802
*
803803
* Opclasses that implement a fetch function support index-only scans.
804+
* Opclasses without compression functions also support index-only scans.
804805
*/
805806
bool
806807
gistcanreturn(Relation index, int attno)
807808
{
808-
if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
809+
if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
810+
!OidIsValid(index_getprocid(index, attno, GIST_COMPRESS_PROC)))
809811
return true;
810812
else
811813
return false;

src/backend/access/gist/gistutil.c

+38-4
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,11 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
550550
GISTENTRY *dep;
551551

552552
gistentryinit(*e, k, r, pg, o, l);
553+
554+
/* there may not be a decompress function in opclass */
555+
if (!OidIsValid(giststate->decompressFn[nkey].fn_oid))
556+
return;
557+
553558
dep = (GISTENTRY *)
554559
DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
555560
giststate->supportCollation[nkey],
@@ -585,10 +590,14 @@ gistFormTuple(GISTSTATE *giststate, Relation r,
585590

586591
gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
587592
isleaf);
588-
cep = (GISTENTRY *)
589-
DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
590-
giststate->supportCollation[i],
591-
PointerGetDatum(&centry)));
593+
/* there may not be a compress function in opclass */
594+
if (OidIsValid(giststate->compressFn[i].fn_oid))
595+
cep = (GISTENTRY *)
596+
DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
597+
giststate->supportCollation[i],
598+
PointerGetDatum(&centry)));
599+
else
600+
cep = &centry;
592601
compatt[i] = cep->key;
593602
}
594603
}
@@ -648,6 +657,17 @@ gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
648657
else
649658
fetchatt[i] = (Datum) 0;
650659
}
660+
else if (giststate->compressFn[i].fn_oid == InvalidOid)
661+
{
662+
/*
663+
* If opclass does not provide compress method that could change
664+
* original value, att is necessarily stored in original form.
665+
*/
666+
if (!isnull[i])
667+
fetchatt[i] = datum;
668+
else
669+
fetchatt[i] = (Datum) 0;
670+
}
651671
else
652672
{
653673
/*
@@ -934,6 +954,20 @@ gistproperty(Oid index_oid, int attno,
934954
ObjectIdGetDatum(opcintype),
935955
ObjectIdGetDatum(opcintype),
936956
Int16GetDatum(procno));
957+
958+
/*
959+
* Special case: even without a fetch function, AMPROP_RETURNABLE is true
960+
* if the opclass has no compress function.
961+
*/
962+
if (prop == AMPROP_RETURNABLE && !*res)
963+
{
964+
*res = !SearchSysCacheExists4(AMPROCNUM,
965+
ObjectIdGetDatum(opfamily),
966+
ObjectIdGetDatum(opcintype),
967+
ObjectIdGetDatum(opcintype),
968+
Int16GetDatum(GIST_COMPRESS_PROC));
969+
}
970+
937971
return true;
938972
}
939973

src/backend/access/gist/gistvalidate.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ gistvalidate(Oid opclassoid)
258258
if (opclassgroup &&
259259
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
260260
continue; /* got it */
261-
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC)
261+
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
262+
i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC)
262263
continue; /* optional methods */
263264
ereport(INFO,
264265
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy