agora inbox for [email protected]
help / color / mirror / Atom feedFrom: Nikita Glukhov <[email protected]>
Subject: [PATCH 3/5] Force GIN recheck for empty ALL keys more accurately
Date: Fri, 22 Nov 2019 03:24:09 +0300
---
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
--------------C5A6759166AE2CA1CB41B4D8
Content-Type: text/x-patch;
name="0004-Avoid-GIN-full-scan-for-non-empty-ALL-keys-v08.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename*0="0004-Avoid-GIN-full-scan-for-non-empty-ALL-keys-v08.patch"
view thread (2+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH 3/5] Force GIN recheck for empty ALL keys more accurately
In-Reply-To: <no-message-id-209685@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox