diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c index 2a21e2962e..18eee722d3 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.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); /* diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index d2e11d2a8d..258eb546a4 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.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; diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h index 06b5701cf8..898afa64bb 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -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;