Skip to content

Commit fddc2d9

Browse files
committed
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.
1 parent 346182c commit fddc2d9

File tree

2 files changed

+56
-23
lines changed

2 files changed

+56
-23
lines changed

src/backend/access/index/genam.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.37 2003/01/08 19:41:40 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.38 2003/03/24 21:42:33 tgl Exp $
1212
*
1313
* NOTES
1414
* many of the old access method routines have been turned into
@@ -91,7 +91,7 @@ RelationGetIndexScan(Relation indexRelation,
9191

9292
scan->kill_prior_tuple = false;
9393
scan->ignore_killed_tuples = true; /* default setting */
94-
scan->keys_are_unique = false; /* may be set by amrescan */
94+
scan->keys_are_unique = false; /* may be set by index AM */
9595
scan->got_tuple = false;
9696

9797
scan->opaque = NULL;

src/backend/access/index/indexam.c

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.65 2003/03/23 23:01:03 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.66 2003/03/24 21:42:33 tgl Exp $
1212
*
1313
* INTERFACE ROUTINES
1414
* index_open - open an index relation by relation OID
@@ -311,7 +311,7 @@ index_rescan(IndexScanDesc scan, ScanKey key)
311311
GET_SCAN_PROCEDURE(rescan, amrescan);
312312

313313
scan->kill_prior_tuple = false; /* for safety */
314-
scan->keys_are_unique = false; /* may be set by amrescan */
314+
scan->keys_are_unique = false; /* may be set by index AM */
315315
scan->got_tuple = false;
316316
scan->unique_tuple_pos = 0;
317317
scan->unique_tuple_mark = 0;
@@ -413,37 +413,70 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
413413

414414
SCAN_CHECKS;
415415

416+
/* Release any previously held pin */
417+
if (BufferIsValid(scan->xs_cbuf))
418+
{
419+
ReleaseBuffer(scan->xs_cbuf);
420+
scan->xs_cbuf = InvalidBuffer;
421+
}
422+
416423
/*
417-
* Can skip entering the index AM if we already got a tuple and it
418-
* must be unique. Instead, we need a "short circuit" path that
419-
* just keeps track of logical scan position (before/on/after tuple).
424+
* If we already got a tuple and it must be unique, there's no need
425+
* to make the index AM look through any additional tuples. (This can
426+
* save a useful amount of work in scenarios where there are many dead
427+
* tuples due to heavy update activity.)
420428
*
421-
* Note that we hold the pin on the single tuple's buffer throughout
422-
* the scan once we are in this state.
429+
* To do this we must keep track of the logical scan position
430+
* (before/on/after tuple). Also, we have to be sure to release scan
431+
* resources before returning NULL; if we fail to do so then a multi-index
432+
* scan can easily run the system out of free buffers. We can release
433+
* index-level resources fairly cheaply by calling index_rescan. This
434+
* means there are two persistent states as far as the index AM is
435+
* concerned: on-tuple and rescanned. If we are actually asked to
436+
* re-fetch the single tuple, we have to go through a fresh indexscan
437+
* startup, which penalizes that (infrequent) case.
423438
*/
424439
if (scan->keys_are_unique && scan->got_tuple)
425440
{
441+
int new_tuple_pos = scan->unique_tuple_pos;
442+
426443
if (ScanDirectionIsForward(direction))
427444
{
428-
if (scan->unique_tuple_pos <= 0)
429-
scan->unique_tuple_pos++;
445+
if (new_tuple_pos <= 0)
446+
new_tuple_pos++;
447+
}
448+
else
449+
{
450+
if (new_tuple_pos >= 0)
451+
new_tuple_pos--;
430452
}
431-
else if (ScanDirectionIsBackward(direction))
453+
if (new_tuple_pos == 0)
432454
{
433-
if (scan->unique_tuple_pos >= 0)
434-
scan->unique_tuple_pos--;
455+
/*
456+
* We are moving onto the unique tuple from having been off it.
457+
* We just fall through and let the index AM do the work. Note
458+
* we should get the right answer regardless of scan direction.
459+
*/
460+
scan->unique_tuple_pos = 0; /* need to update position */
435461
}
436-
if (scan->unique_tuple_pos == 0)
437-
return heapTuple;
438462
else
439-
return NULL;
440-
}
463+
{
464+
/*
465+
* Moving off the tuple; must do amrescan to release index-level
466+
* pins before we return NULL. Since index_rescan will reset
467+
* my state, must save and restore...
468+
*/
469+
int unique_tuple_mark = scan->unique_tuple_mark;
441470

442-
/* Release any previously held pin */
443-
if (BufferIsValid(scan->xs_cbuf))
444-
{
445-
ReleaseBuffer(scan->xs_cbuf);
446-
scan->xs_cbuf = InvalidBuffer;
471+
index_rescan(scan, NULL /* no change to key */);
472+
473+
scan->keys_are_unique = true;
474+
scan->got_tuple = true;
475+
scan->unique_tuple_pos = new_tuple_pos;
476+
scan->unique_tuple_mark = unique_tuple_mark;
477+
478+
return NULL;
479+
}
447480
}
448481

449482
/* just make sure this is false... */

0 commit comments

Comments
 (0)