From 8ab99c472801eab2bf10dea2c895b7b2716dec06 Mon Sep 17 00:00:00 2001 From: Nikita Glukhov Date: Fri, 22 Nov 2019 03:24:09 +0300 Subject: [PATCH 3/5] Force GIN recheck for empty ALL keys more accurately --- src/backend/access/gin/ginscan.c | 81 +++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c index 903891f..47beef3 100644 --- a/src/backend/access/gin/ginscan.c +++ b/src/backend/access/gin/ginscan.c @@ -129,19 +129,21 @@ ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum, * Initialize the next GinScanKey using the output from the extractQueryFn */ static void -ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, - StrategyNumber strategy, int32 searchMode, +ginFillScanKey(GinScanOpaque so, GinScanKey key, bool initHiddenEntries, + OffsetNumber attnum, StrategyNumber strategy, int32 searchMode, Datum query, uint32 nQueryValues, Datum *queryValues, GinNullCategory *queryCategories, bool *partial_matches, Pointer *extra_data) { - GinScanKey key = &(so->keys[so->nkeys++]); GinState *ginstate = &so->ginstate; uint32 nUserQueryValues = nQueryValues; uint32 i; + if (key == NULL) + key = &(so->keys[so->nkeys++]); + /* Non-default search modes add one "hidden" entry to each key */ - if (searchMode != GIN_SEARCH_MODE_DEFAULT) + if (searchMode != GIN_SEARCH_MODE_DEFAULT && initHiddenEntries) nQueryValues++; key->nentries = nQueryValues; key->nuserentries = nUserQueryValues; @@ -270,8 +272,14 @@ ginNewScanKey(IndexScanDesc scan) GinScanOpaque so = (GinScanOpaque) scan->opaque; int i; bool hasNullQuery = false; - bool colNotNull[INDEX_MAX_KEYS] = {0}; - int numNotNullKeys = 0; + /* + * GIN_FALSE - column has no search keys + * GIN_TRUE - NOT NULL is implied by some non-empty search key + * GIN_MAYBE - NOT NULL is implied by some empty ALL key + */ + GinTernaryValue colNotNull[INDEX_MAX_KEYS] = {0}; + /* Number of GIN_MAYBE values in colNotNull[] */ + int colNotNullCount = 0; MemoryContext oldCtx; /* @@ -360,13 +368,34 @@ ginNewScanKey(IndexScanDesc scan) * any non-ALL scankeys; otherwise we end up adding a NOT_NULL * scankey below. */ - so->forcedRecheck = true; + GinScanKeyData key; + GinTernaryValue res; + + /* Check whether unconditional recheck is needed. */ + ginFillScanKey(so, &key, false, skey->sk_attno, + skey->sk_strategy, searchMode, + skey->sk_argument, 0, + NULL, NULL, NULL, NULL); + + res = key.triConsistentFn(&key); + + if (res == GIN_FALSE) + { + so->isVoidRes = true; /* unsatisfiable query */ + break; + } + + so->forcedRecheck |= res != GIN_TRUE; /* - * Increment the number of columns with NOT NULL constraints. + * Increment the number of columns with NOT NULL constraints + * if NOT NULL is not yet implied by some non-empty key. */ - colNotNull[colno] = true; - numNotNullKeys++; + if (colNotNull[colno] == GIN_FALSE) + { + colNotNull[colno] = GIN_MAYBE; + colNotNullCount++; + } continue; } @@ -375,6 +404,16 @@ ginNewScanKey(IndexScanDesc scan) } /* + * Current non-empty key implies that column is NOT NULL, so decrement + * the number of columns with NOT NULL if NOT NULL already is implied by + * some empty ALL key. + */ + if (colNotNull[colno] == GIN_MAYBE) + colNotNullCount--; + + colNotNull[colno] = GIN_TRUE; + + /* * Create GinNullCategory representation. If the extractQueryFn * didn't create a nullFlags array, we assume everything is non-null. * While at it, detect whether any null keys are present. @@ -394,7 +433,7 @@ ginNewScanKey(IndexScanDesc scan) } } - ginFillScanKey(so, skey->sk_attno, + ginFillScanKey(so, NULL, true, skey->sk_attno, skey->sk_strategy, searchMode, skey->sk_argument, nQueryValues, queryValues, categories, @@ -412,24 +451,32 @@ ginNewScanKey(IndexScanDesc scan) hasNullQuery = true; /* Initialize EVERYTHING key if there are no non-NULL columns. */ - if (!numNotNullKeys) + if (!colNotNullCount) { - ginFillScanKey(so, FirstOffsetNumber, InvalidStrategy, - GIN_SEARCH_MODE_EVERYTHING, (Datum) 0, 0, - NULL, NULL, NULL, NULL); + ginFillScanKey(so, NULL, true, FirstOffsetNumber, + InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING, + (Datum) 0, 0, NULL, NULL, NULL, NULL); } else { /* Initialize NOT_NULL key for each non-NULL column. */ for (i = 0; i < scan->indexRelation->rd_att->natts; i++) { - if (colNotNull[i]) - ginFillScanKey(so, i + 1, InvalidStrategy, + if (colNotNull[i] == GIN_MAYBE) + ginFillScanKey(so, NULL, true, i + 1, InvalidStrategy, GIN_SEARCH_MODE_NOT_NULL, (Datum) 0, 0, NULL, NULL, NULL, NULL); } } } + else if (colNotNullCount) + { + /* + * We use recheck instead of adding NOT_NULL entries to eliminate + * rows with NULL columns. + */ + so->forcedRecheck = true; + } /* * If the index is version 0, it may be missing null and placeholder -- 2.7.4