1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-10 17:42:29 +03:00

Replace "amgetmulti" AM functions with "amgetbitmap", in which the whole

indexscan always occurs in one call, and the results are returned in a
TIDBitmap instead of a limited-size array of TIDs.  This should improve
speed a little by reducing AM entry/exit overhead, and it is necessary
infrastructure if we are ever to support bitmap indexes.

In an only slightly related change, add support for TIDBitmaps to preserve
(somewhat lossily) the knowledge that particular TIDs reported by an index
need to have their quals rechecked when the heap is visited.  This facility
is not really used yet; we'll need to extend the forced-recheck feature to
plain indexscans before it's useful, and that hasn't been coded yet.
The intent is to use it to clean up 8.3's horrid @@@ kluge for text search
with weighted queries.  There might be other uses in future, but that one
alone is sufficient reason.

Heikki Linnakangas, with some adjustments by me.
This commit is contained in:
Tom Lane
2008-04-10 22:25:26 +00:00
parent f260edb144
commit 4e82a95476
30 changed files with 420 additions and 269 deletions

View File

@@ -8,13 +8,15 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.10 2008/01/01 19:45:46 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.11 2008/04/10 22:25:25 tgl Exp $
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gin.h"
#include "catalog/index.h"
#include "miscadmin.h"
#include "utils/memutils.h"
static bool
@@ -476,34 +478,37 @@ scanGetItem(IndexScanDesc scan, ItemPointerData *item)
#define GinIsVoidRes(s) ( ((GinScanOpaque) scan->opaque)->isVoidRes == true )
Datum
gingetmulti(PG_FUNCTION_ARGS)
gingetbitmap(PG_FUNCTION_ARGS)
{
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
int32 max_tids = PG_GETARG_INT32(2);
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
int64 ntids;
if (GinIsNewKey(scan))
newScanKey(scan);
*returned_tids = 0;
if (GinIsVoidRes(scan))
PG_RETURN_BOOL(false);
PG_RETURN_INT64(0);
startScan(scan);
do
ntids = 0;
for (;;)
{
if (scanGetItem(scan, tids + *returned_tids))
(*returned_tids)++;
else
ItemPointerData iptr;
CHECK_FOR_INTERRUPTS();
if (!scanGetItem(scan, &iptr))
break;
} while (*returned_tids < max_tids);
tbm_add_tuples(tbm, &iptr, 1, false);
ntids++;
}
stopScan(scan);
PG_RETURN_BOOL(*returned_tids == max_tids);
PG_RETURN_INT64(ntids);
}
Datum

View File

@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.69 2008/01/01 19:45:46 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.70 2008/04/10 22:25:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -16,13 +16,16 @@
#include "access/gist_private.h"
#include "executor/execdebug.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "utils/memutils.h"
static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n,
ScanDirection dir);
static int gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids, int maxtids, bool ignore_killed_tuples);
static int64 gistnext(IndexScanDesc scan, ScanDirection dir,
ItemPointer tid, TIDBitmap *tbm,
bool ignore_killed_tuples);
static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
OffsetNumber offset);
@@ -114,32 +117,37 @@ gistgettuple(PG_FUNCTION_ARGS)
* tuples, continue looping until we find a non-killed tuple that matches
* the search key.
*/
res = (gistnext(scan, dir, &tid, 1, scan->ignore_killed_tuples)) ? true : false;
res = (gistnext(scan, dir, &tid, NULL, scan->ignore_killed_tuples) > 0) ? true : false;
PG_RETURN_BOOL(res);
}
Datum
gistgetmulti(PG_FUNCTION_ARGS)
gistgetbitmap(PG_FUNCTION_ARGS)
{
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
int32 max_tids = PG_GETARG_INT32(2);
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
int64 ntids;
*returned_tids = gistnext(scan, ForwardScanDirection, tids, max_tids, false);
ntids = gistnext(scan, ForwardScanDirection, NULL, tbm, false);
PG_RETURN_BOOL(*returned_tids == max_tids);
PG_RETURN_INT64(ntids);
}
/*
* Fetch a tuples that matchs the search key; this can be invoked
* either to fetch the first such tuple or subsequent matching
* tuples. Returns true iff a matching tuple was found.
* Fetch tuple(s) that match the search key; this can be invoked
* either to fetch the first such tuple or subsequent matching tuples.
*
* This function is used by both gistgettuple and gistgetbitmap. When
* invoked from gistgettuple, tbm is null and the next matching tuple
* is returned in *tid. When invoked from getbitmap, tid is null and
* all matching tuples are added to tbm. In both cases, the function
* result is the number of returned tuples.
*/
static int
gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
int maxtids, bool ignore_killed_tuples)
static int64
gistnext(IndexScanDesc scan, ScanDirection dir,
ItemPointer tid, TIDBitmap *tbm,
bool ignore_killed_tuples)
{
Page p;
OffsetNumber n;
@@ -148,7 +156,7 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
IndexTuple it;
GISTPageOpaque opaque;
bool resetoffset = false;
int ntids = 0;
int64 ntids = 0;
so = (GISTScanOpaque) scan->opaque;
@@ -174,6 +182,8 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
for (;;)
{
CHECK_FOR_INTERRUPTS();
/* First of all, we need lock buffer */
Assert(so->curbuf != InvalidBuffer);
LockBuffer(so->curbuf, GIST_SHARE);
@@ -285,20 +295,21 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
* return success. Note that we keep "curbuf" pinned so that
* we can efficiently resume the index scan later.
*/
ItemPointerSet(&(so->curpos),
BufferGetBlockNumber(so->curbuf), n);
if (!(ignore_killed_tuples && ItemIdIsDead(PageGetItemId(p, n))))
{
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
tids[ntids] = scan->xs_ctup.t_self = it->t_tid;
ntids++;
if (ntids == maxtids)
if (tbm != NULL)
tbm_add_tuples(tbm, &it->t_tid, 1, false);
else
{
*tid = scan->xs_ctup.t_self = it->t_tid;
LockBuffer(so->curbuf, GIST_UNLOCK);
return ntids;
return ntids; /* always 1 */
}
}
}
@@ -308,7 +319,6 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
* We've found an entry in an internal node whose key is
* consistent with the search key, so push it to stack
*/
stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
@@ -318,7 +328,6 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ItemPointer tids,
stk->next = so->stack->next;
so->stack->next = stk;
}
if (ScanDirectionIsBackward(dir))

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.100 2008/03/16 23:15:08 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.101 2008/04/10 22:25:25 tgl Exp $
*
* NOTES
* This file contains only the public interface routines.
@@ -22,6 +22,7 @@
#include "access/hash.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "optimizer/plancat.h"
@@ -275,72 +276,51 @@ hashgettuple(PG_FUNCTION_ARGS)
/*
* hashgetmulti() -- get multiple tuples at once
*
* This is a somewhat generic implementation: it avoids lock reacquisition
* overhead, but there's no smarts about picking especially good stopping
* points such as index page boundaries.
* hashgetbitmap() -- get all tuples at once
*/
Datum
hashgetmulti(PG_FUNCTION_ARGS)
hashgetbitmap(PG_FUNCTION_ARGS)
{
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
int32 max_tids = PG_GETARG_INT32(2);
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
HashScanOpaque so = (HashScanOpaque) scan->opaque;
Relation rel = scan->indexRelation;
bool res = true;
int32 ntids = 0;
bool res;
int64 ntids = 0;
/*
* We hold pin but not lock on current buffer while outside the hash AM.
* Reacquire the read lock here.
*/
if (BufferIsValid(so->hashso_curbuf))
_hash_chgbufaccess(rel, so->hashso_curbuf, HASH_NOLOCK, HASH_READ);
res = _hash_first(scan, ForwardScanDirection);
while (ntids < max_tids)
while (res)
{
/*
* Start scan, or advance to next tuple.
*/
if (ItemPointerIsValid(&(so->hashso_curpos)))
res = _hash_next(scan, ForwardScanDirection);
else
res = _hash_first(scan, ForwardScanDirection);
bool add_tuple;
CHECK_FOR_INTERRUPTS();
/*
* Skip killed tuples if asked to.
*/
if (scan->ignore_killed_tuples)
{
while (res)
{
Page page;
OffsetNumber offnum;
Page page;
OffsetNumber offnum;
offnum = ItemPointerGetOffsetNumber(&(so->hashso_curpos));
page = BufferGetPage(so->hashso_curbuf);
if (!ItemIdIsDead(PageGetItemId(page, offnum)))
break;
res = _hash_next(scan, ForwardScanDirection);
}
offnum = ItemPointerGetOffsetNumber(&(so->hashso_curpos));
page = BufferGetPage(so->hashso_curbuf);
add_tuple = !ItemIdIsDead(PageGetItemId(page, offnum));
}
else
add_tuple = true;
/* Save tuple ID, and continue scanning */
if (add_tuple)
{
tbm_add_tuples(tbm, &scan->xs_ctup.t_self, 1, false);
ntids++;
}
if (!res)
break;
/* Save tuple ID, and continue scanning */
tids[ntids] = scan->xs_ctup.t_self;
ntids++;
res = _hash_next(scan, ForwardScanDirection);
}
/* Release read lock on current buffer, but keep it pinned */
if (BufferIsValid(so->hashso_curbuf))
_hash_chgbufaccess(rel, so->hashso_curbuf, HASH_READ, HASH_NOLOCK);
*returned_tids = ntids;
PG_RETURN_BOOL(res);
PG_RETURN_INT64(ntids);
}

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.65 2008/03/26 21:10:37 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.66 2008/04/10 22:25:25 tgl Exp $
*
* NOTES
* many of the old access method routines have been turned into
@@ -88,7 +88,6 @@ RelationGetIndexScan(Relation indexRelation,
else
scan->keyData = NULL;
scan->is_multiscan = false; /* caller may change this */
scan->kill_prior_tuple = false;
scan->ignore_killed_tuples = true; /* default setting */

View File

@@ -8,20 +8,20 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.104 2008/03/26 21:10:37 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.105 2008/04/10 22:25:25 tgl Exp $
*
* INTERFACE ROUTINES
* index_open - open an index relation by relation OID
* index_close - close an index relation
* index_beginscan - start a scan of an index with amgettuple
* index_beginscan_multi - start a scan of an index with amgetmulti
* index_beginscan_bitmap - start a scan of an index with amgetbitmap
* index_rescan - restart a scan of an index
* index_endscan - end a scan
* index_insert - insert an index tuple into a relation
* index_markpos - mark a scan position
* index_restrpos - restore a scan position
* index_getnext - get the next tuple from a scan
* index_getmulti - get multiple tuples from a scan
* index_getbitmap - get all tuples from a scan
* index_bulk_delete - bulk deletion of index tuples
* index_vacuum_cleanup - post-deletion cleanup of an index
* index_getprocid - get a support procedure OID
@@ -227,7 +227,6 @@ index_beginscan(Relation heapRelation,
* Save additional parameters into the scandesc. Everything else was set
* up by RelationGetIndexScan.
*/
scan->is_multiscan = false;
scan->heapRelation = heapRelation;
scan->xs_snapshot = snapshot;
@@ -235,15 +234,15 @@ index_beginscan(Relation heapRelation,
}
/*
* index_beginscan_multi - start a scan of an index with amgetmulti
* index_beginscan_bitmap - start a scan of an index with amgetbitmap
*
* As above, caller had better be holding some lock on the parent heap
* relation, even though it's not explicitly mentioned here.
*/
IndexScanDesc
index_beginscan_multi(Relation indexRelation,
Snapshot snapshot,
int nkeys, ScanKey key)
index_beginscan_bitmap(Relation indexRelation,
Snapshot snapshot,
int nkeys, ScanKey key)
{
IndexScanDesc scan;
@@ -253,7 +252,6 @@ index_beginscan_multi(Relation indexRelation,
* Save additional parameters into the scandesc. Everything else was set
* up by RelationGetIndexScan.
*/
scan->is_multiscan = true;
scan->xs_snapshot = snapshot;
return scan;
@@ -676,44 +674,39 @@ index_getnext_indexitem(IndexScanDesc scan,
}
/* ----------------
* index_getmulti - get multiple tuples from an index scan
* index_getbitmap - get all tuples at once from an index scan
*
* Collects the TIDs of multiple heap tuples satisfying the scan keys.
* Adds the TIDs of all heap tuples satisfying the scan keys to a bitmap.
* Since there's no interlock between the index scan and the eventual heap
* access, this is only safe to use with MVCC-based snapshots: the heap
* item slot could have been replaced by a newer tuple by the time we get
* to it.
*
* A TRUE result indicates more calls should occur; a FALSE result says the
* scan is done. *returned_tids could be zero or nonzero in either case.
* Returns the number of matching tuples found.
* ----------------
*/
bool
index_getmulti(IndexScanDesc scan,
ItemPointer tids, int32 max_tids,
int32 *returned_tids)
int64
index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
{
FmgrInfo *procedure;
bool found;
int64 ntids;
SCAN_CHECKS;
GET_SCAN_PROCEDURE(amgetmulti);
GET_SCAN_PROCEDURE(amgetbitmap);
/* just make sure this is false... */
scan->kill_prior_tuple = false;
/*
* have the am's getmulti proc do all the work.
* have the am's getbitmap proc do all the work.
*/
found = DatumGetBool(FunctionCall4(procedure,
PointerGetDatum(scan),
PointerGetDatum(tids),
Int32GetDatum(max_tids),
PointerGetDatum(returned_tids)));
ntids = DatumGetInt64(FunctionCall2(procedure,
PointerGetDatum(scan),
PointerGetDatum(bitmap)));
pgstat_count_index_tuples(scan->indexRelation, *returned_tids);
pgstat_count_index_tuples(scan->indexRelation, ntids);
return found;
return ntids;
}
/* ----------------

View File

@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.156 2008/01/01 19:45:46 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.157 2008/04/10 22:25:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -22,6 +22,7 @@
#include "access/nbtree.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
#include "utils/memutils.h"
@@ -278,42 +279,29 @@ btgettuple(PG_FUNCTION_ARGS)
}
/*
* btgetmulti() -- get multiple tuples at once
*
* In the current implementation there seems no strong reason to stop at
* index page boundaries; we just press on until we fill the caller's buffer
* or run out of matches.
* btgetbitmap() -- gets all matching tuples, and adds them to a bitmap
*/
Datum
btgetmulti(PG_FUNCTION_ARGS)
btgetbitmap(PG_FUNCTION_ARGS)
{
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1);
int32 max_tids = PG_GETARG_INT32(2);
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3);
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
BTScanOpaque so = (BTScanOpaque) scan->opaque;
bool res = true;
int32 ntids = 0;
int64 ntids = 0;
ItemPointer heapTid;
if (max_tids <= 0) /* behave correctly in boundary case */
PG_RETURN_BOOL(true);
/* If we haven't started the scan yet, fetch the first page & tuple. */
if (!BTScanPosIsValid(so->currPos))
/* Fetch the first page & tuple. */
if (!_bt_first(scan, ForwardScanDirection))
{
res = _bt_first(scan, ForwardScanDirection);
if (!res)
{
/* empty scan */
*returned_tids = ntids;
PG_RETURN_BOOL(res);
}
/* Save tuple ID, and continue scanning */
tids[ntids] = scan->xs_ctup.t_self;
ntids++;
/* empty scan */
PG_RETURN_INT64(0);
}
/* Save tuple ID, and continue scanning */
heapTid = &scan->xs_ctup.t_self;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
while (ntids < max_tids)
for (;;)
{
/*
* Advance to next tuple within page. This is the same as the easy
@@ -321,19 +309,20 @@ btgetmulti(PG_FUNCTION_ARGS)
*/
if (++so->currPos.itemIndex > so->currPos.lastItem)
{
CHECK_FOR_INTERRUPTS();
/* let _bt_next do the heavy lifting */
res = _bt_next(scan, ForwardScanDirection);
if (!res)
if (!_bt_next(scan, ForwardScanDirection))
break;
}
/* Save tuple ID, and continue scanning */
tids[ntids] = so->currPos.items[so->currPos.itemIndex].heapTid;
heapTid = &so->currPos.items[so->currPos.itemIndex].heapTid;
tbm_add_tuples(tbm, heapTid, 1, false);
ntids++;
}
*returned_tids = ntids;
PG_RETURN_BOOL(res);
PG_RETURN_INT64(ntids);
}
/*