From 42ea4697cb944f72767379fbd43dc9e2a3c6fa08 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Mon, 11 Sep 2017 15:06:59 +0500
Subject: [PATCH] Allow uncompressed GiST 3

---
 doc/src/sgml/gist.sgml                 | 11 ++++++----
 src/backend/access/gist/gist.c         | 24 +++++++++++++++------
 src/backend/access/gist/gistget.c      |  4 +++-
 src/backend/access/gist/gistutil.c     | 38 +++++++++++++++++++++++++++++-----
 src/backend/access/gist/gistvalidate.c |  3 ++-
 5 files changed, 63 insertions(+), 17 deletions(-)

diff --git a/doc/src/sgml/gist.sgml b/doc/src/sgml/gist.sgml
index b3cc347e5c..56fd1d8fde 100644
--- a/doc/src/sgml/gist.sgml
+++ b/doc/src/sgml/gist.sgml
@@ -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
+   The pair of basic 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,10 @@ 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. If the opcalss do not
+   provide neither <function>fetch</> nor <function>compress</> index-only
+   scans are still possible, because original value is not distorted by
+   the code before placement to the page.
  </para>
 
  <variablelist>
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 565525bbdf..7453814d63 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -1453,12 +1453,24 @@ 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);
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 760ea0c997..f99ff6ef14 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -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 compressions 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;
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index b6ccc1a66a..ff3db83a6f 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -550,6 +550,11 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
 		GISTENTRY  *dep;
 
 		gistentryinit(*e, k, r, pg, o, l);
+
+		/* there may be not decompress function in opclass */
+		if(giststate->decompressFn[nkey].fn_oid == InvalidOid)
+			return;
+
 		dep = (GISTENTRY *)
 			DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
 											  giststate->supportCollation[nkey],
@@ -585,10 +590,12 @@ 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 be not compress function in opclass */
+			cep = giststate->compressFn[i].fn_oid != InvalidOid ?
+					(GISTENTRY *)DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[i],
+											  giststate->supportCollation[i],
+												  PointerGetDatum(&centry)))
+												  : &centry;
 			compatt[i] = cep->key;
 		}
 	}
@@ -648,6 +655,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 expected to be stored in original form
+			 */
+			if (!isnull[i])
+				fetchatt[i] = datum;
+			else
+				fetchatt[i] = (Datum) 0;
+		}
 		else
 		{
 			/*
@@ -913,7 +931,6 @@ gistproperty(Oid index_oid, int attno,
 	ReleaseSysCache(tuple);
 
 	/* Now look up the opclass family and input datatype. */
-
 	tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
 	if (!HeapTupleIsValid(tuple))
 	{
@@ -934,6 +951,17 @@ gistproperty(Oid index_oid, int attno,
 								 ObjectIdGetDatum(opcintype),
 								 ObjectIdGetDatum(opcintype),
 								 Int16GetDatum(procno));
+
+	/* Fetch function is not neccesary to fetch att if we have no compress function*/
+	if(prop == AMPROP_RETURNABLE && !*res)
+	{
+		*res = !SearchSysCacheExists4(AMPROCNUM,
+										 ObjectIdGetDatum(opfamily),
+										 ObjectIdGetDatum(opcintype),
+										 ObjectIdGetDatum(opcintype),
+										 Int16GetDatum(GIST_COMPRESS_PROC));
+	}
+
 	return true;
 }
 
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
index 42254c5f15..80e43b8f9b 100644
--- a/src/backend/access/gist/gistvalidate.c
+++ b/src/backend/access/gist/gistvalidate.c
@@ -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),
-- 
2.11.0 (Apple Git-81)

