aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/gin/ginarrayproc.c84
-rw-r--r--src/backend/access/gin/ginlogic.c68
-rw-r--r--src/backend/access/gin/ginutil.c28
-rw-r--r--src/backend/utils/adt/tsginidx.c104
-rw-r--r--src/include/access/gin.h18
-rw-r--r--src/include/access/gin_private.h15
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_am.h2
-rw-r--r--src/include/catalog/pg_amproc.h31
-rw-r--r--src/include/catalog/pg_proc.h4
-rw-r--r--src/include/tsearch/ts_utils.h1
-rw-r--r--src/include/utils/builtins.h1
-rw-r--r--src/test/regress/expected/opr_sanity.out77
-rw-r--r--src/test/regress/sql/opr_sanity.sql70
14 files changed, 410 insertions, 95 deletions
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index e02a91b82df..d04b851e3cf 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -218,3 +218,87 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+
+/*
+ * triconsistent support function
+ */
+Datum
+ginarraytriconsistent(PG_FUNCTION_ARGS)
+{
+ GinLogicValue *check = (GinLogicValue *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = PG_GETARG_UINT16(1);
+
+ /* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */
+ int32 nkeys = PG_GETARG_INT32(3);
+
+ /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
+ /* Datum *queryKeys = (Datum *) PG_GETARG_POINTER(5); */
+ bool *nullFlags = (bool *) PG_GETARG_POINTER(6);
+ GinLogicValue res;
+ int32 i;
+
+ switch (strategy)
+ {
+ case GinOverlapStrategy:
+ /* must have a match for at least one non-null element */
+ res = GIN_FALSE;
+ for (i = 0; i < nkeys; i++)
+ {
+ if (!nullFlags[i])
+ {
+ if (check[i] == GIN_TRUE)
+ {
+ res = GIN_TRUE;
+ break;
+ }
+ else if (check[i] == GIN_MAYBE && res == GIN_FALSE)
+ {
+ res = GIN_MAYBE;
+ }
+ }
+ }
+ break;
+ case GinContainsStrategy:
+ /* must have all elements in check[] true, and no nulls */
+ res = GIN_TRUE;
+ for (i = 0; i < nkeys; i++)
+ {
+ if (check[i] == GIN_FALSE || nullFlags[i])
+ {
+ res = GIN_FALSE;
+ break;
+ }
+ if (check[i] == GIN_MAYBE)
+ {
+ res = GIN_MAYBE;
+ }
+ }
+ break;
+ case GinContainedStrategy:
+ /* can't do anything else useful here */
+ res = GIN_MAYBE;
+ break;
+ case GinEqualStrategy:
+ /*
+ * Must have all elements in check[] true; no discrimination
+ * against nulls here. This is because array_contain_compare and
+ * array_eq handle nulls differently ...
+ */
+ res = GIN_MAYBE;
+ for (i = 0; i < nkeys; i++)
+ {
+ if (check[i] == GIN_FALSE)
+ {
+ res = GIN_FALSE;
+ break;
+ }
+ }
+ break;
+ default:
+ elog(ERROR, "ginarrayconsistent: unknown strategy number: %d",
+ strategy);
+ res = false;
+ }
+
+ PG_RETURN_GIN_LOGIC_VALUE(res);
+}
diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c
index dc8e6304a2f..4c8d706361c 100644
--- a/src/backend/access/gin/ginlogic.c
+++ b/src/backend/access/gin/ginlogic.c
@@ -61,7 +61,7 @@ trueTriConsistentFn(GinScanKey key)
* A helper function for calling a regular, binary logic, consistent function.
*/
static bool
-normalBoolConsistentFn(GinScanKey key)
+directBoolConsistentFn(GinScanKey key)
{
/*
* Initialize recheckCurItem in case the consistentFn doesn't know it
@@ -82,6 +82,53 @@ normalBoolConsistentFn(GinScanKey key)
}
/*
+ * A helper function for calling a native ternary logic consistent function.
+ */
+static GinLogicValue
+directTriConsistentFn(GinScanKey key)
+{
+ return DatumGetGinLogicValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
+ key->collation,
+ PointerGetDatum(key->entryRes),
+ UInt16GetDatum(key->strategy),
+ key->query,
+ UInt32GetDatum(key->nuserentries),
+ PointerGetDatum(key->extra_data),
+ PointerGetDatum(key->queryValues),
+ PointerGetDatum(key->queryCategories)));
+}
+
+/*
+ * This function implements a binary logic consistency check, using a ternary
+ * logic consistent function provided by the opclass. GIN_MAYBE return value
+ * is interpreted as true with recheck flag.
+ */
+static bool
+shimBoolConsistentFn(GinScanKey key)
+{
+ GinLogicValue result;
+ result = DatumGetGinLogicValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
+ key->collation,
+ PointerGetDatum(key->entryRes),
+ UInt16GetDatum(key->strategy),
+ key->query,
+ UInt32GetDatum(key->nuserentries),
+ PointerGetDatum(key->extra_data),
+ PointerGetDatum(key->queryValues),
+ PointerGetDatum(key->queryCategories)));
+ if (result == GIN_MAYBE)
+ {
+ key->recheckCurItem = true;
+ return true;
+ }
+ else
+ {
+ key->recheckCurItem = false;
+ return result;
+ }
+}
+
+/*
* This function implements a tri-state consistency check, using a boolean
* consistent function provided by the opclass.
*
@@ -124,12 +171,12 @@ shimTriConsistentFn(GinScanKey key)
* function as is.
*/
if (nmaybe == 0)
- return normalBoolConsistentFn(key);
+ return directBoolConsistentFn(key);
/* First call consistent function with all the maybe-inputs set FALSE */
for (i = 0; i < nmaybe; i++)
key->entryRes[maybeEntries[i]] = GIN_FALSE;
- curResult = normalBoolConsistentFn(key);
+ curResult = directBoolConsistentFn(key);
for (;;)
{
@@ -147,7 +194,7 @@ shimTriConsistentFn(GinScanKey key)
if (i == nmaybe)
break;
- boolResult = normalBoolConsistentFn(key);
+ boolResult = directBoolConsistentFn(key);
recheck |= key->recheckCurItem;
if (curResult != boolResult)
@@ -175,8 +222,17 @@ ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
else
{
key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1];
+ key->triConsistentFmgrInfo = &ginstate->triConsistentFn[key->attnum - 1];
key->collation = ginstate->supportCollation[key->attnum - 1];
- key->boolConsistentFn = normalBoolConsistentFn;
- key->triConsistentFn = shimTriConsistentFn;
+
+ if (OidIsValid(ginstate->consistentFn[key->attnum - 1].fn_oid))
+ key->boolConsistentFn = directBoolConsistentFn;
+ else
+ key->boolConsistentFn = shimBoolConsistentFn;
+
+ if (OidIsValid(ginstate->triConsistentFn[key->attnum - 1].fn_oid))
+ key->triConsistentFn = directTriConsistentFn;
+ else
+ key->triConsistentFn = shimTriConsistentFn;
}
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 486f2ef1f11..4dadb50dcaa 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -67,9 +67,31 @@ initGinState(GinState *state, Relation index)
fmgr_info_copy(&(state->extractQueryFn[i]),
index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
CurrentMemoryContext);
- fmgr_info_copy(&(state->consistentFn[i]),
- index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
- CurrentMemoryContext);
+ /*
+ * Check opclass capability to do tri-state or binary logic consistent
+ * check.
+ */
+ if (index_getprocid(index, i + 1, GIN_TRICONSISTENT_PROC) != InvalidOid)
+ {
+ fmgr_info_copy(&(state->triConsistentFn[i]),
+ index_getprocinfo(index, i + 1, GIN_TRICONSISTENT_PROC),
+ CurrentMemoryContext);
+ }
+
+ if (index_getprocid(index, i + 1, GIN_CONSISTENT_PROC) != InvalidOid)
+ {
+ fmgr_info_copy(&(state->consistentFn[i]),
+ index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
+ CurrentMemoryContext);
+ }
+
+ if (state->consistentFn[i].fn_oid == InvalidOid &&
+ state->triConsistentFn[i].fn_oid == InvalidOid)
+ {
+ elog(ERROR, "missing GIN support function (%d or %d) for attribute %d of index \"%s\"",
+ GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC,
+ i + 1, RelationGetRelationName(index));
+ }
/*
* Check opclass capability to do partial match.
diff --git a/src/backend/utils/adt/tsginidx.c b/src/backend/utils/adt/tsginidx.c
index 9f6e8e9fd57..47685e8b930 100644
--- a/src/backend/utils/adt/tsginidx.c
+++ b/src/backend/utils/adt/tsginidx.c
@@ -15,6 +15,7 @@
#include "access/gin.h"
#include "access/skey.h"
+#include "miscadmin.h"
#include "tsearch/ts_type.h"
#include "tsearch/ts_utils.h"
#include "utils/builtins.h"
@@ -172,12 +173,12 @@ gin_extract_tsquery(PG_FUNCTION_ARGS)
typedef struct
{
QueryItem *first_item;
- bool *check;
+ GinLogicValue *check;
int *map_item_operand;
bool *need_recheck;
} GinChkVal;
-static bool
+static GinLogicValue
checkcondition_gin(void *checkval, QueryOperand *val)
{
GinChkVal *gcv = (GinChkVal *) checkval;
@@ -194,6 +195,66 @@ checkcondition_gin(void *checkval, QueryOperand *val)
return gcv->check[j];
}
+/*
+ * Evaluate tsquery boolean expression using ternary logic.
+ *
+ * chkcond is a callback function used to evaluate each VAL node in the query.
+ * checkval can be used to pass information to the callback. TS_execute doesn't
+ * do anything with it.
+ */
+static GinLogicValue
+TS_execute_ternary(QueryItem *curitem, void *checkval,
+ GinLogicValue (*chkcond) (void *checkval, QueryOperand *val))
+{
+ GinLogicValue val1, val2, result;
+ /* since this function recurses, it could be driven to stack overflow */
+ check_stack_depth();
+
+ if (curitem->type == QI_VAL)
+ return chkcond(checkval, (QueryOperand *) curitem);
+
+ switch (curitem->qoperator.oper)
+ {
+ case OP_NOT:
+ result = TS_execute_ternary(curitem + 1, checkval, chkcond);
+ if (result == GIN_MAYBE)
+ return result;
+ return !result;
+
+ case OP_AND:
+ val1 = TS_execute_ternary(curitem + curitem->qoperator.left,
+ checkval, chkcond);
+ if (val1 == GIN_FALSE)
+ return GIN_FALSE;
+ val2 = TS_execute_ternary(curitem + 1, checkval, chkcond);
+ if (val2 == GIN_FALSE)
+ return GIN_FALSE;
+ if (val1 == GIN_TRUE && val2 == GIN_TRUE)
+ return GIN_TRUE;
+ else
+ return GIN_MAYBE;
+
+ case OP_OR:
+ val1 = TS_execute_ternary(curitem + curitem->qoperator.left,
+ checkval, chkcond);
+ if (val1 == GIN_TRUE)
+ return GIN_TRUE;
+ val2 = TS_execute_ternary(curitem + 1, checkval, chkcond);
+ if (val2 == GIN_TRUE)
+ return GIN_TRUE;
+ if (val1 == GIN_FALSE && val2 == GIN_FALSE)
+ return GIN_FALSE;
+ else
+ return GIN_MAYBE;
+
+ default:
+ elog(ERROR, "unrecognized operator: %d", curitem->qoperator.oper);
+ }
+
+ /* not reachable, but keep compiler quiet */
+ return false;
+}
+
Datum
gin_tsquery_consistent(PG_FUNCTION_ARGS)
{
@@ -233,6 +294,45 @@ gin_tsquery_consistent(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+Datum
+gin_tsquery_triconsistent(PG_FUNCTION_ARGS)
+{
+ GinLogicValue *check = (GinLogicValue *) PG_GETARG_POINTER(0);
+
+ /* StrategyNumber strategy = PG_GETARG_UINT16(1); */
+ TSQuery query = PG_GETARG_TSQUERY(2);
+
+ /* int32 nkeys = PG_GETARG_INT32(3); */
+ Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
+ GinLogicValue res = GIN_FALSE;
+ bool recheck;
+
+ /* The query requires recheck only if it involves weights */
+ if (query->size > 0)
+ {
+ QueryItem *item;
+ GinChkVal gcv;
+
+ /*
+ * check-parameter array has one entry for each value (operand) in the
+ * query.
+ */
+ gcv.first_item = item = GETQUERY(query);
+ gcv.check = check;
+ gcv.map_item_operand = (int *) (extra_data[0]);
+ gcv.need_recheck = &recheck;
+
+ res = TS_execute_ternary(GETQUERY(query),
+ &gcv,
+ checkcondition_gin);
+
+ if (res == GIN_TRUE && recheck)
+ res = GIN_MAYBE;
+ }
+
+ PG_RETURN_GIN_LOGIC_VALUE(res);
+}
+
/*
* Formerly, gin_extract_tsvector had only two arguments. Now it has three,
* but we still need a pg_proc entry with two args to support reloading
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index 03e58c9b1cb..75a86c4319a 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -23,7 +23,8 @@
#define GIN_EXTRACTQUERY_PROC 3
#define GIN_CONSISTENT_PROC 4
#define GIN_COMPARE_PARTIAL_PROC 5
-#define GINNProcs 5
+#define GIN_TRICONSISTENT_PROC 6
+#define GINNProcs 6
/*
* searchMode settings for extractQueryFn.
@@ -46,6 +47,21 @@ typedef struct GinStatsData
int32 ginVersion;
} GinStatsData;
+/* ginlogic.c */
+enum
+{
+ GIN_FALSE = 0, /* item is present / matches */
+ GIN_TRUE = 1, /* item is not present / does not match */
+ GIN_MAYBE = 2 /* don't know if item is present / don't know if
+ * matches */
+} GinLogicValueEnum;
+
+typedef char GinLogicValue;
+
+#define DatumGetGinLogicValue(X) ((GinLogicValue)(X))
+#define GinLogicValueGetDatum(X) ((Datum)(X))
+#define PG_RETURN_GIN_LOGIC_VALUE(x) return GinLogicValueGetDatum(x)
+
/* GUC parameter */
extern PGDLLIMPORT int GinFuzzySearchLimit;
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 2cf042a766e..009927282f5 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -348,6 +348,7 @@ typedef struct GinState
FmgrInfo extractValueFn[INDEX_MAX_KEYS];
FmgrInfo extractQueryFn[INDEX_MAX_KEYS];
FmgrInfo consistentFn[INDEX_MAX_KEYS];
+ FmgrInfo triConsistentFn[INDEX_MAX_KEYS];
FmgrInfo comparePartialFn[INDEX_MAX_KEYS]; /* optional method */
/* canPartialMatch[i] is true if comparePartialFn[i] is valid */
bool canPartialMatch[INDEX_MAX_KEYS];
@@ -762,8 +763,9 @@ typedef struct GinScanKeyData
/* array of check flags, reported to consistentFn */
bool *entryRes;
bool (*boolConsistentFn) (GinScanKey key);
- bool (*triConsistentFn) (GinScanKey key);
+ GinLogicValue (*triConsistentFn) (GinScanKey key);
FmgrInfo *consistentFmgrInfo;
+ FmgrInfo *triConsistentFmgrInfo;
Oid collation;
/* other data needed for calling consistentFn */
@@ -850,17 +852,6 @@ extern void ginNewScanKey(IndexScanDesc scan);
extern Datum gingetbitmap(PG_FUNCTION_ARGS);
/* ginlogic.c */
-
-enum
-{
- GIN_FALSE = 0, /* item is present / matches */
- GIN_TRUE = 1, /* item is not present / does not match */
- GIN_MAYBE = 2 /* don't know if item is present / don't know if
- * matches */
-} GinLogicValueEnum;
-
-typedef char GinLogicValue;
-
extern void ginInitConsistentFunction(GinState *ginstate, GinScanKey key);
/* ginvacuum.c */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 047039a4176..60f78d24a96 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201403041
+#define CATALOG_VERSION_NO 201403121
#endif
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 4f46dddcff6..759ea705702 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -126,7 +126,7 @@ DESCR("hash index access method");
DATA(insert OID = 783 ( gist 0 8 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup - gistcostestimate gistoptions ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
-DATA(insert OID = 2742 ( gin 0 5 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
+DATA(insert OID = 2742 ( gin 0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742
DATA(insert OID = 4000 ( spgist 0 5 f f f f f t f t f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcanreturn spgcostestimate spgoptions ));
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 66bd76505a0..e3773e97593 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -235,127 +235,158 @@ DATA(insert ( 2745 1007 1007 1 351 ));
DATA(insert ( 2745 1007 1007 2 2743 ));
DATA(insert ( 2745 1007 1007 3 2774 ));
DATA(insert ( 2745 1007 1007 4 2744 ));
+DATA(insert ( 2745 1007 1007 6 3920 ));
DATA(insert ( 2745 1009 1009 1 360 ));
DATA(insert ( 2745 1009 1009 2 2743 ));
DATA(insert ( 2745 1009 1009 3 2774 ));
DATA(insert ( 2745 1009 1009 4 2744 ));
+DATA(insert ( 2745 1009 1009 6 3920 ));
DATA(insert ( 2745 1015 1015 1 360 ));
DATA(insert ( 2745 1015 1015 2 2743 ));
DATA(insert ( 2745 1015 1015 3 2774 ));
DATA(insert ( 2745 1015 1015 4 2744 ));
+DATA(insert ( 2745 1015 1015 6 3920 ));
DATA(insert ( 2745 1023 1023 1 357 ));
DATA(insert ( 2745 1023 1023 2 2743 ));
DATA(insert ( 2745 1023 1023 3 2774 ));
DATA(insert ( 2745 1023 1023 4 2744 ));
+DATA(insert ( 2745 1023 1023 6 3920 ));
DATA(insert ( 2745 1561 1561 1 1596 ));
DATA(insert ( 2745 1561 1561 2 2743 ));
DATA(insert ( 2745 1561 1561 3 2774 ));
DATA(insert ( 2745 1561 1561 4 2744 ));
+DATA(insert ( 2745 1561 1561 6 3920 ));
DATA(insert ( 2745 1000 1000 1 1693 ));
DATA(insert ( 2745 1000 1000 2 2743 ));
DATA(insert ( 2745 1000 1000 3 2774 ));
DATA(insert ( 2745 1000 1000 4 2744 ));
+DATA(insert ( 2745 1000 1000 6 3920 ));
DATA(insert ( 2745 1014 1014 1 1078 ));
DATA(insert ( 2745 1014 1014 2 2743 ));
DATA(insert ( 2745 1014 1014 3 2774 ));
DATA(insert ( 2745 1014 1014 4 2744 ));
+DATA(insert ( 2745 1014 1014 6 3920 ));
DATA(insert ( 2745 1001 1001 1 1954 ));
DATA(insert ( 2745 1001 1001 2 2743 ));
DATA(insert ( 2745 1001 1001 3 2774 ));
DATA(insert ( 2745 1001 1001 4 2744 ));
+DATA(insert ( 2745 1001 1001 6 3920 ));
DATA(insert ( 2745 1002 1002 1 358 ));
DATA(insert ( 2745 1002 1002 2 2743 ));
DATA(insert ( 2745 1002 1002 3 2774 ));
DATA(insert ( 2745 1002 1002 4 2744 ));
+DATA(insert ( 2745 1002 1002 6 3920 ));
DATA(insert ( 2745 1182 1182 1 1092 ));
DATA(insert ( 2745 1182 1182 2 2743 ));
DATA(insert ( 2745 1182 1182 3 2774 ));
DATA(insert ( 2745 1182 1182 4 2744 ));
+DATA(insert ( 2745 1182 1182 6 3920 ));
DATA(insert ( 2745 1021 1021 1 354 ));
DATA(insert ( 2745 1021 1021 2 2743 ));
DATA(insert ( 2745 1021 1021 3 2774 ));
DATA(insert ( 2745 1021 1021 4 2744 ));
+DATA(insert ( 2745 1021 1021 6 3920 ));
DATA(insert ( 2745 1022 1022 1 355 ));
DATA(insert ( 2745 1022 1022 2 2743 ));
DATA(insert ( 2745 1022 1022 3 2774 ));
DATA(insert ( 2745 1022 1022 4 2744 ));
+DATA(insert ( 2745 1022 1022 6 3920 ));
DATA(insert ( 2745 1041 1041 1 926 ));
DATA(insert ( 2745 1041 1041 2 2743 ));
DATA(insert ( 2745 1041 1041 3 2774 ));
DATA(insert ( 2745 1041 1041 4 2744 ));
+DATA(insert ( 2745 1041 1041 6 3920 ));
DATA(insert ( 2745 651 651 1 926 ));
DATA(insert ( 2745 651 651 2 2743 ));
DATA(insert ( 2745 651 651 3 2774 ));
DATA(insert ( 2745 651 651 4 2744 ));
+DATA(insert ( 2745 651 651 6 3920 ));
DATA(insert ( 2745 1005 1005 1 350 ));
DATA(insert ( 2745 1005 1005 2 2743 ));
DATA(insert ( 2745 1005 1005 3 2774 ));
DATA(insert ( 2745 1005 1005 4 2744 ));
+DATA(insert ( 2745 1005 1005 6 3920 ));
DATA(insert ( 2745 1016 1016 1 842 ));
DATA(insert ( 2745 1016 1016 2 2743 ));
DATA(insert ( 2745 1016 1016 3 2774 ));
DATA(insert ( 2745 1016 1016 4 2744 ));
+DATA(insert ( 2745 1016 1016 6 3920 ));
DATA(insert ( 2745 1187 1187 1 1315 ));
DATA(insert ( 2745 1187 1187 2 2743 ));
DATA(insert ( 2745 1187 1187 3 2774 ));
DATA(insert ( 2745 1187 1187 4 2744 ));
+DATA(insert ( 2745 1187 1187 6 3920 ));
DATA(insert ( 2745 1040 1040 1 836 ));
DATA(insert ( 2745 1040 1040 2 2743 ));
DATA(insert ( 2745 1040 1040 3 2774 ));
DATA(insert ( 2745 1040 1040 4 2744 ));
+DATA(insert ( 2745 1040 1040 6 3920 ));
DATA(insert ( 2745 1003 1003 1 359 ));
DATA(insert ( 2745 1003 1003 2 2743 ));
DATA(insert ( 2745 1003 1003 3 2774 ));
DATA(insert ( 2745 1003 1003 4 2744 ));
+DATA(insert ( 2745 1003 1003 6 3920 ));
DATA(insert ( 2745 1231 1231 1 1769 ));
DATA(insert ( 2745 1231 1231 2 2743 ));
DATA(insert ( 2745 1231 1231 3 2774 ));
DATA(insert ( 2745 1231 1231 4 2744 ));
+DATA(insert ( 2745 1231 1231 6 3920 ));
DATA(insert ( 2745 1028 1028 1 356 ));
DATA(insert ( 2745 1028 1028 2 2743 ));
DATA(insert ( 2745 1028 1028 3 2774 ));
DATA(insert ( 2745 1028 1028 4 2744 ));
+DATA(insert ( 2745 1028 1028 6 3920 ));
DATA(insert ( 2745 1013 1013 1 404 ));
DATA(insert ( 2745 1013 1013 2 2743 ));
DATA(insert ( 2745 1013 1013 3 2774 ));
DATA(insert ( 2745 1013 1013 4 2744 ));
+DATA(insert ( 2745 1013 1013 6 3920 ));
DATA(insert ( 2745 1183 1183 1 1107 ));
DATA(insert ( 2745 1183 1183 2 2743 ));
DATA(insert ( 2745 1183 1183 3 2774 ));
DATA(insert ( 2745 1183 1183 4 2744 ));
+DATA(insert ( 2745 1183 1183 6 3920 ));
DATA(insert ( 2745 1185 1185 1 1314 ));
DATA(insert ( 2745 1185 1185 2 2743 ));
DATA(insert ( 2745 1185 1185 3 2774 ));
DATA(insert ( 2745 1185 1185 4 2744 ));
+DATA(insert ( 2745 1185 1185 6 3920 ));
DATA(insert ( 2745 1270 1270 1 1358 ));
DATA(insert ( 2745 1270 1270 2 2743 ));
DATA(insert ( 2745 1270 1270 3 2774 ));
DATA(insert ( 2745 1270 1270 4 2744 ));
+DATA(insert ( 2745 1270 1270 6 3920 ));
DATA(insert ( 2745 1563 1563 1 1672 ));
DATA(insert ( 2745 1563 1563 2 2743 ));
DATA(insert ( 2745 1563 1563 3 2774 ));
DATA(insert ( 2745 1563 1563 4 2744 ));
+DATA(insert ( 2745 1563 1563 6 3920 ));
DATA(insert ( 2745 1115 1115 1 2045 ));
DATA(insert ( 2745 1115 1115 2 2743 ));
DATA(insert ( 2745 1115 1115 3 2774 ));
DATA(insert ( 2745 1115 1115 4 2744 ));
+DATA(insert ( 2745 1115 1115 6 3920 ));
DATA(insert ( 2745 791 791 1 377 ));
DATA(insert ( 2745 791 791 2 2743 ));
DATA(insert ( 2745 791 791 3 2774 ));
DATA(insert ( 2745 791 791 4 2744 ));
+DATA(insert ( 2745 791 791 6 3920 ));
DATA(insert ( 2745 1024 1024 1 380 ));
DATA(insert ( 2745 1024 1024 2 2743 ));
DATA(insert ( 2745 1024 1024 3 2774 ));
DATA(insert ( 2745 1024 1024 4 2744 ));
+DATA(insert ( 2745 1024 1024 6 3920 ));
DATA(insert ( 2745 1025 1025 1 381 ));
DATA(insert ( 2745 1025 1025 2 2743 ));
DATA(insert ( 2745 1025 1025 3 2774 ));
DATA(insert ( 2745 1025 1025 4 2744 ));
+DATA(insert ( 2745 1025 1025 6 3920 ));
DATA(insert ( 3659 3614 3614 1 3724 ));
DATA(insert ( 3659 3614 3614 2 3656 ));
DATA(insert ( 3659 3614 3614 3 3657 ));
DATA(insert ( 3659 3614 3614 4 3658 ));
DATA(insert ( 3659 3614 3614 5 2700 ));
+DATA(insert ( 3659 3614 3614 6 3921 ));
/* sp-gist */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bde018d9578..4bd23fc5fde 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3991,6 +3991,8 @@ DATA(insert OID = 2774 ( ginqueryarrayextract PGNSP PGUID 12 1 0 0 0 f f f f t
DESCR("GIN array support");
DATA(insert OID = 2744 ( ginarrayconsistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 8 0 16 "2281 21 2277 23 2281 2281 2281 2281" _null_ _null_ _null_ _null_ ginarrayconsistent _null_ _null_ _null_ ));
DESCR("GIN array support");
+DATA(insert OID = 3920 ( ginarraytriconsistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 7 0 16 "2281 21 2277 23 2281 2281 2281" _null_ _null_ _null_ _null_ ginarraytriconsistent _null_ _null_ _null_ ));
+DESCR("GIN array support");
DATA(insert OID = 3076 ( ginarrayextract PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 2281 "2277 2281" _null_ _null_ _null_ _null_ ginarrayextract_2args _null_ _null_ _null_ ));
DESCR("GIN array support (obsolete)");
@@ -4334,6 +4336,8 @@ DATA(insert OID = 3657 ( gin_extract_tsquery PGNSP PGUID 12 1 0 0 0 f f f f t f
DESCR("GIN tsvector support");
DATA(insert OID = 3658 ( gin_tsquery_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 8 0 16 "2281 21 3615 23 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gin_tsquery_consistent _null_ _null_ _null_ ));
DESCR("GIN tsvector support");
+DATA(insert OID = 3921 ( gin_tsquery_triconsistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 7 0 16 "2281 21 3615 23 2281 2281 2281" _null_ _null_ _null_ _null_ gin_tsquery_triconsistent _null_ _null_ _null_ ));
+DESCR("GIN tsvector support");
DATA(insert OID = 3724 ( gin_cmp_tslexeme PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "25 25" _null_ _null_ _null_ _null_ gin_cmp_tslexeme _null_ _null_ _null_ ));
DESCR("GIN tsvector support");
DATA(insert OID = 2700 ( gin_cmp_prefix PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 23 "25 25 21 2281" _null_ _null_ _null_ _null_ gin_cmp_prefix _null_ _null_ _null_ ));
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index 974e58cb528..2ffb3fb429e 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -149,6 +149,7 @@ extern Datum gin_cmp_tslexeme(PG_FUNCTION_ARGS);
extern Datum gin_cmp_prefix(PG_FUNCTION_ARGS);
extern Datum gin_extract_tsquery(PG_FUNCTION_ARGS);
extern Datum gin_tsquery_consistent(PG_FUNCTION_ARGS);
+extern Datum gin_tsquery_triconsistent(PG_FUNCTION_ARGS);
extern Datum gin_extract_tsvector_2args(PG_FUNCTION_ARGS);
extern Datum gin_extract_tsquery_5args(PG_FUNCTION_ARGS);
extern Datum gin_tsquery_consistent_6args(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b90d88d3dae..031a43a7e78 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1161,6 +1161,7 @@ extern Datum ginarrayextract(PG_FUNCTION_ARGS);
extern Datum ginarrayextract_2args(PG_FUNCTION_ARGS);
extern Datum ginqueryarrayextract(PG_FUNCTION_ARGS);
extern Datum ginarrayconsistent(PG_FUNCTION_ARGS);
+extern Datum ginarraytriconsistent(PG_FUNCTION_ARGS);
/* access/transam/twophase.c */
extern Datum pg_prepared_xact(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 26abe8abc3a..5627b4a426d 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1297,46 +1297,49 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
-- Detect missing pg_amproc entries: should have as many support functions
-- as AM expects for each datatype combination supported by the opfamily.
--- btree/GiST/GIN each allow one optional support function, though.
-SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
-FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
-WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
- (SELECT count(*) FROM pg_amproc AS p4
- WHERE p4.amprocfamily = p2.oid AND
- p4.amproclefttype = p3.amproclefttype AND
- p4.amprocrighttype = p3.amprocrighttype)
- NOT BETWEEN
- (CASE WHEN p1.amname IN ('btree', 'gist', 'gin') THEN p1.amsupport - 1
- ELSE p1.amsupport END)
- AND p1.amsupport;
- amname | opfname | amproclefttype | amprocrighttype
---------+---------+----------------+-----------------
+SELECT * FROM (
+ SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype,
+ array_agg(p3.amprocnum ORDER BY amprocnum) AS procnums
+ FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
+ WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid
+ GROUP BY p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
+) AS t
+WHERE NOT (
+ -- btree has one mandatory and one optional support function.
+ -- hash has one support function, which is mandatory.
+ -- GiST has eight support functions, one of which is optional.
+ -- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
+ -- at least one of 4 and 6 must be given.
+ -- SP-GiST has five support functions, all mandatory
+ amname = 'btree' AND procnums @> '{1}' OR
+ amname = 'hash' AND procnums = '{1}' OR
+ amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
+ amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
+ amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}'
+);
+ amname | opfname | amproclefttype | amprocrighttype | procnums
+--------+---------+----------------+-----------------+----------
(0 rows)
-- Also, check if there are any pg_opclass entries that don't seem to have
--- pg_amproc support. Again, opclasses with an optional support proc have
--- to be checked specially.
-SELECT amname, opcname, count(*)
-FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
- LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
- amproclefttype = amprocrighttype AND amproclefttype = opcintype
-WHERE am.amname <> 'btree' AND am.amname <> 'gist' AND am.amname <> 'gin'
-GROUP BY amname, amsupport, opcname, amprocfamily
-HAVING count(*) != amsupport OR amprocfamily IS NULL;
- amname | opcname | count
---------+---------+-------
-(0 rows)
-
-SELECT amname, opcname, count(*)
-FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
- LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
- amproclefttype = amprocrighttype AND amproclefttype = opcintype
-WHERE am.amname = 'btree' OR am.amname = 'gist' OR am.amname = 'gin'
-GROUP BY amname, amsupport, opcname, amprocfamily
-HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
- OR amprocfamily IS NULL;
- amname | opcname | count
---------+---------+-------
+-- pg_amproc support.
+SELECT * FROM (
+ SELECT amname, opcname, array_agg(amprocnum ORDER BY amprocnum) as procnums
+ FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
+ LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
+ amproclefttype = amprocrighttype AND amproclefttype = opcintype
+ GROUP BY amname, opcname, amprocfamily
+) AS t
+WHERE NOT (
+ -- same per-AM rules as above
+ amname = 'btree' AND procnums @> '{1}' OR
+ amname = 'hash' AND procnums = '{1}' OR
+ amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
+ amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
+ amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}'
+);
+ amname | opcname | procnums
+--------+---------+----------
(0 rows)
-- Unfortunately, we can't check the amproc link very well because the
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 40e1be2178f..ad371789245 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -992,40 +992,46 @@ WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
-- Detect missing pg_amproc entries: should have as many support functions
-- as AM expects for each datatype combination supported by the opfamily.
--- btree/GiST/GIN each allow one optional support function, though.
-
-SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
-FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
-WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid AND
- (SELECT count(*) FROM pg_amproc AS p4
- WHERE p4.amprocfamily = p2.oid AND
- p4.amproclefttype = p3.amproclefttype AND
- p4.amprocrighttype = p3.amprocrighttype)
- NOT BETWEEN
- (CASE WHEN p1.amname IN ('btree', 'gist', 'gin') THEN p1.amsupport - 1
- ELSE p1.amsupport END)
- AND p1.amsupport;
+
+SELECT * FROM (
+ SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype,
+ array_agg(p3.amprocnum ORDER BY amprocnum) AS procnums
+ FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
+ WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid
+ GROUP BY p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
+) AS t
+WHERE NOT (
+ -- btree has one mandatory and one optional support function.
+ -- hash has one support function, which is mandatory.
+ -- GiST has eight support functions, one of which is optional.
+ -- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
+ -- at least one of 4 and 6 must be given.
+ -- SP-GiST has five support functions, all mandatory
+ amname = 'btree' AND procnums @> '{1}' OR
+ amname = 'hash' AND procnums = '{1}' OR
+ amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
+ amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
+ amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}'
+);
-- Also, check if there are any pg_opclass entries that don't seem to have
--- pg_amproc support. Again, opclasses with an optional support proc have
--- to be checked specially.
-
-SELECT amname, opcname, count(*)
-FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
- LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
- amproclefttype = amprocrighttype AND amproclefttype = opcintype
-WHERE am.amname <> 'btree' AND am.amname <> 'gist' AND am.amname <> 'gin'
-GROUP BY amname, amsupport, opcname, amprocfamily
-HAVING count(*) != amsupport OR amprocfamily IS NULL;
-
-SELECT amname, opcname, count(*)
-FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
- LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
- amproclefttype = amprocrighttype AND amproclefttype = opcintype
-WHERE am.amname = 'btree' OR am.amname = 'gist' OR am.amname = 'gin'
-GROUP BY amname, amsupport, opcname, amprocfamily
-HAVING (count(*) != amsupport AND count(*) != amsupport - 1)
- OR amprocfamily IS NULL;
+-- pg_amproc support.
+
+SELECT * FROM (
+ SELECT amname, opcname, array_agg(amprocnum ORDER BY amprocnum) as procnums
+ FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
+ LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
+ amproclefttype = amprocrighttype AND amproclefttype = opcintype
+ GROUP BY amname, opcname, amprocfamily
+) AS t
+WHERE NOT (
+ -- same per-AM rules as above
+ amname = 'btree' AND procnums @> '{1}' OR
+ amname = 'hash' AND procnums = '{1}' OR
+ amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
+ amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
+ amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}'
+);
-- Unfortunately, we can't check the amproc link very well because the
-- signature of the function may be different for different support routines