Fix for bug #866. 7.3 contains new logic for avoiding redundant calls to
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 8 Jan 2003 19:41:40 +0000 (19:41 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 8 Jan 2003 19:41:40 +0000 (19:41 +0000)
the index AM when we know we are fetching a unique row.  However, this
logic did not consider the possibility that it would be asked to fetch
backwards.  Also fix mark/restore to work correctly in this scenario.

src/backend/access/index/genam.c
src/backend/access/index/indexam.c
src/include/access/relscan.h

index 2a21e2962e9298de34b994e18e03c6628d2a130d..18eee722d3b8b33caf54b6ecea24ffb54abadc9f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.36 2002/09/04 20:31:09 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.37 2003/01/08 19:41:40 tgl Exp $
  *
  * NOTES
  *       many of the old access method routines have been turned into
@@ -107,6 +107,9 @@ RelationGetIndexScan(Relation indexRelation,
        /* mark cached function lookup data invalid; it will be set later */
        scan->fn_getnext.fn_oid = InvalidOid;
 
+       scan->unique_tuple_pos = 0;
+       scan->unique_tuple_mark = 0;
+
        pgstat_initstats(&scan->xs_pgstat_info, indexRelation);
 
        /*
index d2e11d2a8d0516b384da5ed2dd61e778bb0d7195..258eb546a4911d572888e4ced35f6be0cb879cdd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.62 2002/09/04 20:31:09 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.63 2003/01/08 19:41:40 tgl Exp $
  *
  * INTERFACE ROUTINES
  *             index_open              - open an index relation by relation OID
@@ -308,6 +308,8 @@ index_rescan(IndexScanDesc scan, ScanKey key)
        scan->kill_prior_tuple = false;         /* for safety */
        scan->keys_are_unique = false;          /* may be set by amrescan */
        scan->got_tuple = false;
+       scan->unique_tuple_pos = 0;
+       scan->unique_tuple_mark = 0;
 
        OidFunctionCall2(procedure,
                                         PointerGetDatum(scan),
@@ -360,6 +362,8 @@ index_markpos(IndexScanDesc scan)
        SCAN_CHECKS;
        GET_SCAN_PROCEDURE(markpos, ammarkpos);
 
+       scan->unique_tuple_mark = scan->unique_tuple_pos;
+
        OidFunctionCall1(procedure, PointerGetDatum(scan));
 }
 
@@ -376,7 +380,13 @@ index_restrpos(IndexScanDesc scan)
        GET_SCAN_PROCEDURE(restrpos, amrestrpos);
 
        scan->kill_prior_tuple = false;         /* for safety */
-       scan->got_tuple = false;
+
+       /*
+        * We do not reset got_tuple; so if the scan is actually being
+        * short-circuited by index_getnext, the effective position restoration
+        * is done by restoring unique_tuple_pos.
+        */
+       scan->unique_tuple_pos = scan->unique_tuple_mark;
 
        OidFunctionCall1(procedure, PointerGetDatum(scan));
 }
@@ -398,6 +408,32 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
 
        SCAN_CHECKS;
 
+       /*
+        * 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).
+        *
+        * Note that we hold the pin on the single tuple's buffer throughout
+        * the scan once we are in this state.
+        */
+       if (scan->keys_are_unique && scan->got_tuple)
+       {
+               if (ScanDirectionIsForward(direction))
+               {
+                       if (scan->unique_tuple_pos <= 0)
+                               scan->unique_tuple_pos++;
+               }
+               else if (ScanDirectionIsBackward(direction))
+               {
+                       if (scan->unique_tuple_pos >= 0)
+                               scan->unique_tuple_pos--;
+               }
+               if (scan->unique_tuple_pos == 0)
+                       return heapTuple;
+               else
+                       return NULL;
+       }
+
        /* Release any previously held pin */
        if (BufferIsValid(scan->xs_cbuf))
        {
@@ -408,13 +444,6 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
        /* just make sure this is false... */
        scan->kill_prior_tuple = false;
 
-       /*
-        * Can skip entering the index AM if we already got a tuple and it
-        * must be unique.
-        */
-       if (scan->keys_are_unique && scan->got_tuple)
-               return NULL;
-
        for (;;)
        {
                bool            found;
@@ -475,6 +504,12 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
        /* Success exit */
        scan->got_tuple = true;
 
+       /*
+        * If we just fetched a known-unique tuple, then subsequent calls will
+        * go through the short-circuit code above.  unique_tuple_pos has been
+        * initialized to 0, which is the correct state ("on row").
+        */
+
        pgstat_count_index_getnext(&scan->xs_pgstat_info);
 
        return heapTuple;
index 06b5701cf842ee118549a2603eb391f00d6dc126..898afa64bb5aacdeda9a3f62e5160759892bc2c3 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relscan.h,v 1.29 2002/09/04 20:31:37 momjian Exp $
+ * $Id: relscan.h,v 1.30 2003/01/08 19:41:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,6 +70,15 @@ typedef struct IndexScanDescData
 
        FmgrInfo        fn_getnext;             /* cached lookup info for AM's getnext fn */
 
+       /*
+        * If keys_are_unique and got_tuple are both true, we stop calling the
+        * index AM; it is then necessary for index_getnext to keep track of
+        * the logical scan position for itself.  It does that using
+        * unique_tuple_pos: -1 = before row, 0 = on row, +1 = after row.
+        */
+       int                     unique_tuple_pos; /* logical position */
+       int                     unique_tuple_mark; /* logical marked position */
+
        PgStat_Info xs_pgstat_info; /* statistics collector hook */
 } IndexScanDescData;