Commit fddc2d94 authored by Tom Lane's avatar Tom Lane

Modify keys_are_unique optimization to release buffer pins before it

returns NULL.  This avoids out-of-buffers failures during many-way
indexscans, as in Shraibman's complaint of 21-Mar.
parent 346182ca
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.37 2003/01/08 19:41:40 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.38 2003/03/24 21:42:33 tgl Exp $
* *
* NOTES * NOTES
* many of the old access method routines have been turned into * many of the old access method routines have been turned into
...@@ -91,7 +91,7 @@ RelationGetIndexScan(Relation indexRelation, ...@@ -91,7 +91,7 @@ RelationGetIndexScan(Relation indexRelation,
scan->kill_prior_tuple = false; scan->kill_prior_tuple = false;
scan->ignore_killed_tuples = true; /* default setting */ scan->ignore_killed_tuples = true; /* default setting */
scan->keys_are_unique = false; /* may be set by amrescan */ scan->keys_are_unique = false; /* may be set by index AM */
scan->got_tuple = false; scan->got_tuple = false;
scan->opaque = NULL; scan->opaque = NULL;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.65 2003/03/23 23:01:03 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.66 2003/03/24 21:42:33 tgl Exp $
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
* index_open - open an index relation by relation OID * index_open - open an index relation by relation OID
...@@ -311,7 +311,7 @@ index_rescan(IndexScanDesc scan, ScanKey key) ...@@ -311,7 +311,7 @@ index_rescan(IndexScanDesc scan, ScanKey key)
GET_SCAN_PROCEDURE(rescan, amrescan); GET_SCAN_PROCEDURE(rescan, amrescan);
scan->kill_prior_tuple = false; /* for safety */ scan->kill_prior_tuple = false; /* for safety */
scan->keys_are_unique = false; /* may be set by amrescan */ scan->keys_are_unique = false; /* may be set by index AM */
scan->got_tuple = false; scan->got_tuple = false;
scan->unique_tuple_pos = 0; scan->unique_tuple_pos = 0;
scan->unique_tuple_mark = 0; scan->unique_tuple_mark = 0;
...@@ -413,37 +413,70 @@ index_getnext(IndexScanDesc scan, ScanDirection direction) ...@@ -413,37 +413,70 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
SCAN_CHECKS; SCAN_CHECKS;
/* Release any previously held pin */
if (BufferIsValid(scan->xs_cbuf))
{
ReleaseBuffer(scan->xs_cbuf);
scan->xs_cbuf = InvalidBuffer;
}
/* /*
* Can skip entering the index AM if we already got a tuple and it * If we already got a tuple and it must be unique, there's no need
* must be unique. Instead, we need a "short circuit" path that * to make the index AM look through any additional tuples. (This can
* just keeps track of logical scan position (before/on/after tuple). * save a useful amount of work in scenarios where there are many dead
* * tuples due to heavy update activity.)
* Note that we hold the pin on the single tuple's buffer throughout *
* the scan once we are in this state. * To do this we must keep track of the logical scan position
* (before/on/after tuple). Also, we have to be sure to release scan
* resources before returning NULL; if we fail to do so then a multi-index
* scan can easily run the system out of free buffers. We can release
* index-level resources fairly cheaply by calling index_rescan. This
* means there are two persistent states as far as the index AM is
* concerned: on-tuple and rescanned. If we are actually asked to
* re-fetch the single tuple, we have to go through a fresh indexscan
* startup, which penalizes that (infrequent) case.
*/ */
if (scan->keys_are_unique && scan->got_tuple) if (scan->keys_are_unique && scan->got_tuple)
{ {
int new_tuple_pos = scan->unique_tuple_pos;
if (ScanDirectionIsForward(direction)) if (ScanDirectionIsForward(direction))
{ {
if (scan->unique_tuple_pos <= 0) if (new_tuple_pos <= 0)
scan->unique_tuple_pos++; new_tuple_pos++;
} }
else if (ScanDirectionIsBackward(direction)) else
{ {
if (scan->unique_tuple_pos >= 0) if (new_tuple_pos >= 0)
scan->unique_tuple_pos--; new_tuple_pos--;
}
if (new_tuple_pos == 0)
{
/*
* We are moving onto the unique tuple from having been off it.
* We just fall through and let the index AM do the work. Note
* we should get the right answer regardless of scan direction.
*/
scan->unique_tuple_pos = 0; /* need to update position */
} }
if (scan->unique_tuple_pos == 0)
return heapTuple;
else else
{
/*
* Moving off the tuple; must do amrescan to release index-level
* pins before we return NULL. Since index_rescan will reset
* my state, must save and restore...
*/
int unique_tuple_mark = scan->unique_tuple_mark;
index_rescan(scan, NULL /* no change to key */);
scan->keys_are_unique = true;
scan->got_tuple = true;
scan->unique_tuple_pos = new_tuple_pos;
scan->unique_tuple_mark = unique_tuple_mark;
return NULL; return NULL;
} }
/* Release any previously held pin */
if (BufferIsValid(scan->xs_cbuf))
{
ReleaseBuffer(scan->xs_cbuf);
scan->xs_cbuf = InvalidBuffer;
} }
/* just make sure this is false... */ /* just make sure this is false... */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment