summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2003-03-24 21:42:33 +0000
committerTom Lane2003-03-24 21:42:33 +0000
commitfddc2d94cea0b60a704020715e5e6cc15548a412 (patch)
tree1f030e7ecee2a83f4256fbc872b7e732fbc7795e
parent346182ca9223954b6fe1ac3d1f680dfae59e0473 (diff)
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.
-rw-r--r--src/backend/access/index/genam.c4
-rw-r--r--src/backend/access/index/indexam.c75
2 files changed, 56 insertions, 23 deletions
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 18eee722d3b..a99b02c3c60 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -8,7 +8,7 @@
*
*
* 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
* many of the old access method routines have been turned into
@@ -91,7 +91,7 @@ RelationGetIndexScan(Relation indexRelation,
scan->kill_prior_tuple = false;
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->opaque = NULL;
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 2f0c6aac529..90e9af63c28 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -8,7 +8,7 @@
*
*
* 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
* index_open - open an index relation by relation OID
@@ -311,7 +311,7 @@ index_rescan(IndexScanDesc scan, ScanKey key)
GET_SCAN_PROCEDURE(rescan, amrescan);
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->unique_tuple_pos = 0;
scan->unique_tuple_mark = 0;
@@ -413,37 +413,70 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
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
- * must be unique. Instead, we need a "short circuit" path that
- * just keeps track of logical scan position (before/on/after tuple).
+ * If we already got a tuple and it must be unique, there's no need
+ * to make the index AM look through any additional tuples. (This can
+ * 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)
{
+ int new_tuple_pos = scan->unique_tuple_pos;
+
if (ScanDirectionIsForward(direction))
{
- if (scan->unique_tuple_pos <= 0)
- scan->unique_tuple_pos++;
+ if (new_tuple_pos <= 0)
+ new_tuple_pos++;
+ }
+ else
+ {
+ if (new_tuple_pos >= 0)
+ new_tuple_pos--;
}
- else if (ScanDirectionIsBackward(direction))
+ if (new_tuple_pos == 0)
{
- if (scan->unique_tuple_pos >= 0)
- scan->unique_tuple_pos--;
+ /*
+ * 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
- return NULL;
- }
+ {
+ /*
+ * 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;
- /* Release any previously held pin */
- if (BufferIsValid(scan->xs_cbuf))
- {
- ReleaseBuffer(scan->xs_cbuf);
- scan->xs_cbuf = InvalidBuffer;
+ 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;
+ }
}
/* just make sure this is false... */