diff options
Diffstat (limited to 'src/backend/access/gin/ginscan.c')
-rw-r--r-- | src/backend/access/gin/ginscan.c | 127 |
1 files changed, 78 insertions, 49 deletions
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c index c15d06ceba4..0a685bdbfc6 100644 --- a/src/backend/access/gin/ginscan.c +++ b/src/backend/access/gin/ginscan.c @@ -126,6 +126,27 @@ ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum, } /* + * Append hidden scan entry of given category to the scan key. + * + * NB: this had better be called at most once per scan key, since + * ginFillScanKey leaves room for only one hidden entry. Currently, + * it seems sufficiently clear that this is true that we don't bother + * with any cross-check logic. + */ +static void +ginScanKeyAddHiddenEntry(GinScanOpaque so, GinScanKey key, + GinNullCategory queryCategory) +{ + int i = key->nentries++; + + /* strategy is of no interest because this is not a partial-match item */ + key->scanEntry[i] = ginFillScanEntry(so, key->attnum, + InvalidStrategy, key->searchMode, + (Datum) 0, queryCategory, + false, NULL); +} + +/* * Initialize the next GinScanKey using the output from the extractQueryFn */ static void @@ -137,17 +158,16 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, { GinScanKey key = &(so->keys[so->nkeys++]); GinState *ginstate = &so->ginstate; - uint32 nUserQueryValues = nQueryValues; uint32 i; - /* Non-default search modes add one "hidden" entry to each key */ - if (searchMode != GIN_SEARCH_MODE_DEFAULT) - nQueryValues++; key->nentries = nQueryValues; - key->nuserentries = nUserQueryValues; + key->nuserentries = nQueryValues; - key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues); - key->entryRes = (GinTernaryValue *) palloc0(sizeof(GinTernaryValue) * nQueryValues); + /* Allocate one extra array slot for possible "hidden" entry */ + key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * + (nQueryValues + 1)); + key->entryRes = (GinTernaryValue *) palloc0(sizeof(GinTernaryValue) * + (nQueryValues + 1)); key->query = query; key->queryValues = queryValues; @@ -157,6 +177,12 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, key->searchMode = searchMode; key->attnum = attnum; + /* + * Initially, scan keys of GIN_SEARCH_MODE_ALL mode are marked + * excludeOnly. This might get changed later. + */ + key->excludeOnly = (searchMode == GIN_SEARCH_MODE_ALL); + ItemPointerSetMin(&key->curItem); key->curItemMatches = false; key->recheckCurItem = false; @@ -168,6 +194,7 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, ginInitConsistentFunction(ginstate, key); + /* Set up normal scan entries using extractQueryFn's outputs */ for (i = 0; i < nQueryValues; i++) { Datum queryKey; @@ -175,54 +202,28 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, bool isPartialMatch; Pointer this_extra; - if (i < nUserQueryValues) - { - /* set up normal entry using extractQueryFn's outputs */ - queryKey = queryValues[i]; - queryCategory = queryCategories[i]; - isPartialMatch = - (ginstate->canPartialMatch[attnum - 1] && partial_matches) - ? partial_matches[i] : false; - this_extra = (extra_data) ? extra_data[i] : NULL; - } - else - { - /* set up hidden entry */ - queryKey = (Datum) 0; - switch (searchMode) - { - case GIN_SEARCH_MODE_INCLUDE_EMPTY: - queryCategory = GIN_CAT_EMPTY_ITEM; - break; - case GIN_SEARCH_MODE_ALL: - queryCategory = GIN_CAT_EMPTY_QUERY; - break; - case GIN_SEARCH_MODE_EVERYTHING: - queryCategory = GIN_CAT_EMPTY_QUERY; - break; - default: - elog(ERROR, "unexpected searchMode: %d", searchMode); - queryCategory = 0; /* keep compiler quiet */ - break; - } - isPartialMatch = false; - this_extra = NULL; - - /* - * We set the strategy to a fixed value so that ginFillScanEntry - * can combine these entries for different scan keys. This is - * safe because the strategy value in the entry struct is only - * used for partial-match cases. It's OK to overwrite our local - * variable here because this is the last loop iteration. - */ - strategy = InvalidStrategy; - } + queryKey = queryValues[i]; + queryCategory = queryCategories[i]; + isPartialMatch = + (ginstate->canPartialMatch[attnum - 1] && partial_matches) + ? partial_matches[i] : false; + this_extra = (extra_data) ? extra_data[i] : NULL; key->scanEntry[i] = ginFillScanEntry(so, attnum, strategy, searchMode, queryKey, queryCategory, isPartialMatch, this_extra); } + + /* + * For GIN_SEARCH_MODE_INCLUDE_EMPTY and GIN_SEARCH_MODE_EVERYTHING search + * modes, we add the "hidden" entry immediately. GIN_SEARCH_MODE_ALL is + * handled later, since we might be able to omit the hidden entry for it. + */ + if (searchMode == GIN_SEARCH_MODE_INCLUDE_EMPTY) + ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_ITEM); + else if (searchMode == GIN_SEARCH_MODE_EVERYTHING) + ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_QUERY); } /* @@ -265,6 +266,7 @@ ginNewScanKey(IndexScanDesc scan) GinScanOpaque so = (GinScanOpaque) scan->opaque; int i; bool hasNullQuery = false; + bool attrHasNormalScan[INDEX_MAX_KEYS] = {false}; MemoryContext oldCtx; /* @@ -371,6 +373,33 @@ ginNewScanKey(IndexScanDesc scan) skey->sk_argument, nQueryValues, queryValues, categories, partial_matches, extra_data); + + /* Remember if we had any non-excludeOnly keys */ + if (searchMode != GIN_SEARCH_MODE_ALL) + attrHasNormalScan[skey->sk_attno - 1] = true; + } + + /* + * Processing GIN_SEARCH_MODE_ALL scan keys requires us to make a second + * pass over the scan keys. Above we marked each such scan key as + * excludeOnly. If the involved column has any normal (not excludeOnly) + * scan key as well, then we can leave it like that. Otherwise, one + * excludeOnly scan key must receive a GIN_CAT_EMPTY_QUERY hidden entry + * and be set to normal (excludeOnly = false). + */ + for (i = 0; i < so->nkeys; i++) + { + GinScanKey key = &so->keys[i]; + + if (key->searchMode != GIN_SEARCH_MODE_ALL) + continue; + + if (!attrHasNormalScan[key->attnum - 1]) + { + key->excludeOnly = false; + ginScanKeyAddHiddenEntry(so, key, GIN_CAT_EMPTY_QUERY); + attrHasNormalScan[key->attnum - 1] = true; + } } /* |