From 332930913ea6d90e6c61cf7093168ebd4d75cd05 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 16 Jan 2018 01:50:09 +0300
Subject: [PATCH 3/9] Add opclass parameters to GIN

---
 doc/src/sgml/xindex.sgml             |  7 +++++++
 src/backend/access/gin/ginget.c      | 11 ++++++-----
 src/backend/access/gin/ginlogic.c    | 15 +++++++++------
 src/backend/access/gin/ginscan.c     |  6 ++++--
 src/backend/access/gin/ginutil.c     | 27 +++++++++++++++++++++------
 src/backend/access/gin/ginvalidate.c | 32 ++++++++++++++++++++------------
 src/backend/utils/adt/selfuncs.c     |  6 ++++--
 src/include/access/gin.h             |  3 ++-
 src/include/access/gin_private.h     |  5 +++++
 9 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 8c5b528..658ec9b 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -665,6 +665,13 @@
        </entry>
        <entry>6</entry>
       </row>
+      <row>
+       <entry><function>options</function></entry>
+       <entry>
+        parse opclass-specific options (optional)
+       </entry>
+       <entry>7</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 8466d94..5eb47e0 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -188,13 +188,13 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
 			 * case cmp < 0 => not match and continue scan
 			 *----------
 			 */
-			cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
+			cmp = DatumGetInt32(FunctionCall5Coll(&btree->ginstate->comparePartialFn[attnum - 1],
 												  btree->ginstate->supportCollation[attnum - 1],
 												  scanEntry->queryKey,
 												  idatum,
 												  UInt16GetDatum(scanEntry->strategy),
-												  PointerGetDatum(scanEntry->extra_data)));
-
+												  PointerGetDatum(scanEntry->extra_data),
+												  PointerGetDatum(btree->ginstate->opclassOptions[attnum - 1])));
 			if (cmp > 0)
 				return true;
 			else if (cmp < 0)
@@ -1508,12 +1508,13 @@ matchPartialInPendingList(GinState *ginstate, Page page,
 		 * case cmp < 0 => not match and continue scan
 		 *----------
 		 */
-		cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
+		cmp = DatumGetInt32(FunctionCall5Coll(&ginstate->comparePartialFn[entry->attnum - 1],
 											  ginstate->supportCollation[entry->attnum - 1],
 											  entry->queryKey,
 											  datum[off - 1],
 											  UInt16GetDatum(entry->strategy),
-											  PointerGetDatum(entry->extra_data)));
+											  PointerGetDatum(entry->extra_data),
+											  PointerGetDatum(ginstate->opclassOptions[entry->attnum - 1])));
 		if (cmp == 0)
 			return true;
 		else if (cmp > 0)
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index 2c42d1a..5ec3931 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -76,7 +76,7 @@ directBoolConsistentFn(GinScanKey key)
 	 */
 	key->recheckCurItem = true;
 
-	return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
+	return DatumGetBool(FunctionCall9Coll(key->consistentFmgrInfo,
 										  key->collation,
 										  PointerGetDatum(key->entryRes),
 										  UInt16GetDatum(key->strategy),
@@ -85,7 +85,8 @@ directBoolConsistentFn(GinScanKey key)
 										  PointerGetDatum(key->extra_data),
 										  PointerGetDatum(&key->recheckCurItem),
 										  PointerGetDatum(key->queryValues),
-										  PointerGetDatum(key->queryCategories)));
+										  PointerGetDatum(key->queryCategories),
+										  PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -94,7 +95,7 @@ directBoolConsistentFn(GinScanKey key)
 static GinTernaryValue
 directTriConsistentFn(GinScanKey key)
 {
-	return DatumGetGinTernaryValue(FunctionCall7Coll(
+	return DatumGetGinTernaryValue(FunctionCall8Coll(
 													 key->triConsistentFmgrInfo,
 													 key->collation,
 													 PointerGetDatum(key->entryRes),
@@ -103,7 +104,8 @@ directTriConsistentFn(GinScanKey key)
 													 UInt32GetDatum(key->nuserentries),
 													 PointerGetDatum(key->extra_data),
 													 PointerGetDatum(key->queryValues),
-													 PointerGetDatum(key->queryCategories)));
+													 PointerGetDatum(key->queryCategories),
+													 PointerGetDatum(key->opclassOptions)));
 }
 
 /*
@@ -116,7 +118,7 @@ shimBoolConsistentFn(GinScanKey key)
 {
 	GinTernaryValue result;
 
-	result = DatumGetGinTernaryValue(FunctionCall7Coll(
+	result = DatumGetGinTernaryValue(FunctionCall8Coll(
 													   key->triConsistentFmgrInfo,
 													   key->collation,
 													   PointerGetDatum(key->entryRes),
@@ -125,7 +127,8 @@ shimBoolConsistentFn(GinScanKey key)
 													   UInt32GetDatum(key->nuserentries),
 													   PointerGetDatum(key->extra_data),
 													   PointerGetDatum(key->queryValues),
-													   PointerGetDatum(key->queryCategories)));
+													   PointerGetDatum(key->queryCategories),
+													   PointerGetDatum(key->opclassOptions)));
 	if (result == GIN_MAYBE)
 	{
 		key->recheckCurItem = true;
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 8ade431..6594a29 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -156,6 +156,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
 	key->strategy = strategy;
 	key->searchMode = searchMode;
 	key->attnum = attnum;
+	key->opclassOptions = ginstate->opclassOptions[attnum - 1];
 
 	ItemPointerSetMin(&key->curItem);
 	key->curItemMatches = false;
@@ -310,7 +311,7 @@ ginNewScanKey(IndexScanDesc scan)
 
 		/* OK to call the extractQueryFn */
 		queryValues = (Datum *)
-			DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+			DatumGetPointer(FunctionCall8Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
 											  so->ginstate.supportCollation[skey->sk_attno - 1],
 											  skey->sk_argument,
 											  PointerGetDatum(&nQueryValues),
@@ -318,7 +319,8 @@ ginNewScanKey(IndexScanDesc scan)
 											  PointerGetDatum(&partial_matches),
 											  PointerGetDatum(&extra_data),
 											  PointerGetDatum(&nullFlags),
-											  PointerGetDatum(&searchMode)));
+											  PointerGetDatum(&searchMode),
+											  PointerGetDatum(so->ginstate.opclassOptions[skey->sk_attno - 1])));
 
 		/*
 		 * If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index d7696a1..d4f5b4c 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -63,6 +63,7 @@ ginhandler(PG_FUNCTION_ARGS)
 	amroutine->amcanreturn = NULL;
 	amroutine->amcostestimate = gincostestimate;
 	amroutine->amoptions = ginoptions;
+	amroutine->amopclassoptions = ginopclassoptions;
 	amroutine->amproperty = NULL;
 	amroutine->amvalidate = ginvalidate;
 	amroutine->ambeginscan = ginbeginscan;
@@ -95,6 +96,7 @@ initGinState(GinState *state, Relation index)
 	state->index = index;
 	state->oneCol = (origTupdesc->natts == 1) ? true : false;
 	state->origTupdesc = origTupdesc;
+	state->opclassOptions = RelationGetParsedOpclassOptions(index);
 
 	for (i = 0; i < origTupdesc->natts; i++)
 	{
@@ -403,9 +405,10 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
 		return 0;
 
 	/* both not null, so safe to call the compareFn */
-	return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
+	return DatumGetInt32(FunctionCall3Coll(&ginstate->compareFn[attnum - 1],
 										   ginstate->supportCollation[attnum - 1],
-										   a, b));
+										   a, b,
+										   PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 }
 
 /*
@@ -441,6 +444,7 @@ typedef struct
 {
 	FmgrInfo   *cmpDatumFunc;
 	Oid			collation;
+	Datum		options;
 	bool		haveDups;
 } cmpEntriesArg;
 
@@ -462,9 +466,10 @@ cmpEntries(const void *a, const void *b, void *arg)
 	else if (bb->isnull)
 		res = -1;				/* not-NULL "<" NULL */
 	else
-		res = DatumGetInt32(FunctionCall2Coll(data->cmpDatumFunc,
+		res = DatumGetInt32(FunctionCall3Coll(data->cmpDatumFunc,
 											  data->collation,
-											  aa->datum, bb->datum));
+											  aa->datum, bb->datum,
+											  data->options));
 
 	/*
 	 * Detect if we have any duplicates.  If there are equal keys, qsort must
@@ -510,11 +515,12 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 	/* OK, call the opclass's extractValueFn */
 	nullFlags = NULL;			/* in case extractValue doesn't set it */
 	entries = (Datum *)
-		DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+		DatumGetPointer(FunctionCall4Coll(&ginstate->extractValueFn[attnum - 1],
 										  ginstate->supportCollation[attnum - 1],
 										  value,
 										  PointerGetDatum(nentries),
-										  PointerGetDatum(&nullFlags)));
+										  PointerGetDatum(&nullFlags),
+										  PointerGetDatum(ginstate->opclassOptions[attnum - 1])));
 
 	/*
 	 * Generate a placeholder if the item contained no keys.
@@ -557,6 +563,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
 
 		arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
 		arg.collation = ginstate->supportCollation[attnum - 1];
+		arg.options = PointerGetDatum(ginstate->opclassOptions[attnum - 1]);
 		arg.haveDups = false;
 		qsort_arg(keydata, *nentries, sizeof(keyEntryData),
 				  cmpEntries, (void *) &arg);
@@ -632,6 +639,14 @@ ginoptions(Datum reloptions, bool validate)
 	return (bytea *) rdopts;
 }
 
+bytea *
+ginopclassoptions(Relation index, AttrNumber colno, Datum attoptions,
+				  bool validate)
+{
+	return index_opclass_options_generic(index, colno, GIN_OPCLASSOPTIONS_PROC,
+										 attoptions, validate);
+}
+
 /*
  * Fetch index's statistical data into *stats
  *
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
index 1922260..4f87d12 100644
--- a/src/backend/access/gin/ginvalidate.c
+++ b/src/backend/access/gin/ginvalidate.c
@@ -108,40 +108,47 @@ ginvalidate(Oid opclassoid)
 		{
 			case GIN_COMPARE_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											2, 2, opckeytype, opckeytype);
+											2, 3, opckeytype, opckeytype,
+											INTERNALOID);
 				break;
 			case GIN_EXTRACTVALUE_PROC:
 				/* Some opclasses omit nullFlags */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											2, 3, opcintype, INTERNALOID,
-											INTERNALOID);
+											2, 4, opcintype, INTERNALOID,
+											INTERNALOID, INTERNALOID);
 				break;
 			case GIN_EXTRACTQUERY_PROC:
 				/* Some opclasses omit nullFlags and searchMode */
 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
-											5, 7, opcintype, INTERNALOID,
+											5, 8, opcintype, INTERNALOID,
 											INT2OID, INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_CONSISTENT_PROC:
 				/* Some opclasses omit queryKeys and nullFlags */
 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
-											6, 8, INTERNALOID, INT2OID,
+											6, 9, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID, INTERNALOID);
+											INTERNALOID, INTERNALOID,
+											INTERNALOID);
 				break;
 			case GIN_COMPARE_PARTIAL_PROC:
 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
-											4, 4, opckeytype, opckeytype,
-											INT2OID, INTERNALOID);
+											4, 5, opckeytype, opckeytype,
+											INT2OID, INTERNALOID, INTERNALOID);
 				break;
 			case GIN_TRICONSISTENT_PROC:
 				ok = check_amproc_signature(procform->amproc, CHAROID, false,
-											7, 7, INTERNALOID, INT2OID,
+											7, 8, INTERNALOID, INT2OID,
 											opcintype, INT4OID,
 											INTERNALOID, INTERNALOID,
-											INTERNALOID);
+											INTERNALOID, INTERNALOID);
+				break;
+			case GIN_OPCLASSOPTIONS_PROC:
+				ok = check_amproc_signature(procform->amproc, INTERNALOID,
+											false, 2, 2, INTERNALOID, BOOLOID);
 				break;
 			default:
 				ereport(INFO,
@@ -238,7 +245,8 @@ ginvalidate(Oid opclassoid)
 		if (opclassgroup &&
 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
 			continue;			/* got it */
-		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC)
+		if (i == GIN_COMPARE_PROC || i == GIN_COMPARE_PARTIAL_PROC ||
+			i == GIN_OPCLASSOPTIONS_PROC)
 			continue;			/* optional method */
 		if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
 			continue;			/* don't need both, see check below loop */
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index ffca0fe..67431c7 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -139,6 +139,7 @@
 #include "utils/lsyscache.h"
 #include "utils/pg_locale.h"
 #include "utils/rel.h"
+#include "utils/relcache.h"
 #include "utils/selfuncs.h"
 #include "utils/snapmgr.h"
 #include "utils/spccache.h"
@@ -7474,7 +7475,7 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 	else
 		collation = DEFAULT_COLLATION_OID;
 
-	OidFunctionCall7Coll(extractProcOid,
+	OidFunctionCall8Coll(extractProcOid,
 						 collation,
 						 query,
 						 PointerGetDatum(&nentries),
@@ -7482,7 +7483,8 @@ gincost_pattern(IndexOptInfo *index, int indexcol,
 						 PointerGetDatum(&partial_matches),
 						 PointerGetDatum(&extra_data),
 						 PointerGetDatum(&nullFlags),
-						 PointerGetDatum(&searchMode));
+						 PointerGetDatum(&searchMode),
+						 PointerGetDatum(index->opclassoptions[indexcol]));
 
 	if (nentries <= 0 && searchMode == GIN_SEARCH_MODE_DEFAULT)
 	{
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 3d8a130..20ce792 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -25,7 +25,8 @@
 #define GIN_CONSISTENT_PROC			   4
 #define GIN_COMPARE_PARTIAL_PROC	   5
 #define GIN_TRICONSISTENT_PROC		   6
-#define GINNProcs					   6
+#define GIN_OPCLASSOPTIONS_PROC		   7
+#define GINNProcs					   7
 
 /*
  * searchMode settings for extractQueryFn.
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 81bf873..6fecfe0 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -67,6 +67,8 @@ typedef struct GinState
 	TupleDesc	origTupdesc;
 	TupleDesc	tupdesc[INDEX_MAX_KEYS];
 
+	bytea	  **opclassOptions;	/* per-index-column opclass options */
+
 	/*
 	 * Per-index-column opclass support functions
 	 */
@@ -85,6 +87,8 @@ typedef struct GinState
 
 /* ginutil.c */
 extern bytea *ginoptions(Datum reloptions, bool validate);
+extern bytea *ginopclassoptions(Relation index, AttrNumber colno,
+				  Datum attoptions, bool validate);
 extern void initGinState(GinState *state, Relation index);
 extern Buffer GinNewBuffer(Relation index);
 extern void GinInitBuffer(Buffer b, uint32 f);
@@ -296,6 +300,7 @@ typedef struct GinScanKeyData
 	StrategyNumber strategy;
 	int32		searchMode;
 	OffsetNumber attnum;
+	bytea	   *opclassOptions;
 
 	/*
 	 * Match status data.  curItem is the TID most recently tested (could be a
-- 
2.7.4

