mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Support index-only scans using the visibility map to avoid heap fetches.
When a btree index contains all columns required by the query, and the visibility map shows that all tuples on a target heap page are visible-to-all, we don't need to fetch that heap page. This patch depends on the previous patches that made the visibility map reliable. There's a fair amount left to do here, notably trying to figure out a less chintzy way of estimating the cost of an index-only scan, but the core functionality seems ready to commit. Robert Haas and Ibrar Ahmed, with some previous work by Heikki Linnakangas.
This commit is contained in:
@ -73,6 +73,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
|
||||
BTCycleId cycleid);
|
||||
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
|
||||
BlockNumber orig_blkno);
|
||||
static IndexTuple bt_getindextuple(IndexScanDesc scan);
|
||||
|
||||
|
||||
/*
|
||||
@ -310,9 +311,94 @@ btgettuple(PG_FUNCTION_ARGS)
|
||||
else
|
||||
res = _bt_first(scan, dir);
|
||||
|
||||
/* Return the whole index tuple if requested */
|
||||
if (scan->xs_want_itup)
|
||||
{
|
||||
/* First, free the last one ... */
|
||||
if (scan->xs_itup != NULL)
|
||||
{
|
||||
pfree(scan->xs_itup);
|
||||
scan->xs_itup = NULL;
|
||||
}
|
||||
|
||||
if (res)
|
||||
scan->xs_itup = bt_getindextuple(scan);
|
||||
}
|
||||
|
||||
PG_RETURN_BOOL(res);
|
||||
}
|
||||
|
||||
/*
|
||||
* bt_getindextuple - fetch index tuple at current position.
|
||||
*
|
||||
* This can fail to find the tuple if new tuples have been inserted on the
|
||||
* index page since we stepped onto the page. NULL is returned in that case.
|
||||
* (We could try a bit harder by searching for the TID; but if insertions
|
||||
* are happening, it's reasonably likely that an index-only scan will fail
|
||||
* anyway because of visibility. So probably not worth the trouble.)
|
||||
*
|
||||
* The tuple returned is a palloc'd copy, so that we don't need to keep a
|
||||
* lock on the index page.
|
||||
*
|
||||
* The caller must have pin on so->currPos.buf.
|
||||
*/
|
||||
static IndexTuple
|
||||
bt_getindextuple(IndexScanDesc scan)
|
||||
{
|
||||
BTScanOpaque so = (BTScanOpaque) scan->opaque;
|
||||
Page page;
|
||||
BTPageOpaque opaque;
|
||||
OffsetNumber minoff;
|
||||
OffsetNumber maxoff;
|
||||
int itemIndex;
|
||||
OffsetNumber offnum;
|
||||
IndexTuple ituple,
|
||||
result;
|
||||
|
||||
Assert(BufferIsValid(so->currPos.buf));
|
||||
|
||||
LockBuffer(so->currPos.buf, BT_READ);
|
||||
|
||||
/* Locate the tuple, being paranoid about possibility the page changed */
|
||||
page = BufferGetPage(so->currPos.buf);
|
||||
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
|
||||
minoff = P_FIRSTDATAKEY(opaque);
|
||||
maxoff = PageGetMaxOffsetNumber(page);
|
||||
|
||||
itemIndex = so->currPos.itemIndex;
|
||||
/* pure paranoia */
|
||||
Assert(itemIndex >= so->currPos.firstItem &&
|
||||
itemIndex <= so->currPos.lastItem);
|
||||
|
||||
offnum = so->currPos.items[itemIndex].indexOffset;
|
||||
if (offnum < minoff || offnum > maxoff)
|
||||
{
|
||||
/* should never happen, since we have pin on page, but be careful */
|
||||
LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ituple = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
|
||||
|
||||
if (ItemPointerEquals(&ituple->t_tid, &scan->xs_ctup.t_self))
|
||||
{
|
||||
/* yup, it's the desired tuple, so make a copy */
|
||||
Size itupsz = IndexTupleSize(ituple);
|
||||
|
||||
result = palloc(itupsz);
|
||||
memcpy(result, ituple, itupsz);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* oops, it got moved */
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* btgetbitmap() -- gets all matching tuples, and adds them to a bitmap
|
||||
*/
|
||||
@ -464,6 +550,12 @@ btendscan(PG_FUNCTION_ARGS)
|
||||
pfree(so->keyData);
|
||||
pfree(so);
|
||||
|
||||
if (scan->xs_itup != NULL)
|
||||
{
|
||||
pfree(scan->xs_itup);
|
||||
scan->xs_itup = NULL;
|
||||
}
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user