Modify keys_are_unique optimization to release buffer pins before it
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 24 Mar 2003 21:42:33 +0000 (21:42 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 24 Mar 2003 21:42:33 +0000 (21:42 +0000)
returns NULL.  This avoids out-of-buffers failures during many-way
indexscans, as in Shraibman's complaint of 21-Mar.

src/backend/access/index/genam.c
src/backend/access/index/indexam.c

index 18eee722d3b8b33caf54b6ecea24ffb54abadc9f..a99b02c3c608ac77c054ed2d6260b218a52677d2 100644 (file)
@@ -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;
index 2f0c6aac529f585af8a649903f4e18e04b0aeb72..90e9af63c281c65d1b33e6be7128979533ef0519 100644 (file)
@@ -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... */