diff options
Diffstat (limited to 'src/backend/access/gin/ginscan.c')
-rw-r--r-- | src/backend/access/gin/ginscan.c | 294 |
1 files changed, 161 insertions, 133 deletions
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c index c9cf77514a1..25f60e15a0d 100644 --- a/src/backend/access/gin/ginscan.c +++ b/src/backend/access/gin/ginscan.c @@ -53,19 +53,95 @@ ginbeginscan(PG_FUNCTION_ARGS) } /* - * Initialize a GinScanKey using the output from the extractQueryFn + * Create a new GinScanEntry, unless an equivalent one already exists, + * in which case just return it + */ +static GinScanEntry +ginFillScanEntry(GinScanOpaque so, OffsetNumber attnum, + StrategyNumber strategy, int32 searchMode, + Datum queryKey, GinNullCategory queryCategory, + bool isPartialMatch, Pointer extra_data) +{ + GinState *ginstate = &so->ginstate; + GinScanEntry scanEntry; + uint32 i; + + /* + * Look for an existing equivalent entry. + * + * Entries with non-null extra_data are never considered identical, since + * we can't know exactly what the opclass might be doing with that. + */ + if (extra_data == NULL) + { + for (i = 0; i < so->totalentries; i++) + { + GinScanEntry prevEntry = so->entries[i]; + + if (prevEntry->extra_data == NULL && + prevEntry->isPartialMatch == isPartialMatch && + prevEntry->strategy == strategy && + prevEntry->searchMode == searchMode && + prevEntry->attnum == attnum && + ginCompareEntries(ginstate, attnum, + prevEntry->queryKey, + prevEntry->queryCategory, + queryKey, + queryCategory) == 0) + { + /* Successful match */ + return prevEntry; + } + } + } + + /* Nope, create a new entry */ + scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData)); + scanEntry->queryKey = queryKey; + scanEntry->queryCategory = queryCategory; + scanEntry->isPartialMatch = isPartialMatch; + scanEntry->extra_data = extra_data; + scanEntry->strategy = strategy; + scanEntry->searchMode = searchMode; + scanEntry->attnum = attnum; + + scanEntry->buffer = InvalidBuffer; + ItemPointerSetMin(&scanEntry->curItem); + scanEntry->matchBitmap = NULL; + scanEntry->matchIterator = NULL; + scanEntry->matchResult = NULL; + scanEntry->list = NULL; + scanEntry->nlist = 0; + scanEntry->offset = InvalidOffsetNumber; + scanEntry->isFinished = false; + scanEntry->reduceResult = false; + + /* Add it to so's array */ + if (so->totalentries >= so->allocentries) + { + so->allocentries *= 2; + so->entries = (GinScanEntry *) + repalloc(so->entries, so->allocentries * sizeof(GinScanEntry)); + } + so->entries[so->totalentries++] = scanEntry; + + return scanEntry; +} + +/* + * Initialize the next GinScanKey using the output from the extractQueryFn */ static void -ginFillScanKey(GinState *ginstate, GinScanKey key, - OffsetNumber attnum, Datum query, +ginFillScanKey(GinScanOpaque so, OffsetNumber attnum, + StrategyNumber strategy, int32 searchMode, + Datum query, uint32 nQueryValues, Datum *queryValues, GinNullCategory *queryCategories, - bool *partial_matches, uint32 nQueryValues, - StrategyNumber strategy, Pointer *extra_data, - int32 searchMode) + bool *partial_matches, Pointer *extra_data) { + GinScanKey key = &(so->keys[so->nkeys++]); + GinState *ginstate = &so->ginstate; uint32 nUserQueryValues = nQueryValues; - uint32 i, - j; + uint32 i; /* Non-default search modes add one "hidden" entry to each key */ if (searchMode != GIN_SEARCH_MODE_DEFAULT) @@ -73,8 +149,9 @@ ginFillScanKey(GinState *ginstate, GinScanKey key, key->nentries = nQueryValues; key->nuserentries = nUserQueryValues; - key->scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * nQueryValues); + key->scanEntry = (GinScanEntry *) palloc(sizeof(GinScanEntry) * nQueryValues); key->entryRes = (bool *) palloc0(sizeof(bool) * nQueryValues); + key->query = query; key->queryValues = queryValues; key->queryCategories = queryCategories; @@ -83,156 +160,106 @@ ginFillScanKey(GinState *ginstate, GinScanKey key, key->searchMode = searchMode; key->attnum = attnum; - key->firstCall = TRUE; ItemPointerSetMin(&key->curItem); + key->curItemMatches = false; + key->recheckCurItem = false; + key->isFinished = false; for (i = 0; i < nQueryValues; i++) { - GinScanEntry scanEntry = key->scanEntry + i; + Datum queryKey; + GinNullCategory queryCategory; + bool isPartialMatch; + Pointer this_extra; - scanEntry->pval = key->entryRes + i; if (i < nUserQueryValues) { - scanEntry->queryKey = queryValues[i]; - scanEntry->queryCategory = queryCategories[i]; - scanEntry->isPartialMatch = + /* 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; - scanEntry->extra_data = (extra_data) ? extra_data[i] : NULL; + this_extra = (extra_data) ? extra_data[i] : NULL; } else { /* set up hidden entry */ - scanEntry->queryKey = (Datum) 0; + queryKey = (Datum) 0; switch (searchMode) { case GIN_SEARCH_MODE_INCLUDE_EMPTY: - scanEntry->queryCategory = GIN_CAT_EMPTY_ITEM; + queryCategory = GIN_CAT_EMPTY_ITEM; break; case GIN_SEARCH_MODE_ALL: - scanEntry->queryCategory = GIN_CAT_EMPTY_QUERY; + queryCategory = GIN_CAT_EMPTY_QUERY; break; case GIN_SEARCH_MODE_EVERYTHING: - scanEntry->queryCategory = GIN_CAT_EMPTY_QUERY; + queryCategory = GIN_CAT_EMPTY_QUERY; break; default: elog(ERROR, "unexpected searchMode: %d", searchMode); + queryCategory = 0; /* keep compiler quiet */ break; } - scanEntry->isPartialMatch = false; - scanEntry->extra_data = NULL; + 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; } - scanEntry->strategy = strategy; - scanEntry->searchMode = searchMode; - scanEntry->attnum = attnum; - - ItemPointerSetMin(&scanEntry->curItem); - scanEntry->isFinished = FALSE; - scanEntry->offset = InvalidOffsetNumber; - scanEntry->buffer = InvalidBuffer; - scanEntry->list = NULL; - scanEntry->nlist = 0; - scanEntry->matchBitmap = NULL; - scanEntry->matchIterator = NULL; - scanEntry->matchResult = NULL; - /* - * Link to any preceding identical entry in current scan key. - * - * Entries with non-null extra_data are never considered identical, - * since we can't know exactly what the opclass might be doing with - * that. - */ - scanEntry->master = NULL; - if (scanEntry->extra_data == NULL) - { - for (j = 0; j < i; j++) - { - GinScanEntry prevEntry = key->scanEntry + j; - - if (prevEntry->extra_data == NULL && - scanEntry->isPartialMatch == prevEntry->isPartialMatch && - ginCompareEntries(ginstate, attnum, - scanEntry->queryKey, - scanEntry->queryCategory, - prevEntry->queryKey, - prevEntry->queryCategory) == 0) - { - scanEntry->master = prevEntry; - break; - } - } - } + key->scanEntry[i] = ginFillScanEntry(so, attnum, + strategy, searchMode, + queryKey, queryCategory, + isPartialMatch, this_extra); } } -#ifdef NOT_USED - static void -resetScanKeys(GinScanKey keys, uint32 nkeys) +freeScanKeys(GinScanOpaque so) { - uint32 i, - j; + uint32 i; - if (keys == NULL) + if (so->keys == NULL) return; - for (i = 0; i < nkeys; i++) + for (i = 0; i < so->nkeys; i++) { - GinScanKey key = keys + i; - - key->firstCall = TRUE; - ItemPointerSetMin(&key->curItem); + GinScanKey key = so->keys + i; - for (j = 0; j < key->nentries; j++) - { - if (key->scanEntry[j].buffer != InvalidBuffer) - ReleaseBuffer(key->scanEntry[i].buffer); - - ItemPointerSetMin(&key->scanEntry[j].curItem); - key->scanEntry[j].isFinished = FALSE; - key->scanEntry[j].offset = InvalidOffsetNumber; - key->scanEntry[j].buffer = InvalidBuffer; - key->scanEntry[j].list = NULL; - key->scanEntry[j].nlist = 0; - key->scanEntry[j].matchBitmap = NULL; - key->scanEntry[j].matchIterator = NULL; - key->scanEntry[j].matchResult = NULL; - } + pfree(key->scanEntry); + pfree(key->entryRes); } -} -#endif - -static void -freeScanKeys(GinScanKey keys, uint32 nkeys) -{ - uint32 i, - j; - if (keys == NULL) - return; + pfree(so->keys); + so->keys = NULL; + so->nkeys = 0; - for (i = 0; i < nkeys; i++) + for (i = 0; i < so->totalentries; i++) { - GinScanKey key = keys + i; - - for (j = 0; j < key->nentries; j++) - { - if (key->scanEntry[j].buffer != InvalidBuffer) - ReleaseBuffer(key->scanEntry[j].buffer); - if (key->scanEntry[j].list) - pfree(key->scanEntry[j].list); - if (key->scanEntry[j].matchIterator) - tbm_end_iterate(key->scanEntry[j].matchIterator); - if (key->scanEntry[j].matchBitmap) - tbm_free(key->scanEntry[j].matchBitmap); - } - - pfree(key->entryRes); - pfree(key->scanEntry); + GinScanEntry entry = so->entries[i]; + + if (entry->buffer != InvalidBuffer) + ReleaseBuffer(entry->buffer); + if (entry->list) + pfree(entry->list); + if (entry->matchIterator) + tbm_end_iterate(entry->matchIterator); + if (entry->matchBitmap) + tbm_free(entry->matchBitmap); + pfree(entry); } - pfree(keys); + pfree(so->entries); + so->entries = NULL; + so->totalentries = 0; } void @@ -241,12 +268,18 @@ ginNewScanKey(IndexScanDesc scan) ScanKey scankey = scan->keyData; GinScanOpaque so = (GinScanOpaque) scan->opaque; int i; - uint32 nkeys = 0; bool hasNullQuery = false; /* if no scan keys provided, allocate extra EVERYTHING GinScanKey */ so->keys = (GinScanKey) palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData)); + so->nkeys = 0; + + /* initialize expansible array of GinScanEntry pointers */ + so->totalentries = 0; + so->allocentries = 32; + so->entries = (GinScanEntry *) + palloc0(so->allocentries * sizeof(GinScanEntry)); so->isVoidRes = false; @@ -331,26 +364,24 @@ ginNewScanKey(IndexScanDesc scan) } /* now we can use the nullFlags as category codes */ - ginFillScanKey(&so->ginstate, &(so->keys[nkeys]), - skey->sk_attno, skey->sk_argument, + ginFillScanKey(so, skey->sk_attno, + skey->sk_strategy, searchMode, + skey->sk_argument, nQueryValues, queryValues, (GinNullCategory *) nullFlags, - partial_matches, nQueryValues, - skey->sk_strategy, extra_data, searchMode); - nkeys++; + partial_matches, extra_data); } /* * If there are no regular scan keys, generate an EVERYTHING scankey to * drive a full-index scan. */ - if (nkeys == 0 && !so->isVoidRes) + if (so->nkeys == 0 && !so->isVoidRes) { hasNullQuery = true; - ginFillScanKey(&so->ginstate, &(so->keys[nkeys]), - FirstOffsetNumber, (Datum) 0, - NULL, NULL, NULL, 0, - InvalidStrategy, NULL, GIN_SEARCH_MODE_EVERYTHING); - nkeys++; + ginFillScanKey(so, FirstOffsetNumber, + InvalidStrategy, GIN_SEARCH_MODE_EVERYTHING, + (Datum) 0, 0, + NULL, NULL, NULL, NULL); } /* @@ -371,8 +402,6 @@ ginNewScanKey(IndexScanDesc scan) RelationGetRelationName(scan->indexRelation)))); } - so->nkeys = nkeys; - pgstat_count_index_scan(scan->indexRelation); } @@ -384,8 +413,7 @@ ginrescan(PG_FUNCTION_ARGS) /* remaining arguments are ignored */ GinScanOpaque so = (GinScanOpaque) scan->opaque; - freeScanKeys(so->keys, so->nkeys); - so->keys = NULL; + freeScanKeys(so); if (scankey && scan->numberOfKeys > 0) { @@ -403,7 +431,7 @@ ginendscan(PG_FUNCTION_ARGS) IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); GinScanOpaque so = (GinScanOpaque) scan->opaque; - freeScanKeys(so->keys, so->nkeys); + freeScanKeys(so); MemoryContextDelete(so->tempCtx); |