diff --git a/doc/src/sgml/gin.sgml b/doc/src/sgml/gin.sgml new file mode 100644 index 9ffa8be..c8a6b50 *** a/doc/src/sgml/gin.sgml --- b/doc/src/sgml/gin.sgml *************** *** 216,221 **** --- 216,230 ---- arrays previously returned by extractQuery. extra_data is the extra-data array returned by extractQuery, or NULL if none. + consistent can be declared as either 1st or 6th support + function of opclass. If it's declared as 6th then it must support + tri-state logic can be used for fast scan technique which accelerating + gin index scan by skipping parts of large posting-trees. Tri-state + version of consistent accepts UNKNOWN values + in check array. These values means that indexed item can + either contain or not contain corresponding query key. Consistent + might return UNKNOWN values as well when given information + is lacking for exact answer. diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c new file mode 100644 index e02a91b..9e58656 *** a/src/backend/access/gin/ginarrayproc.c --- b/src/backend/access/gin/ginarrayproc.c *************** ginqueryarrayextract(PG_FUNCTION_ARGS) *** 141,147 **** Datum ginarrayconsistent(PG_FUNCTION_ARGS) { ! bool *check = (bool *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); /* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */ --- 141,147 ---- Datum ginarrayconsistent(PG_FUNCTION_ARGS) { ! GinLogicValue *check = (GinLogicValue *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); /* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */ *************** ginarrayconsistent(PG_FUNCTION_ARGS) *** 152,158 **** /* Datum *queryKeys = (Datum *) PG_GETARG_POINTER(6); */ bool *nullFlags = (bool *) PG_GETARG_POINTER(7); ! bool res; int32 i; switch (strategy) --- 152,158 ---- /* Datum *queryKeys = (Datum *) PG_GETARG_POINTER(6); */ bool *nullFlags = (bool *) PG_GETARG_POINTER(7); ! GinLogicValue res; int32 i; switch (strategy) *************** ginarrayconsistent(PG_FUNCTION_ARGS) *** 161,173 **** /* result is not lossy */ *recheck = false; /* must have a match for at least one non-null element */ ! res = false; for (i = 0; i < nkeys; i++) { ! if (check[i] && !nullFlags[i]) { ! res = true; ! break; } } break; --- 161,180 ---- /* result is not lossy */ *recheck = false; /* 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; *************** ginarrayconsistent(PG_FUNCTION_ARGS) *** 175,195 **** /* result is not lossy */ *recheck = false; /* must have all elements in check[] true, and no nulls */ ! res = true; for (i = 0; i < nkeys; i++) { ! if (!check[i] || nullFlags[i]) { ! res = false; break; } } break; case GinContainedStrategy: /* we will need recheck */ *recheck = true; /* can't do anything else useful here */ ! res = true; break; case GinEqualStrategy: /* we will need recheck */ --- 182,206 ---- /* result is not lossy */ *recheck = false; /* 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: /* we will need recheck */ *recheck = true; /* can't do anything else useful here */ ! res = GIN_TRUE; break; case GinEqualStrategy: /* we will need recheck */ *************** ginarrayconsistent(PG_FUNCTION_ARGS) *** 200,213 **** * against nulls here. This is because array_contain_compare and * array_eq handle nulls differently ... */ ! res = true; for (i = 0; i < nkeys; i++) { ! if (!check[i]) { ! res = false; break; } } break; default: --- 211,228 ---- * against nulls here. This is because array_contain_compare and * array_eq handle nulls differently ... */ ! res = GIN_TRUE; for (i = 0; i < nkeys; i++) { ! if (check[i] == GIN_FALSE) { ! res = GIN_FALSE; break; } + else if (check[i] == GIN_MAYBE) + { + res = GIN_MAYBE; + } } break; default: diff --git a/src/backend/access/gin/ginlogic.c b/src/backend/access/gin/ginlogic.c new file mode 100644 index dc8e630..4fb84c1 *** a/src/backend/access/gin/ginlogic.c --- b/src/backend/access/gin/ginlogic.c *************** ginInitConsistentFunction(GinState *gins *** 177,182 **** key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1]; key->collation = ginstate->supportCollation[key->attnum - 1]; key->boolConsistentFn = normalBoolConsistentFn; ! key->triConsistentFn = shimTriConsistentFn; } } --- 177,185 ---- key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1]; key->collation = ginstate->supportCollation[key->attnum - 1]; key->boolConsistentFn = normalBoolConsistentFn; ! if (ginstate->consistentSupportMaybe[key->attnum - 1]) ! key->triConsistentFn = normalBoolConsistentFn; ! else ! key->triConsistentFn = shimTriConsistentFn; } } diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c new file mode 100644 index 486f2ef..d561d60 *** a/src/backend/access/gin/ginutil.c --- b/src/backend/access/gin/ginutil.c *************** initGinState(GinState *state, Relation i *** 67,75 **** 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 partial match. --- 67,89 ---- fmgr_info_copy(&(state->extractQueryFn[i]), index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC), CurrentMemoryContext); ! /* ! * Check opclass capability to do tri-state logic consistent check. ! */ ! if (index_getprocid(index, i + 1, GIN_CONSISTENT_TRISTATE_PROC) != InvalidOid) ! { ! fmgr_info_copy(&(state->consistentFn[i]), ! index_getprocinfo(index, i + 1, GIN_CONSISTENT_TRISTATE_PROC), ! CurrentMemoryContext); ! state->consistentSupportMaybe[i] = true; ! } ! else ! { ! fmgr_info_copy(&(state->consistentFn[i]), ! index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC), ! CurrentMemoryContext); ! state->consistentSupportMaybe[i] = false; ! } /* * Check opclass capability to do partial match. diff --git a/src/backend/utils/adt/tsginidx.c b/src/backend/utils/adt/tsginidx.c new file mode 100644 index 9f6e8e9..c78bf2b *** a/src/backend/utils/adt/tsginidx.c --- b/src/backend/utils/adt/tsginidx.c *************** *** 15,20 **** --- 15,21 ---- #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" *************** gin_extract_tsquery(PG_FUNCTION_ARGS) *** 172,183 **** typedef struct { QueryItem *first_item; ! bool *check; int *map_item_operand; bool *need_recheck; } GinChkVal; ! static bool checkcondition_gin(void *checkval, QueryOperand *val) { GinChkVal *gcv = (GinChkVal *) checkval; --- 173,184 ---- typedef struct { QueryItem *first_item; ! GinLogicValue *check; int *map_item_operand; bool *need_recheck; } GinChkVal; ! static GinLogicValue checkcondition_gin(void *checkval, QueryOperand *val) { GinChkVal *gcv = (GinChkVal *) checkval; *************** checkcondition_gin(void *checkval, Query *** 194,203 **** return gcv->check[j]; } Datum gin_tsquery_consistent(PG_FUNCTION_ARGS) { ! bool *check = (bool *) PG_GETARG_POINTER(0); /* StrategyNumber strategy = PG_GETARG_UINT16(1); */ TSQuery query = PG_GETARG_TSQUERY(2); --- 195,264 ---- return gcv->check[j]; } + /* + * Evaluate tsquery boolean expression using ternary login. + * + * 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) { ! GinLogicValue *check = (bool *) PG_GETARG_POINTER(0); /* StrategyNumber strategy = PG_GETARG_UINT16(1); */ TSQuery query = PG_GETARG_TSQUERY(2); *************** gin_tsquery_consistent(PG_FUNCTION_ARGS) *** 205,211 **** /* int32 nkeys = PG_GETARG_INT32(3); */ Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); bool *recheck = (bool *) PG_GETARG_POINTER(5); ! bool res = FALSE; /* The query requires recheck only if it involves weights */ *recheck = false; --- 266,272 ---- /* int32 nkeys = PG_GETARG_INT32(3); */ Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); bool *recheck = (bool *) PG_GETARG_POINTER(5); ! GinLogicValue res = GIN_FALSE; /* The query requires recheck only if it involves weights */ *recheck = false; *************** gin_tsquery_consistent(PG_FUNCTION_ARGS) *** 224,233 **** gcv.map_item_operand = (int *) (extra_data[0]); gcv.need_recheck = recheck; ! res = TS_execute(GETQUERY(query), ! &gcv, ! true, ! checkcondition_gin); } PG_RETURN_BOOL(res); --- 285,293 ---- gcv.map_item_operand = (int *) (extra_data[0]); gcv.need_recheck = recheck; ! res = TS_execute_ternary(GETQUERY(query), ! &gcv, ! checkcondition_gin); } PG_RETURN_BOOL(res); diff --git a/src/include/access/gin.h b/src/include/access/gin.h new file mode 100644 index 03e58c9..afa13a0 *** a/src/include/access/gin.h --- b/src/include/access/gin.h *************** *** 23,29 **** #define GIN_EXTRACTQUERY_PROC 3 #define GIN_CONSISTENT_PROC 4 #define GIN_COMPARE_PARTIAL_PROC 5 ! #define GINNProcs 5 /* * searchMode settings for extractQueryFn. --- 23,30 ---- #define GIN_EXTRACTQUERY_PROC 3 #define GIN_CONSISTENT_PROC 4 #define GIN_COMPARE_PARTIAL_PROC 5 ! #define GIN_CONSISTENT_TRISTATE_PROC 6 ! #define GINNProcs 6 /* * searchMode settings for extractQueryFn. *************** typedef struct GinStatsData *** 46,51 **** --- 47,63 ---- 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; + /* GUC parameter */ extern PGDLLIMPORT int GinFuzzySearchLimit; diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h new file mode 100644 index 2cf042a..5e195f0 *** a/src/include/access/gin_private.h --- b/src/include/access/gin_private.h *************** typedef struct GinState *** 353,358 **** --- 353,360 ---- bool canPartialMatch[INDEX_MAX_KEYS]; /* Collations to pass to the support functions */ Oid supportCollation[INDEX_MAX_KEYS]; + /* Consistent function supportsunknown values? */ + bool consistentSupportMaybe[INDEX_MAX_KEYS]; } GinState; *************** extern void ginNewScanKey(IndexScanDesc *** 850,866 **** 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 */ --- 852,857 ---- diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h new file mode 100644 index 4f46ddd..759ea70 *** a/src/include/catalog/pg_am.h --- b/src/include/catalog/pg_am.h *************** DESCR("hash index access method"); *** 126,132 **** 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 )); 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 )); --- 126,132 ---- 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 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 new file mode 100644 index 66bd765..636847b *** a/src/include/catalog/pg_amproc.h --- b/src/include/catalog/pg_amproc.h *************** DATA(insert ( 3919 3831 3831 7 3881 )) *** 234,360 **** 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 ( 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 )); --- 234,360 ---- 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 2744 )); 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 6 3658 )); DATA(insert ( 3659 3614 3614 5 2700 )); diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out new file mode 100644 index 26abe8a..3b91877 *** a/src/test/regress/expected/opr_sanity.out --- b/src/test/regress/expected/opr_sanity.out *************** WHERE p2.opfmethod = p1.oid AND p3.ampro *** 1306,1312 **** 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 --- 1306,1313 ---- p4.amproclefttype = p3.amproclefttype AND p4.amprocrighttype = p3.amprocrighttype) NOT BETWEEN ! (CASE WHEN p1.amname IN ('btree', 'gist') THEN p1.amsupport - 1 ! WHEN p1.amname = 'gin' THEN p1.amsupport - 2 ELSE p1.amsupport END) AND p1.amsupport; amname | opfname | amproclefttype | amprocrighttype *************** FROM pg_am am JOIN pg_opclass op ON opcm *** 1333,1339 **** 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 --------+---------+------- --- 1334,1341 ---- 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 AND ! (count(*) != amsupport - 2 OR am.amname <> 'gin')) OR amprocfamily IS NULL; amname | opcname | count --------+---------+------- diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql new file mode 100644 index 40e1be2..6dd93f9 *** a/src/test/regress/sql/opr_sanity.sql --- b/src/test/regress/sql/opr_sanity.sql *************** WHERE p2.opfmethod = p1.oid AND p3.ampro *** 1002,1008 **** 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; --- 1002,1009 ---- p4.amproclefttype = p3.amproclefttype AND p4.amprocrighttype = p3.amprocrighttype) NOT BETWEEN ! (CASE WHEN p1.amname IN ('btree', 'gist') THEN p1.amsupport - 1 ! WHEN p1.amname = 'gin' THEN p1.amsupport - 2 ELSE p1.amsupport END) AND p1.amsupport; *************** FROM pg_am am JOIN pg_opclass op ON opcm *** 1024,1030 **** 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; -- Unfortunately, we can't check the amproc link very well because the --- 1025,1032 ---- 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 AND ! (count(*) != amsupport - 2 OR am.amname <> 'gin')) OR amprocfamily IS NULL; -- Unfortunately, we can't check the amproc link very well because the