PostgreSQL Source Code git master
ginget.c File Reference
#include "postgres.h"
#include "access/gin_private.h"
#include "access/relscan.h"
#include "common/pg_prng.h"
#include "miscadmin.h"
#include "storage/predicate.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for ginget.c:

Go to the source code of this file.

Data Structures

struct  pendingPosition
 

Macros

#define gin_rand()   pg_prng_double(&pg_global_prng_state)
 
#define dropItem(e)   ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )
 
#define GinIsVoidRes(s)   ( ((GinScanOpaque) scan->opaque)->isVoidRes )
 

Typedefs

typedef struct pendingPosition pendingPosition
 

Functions

static bool moveRightIfItNeeded (GinBtreeData *btree, GinBtreeStack *stack, Snapshot snapshot)
 
static void scanPostingTree (Relation index, GinScanEntry scanEntry, BlockNumber rootPostingTree)
 
static bool collectMatchBitmap (GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry scanEntry, Snapshot snapshot)
 
static void startScanEntry (GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
 
static int entryIndexByFrequencyCmp (const void *a1, const void *a2, void *arg)
 
static void startScanKey (GinState *ginstate, GinScanOpaque so, GinScanKey key)
 
static void startScan (IndexScanDesc scan)
 
static void entryLoadMoreItems (GinState *ginstate, GinScanEntry entry, ItemPointerData advancePast)
 
static void entryGetItem (GinState *ginstate, GinScanEntry entry, ItemPointerData advancePast)
 
static void keyGetItem (GinState *ginstate, MemoryContext tempCtx, GinScanKey key, ItemPointerData advancePast)
 
static bool scanGetItem (IndexScanDesc scan, ItemPointerData advancePast, ItemPointerData *item, bool *recheck)
 
static bool scanGetCandidate (IndexScanDesc scan, pendingPosition *pos)
 
static bool matchPartialInPendingList (GinState *ginstate, Page page, OffsetNumber off, OffsetNumber maxoff, GinScanEntry entry, Datum *datum, GinNullCategory *category, bool *datumExtracted)
 
static bool collectMatchesForHeapRow (IndexScanDesc scan, pendingPosition *pos)
 
static void scanPendingInsert (IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
 
int64 gingetbitmap (IndexScanDesc scan, TIDBitmap *tbm)
 

Variables

int GinFuzzySearchLimit = 0
 

Macro Definition Documentation

◆ dropItem

#define dropItem (   e)    ( gin_rand() > ((double)GinFuzzySearchLimit)/((double)((e)->predictNumberResult)) )

Definition at line 793 of file ginget.c.

◆ gin_rand

#define gin_rand ( )    pg_prng_double(&pg_global_prng_state)

Definition at line 792 of file ginget.c.

◆ GinIsVoidRes

#define GinIsVoidRes (   s)    ( ((GinScanOpaque) scan->opaque)->isVoidRes )

Definition at line 1913 of file ginget.c.

Typedef Documentation

◆ pendingPosition

Function Documentation

◆ collectMatchBitmap()

static bool collectMatchBitmap ( GinBtreeData btree,
GinBtreeStack stack,
GinScanEntry  scanEntry,
Snapshot  snapshot 
)
static

Definition at line 121 of file ginget.c.

123{
125 CompactAttribute *attr;
126
127 /* Initialize empty bitmap result */
128 scanEntry->matchBitmap = tbm_create(work_mem * (Size) 1024, NULL);
129
130 /* Null query cannot partial-match anything */
131 if (scanEntry->isPartialMatch &&
132 scanEntry->queryCategory != GIN_CAT_NORM_KEY)
133 return true;
134
135 /* Locate tupdesc entry for key column (for attbyval/attlen data) */
136 attnum = scanEntry->attnum;
137 attr = TupleDescCompactAttr(btree->ginstate->origTupdesc, attnum - 1);
138
139 /*
140 * Predicate lock entry leaf page, following pages will be locked by
141 * moveRightIfItNeeded()
142 */
145 snapshot);
146
147 for (;;)
148 {
149 Page page;
150 IndexTuple itup;
151 Datum idatum;
152 GinNullCategory icategory;
153
154 /*
155 * stack->off points to the interested entry, buffer is already locked
156 */
157 if (moveRightIfItNeeded(btree, stack, snapshot) == false)
158 return true;
159
160 page = BufferGetPage(stack->buffer);
161 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
162
163 /*
164 * If tuple stores another attribute then stop scan
165 */
166 if (gintuple_get_attrnum(btree->ginstate, itup) != attnum)
167 return true;
168
169 /* Safe to fetch attribute value */
170 idatum = gintuple_get_key(btree->ginstate, itup, &icategory);
171
172 /*
173 * Check for appropriate scan stop conditions
174 */
175 if (scanEntry->isPartialMatch)
176 {
177 int32 cmp;
178
179 /*
180 * In partial match, stop scan at any null (including
181 * placeholders); partial matches never match nulls
182 */
183 if (icategory != GIN_CAT_NORM_KEY)
184 return true;
185
186 /*----------
187 * Check of partial match.
188 * case cmp == 0 => match
189 * case cmp > 0 => not match and finish scan
190 * case cmp < 0 => not match and continue scan
191 *----------
192 */
194 btree->ginstate->supportCollation[attnum - 1],
195 scanEntry->queryKey,
196 idatum,
197 UInt16GetDatum(scanEntry->strategy),
198 PointerGetDatum(scanEntry->extra_data)));
199
200 if (cmp > 0)
201 return true;
202 else if (cmp < 0)
203 {
204 stack->off++;
205 continue;
206 }
207 }
208 else if (scanEntry->searchMode == GIN_SEARCH_MODE_ALL)
209 {
210 /*
211 * In ALL mode, we are not interested in null items, so we can
212 * stop if we get to a null-item placeholder (which will be the
213 * last entry for a given attnum). We do want to include NULL_KEY
214 * and EMPTY_ITEM entries, though.
215 */
216 if (icategory == GIN_CAT_NULL_ITEM)
217 return true;
218 }
219
220 /*
221 * OK, we want to return the TIDs listed in this entry.
222 */
223 if (GinIsPostingTree(itup))
224 {
225 BlockNumber rootPostingTree = GinGetPostingTree(itup);
226
227 /*
228 * We should unlock current page (but not unpin) during tree scan
229 * to prevent deadlock with vacuum processes.
230 *
231 * We save current entry value (idatum) to be able to re-find our
232 * tuple after re-locking
233 */
234 if (icategory == GIN_CAT_NORM_KEY)
235 idatum = datumCopy(idatum, attr->attbyval, attr->attlen);
236
238
239 /*
240 * Acquire predicate lock on the posting tree. We already hold a
241 * lock on the entry page, but insertions to the posting tree
242 * don't check for conflicts on that level.
243 */
244 PredicateLockPage(btree->index, rootPostingTree, snapshot);
245
246 /* Collect all the TIDs in this entry's posting tree */
247 scanPostingTree(btree->index, scanEntry, rootPostingTree);
248
249 /*
250 * We lock again the entry page and while it was unlocked insert
251 * might have occurred, so we need to re-find our position.
252 */
253 LockBuffer(stack->buffer, GIN_SHARE);
254 page = BufferGetPage(stack->buffer);
255 if (!GinPageIsLeaf(page))
256 {
257 /*
258 * Root page becomes non-leaf while we unlock it. We will
259 * start again, this situation doesn't occur often - root can
260 * became a non-leaf only once per life of index.
261 */
262 return false;
263 }
264
265 /* Search forward to re-find idatum */
266 for (;;)
267 {
268 if (moveRightIfItNeeded(btree, stack, snapshot) == false)
270 (errcode(ERRCODE_INTERNAL_ERROR),
271 errmsg("failed to re-find tuple within index \"%s\"",
273
274 page = BufferGetPage(stack->buffer);
275 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
276
277 if (gintuple_get_attrnum(btree->ginstate, itup) == attnum)
278 {
279 Datum newDatum;
280 GinNullCategory newCategory;
281
282 newDatum = gintuple_get_key(btree->ginstate, itup,
283 &newCategory);
284
286 newDatum, newCategory,
287 idatum, icategory) == 0)
288 break; /* Found! */
289 }
290
291 stack->off++;
292 }
293
294 if (icategory == GIN_CAT_NORM_KEY && !attr->attbyval)
295 pfree(DatumGetPointer(idatum));
296 }
297 else
298 {
299 ItemPointer ipd;
300 int nipd;
301
302 ipd = ginReadTuple(btree->ginstate, scanEntry->attnum, itup, &nipd);
303 tbm_add_tuples(scanEntry->matchBitmap, ipd, nipd, false);
304 scanEntry->predictNumberResult += GinGetNPosting(itup);
305 pfree(ipd);
306 }
307
308 /*
309 * Done with this entry, go to the next
310 */
311 stack->off++;
312 }
313}
uint32 BlockNumber
Definition: block.h:31
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3724
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5100
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:396
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:354
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
PageData * Page
Definition: bufpage.h:82
int32_t int32
Definition: c.h:484
size_t Size
Definition: c.h:562
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
Datum FunctionCall4Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4)
Definition: fmgr.c:1196
#define GIN_SEARCH_MODE_ALL
Definition: gin.h:36
#define GIN_UNLOCK
Definition: gin_private.h:49
#define GIN_SHARE
Definition: gin_private.h:50
#define GinIsPostingTree(itup)
Definition: ginblock.h:231
#define GIN_CAT_NORM_KEY
Definition: ginblock.h:208
#define GinGetNPosting(itup)
Definition: ginblock.h:228
#define GinGetPostingTree(itup)
Definition: ginblock.h:233
signed char GinNullCategory
Definition: ginblock.h:206
#define GIN_CAT_NULL_ITEM
Definition: ginblock.h:211
#define GinPageIsLeaf(page)
Definition: ginblock.h:112
ItemPointer ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup, int *nitems)
Definition: ginentrypage.c:162
static void scanPostingTree(Relation index, GinScanEntry scanEntry, BlockNumber rootPostingTree)
Definition: ginget.c:69
static bool moveRightIfItNeeded(GinBtreeData *btree, GinBtreeStack *stack, Snapshot snapshot)
Definition: ginget.c:43
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Definition: ginutil.c:227
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:260
int ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, GinNullCategory categorya, Datum b, GinNullCategory categoryb)
Definition: ginutil.c:389
int work_mem
Definition: globals.c:130
IndexTupleData * IndexTuple
Definition: itup.h:53
void pfree(void *pointer)
Definition: mcxt.c:1521
uint16 OffsetNumber
Definition: off.h:24
int16 attnum
Definition: pg_attribute.h:74
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static Datum UInt16GetDatum(uint16 X)
Definition: postgres.h:197
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:207
void PredicateLockPage(Relation relation, BlockNumber blkno, Snapshot snapshot)
Definition: predicate.c:2589
static int cmp(const chr *x, const chr *y, size_t len)
Definition: regc_locale.c:743
#define RelationGetRelationName(relation)
Definition: rel.h:546
int16 attlen
Definition: tupdesc.h:71
GinState * ginstate
Definition: gin_private.h:169
Relation index
Definition: gin_private.h:167
OffsetNumber off
Definition: gin_private.h:133
TIDBitmap * matchBitmap
Definition: gin_private.h:354
GinNullCategory queryCategory
Definition: gin_private.h:340
StrategyNumber strategy
Definition: gin_private.h:343
Pointer extra_data
Definition: gin_private.h:342
uint32 predictNumberResult
Definition: gin_private.h:365
OffsetNumber attnum
Definition: gin_private.h:345
FmgrInfo comparePartialFn[INDEX_MAX_KEYS]
Definition: gin_private.h:84
TupleDesc origTupdesc
Definition: gin_private.h:73
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gin_private.h:88
void tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, bool recheck)
Definition: tidbitmap.c:377
TIDBitmap * tbm_create(Size maxbytes, dsa_area *dsa)
Definition: tidbitmap.c:266
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:169

References CompactAttribute::attbyval, CompactAttribute::attlen, GinScanEntryData::attnum, attnum, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage(), cmp(), GinState::comparePartialFn, datumCopy(), DatumGetInt32(), DatumGetPointer(), ereport, errcode(), errmsg(), ERROR, GinScanEntryData::extra_data, FunctionCall4Coll(), GIN_CAT_NORM_KEY, GIN_CAT_NULL_ITEM, GIN_SEARCH_MODE_ALL, GIN_SHARE, GIN_UNLOCK, ginCompareEntries(), GinGetNPosting, GinGetPostingTree, GinIsPostingTree, GinPageIsLeaf, ginReadTuple(), GinBtreeData::ginstate, gintuple_get_attrnum(), gintuple_get_key(), GinBtreeData::index, GinScanEntryData::isPartialMatch, LockBuffer(), GinScanEntryData::matchBitmap, moveRightIfItNeeded(), GinBtreeStack::off, GinState::origTupdesc, PageGetItem(), PageGetItemId(), pfree(), PointerGetDatum(), PredicateLockPage(), GinScanEntryData::predictNumberResult, GinScanEntryData::queryCategory, GinScanEntryData::queryKey, RelationGetRelationName, scanPostingTree(), GinScanEntryData::searchMode, GinScanEntryData::strategy, GinState::supportCollation, tbm_add_tuples(), tbm_create(), TupleDescCompactAttr(), UInt16GetDatum(), and work_mem.

Referenced by startScanEntry().

◆ collectMatchesForHeapRow()

static bool collectMatchesForHeapRow ( IndexScanDesc  scan,
pendingPosition pos 
)
static

Definition at line 1607 of file ginget.c.

1608{
1609 GinScanOpaque so = (GinScanOpaque) scan->opaque;
1610 OffsetNumber attrnum;
1611 Page page;
1612 IndexTuple itup;
1613 int i,
1614 j;
1615
1616 /*
1617 * Reset all entryRes and hasMatchKey flags
1618 */
1619 for (i = 0; i < so->nkeys; i++)
1620 {
1621 GinScanKey key = so->keys + i;
1622
1623 memset(key->entryRes, GIN_FALSE, key->nentries);
1624 }
1625 memset(pos->hasMatchKey, false, so->nkeys);
1626
1627 /*
1628 * Outer loop iterates over multiple pending-list pages when a single heap
1629 * row has entries spanning those pages.
1630 */
1631 for (;;)
1632 {
1633 Datum datum[BLCKSZ / sizeof(IndexTupleData)];
1634 GinNullCategory category[BLCKSZ / sizeof(IndexTupleData)];
1635 bool datumExtracted[BLCKSZ / sizeof(IndexTupleData)];
1636
1637 Assert(pos->lastOffset > pos->firstOffset);
1638 memset(datumExtracted + pos->firstOffset - 1, 0,
1639 sizeof(bool) * (pos->lastOffset - pos->firstOffset));
1640
1641 page = BufferGetPage(pos->pendingBuffer);
1642
1643 for (i = 0; i < so->nkeys; i++)
1644 {
1645 GinScanKey key = so->keys + i;
1646
1647 for (j = 0; j < key->nentries; j++)
1648 {
1649 GinScanEntry entry = key->scanEntry[j];
1650 OffsetNumber StopLow = pos->firstOffset,
1651 StopHigh = pos->lastOffset,
1652 StopMiddle;
1653
1654 /* If already matched on earlier page, do no extra work */
1655 if (key->entryRes[j])
1656 continue;
1657
1658 /*
1659 * Interesting tuples are from pos->firstOffset to
1660 * pos->lastOffset and they are ordered by (attnum, Datum) as
1661 * it's done in entry tree. So we can use binary search to
1662 * avoid linear scanning.
1663 */
1664 while (StopLow < StopHigh)
1665 {
1666 int res;
1667
1668 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
1669
1670 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, StopMiddle));
1671
1672 attrnum = gintuple_get_attrnum(&so->ginstate, itup);
1673
1674 if (key->attnum < attrnum)
1675 {
1676 StopHigh = StopMiddle;
1677 continue;
1678 }
1679 if (key->attnum > attrnum)
1680 {
1681 StopLow = StopMiddle + 1;
1682 continue;
1683 }
1684
1685 if (datumExtracted[StopMiddle - 1] == false)
1686 {
1687 datum[StopMiddle - 1] =
1688 gintuple_get_key(&so->ginstate, itup,
1689 &category[StopMiddle - 1]);
1690 datumExtracted[StopMiddle - 1] = true;
1691 }
1692
1693 if (entry->queryCategory == GIN_CAT_EMPTY_QUERY)
1694 {
1695 /* special behavior depending on searchMode */
1696 if (entry->searchMode == GIN_SEARCH_MODE_ALL)
1697 {
1698 /* match anything except NULL_ITEM */
1699 if (category[StopMiddle - 1] == GIN_CAT_NULL_ITEM)
1700 res = -1;
1701 else
1702 res = 0;
1703 }
1704 else
1705 {
1706 /* match everything */
1707 res = 0;
1708 }
1709 }
1710 else
1711 {
1713 entry->attnum,
1714 entry->queryKey,
1715 entry->queryCategory,
1716 datum[StopMiddle - 1],
1717 category[StopMiddle - 1]);
1718 }
1719
1720 if (res == 0)
1721 {
1722 /*
1723 * Found exact match (there can be only one, except in
1724 * EMPTY_QUERY mode).
1725 *
1726 * If doing partial match, scan forward from here to
1727 * end of page to check for matches.
1728 *
1729 * See comment above about tuple's ordering.
1730 */
1731 if (entry->isPartialMatch)
1732 key->entryRes[j] =
1734 page,
1735 StopMiddle,
1736 pos->lastOffset,
1737 entry,
1738 datum,
1739 category,
1740 datumExtracted);
1741 else
1742 key->entryRes[j] = true;
1743
1744 /* done with binary search */
1745 break;
1746 }
1747 else if (res < 0)
1748 StopHigh = StopMiddle;
1749 else
1750 StopLow = StopMiddle + 1;
1751 }
1752
1753 if (StopLow >= StopHigh && entry->isPartialMatch)
1754 {
1755 /*
1756 * No exact match on this page. If doing partial match,
1757 * scan from the first tuple greater than target value to
1758 * end of page. Note that since we don't remember whether
1759 * the comparePartialFn told us to stop early on a
1760 * previous page, we will uselessly apply comparePartialFn
1761 * to the first tuple on each subsequent page.
1762 */
1763 key->entryRes[j] =
1765 page,
1766 StopHigh,
1767 pos->lastOffset,
1768 entry,
1769 datum,
1770 category,
1771 datumExtracted);
1772 }
1773
1774 pos->hasMatchKey[i] |= key->entryRes[j];
1775 }
1776 }
1777
1778 /* Advance firstOffset over the scanned tuples */
1779 pos->firstOffset = pos->lastOffset;
1780
1781 if (GinPageHasFullRow(page))
1782 {
1783 /*
1784 * We have examined all pending entries for the current heap row.
1785 * Break out of loop over pages.
1786 */
1787 break;
1788 }
1789 else
1790 {
1791 /*
1792 * Advance to next page of pending entries for the current heap
1793 * row. Complain if there isn't one.
1794 */
1795 ItemPointerData item = pos->item;
1796
1797 if (scanGetCandidate(scan, pos) == false ||
1798 !ItemPointerEquals(&pos->item, &item))
1799 elog(ERROR, "could not find additional pending pages for same heap tuple");
1800 }
1801 }
1802
1803 /*
1804 * All scan keys except excludeOnly require at least one entry to match.
1805 * excludeOnly keys are an exception, because their implied
1806 * GIN_CAT_EMPTY_QUERY scanEntry always matches. So return "true" if all
1807 * non-excludeOnly scan keys have at least one match.
1808 */
1809 for (i = 0; i < so->nkeys; i++)
1810 {
1811 if (pos->hasMatchKey[i] == false && !so->keys[i].excludeOnly)
1812 return false;
1813 }
1814
1815 return true;
1816}
#define Assert(condition)
Definition: c.h:815
#define elog(elevel,...)
Definition: elog.h:225
#define GIN_FALSE
Definition: gin.h:63
GinScanOpaqueData * GinScanOpaque
Definition: gin_private.h:386
#define GinPageHasFullRow(page)
Definition: ginblock.h:119
#define GIN_CAT_EMPTY_QUERY
Definition: ginblock.h:212
static bool matchPartialInPendingList(GinState *ginstate, Page page, OffsetNumber off, OffsetNumber maxoff, GinScanEntry entry, Datum *datum, GinNullCategory *category, bool *datumExtracted)
Definition: ginget.c:1539
static bool scanGetCandidate(IndexScanDesc scan, pendingPosition *pos)
Definition: ginget.c:1452
for(;;)
int j
Definition: isn.c:73
int i
Definition: isn.c:72
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
struct IndexTupleData IndexTupleData
GinScanKey keys
Definition: gin_private.h:374
Buffer pendingBuffer
Definition: ginget.c:31
OffsetNumber lastOffset
Definition: ginget.c:33
OffsetNumber firstOffset
Definition: ginget.c:32
bool * hasMatchKey
Definition: ginget.c:35
ItemPointerData item
Definition: ginget.c:34

References Assert, GinScanEntryData::attnum, BufferGetPage(), elog, ERROR, GinScanKeyData::excludeOnly, pendingPosition::firstOffset, for(), GIN_CAT_EMPTY_QUERY, GIN_CAT_NULL_ITEM, GIN_FALSE, GIN_SEARCH_MODE_ALL, ginCompareEntries(), GinPageHasFullRow, GinScanOpaqueData::ginstate, gintuple_get_attrnum(), gintuple_get_key(), pendingPosition::hasMatchKey, i, GinScanEntryData::isPartialMatch, pendingPosition::item, ItemPointerEquals(), j, sort-test::key, GinScanOpaqueData::keys, pendingPosition::lastOffset, matchPartialInPendingList(), GinScanOpaqueData::nkeys, IndexScanDescData::opaque, PageGetItem(), PageGetItemId(), pendingPosition::pendingBuffer, GinScanEntryData::queryCategory, GinScanEntryData::queryKey, res, scanGetCandidate(), and GinScanEntryData::searchMode.

Referenced by scanPendingInsert().

◆ entryGetItem()

static void entryGetItem ( GinState ginstate,
GinScanEntry  entry,
ItemPointerData  advancePast 
)
static

Definition at line 809 of file ginget.c.

811{
812 Assert(!entry->isFinished);
813
815 ginCompareItemPointers(&entry->curItem, &advancePast) <= 0);
816
817 if (entry->matchBitmap)
818 {
819 /* A bitmap result */
820 BlockNumber advancePastBlk = GinItemPointerGetBlockNumber(&advancePast);
821 OffsetNumber advancePastOff = GinItemPointerGetOffsetNumber(&advancePast);
822
823 for (;;)
824 {
825 /*
826 * If we've exhausted all items on this block, move to next block
827 * in the bitmap.
828 */
829 while (entry->matchResult == NULL ||
830 (entry->matchResult->ntuples >= 0 &&
831 entry->offset >= entry->matchResult->ntuples) ||
832 entry->matchResult->blockno < advancePastBlk ||
833 (ItemPointerIsLossyPage(&advancePast) &&
834 entry->matchResult->blockno == advancePastBlk))
835 {
836 entry->matchResult =
838
839 if (entry->matchResult == NULL)
840 {
843 entry->matchIterator = NULL;
844 entry->isFinished = true;
845 break;
846 }
847
848 /*
849 * Reset counter to the beginning of entry->matchResult. Note:
850 * entry->offset is still greater than matchResult->ntuples if
851 * matchResult is lossy. So, on next call we will get next
852 * result from TIDBitmap.
853 */
854 entry->offset = 0;
855 }
856 if (entry->isFinished)
857 break;
858
859 /*
860 * We're now on the first page after advancePast which has any
861 * items on it. If it's a lossy result, return that.
862 */
863 if (entry->matchResult->ntuples < 0)
864 {
866 entry->matchResult->blockno);
867
868 /*
869 * We might as well fall out of the loop; we could not
870 * estimate number of results on this page to support correct
871 * reducing of result even if it's enabled.
872 */
873 break;
874 }
875
876 /*
877 * Not a lossy page. Skip over any offsets <= advancePast, and
878 * return that.
879 */
880 if (entry->matchResult->blockno == advancePastBlk)
881 {
882 /*
883 * First, do a quick check against the last offset on the
884 * page. If that's > advancePast, so are all the other
885 * offsets, so just go back to the top to get the next page.
886 */
887 if (entry->matchResult->offsets[entry->matchResult->ntuples - 1] <= advancePastOff)
888 {
889 entry->offset = entry->matchResult->ntuples;
890 continue;
891 }
892
893 /* Otherwise scan to find the first item > advancePast */
894 while (entry->matchResult->offsets[entry->offset] <= advancePastOff)
895 entry->offset++;
896 }
897
898 ItemPointerSet(&entry->curItem,
899 entry->matchResult->blockno,
900 entry->matchResult->offsets[entry->offset]);
901 entry->offset++;
902
903 /* Done unless we need to reduce the result */
904 if (!entry->reduceResult || !dropItem(entry))
905 break;
906 }
907 }
908 else if (!BufferIsValid(entry->buffer))
909 {
910 /*
911 * A posting list from an entry tuple, or the last page of a posting
912 * tree.
913 */
914 for (;;)
915 {
916 if (entry->offset >= entry->nlist)
917 {
919 entry->isFinished = true;
920 break;
921 }
922
923 entry->curItem = entry->list[entry->offset++];
924
925 /* If we're not past advancePast, keep scanning */
926 if (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0)
927 continue;
928
929 /* Done unless we need to reduce the result */
930 if (!entry->reduceResult || !dropItem(entry))
931 break;
932 }
933 }
934 else
935 {
936 /* A posting tree */
937 for (;;)
938 {
939 /* If we've processed the current batch, load more items */
940 while (entry->offset >= entry->nlist)
941 {
942 entryLoadMoreItems(ginstate, entry, advancePast);
943
944 if (entry->isFinished)
945 {
947 return;
948 }
949 }
950
951 entry->curItem = entry->list[entry->offset++];
952
953 /* If we're not past advancePast, keep scanning */
954 if (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0)
955 continue;
956
957 /* Done unless we need to reduce the result */
958 if (!entry->reduceResult || !dropItem(entry))
959 break;
960
961 /*
962 * Advance advancePast (so that entryLoadMoreItems will load the
963 * right data), and keep scanning
964 */
965 advancePast = entry->curItem;
966 }
967 }
968}
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:347
static int ginCompareItemPointers(ItemPointer a, ItemPointer b)
Definition: gin_private.h:488
#define ItemPointerSetLossyPage(p, b)
Definition: ginblock.h:173
#define GinItemPointerGetOffsetNumber(pointer)
Definition: ginblock.h:146
#define ItemPointerIsLossyPage(p)
Definition: ginblock.h:175
#define GinItemPointerGetBlockNumber(pointer)
Definition: ginblock.h:143
#define dropItem(e)
Definition: ginget.c:793
static void entryLoadMoreItems(GinState *ginstate, GinScanEntry entry, ItemPointerData advancePast)
Definition: ginget.c:654
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
ItemPointerData curItem
Definition: gin_private.h:351
TBMIterateResult * matchResult
Definition: gin_private.h:356
ItemPointerData * list
Definition: gin_private.h:359
TBMPrivateIterator * matchIterator
Definition: gin_private.h:355
OffsetNumber offset
Definition: gin_private.h:361
OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]
Definition: tidbitmap.h:60
BlockNumber blockno
Definition: tidbitmap.h:56
TBMIterateResult * tbm_private_iterate(TBMPrivateIterator *iterator)
Definition: tidbitmap.c:972
void tbm_end_private_iterate(TBMPrivateIterator *iterator)
Definition: tidbitmap.c:1147

References Assert, TBMIterateResult::blockno, GinScanEntryData::buffer, BufferIsValid(), GinScanEntryData::curItem, dropItem, entryLoadMoreItems(), ginCompareItemPointers(), GinItemPointerGetBlockNumber, GinItemPointerGetOffsetNumber, GinScanEntryData::isFinished, ItemPointerIsLossyPage, ItemPointerIsValid(), ItemPointerSet(), ItemPointerSetInvalid(), ItemPointerSetLossyPage, GinScanEntryData::list, GinScanEntryData::matchBitmap, GinScanEntryData::matchIterator, GinScanEntryData::matchResult, GinScanEntryData::nlist, TBMIterateResult::ntuples, GinScanEntryData::offset, TBMIterateResult::offsets, GinScanEntryData::reduceResult, tbm_end_private_iterate(), and tbm_private_iterate().

Referenced by keyGetItem().

◆ entryIndexByFrequencyCmp()

static int entryIndexByFrequencyCmp ( const void *  a1,
const void *  a2,
void *  arg 
)
static

Definition at line 489 of file ginget.c.

490{
491 const GinScanKey key = (const GinScanKey) arg;
492 int i1 = *(const int *) a1;
493 int i2 = *(const int *) a2;
494 uint32 n1 = key->scanEntry[i1]->predictNumberResult;
495 uint32 n2 = key->scanEntry[i2]->predictNumberResult;
496
497 if (n1 < n2)
498 return -1;
499 else if (n1 == n2)
500 return 0;
501 else
502 return 1;
503}
uint32_t uint32
Definition: c.h:488
struct GinScanKeyData * GinScanKey
Definition: gin_private.h:264
static const FormData_pg_attribute a1
Definition: heap.c:143
static const FormData_pg_attribute a2
Definition: heap.c:156
void * arg

References a1, a2, arg, and sort-test::key.

Referenced by startScanKey().

◆ entryLoadMoreItems()

static void entryLoadMoreItems ( GinState ginstate,
GinScanEntry  entry,
ItemPointerData  advancePast 
)
static

Definition at line 654 of file ginget.c.

656{
657 Page page;
658 int i;
659 bool stepright;
660
661 if (!BufferIsValid(entry->buffer))
662 {
663 entry->isFinished = true;
664 return;
665 }
666
667 /*
668 * We have two strategies for finding the correct page: step right from
669 * the current page, or descend the tree again from the root. If
670 * advancePast equals the current item, the next matching item should be
671 * on the next page, so we step right. Otherwise, descend from root.
672 */
673 if (ginCompareItemPointers(&entry->curItem, &advancePast) == 0)
674 {
675 stepright = true;
676 LockBuffer(entry->buffer, GIN_SHARE);
677 }
678 else
679 {
680 GinBtreeStack *stack;
681
682 ReleaseBuffer(entry->buffer);
683
684 /*
685 * Set the search key, and find the correct leaf page.
686 */
687 if (ItemPointerIsLossyPage(&advancePast))
688 {
690 GinItemPointerGetBlockNumber(&advancePast) + 1,
692 }
693 else
694 {
696 GinItemPointerGetBlockNumber(&advancePast),
698 }
699 entry->btree.fullScan = false;
700 stack = ginFindLeafPage(&entry->btree, true, false);
701
702 /* we don't need the stack, just the buffer. */
703 entry->buffer = stack->buffer;
705 freeGinBtreeStack(stack);
706 stepright = false;
707 }
708
709 elog(DEBUG2, "entryLoadMoreItems, %u/%u, skip: %d",
710 GinItemPointerGetBlockNumber(&advancePast),
711 GinItemPointerGetOffsetNumber(&advancePast),
712 !stepright);
713
714 page = BufferGetPage(entry->buffer);
715 for (;;)
716 {
718 if (entry->list)
719 {
720 pfree(entry->list);
721 entry->list = NULL;
722 entry->nlist = 0;
723 }
724
725 if (stepright)
726 {
727 /*
728 * We've processed all the entries on this page. If it was the
729 * last page in the tree, we're done.
730 */
731 if (GinPageRightMost(page))
732 {
734 entry->buffer = InvalidBuffer;
735 entry->isFinished = true;
736 return;
737 }
738
739 /*
740 * Step to next page, following the right link. then find the
741 * first ItemPointer greater than advancePast.
742 */
743 entry->buffer = ginStepRight(entry->buffer,
744 ginstate->index,
745 GIN_SHARE);
746 page = BufferGetPage(entry->buffer);
747 }
748 stepright = true;
749
750 if (GinPageGetOpaque(page)->flags & GIN_DELETED)
751 continue; /* page was deleted by concurrent vacuum */
752
753 /*
754 * The first item > advancePast might not be on this page, but
755 * somewhere to the right, if the page was split, or a non-match from
756 * another key in the query allowed us to skip some items from this
757 * entry. Keep following the right-links until we re-find the correct
758 * page.
759 */
760 if (!GinPageRightMost(page) &&
761 ginCompareItemPointers(&advancePast, GinDataPageGetRightBound(page)) >= 0)
762 {
763 /*
764 * the item we're looking is > the right bound of the page, so it
765 * can't be on this page.
766 */
767 continue;
768 }
769
770 entry->list = GinDataLeafPageGetItems(page, &entry->nlist, advancePast);
771
772 for (i = 0; i < entry->nlist; i++)
773 {
774 if (ginCompareItemPointers(&advancePast, &entry->list[i]) < 0)
775 {
776 entry->offset = i;
777
778 if (GinPageRightMost(page))
779 {
780 /* after processing the copied items, we're done. */
782 entry->buffer = InvalidBuffer;
783 }
784 else
786 return;
787 }
788 }
789 }
790}
#define InvalidBuffer
Definition: buf.h:25
void IncrBufferRefCount(Buffer buffer)
Definition: bufmgr.c:4898
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4866
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4883
#define DEBUG2
Definition: elog.h:29
#define GinPageGetOpaque(page)
Definition: ginblock.h:110
#define GIN_DELETED
Definition: ginblock.h:43
#define GinDataPageGetRightBound(page)
Definition: ginblock.h:288
#define GinPageRightMost(page)
Definition: ginblock.h:129
void freeGinBtreeStack(GinBtreeStack *stack)
Definition: ginbtree.c:198
GinBtreeStack * ginFindLeafPage(GinBtree btree, bool searchMode, bool rootConflictCheck)
Definition: ginbtree.c:83
Buffer ginStepRight(Buffer buffer, Relation index, int lockmode)
Definition: ginbtree.c:177
ItemPointer GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast)
Definition: gindatapage.c:135
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define FirstOffsetNumber
Definition: off.h:27
ItemPointerData itemptr
Definition: gin_private.h:179
GinBtreeData btree
Definition: gin_private.h:366
Relation index
Definition: gin_private.h:59

References GinScanEntryData::btree, GinBtreeStack::buffer, GinScanEntryData::buffer, BufferGetPage(), BufferIsValid(), GinScanEntryData::curItem, DEBUG2, elog, FirstOffsetNumber, freeGinBtreeStack(), GinBtreeData::fullScan, GIN_DELETED, GIN_SHARE, GIN_UNLOCK, ginCompareItemPointers(), GinDataLeafPageGetItems(), GinDataPageGetRightBound, ginFindLeafPage(), GinItemPointerGetBlockNumber, GinItemPointerGetOffsetNumber, GinPageGetOpaque, GinPageRightMost, ginStepRight(), i, IncrBufferRefCount(), GinState::index, InvalidBuffer, InvalidOffsetNumber, GinScanEntryData::isFinished, ItemPointerIsLossyPage, ItemPointerSet(), GinBtreeData::itemptr, GinScanEntryData::list, LockBuffer(), GinScanEntryData::nlist, GinScanEntryData::offset, OffsetNumberNext, pfree(), ReleaseBuffer(), and UnlockReleaseBuffer().

Referenced by entryGetItem().

◆ gingetbitmap()

int64 gingetbitmap ( IndexScanDesc  scan,
TIDBitmap tbm 
)

Definition at line 1916 of file ginget.c.

1917{
1918 GinScanOpaque so = (GinScanOpaque) scan->opaque;
1919 int64 ntids;
1920 ItemPointerData iptr;
1921 bool recheck;
1922
1923 /*
1924 * Set up the scan keys, and check for unsatisfiable query.
1925 */
1926 ginFreeScanKeys(so); /* there should be no keys yet, but just to be
1927 * sure */
1928 ginNewScanKey(scan);
1929
1930 if (GinIsVoidRes(scan))
1931 return 0;
1932
1933 ntids = 0;
1934
1935 /*
1936 * First, scan the pending list and collect any matching entries into the
1937 * bitmap. After we scan a pending item, some other backend could post it
1938 * into the main index, and so we might visit it a second time during the
1939 * main scan. This is okay because we'll just re-set the same bit in the
1940 * bitmap. (The possibility of duplicate visits is a major reason why GIN
1941 * can't support the amgettuple API, however.) Note that it would not do
1942 * to scan the main index before the pending list, since concurrent
1943 * cleanup could then make us miss entries entirely.
1944 */
1945 scanPendingInsert(scan, tbm, &ntids);
1946
1947 /*
1948 * Now scan the main index.
1949 */
1950 startScan(scan);
1951
1952 ItemPointerSetMin(&iptr);
1953
1954 for (;;)
1955 {
1957
1958 if (!scanGetItem(scan, iptr, &iptr, &recheck))
1959 break;
1960
1961 if (ItemPointerIsLossyPage(&iptr))
1963 else
1964 tbm_add_tuples(tbm, &iptr, 1, recheck);
1965 ntids++;
1966 }
1967
1968 return ntids;
1969}
int64_t int64
Definition: c.h:485
#define ItemPointerSetMin(p)
Definition: ginblock.h:166
static void scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
Definition: ginget.c:1822
static void startScan(IndexScanDesc scan)
Definition: ginget.c:602
#define GinIsVoidRes(s)
Definition: ginget.c:1913
static bool scanGetItem(IndexScanDesc scan, ItemPointerData advancePast, ItemPointerData *item, bool *recheck)
Definition: ginget.c:1287
void ginFreeScanKeys(GinScanOpaque so)
Definition: ginscan.c:233
void ginNewScanKey(IndexScanDesc scan)
Definition: ginscan.c:263
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
void tbm_add_page(TIDBitmap *tbm, BlockNumber pageno)
Definition: tidbitmap.c:443

References CHECK_FOR_INTERRUPTS, ginFreeScanKeys(), GinIsVoidRes, ginNewScanKey(), ItemPointerGetBlockNumber(), ItemPointerIsLossyPage, ItemPointerSetMin, IndexScanDescData::opaque, scanGetItem(), scanPendingInsert(), startScan(), tbm_add_page(), and tbm_add_tuples().

Referenced by ginhandler().

◆ keyGetItem()

static void keyGetItem ( GinState ginstate,
MemoryContext  tempCtx,
GinScanKey  key,
ItemPointerData  advancePast 
)
static

Definition at line 992 of file ginget.c.

994{
995 ItemPointerData minItem;
996 ItemPointerData curPageLossy;
997 uint32 i;
998 bool haveLossyEntry;
999 GinScanEntry entry;
1001 MemoryContext oldCtx;
1002 bool allFinished;
1003
1004 Assert(!key->isFinished);
1005
1006 /*
1007 * We might have already tested this item; if so, no need to repeat work.
1008 * (Note: the ">" case can happen, if advancePast is exact but we
1009 * previously had to set curItem to a lossy-page pointer.)
1010 */
1011 if (ginCompareItemPointers(&key->curItem, &advancePast) > 0)
1012 return;
1013
1014 /*
1015 * Find the minimum item > advancePast among the active entry streams.
1016 *
1017 * Note: a lossy-page entry is encoded by a ItemPointer with max value for
1018 * offset (0xffff), so that it will sort after any exact entries for the
1019 * same page. So we'll prefer to return exact pointers not lossy
1020 * pointers, which is good.
1021 */
1022 ItemPointerSetMax(&minItem);
1023 allFinished = true;
1024 for (i = 0; i < key->nrequired; i++)
1025 {
1026 entry = key->requiredEntries[i];
1027
1028 if (entry->isFinished)
1029 continue;
1030
1031 /*
1032 * Advance this stream if necessary.
1033 *
1034 * In particular, since entry->curItem was initialized with
1035 * ItemPointerSetMin, this ensures we fetch the first item for each
1036 * entry on the first call.
1037 */
1038 if (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0)
1039 {
1040 entryGetItem(ginstate, entry, advancePast);
1041 if (entry->isFinished)
1042 continue;
1043 }
1044
1045 allFinished = false;
1046 if (ginCompareItemPointers(&entry->curItem, &minItem) < 0)
1047 minItem = entry->curItem;
1048 }
1049
1050 if (allFinished && !key->excludeOnly)
1051 {
1052 /* all entries are finished */
1053 key->isFinished = true;
1054 return;
1055 }
1056
1057 if (!key->excludeOnly)
1058 {
1059 /*
1060 * For a normal scan key, we now know there are no matches < minItem.
1061 *
1062 * If minItem is lossy, it means that there were no exact items on the
1063 * page among requiredEntries, because lossy pointers sort after exact
1064 * items. However, there might be exact items for the same page among
1065 * additionalEntries, so we mustn't advance past them.
1066 */
1067 if (ItemPointerIsLossyPage(&minItem))
1068 {
1069 if (GinItemPointerGetBlockNumber(&advancePast) <
1071 {
1072 ItemPointerSet(&advancePast,
1075 }
1076 }
1077 else
1078 {
1080 ItemPointerSet(&advancePast,
1083 }
1084 }
1085 else
1086 {
1087 /*
1088 * excludeOnly scan keys don't have any entries that are necessarily
1089 * present in matching items. So, we consider the item just after
1090 * advancePast.
1091 */
1092 Assert(key->nrequired == 0);
1093 ItemPointerSet(&minItem,
1094 GinItemPointerGetBlockNumber(&advancePast),
1096 }
1097
1098 /*
1099 * We might not have loaded all the entry streams for this TID yet. We
1100 * could call the consistent function, passing MAYBE for those entries, to
1101 * see if it can decide if this TID matches based on the information we
1102 * have. But if the consistent-function is expensive, and cannot in fact
1103 * decide with partial information, that could be a big loss. So, load all
1104 * the additional entries, before calling the consistent function.
1105 */
1106 for (i = 0; i < key->nadditional; i++)
1107 {
1108 entry = key->additionalEntries[i];
1109
1110 if (entry->isFinished)
1111 continue;
1112
1113 if (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0)
1114 {
1115 entryGetItem(ginstate, entry, advancePast);
1116 if (entry->isFinished)
1117 continue;
1118 }
1119
1120 /*
1121 * Normally, none of the items in additionalEntries can have a curItem
1122 * larger than minItem. But if minItem is a lossy page, then there
1123 * might be exact items on the same page among additionalEntries.
1124 */
1125 if (ginCompareItemPointers(&entry->curItem, &minItem) < 0)
1126 {
1127 Assert(ItemPointerIsLossyPage(&minItem));
1128 minItem = entry->curItem;
1129 }
1130 }
1131
1132 /*
1133 * Ok, we've advanced all the entries up to minItem now. Set key->curItem,
1134 * and perform consistentFn test.
1135 *
1136 * Lossy-page entries pose a problem, since we don't know the correct
1137 * entryRes state to pass to the consistentFn, and we also don't know what
1138 * its combining logic will be (could be AND, OR, or even NOT). If the
1139 * logic is OR then the consistentFn might succeed for all items in the
1140 * lossy page even when none of the other entries match.
1141 *
1142 * Our strategy is to call the tri-state consistent function, with the
1143 * lossy-page entries set to MAYBE, and all the other entries FALSE. If it
1144 * returns FALSE, none of the lossy items alone are enough for a match, so
1145 * we don't need to return a lossy-page pointer. Otherwise, return a
1146 * lossy-page pointer to indicate that the whole heap page must be
1147 * checked. (On subsequent calls, we'll do nothing until minItem is past
1148 * the page altogether, thus ensuring that we never return both regular
1149 * and lossy pointers for the same page.)
1150 *
1151 * An exception is that it doesn't matter what we pass for lossy pointers
1152 * in "hidden" entries, because the consistentFn's result can't depend on
1153 * them. We could pass them as MAYBE as well, but if we're using the
1154 * "shim" implementation of a tri-state consistent function (see
1155 * ginlogic.c), it's better to pass as few MAYBEs as possible. So pass
1156 * them as true.
1157 *
1158 * Note that only lossy-page entries pointing to the current item's page
1159 * should trigger this processing; we might have future lossy pages in the
1160 * entry array, but they aren't relevant yet.
1161 */
1162 key->curItem = minItem;
1163 ItemPointerSetLossyPage(&curPageLossy,
1165 haveLossyEntry = false;
1166 for (i = 0; i < key->nentries; i++)
1167 {
1168 entry = key->scanEntry[i];
1169 if (entry->isFinished == false &&
1170 ginCompareItemPointers(&entry->curItem, &curPageLossy) == 0)
1171 {
1172 if (i < key->nuserentries)
1173 key->entryRes[i] = GIN_MAYBE;
1174 else
1175 key->entryRes[i] = GIN_TRUE;
1176 haveLossyEntry = true;
1177 }
1178 else
1179 key->entryRes[i] = GIN_FALSE;
1180 }
1181
1182 /* prepare for calling consistentFn in temp context */
1183 oldCtx = MemoryContextSwitchTo(tempCtx);
1184
1185 if (haveLossyEntry)
1186 {
1187 /* Have lossy-page entries, so see if whole page matches */
1188 res = key->triConsistentFn(key);
1189
1190 if (res == GIN_TRUE || res == GIN_MAYBE)
1191 {
1192 /* Yes, so clean up ... */
1193 MemoryContextSwitchTo(oldCtx);
1194 MemoryContextReset(tempCtx);
1195
1196 /* and return lossy pointer for whole page */
1197 key->curItem = curPageLossy;
1198 key->curItemMatches = true;
1199 key->recheckCurItem = true;
1200 return;
1201 }
1202 }
1203
1204 /*
1205 * At this point we know that we don't need to return a lossy whole-page
1206 * pointer, but we might have matches for individual exact item pointers,
1207 * possibly in combination with a lossy pointer. Pass lossy pointers as
1208 * MAYBE to the ternary consistent function, to let it decide if this
1209 * tuple satisfies the overall key, even though we don't know if the lossy
1210 * entries match.
1211 *
1212 * Prepare entryRes array to be passed to consistentFn.
1213 */
1214 for (i = 0; i < key->nentries; i++)
1215 {
1216 entry = key->scanEntry[i];
1217 if (entry->isFinished)
1218 key->entryRes[i] = GIN_FALSE;
1219#if 0
1220
1221 /*
1222 * This case can't currently happen, because we loaded all the entries
1223 * for this item earlier.
1224 */
1225 else if (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0)
1226 key->entryRes[i] = GIN_MAYBE;
1227#endif
1228 else if (ginCompareItemPointers(&entry->curItem, &curPageLossy) == 0)
1229 key->entryRes[i] = GIN_MAYBE;
1230 else if (ginCompareItemPointers(&entry->curItem, &minItem) == 0)
1231 key->entryRes[i] = GIN_TRUE;
1232 else
1233 key->entryRes[i] = GIN_FALSE;
1234 }
1235
1236 res = key->triConsistentFn(key);
1237
1238 switch (res)
1239 {
1240 case GIN_TRUE:
1241 key->curItemMatches = true;
1242 /* triConsistentFn set recheckCurItem */
1243 break;
1244
1245 case GIN_FALSE:
1246 key->curItemMatches = false;
1247 break;
1248
1249 case GIN_MAYBE:
1250 key->curItemMatches = true;
1251 key->recheckCurItem = true;
1252 break;
1253
1254 default:
1255
1256 /*
1257 * the 'default' case shouldn't happen, but if the consistent
1258 * function returns something bogus, this is the safe result
1259 */
1260 key->curItemMatches = true;
1261 key->recheckCurItem = true;
1262 break;
1263 }
1264
1265 /*
1266 * We have a tuple, and we know if it matches or not. If it's a non-match,
1267 * we could continue to find the next matching tuple, but let's break out
1268 * and give scanGetItem a chance to advance the other keys. They might be
1269 * able to skip past to a much higher TID, allowing us to save work.
1270 */
1271
1272 /* clean up after consistentFn calls */
1273 MemoryContextSwitchTo(oldCtx);
1274 MemoryContextReset(tempCtx);
1275}
char GinTernaryValue
Definition: gin.h:58
#define GIN_MAYBE
Definition: gin.h:65
#define GIN_TRUE
Definition: gin.h:64
#define ItemPointerSetMax(p)
Definition: ginblock.h:171
static void entryGetItem(GinState *ginstate, GinScanEntry entry, ItemPointerData advancePast)
Definition: ginget.c:809
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
#define OffsetNumberPrev(offsetNumber)
Definition: off.h:54
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124

References Assert, GinScanEntryData::curItem, entryGetItem(), GIN_FALSE, GIN_MAYBE, GIN_TRUE, ginCompareItemPointers(), GinItemPointerGetBlockNumber, GinItemPointerGetOffsetNumber, i, InvalidOffsetNumber, GinScanEntryData::isFinished, ItemPointerIsLossyPage, ItemPointerSet(), ItemPointerSetLossyPage, ItemPointerSetMax, sort-test::key, MemoryContextReset(), MemoryContextSwitchTo(), OffsetNumberNext, OffsetNumberPrev, and res.

Referenced by scanGetItem().

◆ matchPartialInPendingList()

static bool matchPartialInPendingList ( GinState ginstate,
Page  page,
OffsetNumber  off,
OffsetNumber  maxoff,
GinScanEntry  entry,
Datum datum,
GinNullCategory category,
bool *  datumExtracted 
)
static

Definition at line 1539 of file ginget.c.

1544{
1545 IndexTuple itup;
1546 int32 cmp;
1547
1548 /* Partial match to a null is not possible */
1549 if (entry->queryCategory != GIN_CAT_NORM_KEY)
1550 return false;
1551
1552 while (off < maxoff)
1553 {
1554 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
1555
1556 if (gintuple_get_attrnum(ginstate, itup) != entry->attnum)
1557 return false;
1558
1559 if (datumExtracted[off - 1] == false)
1560 {
1561 datum[off - 1] = gintuple_get_key(ginstate, itup,
1562 &category[off - 1]);
1563 datumExtracted[off - 1] = true;
1564 }
1565
1566 /* Once we hit nulls, no further match is possible */
1567 if (category[off - 1] != GIN_CAT_NORM_KEY)
1568 return false;
1569
1570 /*----------
1571 * Check partial match.
1572 * case cmp == 0 => match
1573 * case cmp > 0 => not match and end scan (no later match possible)
1574 * case cmp < 0 => not match and continue scan
1575 *----------
1576 */
1578 ginstate->supportCollation[entry->attnum - 1],
1579 entry->queryKey,
1580 datum[off - 1],
1581 UInt16GetDatum(entry->strategy),
1582 PointerGetDatum(entry->extra_data)));
1583 if (cmp == 0)
1584 return true;
1585 else if (cmp > 0)
1586 return false;
1587
1588 off++;
1589 }
1590
1591 return false;
1592}

References GinScanEntryData::attnum, cmp(), GinState::comparePartialFn, DatumGetInt32(), GinScanEntryData::extra_data, FunctionCall4Coll(), GIN_CAT_NORM_KEY, gintuple_get_attrnum(), gintuple_get_key(), PageGetItem(), PageGetItemId(), PointerGetDatum(), GinScanEntryData::queryCategory, GinScanEntryData::queryKey, GinScanEntryData::strategy, GinState::supportCollation, and UInt16GetDatum().

Referenced by collectMatchesForHeapRow().

◆ moveRightIfItNeeded()

static bool moveRightIfItNeeded ( GinBtreeData btree,
GinBtreeStack stack,
Snapshot  snapshot 
)
static

Definition at line 43 of file ginget.c.

44{
45 Page page = BufferGetPage(stack->buffer);
46
47 if (stack->off > PageGetMaxOffsetNumber(page))
48 {
49 /*
50 * We scanned the whole page, so we should take right page
51 */
52 if (GinPageRightMost(page))
53 return false; /* no more pages */
54
55 stack->buffer = ginStepRight(stack->buffer, btree->index, GIN_SHARE);
56 stack->blkno = BufferGetBlockNumber(stack->buffer);
57 stack->off = FirstOffsetNumber;
58 PredicateLockPage(btree->index, stack->blkno, snapshot);
59 }
60
61 return true;
62}
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
BlockNumber blkno
Definition: gin_private.h:131

References GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage(), FirstOffsetNumber, GIN_SHARE, GinPageRightMost, ginStepRight(), GinBtreeData::index, GinBtreeStack::off, PageGetMaxOffsetNumber(), and PredicateLockPage().

Referenced by collectMatchBitmap().

◆ scanGetCandidate()

static bool scanGetCandidate ( IndexScanDesc  scan,
pendingPosition pos 
)
static

Definition at line 1452 of file ginget.c.

1453{
1454 OffsetNumber maxoff;
1455 Page page;
1456 IndexTuple itup;
1457
1459 for (;;)
1460 {
1461 page = BufferGetPage(pos->pendingBuffer);
1462
1463 maxoff = PageGetMaxOffsetNumber(page);
1464 if (pos->firstOffset > maxoff)
1465 {
1466 BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
1467
1468 if (blkno == InvalidBlockNumber)
1469 {
1472
1473 return false;
1474 }
1475 else
1476 {
1477 /*
1478 * Here we must prevent deletion of next page by insertcleanup
1479 * process, which may be trying to obtain exclusive lock on
1480 * current page. So, we lock next page before releasing the
1481 * current one
1482 */
1483 Buffer tmpbuf = ReadBuffer(scan->indexRelation, blkno);
1484
1487
1488 pos->pendingBuffer = tmpbuf;
1490 }
1491 }
1492 else
1493 {
1494 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, pos->firstOffset));
1495 pos->item = itup->t_tid;
1496 if (GinPageHasFullRow(page))
1497 {
1498 /*
1499 * find itempointer to the next row
1500 */
1501 for (pos->lastOffset = pos->firstOffset + 1; pos->lastOffset <= maxoff; pos->lastOffset++)
1502 {
1503 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, pos->lastOffset));
1504 if (!ItemPointerEquals(&pos->item, &itup->t_tid))
1505 break;
1506 }
1507 }
1508 else
1509 {
1510 /*
1511 * All itempointers are the same on this page
1512 */
1513 pos->lastOffset = maxoff + 1;
1514 }
1515
1516 /*
1517 * Now pos->firstOffset points to the first tuple of current heap
1518 * row, pos->lastOffset points to the first tuple of next heap row
1519 * (or to the end of page)
1520 */
1521 break;
1522 }
1523 }
1524
1525 return true;
1526}
#define InvalidBlockNumber
Definition: block.h:33
int Buffer
Definition: buf.h:23
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:746
Relation indexRelation
Definition: relscan.h:135
ItemPointerData t_tid
Definition: itup.h:37
static StringInfoData tmpbuf
Definition: walsender.c:170

References BufferGetPage(), pendingPosition::firstOffset, FirstOffsetNumber, GIN_SHARE, GinPageGetOpaque, GinPageHasFullRow, IndexScanDescData::indexRelation, InvalidBlockNumber, InvalidBuffer, pendingPosition::item, ItemPointerEquals(), ItemPointerSetInvalid(), pendingPosition::lastOffset, LockBuffer(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), pendingPosition::pendingBuffer, ReadBuffer(), IndexTupleData::t_tid, tmpbuf, and UnlockReleaseBuffer().

Referenced by collectMatchesForHeapRow(), and scanPendingInsert().

◆ scanGetItem()

static bool scanGetItem ( IndexScanDesc  scan,
ItemPointerData  advancePast,
ItemPointerData item,
bool *  recheck 
)
static

Definition at line 1287 of file ginget.c.

1289{
1290 GinScanOpaque so = (GinScanOpaque) scan->opaque;
1291 uint32 i;
1292 bool match;
1293
1294 /*----------
1295 * Advance the scan keys in lock-step, until we find an item that matches
1296 * all the keys. If any key reports isFinished, meaning its subset of the
1297 * entries is exhausted, we can stop. Otherwise, set *item to the next
1298 * matching item.
1299 *
1300 * This logic works only if a keyGetItem stream can never contain both
1301 * exact and lossy pointers for the same page. Else we could have a
1302 * case like
1303 *
1304 * stream 1 stream 2
1305 * ... ...
1306 * 42/6 42/7
1307 * 50/1 42/0xffff
1308 * ... ...
1309 *
1310 * We would conclude that 42/6 is not a match and advance stream 1,
1311 * thus never detecting the match to the lossy pointer in stream 2.
1312 * (keyGetItem has a similar problem versus entryGetItem.)
1313 *----------
1314 */
1315 do
1316 {
1317 ItemPointerSetMin(item);
1318 match = true;
1319 for (i = 0; i < so->nkeys && match; i++)
1320 {
1321 GinScanKey key = so->keys + i;
1322
1323 /*
1324 * If we're considering a lossy page, skip excludeOnly keys. They
1325 * can't exclude the whole page anyway.
1326 */
1327 if (ItemPointerIsLossyPage(item) && key->excludeOnly)
1328 {
1329 /*
1330 * ginNewScanKey() should never mark the first key as
1331 * excludeOnly.
1332 */
1333 Assert(i > 0);
1334 continue;
1335 }
1336
1337 /* Fetch the next item for this key that is > advancePast. */
1338 keyGetItem(&so->ginstate, so->tempCtx, key, advancePast);
1339
1340 if (key->isFinished)
1341 return false;
1342
1343 /*
1344 * If it's not a match, we can immediately conclude that nothing
1345 * <= this item matches, without checking the rest of the keys.
1346 */
1347 if (!key->curItemMatches)
1348 {
1349 advancePast = key->curItem;
1350 match = false;
1351 break;
1352 }
1353
1354 /*
1355 * It's a match. We can conclude that nothing < matches, so the
1356 * other key streams can skip to this item.
1357 *
1358 * Beware of lossy pointers, though; from a lossy pointer, we can
1359 * only conclude that nothing smaller than this *block* matches.
1360 */
1361 if (ItemPointerIsLossyPage(&key->curItem))
1362 {
1363 if (GinItemPointerGetBlockNumber(&advancePast) <
1365 {
1366 ItemPointerSet(&advancePast,
1369 }
1370 }
1371 else
1372 {
1374 ItemPointerSet(&advancePast,
1377 }
1378
1379 /*
1380 * If this is the first key, remember this location as a potential
1381 * match, and proceed to check the rest of the keys.
1382 *
1383 * Otherwise, check if this is the same item that we checked the
1384 * previous keys for (or a lossy pointer for the same page). If
1385 * not, loop back to check the previous keys for this item (we
1386 * will check this key again too, but keyGetItem returns quickly
1387 * for that)
1388 */
1389 if (i == 0)
1390 {
1391 *item = key->curItem;
1392 }
1393 else
1394 {
1395 if (ItemPointerIsLossyPage(&key->curItem) ||
1397 {
1399 match = (GinItemPointerGetBlockNumber(&key->curItem) ==
1401 }
1402 else
1403 {
1404 Assert(ginCompareItemPointers(&key->curItem, item) >= 0);
1405 match = (ginCompareItemPointers(&key->curItem, item) == 0);
1406 }
1407 }
1408 }
1409 } while (!match);
1410
1411 Assert(!ItemPointerIsMin(item));
1412
1413 /*
1414 * Now *item contains the first ItemPointer after previous result that
1415 * satisfied all the keys for that exact TID, or a lossy reference to the
1416 * same page.
1417 *
1418 * We must return recheck = true if any of the keys are marked recheck.
1419 */
1420 *recheck = false;
1421 for (i = 0; i < so->nkeys; i++)
1422 {
1423 GinScanKey key = so->keys + i;
1424
1425 if (key->recheckCurItem)
1426 {
1427 *recheck = true;
1428 break;
1429 }
1430 }
1431
1432 return true;
1433}
#define ItemPointerIsMin(p)
Definition: ginblock.h:168
static void keyGetItem(GinState *ginstate, MemoryContext tempCtx, GinScanKey key, ItemPointerData advancePast)
Definition: ginget.c:992
MemoryContext tempCtx
Definition: gin_private.h:371

References Assert, ginCompareItemPointers(), GinItemPointerGetBlockNumber, GinItemPointerGetOffsetNumber, GinScanOpaqueData::ginstate, i, InvalidOffsetNumber, ItemPointerIsLossyPage, ItemPointerSet(), ItemPointerSetMin, sort-test::key, keyGetItem(), GinScanOpaqueData::keys, GinScanOpaqueData::nkeys, OffsetNumberPrev, IndexScanDescData::opaque, and GinScanOpaqueData::tempCtx.

Referenced by gingetbitmap().

◆ scanPendingInsert()

static void scanPendingInsert ( IndexScanDesc  scan,
TIDBitmap tbm,
int64 ntids 
)
static

Definition at line 1822 of file ginget.c.

1823{
1824 GinScanOpaque so = (GinScanOpaque) scan->opaque;
1825 MemoryContext oldCtx;
1826 bool recheck,
1827 match;
1828 int i;
1829 pendingPosition pos;
1831 Page page;
1832 BlockNumber blkno;
1833
1834 *ntids = 0;
1835
1836 /*
1837 * Acquire predicate lock on the metapage, to conflict with any fastupdate
1838 * insertions.
1839 */
1841
1842 LockBuffer(metabuffer, GIN_SHARE);
1843 page = BufferGetPage(metabuffer);
1844 blkno = GinPageGetMeta(page)->head;
1845
1846 /*
1847 * fetch head of list before unlocking metapage. head page must be pinned
1848 * to prevent deletion by vacuum process
1849 */
1850 if (blkno == InvalidBlockNumber)
1851 {
1852 /* No pending list, so proceed with normal scan */
1853 UnlockReleaseBuffer(metabuffer);
1854 return;
1855 }
1856
1857 pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno);
1858 LockBuffer(pos.pendingBuffer, GIN_SHARE);
1859 pos.firstOffset = FirstOffsetNumber;
1860 UnlockReleaseBuffer(metabuffer);
1861 pos.hasMatchKey = palloc(sizeof(bool) * so->nkeys);
1862
1863 /*
1864 * loop for each heap row. scanGetCandidate returns full row or row's
1865 * tuples from first page.
1866 */
1867 while (scanGetCandidate(scan, &pos))
1868 {
1869 /*
1870 * Check entries in tuple and set up entryRes array.
1871 *
1872 * If pending tuples belonging to the current heap row are spread
1873 * across several pages, collectMatchesForHeapRow will read all of
1874 * those pages.
1875 */
1876 if (!collectMatchesForHeapRow(scan, &pos))
1877 continue;
1878
1879 /*
1880 * Matching of entries of one row is finished, so check row using
1881 * consistent functions.
1882 */
1883 oldCtx = MemoryContextSwitchTo(so->tempCtx);
1884 recheck = false;
1885 match = true;
1886
1887 for (i = 0; i < so->nkeys; i++)
1888 {
1889 GinScanKey key = so->keys + i;
1890
1891 if (!key->boolConsistentFn(key))
1892 {
1893 match = false;
1894 break;
1895 }
1896 recheck |= key->recheckCurItem;
1897 }
1898
1899 MemoryContextSwitchTo(oldCtx);
1901
1902 if (match)
1903 {
1904 tbm_add_tuples(tbm, &pos.item, 1, recheck);
1905 (*ntids)++;
1906 }
1907 }
1908
1909 pfree(pos.hasMatchKey);
1910}
#define GIN_METAPAGE_BLKNO
Definition: ginblock.h:51
#define GinPageGetMeta(p)
Definition: ginblock.h:104
static bool collectMatchesForHeapRow(IndexScanDesc scan, pendingPosition *pos)
Definition: ginget.c:1607
void * palloc(Size size)
Definition: mcxt.c:1317
struct SnapshotData * xs_snapshot
Definition: relscan.h:136

References BufferGetPage(), collectMatchesForHeapRow(), FirstOffsetNumber, GIN_METAPAGE_BLKNO, GIN_SHARE, GinPageGetMeta, i, IndexScanDescData::indexRelation, InvalidBlockNumber, sort-test::key, GinScanOpaqueData::keys, LockBuffer(), MemoryContextReset(), MemoryContextSwitchTo(), GinScanOpaqueData::nkeys, IndexScanDescData::opaque, palloc(), pfree(), PredicateLockPage(), ReadBuffer(), scanGetCandidate(), tbm_add_tuples(), GinScanOpaqueData::tempCtx, UnlockReleaseBuffer(), and IndexScanDescData::xs_snapshot.

Referenced by gingetbitmap().

◆ scanPostingTree()

static void scanPostingTree ( Relation  index,
GinScanEntry  scanEntry,
BlockNumber  rootPostingTree 
)
static

Definition at line 69 of file ginget.c.

71{
72 GinBtreeData btree;
73 GinBtreeStack *stack;
74 Buffer buffer;
75 Page page;
76
77 /* Descend to the leftmost leaf page */
78 stack = ginScanBeginPostingTree(&btree, index, rootPostingTree);
79 buffer = stack->buffer;
80
81 IncrBufferRefCount(buffer); /* prevent unpin in freeGinBtreeStack */
82
83 freeGinBtreeStack(stack);
84
85 /*
86 * Loop iterates through all leaf pages of posting tree
87 */
88 for (;;)
89 {
90 page = BufferGetPage(buffer);
91 if ((GinPageGetOpaque(page)->flags & GIN_DELETED) == 0)
92 {
93 int n = GinDataLeafPageGetItemsToTbm(page, scanEntry->matchBitmap);
94
95 scanEntry->predictNumberResult += n;
96 }
97
98 if (GinPageRightMost(page))
99 break; /* no more pages */
100
101 buffer = ginStepRight(buffer, index, GIN_SHARE);
102 }
103
104 UnlockReleaseBuffer(buffer);
105}
GinBtreeStack * ginScanBeginPostingTree(GinBtree btree, Relation index, BlockNumber rootBlkno)
Definition: gindatapage.c:1936
int GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm)
Definition: gindatapage.c:182
Definition: type.h:96

References GinBtreeStack::buffer, BufferGetPage(), freeGinBtreeStack(), GIN_DELETED, GIN_SHARE, GinDataLeafPageGetItemsToTbm(), GinPageGetOpaque, GinPageRightMost, ginScanBeginPostingTree(), ginStepRight(), IncrBufferRefCount(), GinScanEntryData::matchBitmap, GinScanEntryData::predictNumberResult, and UnlockReleaseBuffer().

Referenced by collectMatchBitmap().

◆ startScan()

static void startScan ( IndexScanDesc  scan)
static

Definition at line 602 of file ginget.c.

603{
605 GinState *ginstate = &so->ginstate;
606 uint32 i;
607
608 for (i = 0; i < so->totalentries; i++)
609 startScanEntry(ginstate, so->entries[i], scan->xs_snapshot);
610
611 if (GinFuzzySearchLimit > 0)
612 {
613 /*
614 * If all of keys more than threshold we will try to reduce result, we
615 * hope (and only hope, for intersection operation of array our
616 * supposition isn't true), that total result will not more than
617 * minimal predictNumberResult.
618 */
619 bool reduce = true;
620
621 for (i = 0; i < so->totalentries; i++)
622 {
624 {
625 reduce = false;
626 break;
627 }
628 }
629 if (reduce)
630 {
631 for (i = 0; i < so->totalentries; i++)
632 {
634 so->entries[i]->reduceResult = true;
635 }
636 }
637 }
638
639 /*
640 * Now that we have the estimates for the entry frequencies, finish
641 * initializing the scan keys.
642 */
643 for (i = 0; i < so->nkeys; i++)
644 startScanKey(ginstate, so, so->keys + i);
645}
static void startScanEntry(GinState *ginstate, GinScanEntry entry, Snapshot snapshot)
Definition: ginget.c:319
static void startScanKey(GinState *ginstate, GinScanOpaque so, GinScanKey key)
Definition: ginget.c:506
int GinFuzzySearchLimit
Definition: ginget.c:27
static void reduce(void)
Definition: parse.c:260
GinScanEntry * entries
Definition: gin_private.h:377

References GinScanOpaqueData::entries, for(), GinFuzzySearchLimit, GinScanOpaqueData::ginstate, i, GinScanOpaqueData::keys, GinScanOpaqueData::nkeys, IndexScanDescData::opaque, GinScanEntryData::predictNumberResult, reduce(), GinScanEntryData::reduceResult, startScanEntry(), startScanKey(), GinScanOpaqueData::totalentries, and IndexScanDescData::xs_snapshot.

Referenced by gingetbitmap().

◆ startScanEntry()

static void startScanEntry ( GinState ginstate,
GinScanEntry  entry,
Snapshot  snapshot 
)
static

Definition at line 319 of file ginget.c.

320{
321 GinBtreeData btreeEntry;
322 GinBtreeStack *stackEntry;
323 Page page;
324 bool needUnlock;
325
326restartScanEntry:
327 entry->buffer = InvalidBuffer;
328 ItemPointerSetMin(&entry->curItem);
330 if (entry->list)
331 pfree(entry->list);
332 entry->list = NULL;
333 entry->nlist = 0;
334 entry->matchBitmap = NULL;
335 entry->matchResult = NULL;
336 entry->reduceResult = false;
337 entry->predictNumberResult = 0;
338
339 /*
340 * we should find entry, and begin scan of posting tree or just store
341 * posting list in memory
342 */
343 ginPrepareEntryScan(&btreeEntry, entry->attnum,
344 entry->queryKey, entry->queryCategory,
345 ginstate);
346 stackEntry = ginFindLeafPage(&btreeEntry, true, false);
347 page = BufferGetPage(stackEntry->buffer);
348
349 /* ginFindLeafPage() will have already checked snapshot age. */
350 needUnlock = true;
351
352 entry->isFinished = true;
353
354 if (entry->isPartialMatch ||
356 {
357 /*
358 * btreeEntry.findItem locates the first item >= given search key.
359 * (For GIN_CAT_EMPTY_QUERY, it will find the leftmost index item
360 * because of the way the GIN_CAT_EMPTY_QUERY category code is
361 * assigned.) We scan forward from there and collect all TIDs needed
362 * for the entry type.
363 */
364 btreeEntry.findItem(&btreeEntry, stackEntry);
365 if (collectMatchBitmap(&btreeEntry, stackEntry, entry, snapshot)
366 == false)
367 {
368 /*
369 * GIN tree was seriously restructured, so we will cleanup all
370 * found data and rescan. See comments near 'return false' in
371 * collectMatchBitmap()
372 */
373 if (entry->matchBitmap)
374 {
375 if (entry->matchIterator)
377 entry->matchIterator = NULL;
378 tbm_free(entry->matchBitmap);
379 entry->matchBitmap = NULL;
380 }
381 LockBuffer(stackEntry->buffer, GIN_UNLOCK);
382 freeGinBtreeStack(stackEntry);
383 goto restartScanEntry;
384 }
385
386 if (entry->matchBitmap && !tbm_is_empty(entry->matchBitmap))
387 {
388 entry->matchIterator =
390 entry->isFinished = false;
391 }
392 }
393 else if (btreeEntry.findItem(&btreeEntry, stackEntry))
394 {
395 IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stackEntry->off));
396
397 if (GinIsPostingTree(itup))
398 {
399 BlockNumber rootPostingTree = GinGetPostingTree(itup);
400 GinBtreeStack *stack;
401 Page entrypage;
402 ItemPointerData minItem;
403
404 /*
405 * This is an equality scan, so lock the root of the posting tree.
406 * It represents a lock on the exact key value, and covers all the
407 * items in the posting tree.
408 */
409 PredicateLockPage(ginstate->index, rootPostingTree, snapshot);
410
411 /*
412 * We should unlock entry page before touching posting tree to
413 * prevent deadlocks with vacuum processes. Because entry is never
414 * deleted from page and posting tree is never reduced to the
415 * posting list, we can unlock page after getting BlockNumber of
416 * root of posting tree.
417 */
418 LockBuffer(stackEntry->buffer, GIN_UNLOCK);
419 needUnlock = false;
420
421 stack = ginScanBeginPostingTree(&entry->btree, ginstate->index,
422 rootPostingTree);
423 entry->buffer = stack->buffer;
424
425 /*
426 * We keep buffer pinned because we need to prevent deletion of
427 * page during scan. See GIN's vacuum implementation. RefCount is
428 * increased to keep buffer pinned after freeGinBtreeStack() call.
429 */
431
432 entrypage = BufferGetPage(entry->buffer);
433
434 /*
435 * Load the first page into memory.
436 */
437 ItemPointerSetMin(&minItem);
438 entry->list = GinDataLeafPageGetItems(entrypage, &entry->nlist, minItem);
439
440 entry->predictNumberResult = stack->predictNumber * entry->nlist;
441
443 freeGinBtreeStack(stack);
444 entry->isFinished = false;
445 }
446 else
447 {
448 /*
449 * Lock the entry leaf page. This is more coarse-grained than
450 * necessary, because it will conflict with any insertions that
451 * land on the same leaf page, not only the exact key we searched
452 * for. But locking an individual tuple would require updating
453 * that lock whenever it moves because of insertions or vacuums,
454 * which seems too complicated.
455 */
456 PredicateLockPage(ginstate->index,
457 BufferGetBlockNumber(stackEntry->buffer),
458 snapshot);
459 if (GinGetNPosting(itup) > 0)
460 {
461 entry->list = ginReadTuple(ginstate, entry->attnum, itup,
462 &entry->nlist);
463 entry->predictNumberResult = entry->nlist;
464
465 entry->isFinished = false;
466 }
467 }
468 }
469 else
470 {
471 /*
472 * No entry found. Predicate lock the leaf page, to lock the place
473 * where the entry would've been, had there been one.
474 */
475 PredicateLockPage(ginstate->index,
476 BufferGetBlockNumber(stackEntry->buffer), snapshot);
477 }
478
479 if (needUnlock)
480 LockBuffer(stackEntry->buffer, GIN_UNLOCK);
481 freeGinBtreeStack(stackEntry);
482}
void ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum, Datum key, GinNullCategory category, GinState *ginstate)
Definition: ginentrypage.c:747
static bool collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry scanEntry, Snapshot snapshot)
Definition: ginget.c:121
bool(* findItem)(GinBtree, GinBtreeStack *)
Definition: gin_private.h:156
uint32 predictNumber
Definition: gin_private.h:136
void tbm_free(TIDBitmap *tbm)
Definition: tidbitmap.c:322
bool tbm_is_empty(const TIDBitmap *tbm)
Definition: tidbitmap.c:670
TBMPrivateIterator * tbm_begin_private_iterate(TIDBitmap *tbm)
Definition: tidbitmap.c:689

References GinScanEntryData::attnum, GinScanEntryData::btree, GinBtreeStack::buffer, GinScanEntryData::buffer, BufferGetBlockNumber(), BufferGetPage(), collectMatchBitmap(), GinScanEntryData::curItem, GinBtreeData::findItem, freeGinBtreeStack(), GIN_CAT_EMPTY_QUERY, GIN_UNLOCK, GinDataLeafPageGetItems(), ginFindLeafPage(), GinGetNPosting, GinGetPostingTree, GinIsPostingTree, ginPrepareEntryScan(), ginReadTuple(), ginScanBeginPostingTree(), IncrBufferRefCount(), GinState::index, InvalidBuffer, InvalidOffsetNumber, GinScanEntryData::isFinished, GinScanEntryData::isPartialMatch, ItemPointerSetMin, GinScanEntryData::list, LockBuffer(), GinScanEntryData::matchBitmap, GinScanEntryData::matchIterator, GinScanEntryData::matchResult, GinScanEntryData::nlist, GinBtreeStack::off, GinScanEntryData::offset, PageGetItem(), PageGetItemId(), pfree(), PredicateLockPage(), GinBtreeStack::predictNumber, GinScanEntryData::predictNumberResult, GinScanEntryData::queryCategory, GinScanEntryData::queryKey, GinScanEntryData::reduceResult, tbm_begin_private_iterate(), tbm_end_private_iterate(), tbm_free(), and tbm_is_empty().

Referenced by startScan().

◆ startScanKey()

static void startScanKey ( GinState ginstate,
GinScanOpaque  so,
GinScanKey  key 
)
static

Definition at line 506 of file ginget.c.

507{
509 int i;
510 int j;
511 int *entryIndexes;
512
513 ItemPointerSetMin(&key->curItem);
514 key->curItemMatches = false;
515 key->recheckCurItem = false;
516 key->isFinished = false;
517
518 /*
519 * Divide the entries into two distinct sets: required and additional.
520 * Additional entries are not enough for a match alone, without any items
521 * from the required set, but are needed by the consistent function to
522 * decide if an item matches. When scanning, we can skip over items from
523 * additional entries that have no corresponding matches in any of the
524 * required entries. That speeds up queries like "frequent & rare"
525 * considerably, if the frequent term can be put in the additional set.
526 *
527 * There can be many legal ways to divide them entries into these two
528 * sets. A conservative division is to just put everything in the required
529 * set, but the more you can put in the additional set, the more you can
530 * skip during the scan. To maximize skipping, we try to put as many
531 * frequent items as possible into additional, and less frequent ones into
532 * required. To do that, sort the entries by frequency
533 * (predictNumberResult), and put entries into the required set in that
534 * order, until the consistent function says that none of the remaining
535 * entries can form a match, without any items from the required set. The
536 * rest go to the additional set.
537 *
538 * Exclude-only scan keys are known to have no required entries.
539 */
540 if (key->excludeOnly)
541 {
543
544 key->nrequired = 0;
545 key->nadditional = key->nentries;
546 key->additionalEntries = palloc(key->nadditional * sizeof(GinScanEntry));
547 for (i = 0; i < key->nadditional; i++)
548 key->additionalEntries[i] = key->scanEntry[i];
549 }
550 else if (key->nentries > 1)
551 {
553
554 entryIndexes = (int *) palloc(sizeof(int) * key->nentries);
555 for (i = 0; i < key->nentries; i++)
556 entryIndexes[i] = i;
557 qsort_arg(entryIndexes, key->nentries, sizeof(int),
559
560 for (i = 0; i < key->nentries - 1; i++)
561 {
562 /* Pass all entries <= i as FALSE, and the rest as MAYBE */
563 for (j = 0; j <= i; j++)
564 key->entryRes[entryIndexes[j]] = GIN_FALSE;
565 for (j = i + 1; j < key->nentries; j++)
566 key->entryRes[entryIndexes[j]] = GIN_MAYBE;
567
568 if (key->triConsistentFn(key) == GIN_FALSE)
569 break;
570 }
571 /* i is now the last required entry. */
572
574
575 key->nrequired = i + 1;
576 key->nadditional = key->nentries - key->nrequired;
577 key->requiredEntries = palloc(key->nrequired * sizeof(GinScanEntry));
578 key->additionalEntries = palloc(key->nadditional * sizeof(GinScanEntry));
579
580 j = 0;
581 for (i = 0; i < key->nrequired; i++)
582 key->requiredEntries[i] = key->scanEntry[entryIndexes[j++]];
583 for (i = 0; i < key->nadditional; i++)
584 key->additionalEntries[i] = key->scanEntry[entryIndexes[j++]];
585
586 /* clean up after consistentFn calls (also frees entryIndexes) */
588 }
589 else
590 {
592
593 key->nrequired = 1;
594 key->nadditional = 0;
595 key->requiredEntries = palloc(1 * sizeof(GinScanEntry));
596 key->requiredEntries[0] = key->scanEntry[0];
597 }
598 MemoryContextSwitchTo(oldCtx);
599}
static int entryIndexByFrequencyCmp(const void *a1, const void *a2, void *arg)
Definition: ginget.c:489
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
MemoryContext keyCtx
Definition: gin_private.h:381

References CurrentMemoryContext, entryIndexByFrequencyCmp(), for(), GIN_FALSE, GIN_MAYBE, i, if(), ItemPointerSetMin, j, sort-test::key, GinScanOpaqueData::keyCtx, MemoryContextReset(), MemoryContextSwitchTo(), palloc(), qsort_arg(), and GinScanOpaqueData::tempCtx.

Referenced by startScan().

Variable Documentation

◆ GinFuzzySearchLimit

int GinFuzzySearchLimit = 0

Definition at line 27 of file ginget.c.

Referenced by startScan().