restartScanEntry:
entry->buffer = InvalidBuffer;
+ ItemPointerSetMin(&entry->curItem);
entry->offset = InvalidOffsetNumber;
entry->list = NULL;
entry->nlist = 0;
entry->reduceResult = FALSE;
entry->predictNumberResult = 0;
- if (entry->master != NULL)
- {
- entry->isFinished = entry->master->isFinished;
- return;
- }
-
/*
* we should find entry, and begin scan of posting tree or just store
* posting list in memory
static void
startScanKey(GinState *ginstate, GinScanKey key)
{
- uint32 i;
-
- if (!key->firstCall)
- return;
+ ItemPointerSetMin(&key->curItem);
+ key->curItemMatches = false;
+ key->recheckCurItem = false;
+ key->isFinished = false;
+}
- for (i = 0; i < key->nentries; i++)
- startScanEntry(ginstate, key->scanEntry + i);
+static void
+startScan(IndexScanDesc scan)
+{
+ GinScanOpaque so = (GinScanOpaque) scan->opaque;
+ GinState *ginstate = &so->ginstate;
+ uint32 i;
- key->isFinished = FALSE;
- key->firstCall = FALSE;
+ for (i = 0; i < so->totalentries; i++)
+ startScanEntry(ginstate, so->entries[i]);
if (GinFuzzySearchLimit > 0)
{
* minimal predictNumberResult.
*/
- for (i = 0; i < key->nentries; i++)
- if (key->scanEntry[i].predictNumberResult <= key->nentries * GinFuzzySearchLimit)
+ for (i = 0; i < so->totalentries; i++)
+ if (so->entries[i]->predictNumberResult <= so->totalentries * GinFuzzySearchLimit)
return;
- for (i = 0; i < key->nentries; i++)
- if (key->scanEntry[i].predictNumberResult > key->nentries * GinFuzzySearchLimit)
+ for (i = 0; i < so->totalentries; i++)
+ if (so->entries[i]->predictNumberResult > so->totalentries * GinFuzzySearchLimit)
{
- key->scanEntry[i].predictNumberResult /= key->nentries;
- key->scanEntry[i].reduceResult = TRUE;
+ so->entries[i]->predictNumberResult /= so->totalentries;
+ so->entries[i]->reduceResult = TRUE;
}
}
-}
-
-static void
-startScan(IndexScanDesc scan)
-{
- uint32 i;
- GinScanOpaque so = (GinScanOpaque) scan->opaque;
for (i = 0; i < so->nkeys; i++)
- startScanKey(&so->ginstate, so->keys + i);
+ startScanKey(ginstate, so->keys + i);
}
/*
{
Assert(!entry->isFinished);
- if (entry->master)
- {
- entry->isFinished = entry->master->isFinished;
- entry->curItem = entry->master->curItem;
- }
- else if (entry->matchBitmap)
+ if (entry->matchBitmap)
{
do
{
}
/*
- * Sets key->curItem to next heap item pointer for one scan key, advancing
- * past any item pointers <= advancePast.
- * Sets key->isFinished to TRUE if there are no more.
+ * Identify the "current" item among the input entry streams for this scan key,
+ * and test whether it passes the scan key qual condition.
+ *
+ * The current item is the smallest curItem among the inputs. key->curItem
+ * is set to that value. key->curItemMatches is set to indicate whether that
+ * TID passes the consistentFn test. If so, key->recheckCurItem is set true
+ * iff recheck is needed for this item pointer (including the case where the
+ * item pointer is a lossy page pointer).
*
- * On success, key->recheckCurItem is set true iff recheck is needed for this
- * item pointer (including the case where the item pointer is a lossy page
- * pointer).
+ * If all entry streams are exhausted, sets key->isFinished to TRUE.
*
* Item pointers must be returned in ascending order.
*
* logic in scanGetItem.)
*/
static void
-keyGetItem(GinState *ginstate, MemoryContext tempCtx,
- GinScanKey key, ItemPointer advancePast)
+keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key)
{
- ItemPointerData myAdvancePast = *advancePast;
+ ItemPointerData minItem;
ItemPointerData curPageLossy;
uint32 i;
uint32 lossyEntry;
Assert(!key->isFinished);
- do
- {
- /*
- * Advance any entries that are <= myAdvancePast. In particular,
- * since entry->curItem was initialized with ItemPointerSetMin, this
- * ensures we fetch the first item for each entry on the first call.
- * Then set key->curItem to the minimum of the valid entry curItems.
- *
- * Note: a lossy-page entry is encoded by a ItemPointer with max value
- * for offset (0xffff), so that it will sort after any exact entries
- * for the same page. So we'll prefer to return exact pointers not
- * lossy pointers, which is good. Also, when we advance past an exact
- * entry after processing it, we will not advance past lossy entries
- * for the same page in other keys, which is NECESSARY for correct
- * results (since we might have additional entries for the same page
- * in the first key).
- */
- ItemPointerSetMax(&key->curItem);
-
- for (i = 0; i < key->nentries; i++)
- {
- entry = key->scanEntry + i;
-
- while (entry->isFinished == FALSE &&
- ginCompareItemPointers(&entry->curItem, &myAdvancePast) <= 0)
- entryGetItem(ginstate, entry);
-
- if (entry->isFinished == FALSE &&
- ginCompareItemPointers(&entry->curItem, &key->curItem) < 0)
- key->curItem = entry->curItem;
- }
+ /*
+ * Find the minimum of the active entry curItems.
+ *
+ * Note: a lossy-page entry is encoded by a ItemPointer with max value
+ * for offset (0xffff), so that it will sort after any exact entries
+ * for the same page. So we'll prefer to return exact pointers not
+ * lossy pointers, which is good.
+ */
+ ItemPointerSetMax(&minItem);
- if (ItemPointerIsMax(&key->curItem))
- {
- /* all entries are finished */
- key->isFinished = TRUE;
- return;
- }
+ for (i = 0; i < key->nentries; i++)
+ {
+ entry = key->scanEntry[i];
+ if (entry->isFinished == FALSE &&
+ ginCompareItemPointers(&entry->curItem, &minItem) < 0)
+ minItem = entry->curItem;
+ }
- /*
- * Now key->curItem contains first ItemPointer after previous result.
- * Advance myAdvancePast to this value, so that if the consistentFn
- * rejects the entry and we loop around again, we will advance to the
- * next available item pointer.
- */
- myAdvancePast = key->curItem;
+ if (ItemPointerIsMax(&minItem))
+ {
+ /* all entries are finished */
+ key->isFinished = TRUE;
+ return;
+ }
- /*
- * Lossy-page entries pose a problem, since we don't know the correct
- * entryRes state to pass to the consistentFn, and we also don't know
- * what its combining logic will be (could be AND, OR, or even NOT).
- * If the logic is OR then the consistentFn might succeed for all
- * items in the lossy page even when none of the other entries match.
- *
- * If we have a single lossy-page entry then we check to see if the
- * consistentFn will succeed with only that entry TRUE. If so,
- * we return a lossy-page pointer to indicate that the whole heap
- * page must be checked. (On the next call, we'll advance past all
- * regular and lossy entries for this page before resuming search,
- * thus ensuring that we never return both regular and lossy pointers
- * for the same page.)
- *
- * This idea could be generalized to more than one lossy-page entry,
- * but ideally lossy-page entries should be infrequent so it would
- * seldom be the case that we have more than one at once. So it
- * doesn't seem worth the extra complexity to optimize that case.
- * If we do find more than one, we just punt and return a lossy-page
- * pointer always.
- *
- * Note that only lossy-page entries pointing to the current item's
- * page should trigger this processing; we might have future lossy
- * pages in the entry array, but they aren't relevant yet.
- */
- ItemPointerSetLossyPage(&curPageLossy,
- GinItemPointerGetBlockNumber(&key->curItem));
+ /*
+ * We might have already tested this item; if so, no need to repeat work.
+ * (Note: the ">" case can happen, if minItem is exact but we previously
+ * had to set curItem to a lossy-page pointer.)
+ */
+ if (ginCompareItemPointers(&key->curItem, &minItem) >= 0)
+ return;
- lossyEntry = 0;
- haveLossyEntry = false;
- for (i = 0; i < key->nentries; i++)
- {
- entry = key->scanEntry + i;
- if (entry->isFinished == FALSE &&
- ginCompareItemPointers(&entry->curItem, &curPageLossy) == 0)
- {
- if (haveLossyEntry)
- {
- /* Multiple lossy entries, punt */
- key->curItem = curPageLossy;
- key->recheckCurItem = true;
- return;
- }
- lossyEntry = i;
- haveLossyEntry = true;
- }
- }
+ /*
+ * OK, advance key->curItem and perform consistentFn test.
+ */
+ key->curItem = minItem;
- /* prepare for calling consistentFn in temp context */
- oldCtx = MemoryContextSwitchTo(tempCtx);
+ /*
+ * Lossy-page entries pose a problem, since we don't know the correct
+ * entryRes state to pass to the consistentFn, and we also don't know
+ * what its combining logic will be (could be AND, OR, or even NOT).
+ * If the logic is OR then the consistentFn might succeed for all
+ * items in the lossy page even when none of the other entries match.
+ *
+ * If we have a single lossy-page entry then we check to see if the
+ * consistentFn will succeed with only that entry TRUE. If so,
+ * we return a lossy-page pointer to indicate that the whole heap
+ * page must be checked. (On subsequent calls, we'll do nothing until
+ * minItem is past the page altogether, thus ensuring that we never return
+ * both regular and lossy pointers for the same page.)
+ *
+ * This idea could be generalized to more than one lossy-page entry,
+ * but ideally lossy-page entries should be infrequent so it would
+ * seldom be the case that we have more than one at once. So it
+ * doesn't seem worth the extra complexity to optimize that case.
+ * If we do find more than one, we just punt and return a lossy-page
+ * pointer always.
+ *
+ * Note that only lossy-page entries pointing to the current item's
+ * page should trigger this processing; we might have future lossy
+ * pages in the entry array, but they aren't relevant yet.
+ */
+ ItemPointerSetLossyPage(&curPageLossy,
+ GinItemPointerGetBlockNumber(&key->curItem));
- if (haveLossyEntry)
+ lossyEntry = 0;
+ haveLossyEntry = false;
+ for (i = 0; i < key->nentries; i++)
+ {
+ entry = key->scanEntry[i];
+ if (entry->isFinished == FALSE &&
+ ginCompareItemPointers(&entry->curItem, &curPageLossy) == 0)
{
- /* Single lossy-page entry, so see if whole page matches */
- memset(key->entryRes, FALSE, key->nentries);
- key->entryRes[lossyEntry] = TRUE;
-
- if (callConsistentFn(ginstate, key))
+ if (haveLossyEntry)
{
- /* Yes, so clean up ... */
- MemoryContextSwitchTo(oldCtx);
- MemoryContextReset(tempCtx);
-
- /* and return lossy pointer for whole page */
+ /* Multiple lossy entries, punt */
key->curItem = curPageLossy;
+ key->curItemMatches = true;
key->recheckCurItem = true;
return;
}
+ lossyEntry = i;
+ haveLossyEntry = true;
}
+ }
- /*
- * At this point we know that we don't need to return a lossy
- * whole-page pointer, but we might have matches for individual exact
- * item pointers, possibly in combination with a lossy pointer. Our
- * strategy if there's a lossy pointer is to try the consistentFn both
- * ways and return a hit if it accepts either one (forcing the hit to
- * be marked lossy so it will be rechecked). An exception is that
- * we don't need to try it both ways if the lossy pointer is in a
- * "hidden" entry, because the consistentFn's result can't depend on
- * that.
- *
- * Prepare entryRes array to be passed to consistentFn.
- */
- for (i = 0; i < key->nentries; i++)
- {
- entry = key->scanEntry + i;
- if (entry->isFinished == FALSE &&
- ginCompareItemPointers(&entry->curItem, &key->curItem) == 0)
- key->entryRes[i] = TRUE;
- else
- key->entryRes[i] = FALSE;
- }
- if (haveLossyEntry)
- key->entryRes[lossyEntry] = TRUE;
+ /* prepare for calling consistentFn in temp context */
+ oldCtx = MemoryContextSwitchTo(tempCtx);
- res = callConsistentFn(ginstate, key);
+ if (haveLossyEntry)
+ {
+ /* Single lossy-page entry, so see if whole page matches */
+ memset(key->entryRes, FALSE, key->nentries);
+ key->entryRes[lossyEntry] = TRUE;
- if (!res && haveLossyEntry && lossyEntry < key->nuserentries)
+ if (callConsistentFn(ginstate, key))
{
- /* try the other way for the lossy item */
- key->entryRes[lossyEntry] = FALSE;
+ /* Yes, so clean up ... */
+ MemoryContextSwitchTo(oldCtx);
+ MemoryContextReset(tempCtx);
- res = callConsistentFn(ginstate, key);
+ /* and return lossy pointer for whole page */
+ key->curItem = curPageLossy;
+ key->curItemMatches = true;
+ key->recheckCurItem = true;
+ return;
}
+ }
- /* clean up after consistentFn calls */
- MemoryContextSwitchTo(oldCtx);
- MemoryContextReset(tempCtx);
+ /*
+ * At this point we know that we don't need to return a lossy
+ * whole-page pointer, but we might have matches for individual exact
+ * item pointers, possibly in combination with a lossy pointer. Our
+ * strategy if there's a lossy pointer is to try the consistentFn both
+ * ways and return a hit if it accepts either one (forcing the hit to
+ * be marked lossy so it will be rechecked). An exception is that
+ * we don't need to try it both ways if the lossy pointer is in a
+ * "hidden" entry, because the consistentFn's result can't depend on
+ * that.
+ *
+ * Prepare entryRes array to be passed to consistentFn.
+ */
+ for (i = 0; i < key->nentries; i++)
+ {
+ entry = key->scanEntry[i];
+ if (entry->isFinished == FALSE &&
+ ginCompareItemPointers(&entry->curItem, &key->curItem) == 0)
+ key->entryRes[i] = TRUE;
+ else
+ key->entryRes[i] = FALSE;
+ }
+ if (haveLossyEntry)
+ key->entryRes[lossyEntry] = TRUE;
- /* If we matched a lossy entry, force recheckCurItem = true */
- if (haveLossyEntry)
- key->recheckCurItem = true;
- } while (!res);
+ res = callConsistentFn(ginstate, key);
+
+ if (!res && haveLossyEntry && lossyEntry < key->nuserentries)
+ {
+ /* try the other way for the lossy item */
+ key->entryRes[lossyEntry] = FALSE;
+
+ res = callConsistentFn(ginstate, key);
+ }
+
+ key->curItemMatches = res;
+ /* If we matched a lossy entry, force recheckCurItem = true */
+ if (haveLossyEntry)
+ key->recheckCurItem = true;
+
+ /* clean up after consistentFn calls */
+ MemoryContextSwitchTo(oldCtx);
+ MemoryContextReset(tempCtx);
}
/*
ItemPointerData *item, bool *recheck)
{
GinScanOpaque so = (GinScanOpaque) scan->opaque;
+ GinState *ginstate = &so->ginstate;
ItemPointerData myAdvancePast = *advancePast;
uint32 i;
+ bool allFinished;
bool match;
for (;;)
{
/*
- * Advance any keys that are <= myAdvancePast. In particular,
- * since key->curItem was initialized with ItemPointerSetMin, this
- * ensures we fetch the first item for each key on the first call.
- * Then set *item to the minimum of the key curItems.
- *
- * Note: a lossy-page entry is encoded by a ItemPointer with max value
- * for offset (0xffff), so that it will sort after any exact entries
- * for the same page. So we'll prefer to return exact pointers not
- * lossy pointers, which is good. Also, when we advance past an exact
- * entry after processing it, we will not advance past lossy entries
- * for the same page in other keys, which is NECESSARY for correct
- * results (since we might have additional entries for the same page
- * in the first key).
+ * Advance any entries that are <= myAdvancePast. In particular,
+ * since entry->curItem was initialized with ItemPointerSetMin, this
+ * ensures we fetch the first item for each entry on the first call.
+ */
+ allFinished = TRUE;
+
+ for (i = 0; i < so->totalentries; i++)
+ {
+ GinScanEntry entry = so->entries[i];
+
+ while (entry->isFinished == FALSE &&
+ ginCompareItemPointers(&entry->curItem,
+ &myAdvancePast) <= 0)
+ entryGetItem(ginstate, entry);
+
+ if (entry->isFinished == FALSE)
+ allFinished = FALSE;
+ }
+
+ if (allFinished)
+ {
+ /* all entries exhausted, so we're done */
+ return false;
+ }
+
+ /*
+ * Perform the consistentFn test for each scan key. If any key
+ * reports isFinished, meaning its subset of the entries is exhausted,
+ * we can stop. Otherwise, set *item to the minimum of the key
+ * curItems.
*/
ItemPointerSetMax(item);
{
GinScanKey key = so->keys + i;
- while (key->isFinished == FALSE &&
- ginCompareItemPointers(&key->curItem, &myAdvancePast) <= 0)
- keyGetItem(&so->ginstate, so->tempCtx,
- key, &myAdvancePast);
+ keyGetItem(&so->ginstate, so->tempCtx, key);
if (key->isFinished)
- return FALSE; /* finished one of keys */
+ return false; /* finished one of keys */
if (ginCompareItemPointers(&key->curItem, item) < 0)
*item = key->curItem;
/*----------
* Now *item contains first ItemPointer after previous result.
*
- * The item is a valid hit only if all the keys returned either
+ * The item is a valid hit only if all the keys succeeded for either
* that exact TID, or a lossy reference to the same page.
*
* This logic works only if a keyGetItem stream can never contain both
{
GinScanKey key = so->keys + i;
- if (ginCompareItemPointers(item, &key->curItem) == 0)
- continue;
- if (ItemPointerIsLossyPage(&key->curItem) &&
- GinItemPointerGetBlockNumber(&key->curItem) ==
- GinItemPointerGetBlockNumber(item))
- continue;
+ if (key->curItemMatches)
+ {
+ if (ginCompareItemPointers(item, &key->curItem) == 0)
+ continue;
+ if (ItemPointerIsLossyPage(&key->curItem) &&
+ GinItemPointerGetBlockNumber(&key->curItem) ==
+ GinItemPointerGetBlockNumber(item))
+ continue;
+ }
match = false;
break;
}
for (j = 0; j < key->nentries; j++)
{
- GinScanEntry entry = key->scanEntry + j;
+ GinScanEntry entry = key->scanEntry[j];
OffsetNumber StopLow = pos->firstOffset,
StopHigh = pos->lastOffset,
StopMiddle;
}
/*
- * 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)
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;
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
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;
}
/* 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);
}
/*
RelationGetRelationName(scan->indexRelation))));
}
- so->nkeys = nkeys;
-
pgstat_count_index_scan(scan->indexRelation);
}
/* remaining arguments are ignored */
GinScanOpaque so = (GinScanOpaque) scan->opaque;
- freeScanKeys(so->keys, so->nkeys);
- so->keys = NULL;
+ freeScanKeys(so);
if (scankey && scan->numberOfKeys > 0)
{
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GinScanOpaque so = (GinScanOpaque) scan->opaque;
- freeScanKeys(so->keys, so->nkeys);
+ freeScanKeys(so);
MemoryContextDelete(so->tempCtx);