]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
New ROWIDs are numbered sequentially. (CVS 383)
authordrh <drh@noemail.net>
Tue, 19 Feb 2002 22:42:05 +0000 (22:42 +0000)
committerdrh <drh@noemail.net>
Tue, 19 Feb 2002 22:42:05 +0000 (22:42 +0000)
FossilOrigin-Name: 1686196a8aea326f616bc8205df99cd84d955ec4

13 files changed:
manifest
manifest.uuid
src/main.c
src/select.c
src/sqliteInt.h
src/vdbe.c
test/intpkey.test
test/minmax.test [new file with mode: 0644]
test/rowid.test
www/c_interface.tcl
www/changes.tcl
www/faq.tcl
www/lang.tcl

index f80e6ba02c800b9118c56932768fdea72d727c87..9bc80123df4520cb7d09fd569efaa10f01a3faa3 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Optimize\ssimple\smin()\sand\smax()\squeries.\s(CVS\s382)
-D 2002-02-19T15:00:07
+C New\sROWIDs\sare\snumbered\ssequentially.\s(CVS\s383)
+D 2002-02-19T22:42:05
 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
 F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -27,7 +27,7 @@ F src/expr.c 7aff65ea0732b07d36925087ad611019103ad69a
 F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
 F src/hash.h d1ce47900c7325af5e41c4feb4855c4bf2b841e7
 F src/insert.c eae5aa2e9ac68c4d465e71b2ad34bcbb882979cf
-F src/main.c 669cfd9a8c40f6c9ff2d478e1695d1ea1fdafad1
+F src/main.c fada622b468c54fb211372f38a27ee636915e2ee
 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
 F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea
 F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6
@@ -36,11 +36,11 @@ F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283
 F src/parse.y b82278917959eefd05bd08c90e07a4fa5917ea51
 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 6dadbd3ba1e86334c9af321b48f6e3df1992f602
+F src/select.c 282f37b2fdb9367f0230811e1cf7bba48665fb72
 F src/shell.c c102dfe388c7618a668c944ff157c49cb48f28e3
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c
-F src/sqliteInt.h 75cf79d3d0a0ea40f43bbc2d74a4aefb2ea37ccf
+F src/sqliteInt.h e19efd5b1c6accfb3418cbb5c0e8c143fc1d18d7
 F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a
 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
 F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
@@ -50,7 +50,7 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
 F src/tokenize.c 9e98f94469694a763992860596137e78dbae0cc0
 F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103
 F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6
-F src/vdbe.c 2b03a7d39f05321e5570373cb2f3734c3cf86b70
+F src/vdbe.c 18b6e9ed3bcdd76ae74d60a977a6d8a3a1b0f797
 F src/vdbe.h b4d35e159fbb80a74728b4a96e5b789fffce6f57
 F src/where.c f79bc3179379b46b131a67ab10713779368dceee
 F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
@@ -67,19 +67,20 @@ F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
 F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
 F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9
 F test/insert2.test 65c2b2aae0bae7a7bbe500f77981cd916b81e91b
-F test/intpkey.test ce3de8326082929667cf356855426519cfe2f5c7
+F test/intpkey.test 101ec266222e88b24e6f1e204b9b6873404cd4dc
 F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
 F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a
 F test/lock.test 19593689260c419efe7ced55b1418653a4b7bcd1
 F test/main.test 1626345b5f630c5398eede500d9354813b76b0fd
 F test/malloc.test 70fdd0812e2a57eb746aaf015350f58bb8eee0b1
+F test/minmax.test fb6ab400271ae1f5bc88617c2882f2f081ea8e6d
 F test/misc1.test 7fd54d33547177da86e39e13e9608c5112681831
 F test/notnull.test b1f3e42fc475b0b5827b27b2e9b562081995ff30
 F test/pager.test b0c0d00cd5dce0ce21f16926956b195c0ab5044c
 F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
 F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05
 F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
-F test/rowid.test cb023f2df36886e1fc4cdfd32eaba05cf7db4331
+F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274
 F test/select1.test fd2936aa907559153c78edf2740ea65eb9a614f5
 F test/select2.test ed2c1882857106b85478f54f67000e14966be4c4
 F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18
@@ -108,23 +109,23 @@ F tool/report1.txt 9eae07f26a8fc53889b45fc833a66a33daa22816
 F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
 F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
 F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
-F www/c_interface.tcl 82a026b1681757f13b3f62e035f3a31407c1d353
-F www/changes.tcl 7f9375639b6fefe75670e1881a2ccdc144c6230a
+F www/c_interface.tcl 63efc40f09e2f0d8fea43d174103248b160fdf0e
+F www/changes.tcl e9ac5ffc030ad355fb5973bc2c68e622c3bc9ae2
 F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
 F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
 F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0
 F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
-F www/faq.tcl 32cbc134879871604070d4cc3a32e73fb22a35f9
+F www/faq.tcl c6d1d6d69a9083734ee73d1b5ee4253ae8f10074
 F www/formatchng.tcl 2d9a35c787823b48d72a5c64bb5414a43e26d5ad
 F www/index.tcl eacd99bcc3132d6e6b74a51422d415cc0bf7bfdf
-F www/lang.tcl abf67afeb6527eb20b9088813160877fb413e933
+F www/lang.tcl 01e47b43cb81cd24bd2918a2e1bd31a959097279
 F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc
 F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc
 F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P c6e9048e66c8d8e2d5f6c62aa724eef3e9d9f572
-R 41f437c3e65a91e5a2a52b6609362036
+P cc5abfe392bdb8c3ed00e0610bc2b41851bfc9d7
+R d110cdfb274a665cf18a7abdadd913ff
 U drh
-Z ee034e966e6d17b9655294654ff0765f
+Z 2f4375a6c2ee111a8d6db399f5e3350c
index a91c486c4e6db1e1916c0c0b489c33c9a655d9a3..bf5e71a8c526db56d13d25632024911be20d84a7 100644 (file)
@@ -1 +1 @@
-cc5abfe392bdb8c3ed00e0610bc2b41851bfc9d7
\ No newline at end of file
+1686196a8aea326f616bc8205df99cd84d955ec4
\ No newline at end of file
index 61450217e09f043a5d701bebd9012746e37230d8..553f7468343cf3338adc8cecc2baa2e4a79d7fa5 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.59 2002/02/18 18:30:33 drh Exp $
+** $Id: main.c,v 1.60 2002/02/19 22:42:05 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -275,8 +275,8 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
   sqliteHashInit(&db->idxHash, SQLITE_HASH_STRING, 0);
   sqliteHashInit(&db->tblDrop, SQLITE_HASH_POINTER, 0);
   sqliteHashInit(&db->idxDrop, SQLITE_HASH_POINTER, 0);
-  db->nextRowid = sqliteRandomInteger();
   db->onError = OE_Default;
+  db->priorNewRowid = 0;
   
   /* Open the backend database driver */
   rc = sqliteBtreeOpen(zFilename, mode, MAX_PAGES, &db->pBe);
index 036c35ec5482ff960b5aa8ade3b5546f8442a219..ef6cd216e82700eaa8f17029e9ac99550e9efa95 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.62 2002/02/19 15:00:08 drh Exp $
+** $Id: select.c,v 1.63 2002/02/19 22:42:05 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -783,22 +783,28 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
 
   /* Begin generating code
   */
-  base = pParse->nTab;
-  eList.nExpr = 1;
-  memset(&eListItem, 0, sizeof(eListItem));
-  eList.a = &eListItem;
-  eList.a[0].pExpr = pExpr;
+  if( !pParse->schemaVerified && (pParse->db->flags & SQLITE_InTrans)==0 ){
+    sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
+    pParse->schemaVerified = 1;
+  }
   openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
+  base = pParse->nTab;
   sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
+  sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
   if( pIdx==0 ){
     sqliteVdbeAddOp(v, seekOp, base, 0);
   }else{
     sqliteVdbeAddOp(v, openOp, base+1, pIdx->tnum);
+    sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
     sqliteVdbeAddOp(v, seekOp, base+1, 0);
     sqliteVdbeAddOp(v, OP_IdxRecno, base+1, 0);
     sqliteVdbeAddOp(v, OP_Close, base+1, 0);
     sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
   }
+  eList.nExpr = 1;
+  memset(&eListItem, 0, sizeof(eListItem));
+  eList.a = &eListItem;
+  eList.a[0].pExpr = pExpr;
   cont = sqliteVdbeMakeLabel(v);
   selectInnerLoop(pParse, &eList, base, 1, 0, -1, eDest, iParm, cont, cont);
   sqliteVdbeResolveLabel(v, cont);
@@ -1011,6 +1017,7 @@ int sqliteSelect(
   ** in the result set.
   */
   if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){
+    rc = 0;
     goto select_end;
   }
 
index 1f3db95bf5b7b445b6aaaf2ee77a95b20cfc2c6a..302a2b4949c7da54abd27ffa260f1336306c0cc7 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.87 2002/02/18 18:30:33 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.88 2002/02/19 22:42:05 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -182,7 +182,7 @@ struct sqlite {
   Hash tblDrop;                 /* Uncommitted DROP TABLEs */
   Hash idxDrop;                 /* Uncommitted DROP INDEXs */
   int lastRowid;                /* ROWID of most recent insert */
-  int nextRowid;                /* Next generated rowID */
+  int priorNewRowid;            /* Last randomly generated ROWID */
   int onError;                  /* Default conflict algorithm */
 };
 
index 788c5a9d22c4d862ef9a068364b4ec200cf6f974..f9f08850462496dfcff14d452cde565b58fc92b8 100644 (file)
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.119 2002/02/19 15:00:08 drh Exp $
+** $Id: vdbe.c,v 1.120 2002/02/19 22:42:05 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -71,6 +71,7 @@ struct Cursor {
   Bool recnoIsValid;    /* True if lastRecno is valid */
   Bool keyAsData;       /* The OP_Column command works on key instead of data */
   Bool atFirst;         /* True if pointing to first entry */
+  Bool useRandomRowid;  /* Generate new record numbers semi-randomly */
   Btree *pBt;           /* Separate file holding temporary table */
 };
 typedef struct Cursor Cursor;
@@ -2913,10 +2914,18 @@ case OP_NewRecno: {
   if( VERIFY( i<0 || i>=p->nCursor || ) (pC = &p->aCsr[i])->pCursor==0 ){
     v = 0;
   }else{
-    /* A probablistic algorithm is used to locate an unused rowid.
-    ** We select a rowid at random and see if it exists in the table.
-    ** If it does not exist, we have succeeded.  If the random rowid
-    ** does exist, we select a new one and try again, up to 1000 times.
+    /* The next rowid or record number (different terms for the same
+    ** thing) is obtained in a two-step algorithm.
+    **
+    ** First we attempt to find the largest existing rowid and add one
+    ** to that.  But if the largest existing rowid is already the maximum
+    ** positive integer, we have to fall through to the second
+    ** probabilistic algorithm
+    **
+    ** The second algorithm is to select a rowid at random and see if
+    ** it already exists in the table.  If it does not exist, we have
+    ** succeeded.  If the random rowid does exist, we select a new one
+    ** and try again, up to 1000 times.
     **
     ** For a table with less than 2 billion entries, the probability
     ** of not finding a unused rowid is about 1.0e-300.  This is a 
@@ -2932,28 +2941,46 @@ case OP_NewRecno: {
     ** random number generator based on the RC4 algorithm.
     **
     ** To promote locality of reference for repetitive inserts, the
-    ** first few attempts at chosing a rowid pick values just a little
+    ** first few attempts at chosing a random rowid pick values just a little
     ** larger than the previous rowid.  This has been shown experimentally
     ** to double the speed of the COPY operation.
     */
     int res, rx, cnt, x;
     cnt = 0;
-    v = db->nextRowid;
-    do{
-      if( cnt>5 ){
-        v = sqliteRandomInteger();
+    if( !pC->useRandomRowid ){
+      rx = sqliteBtreeLast(pC->pCursor, &res);
+      if( res ){
+        v = 1;
       }else{
-        v += sqliteRandomByte() + 1;
+        sqliteBtreeKey(pC->pCursor, 0, sizeof(v), (void*)&v);
+        v = keyToInt(v);
+        if( v==0x7fffffff ){
+          pC->useRandomRowid = 1;
+        }else{
+          v++;
+        }
+      }
+    }
+    if( pC->useRandomRowid ){
+      v = db->priorNewRowid;
+      cnt = 0;
+      do{
+        if( v==0 || cnt>2 ){
+          v = sqliteRandomInteger();
+          if( cnt<5 ) v &= 0xffffff;
+        }else{
+          v += sqliteRandomByte() + 1;
+        }
+        if( v==0 ) continue;
+        x = intToKey(v);
+        rx = sqliteBtreeMoveto(pC->pCursor, &x, sizeof(int), &res);
+        cnt++;
+      }while( cnt<1000 && rx==SQLITE_OK && res==0 );
+      db->priorNewRowid = v;
+      if( rx==SQLITE_OK && res==0 ){
+        rc = SQLITE_FULL;
+        goto abort_due_to_error;
       }
-      if( v==0 ) continue;
-      x = intToKey(v);
-      rx = sqliteBtreeMoveto(pC->pCursor, &x, sizeof(int), &res);
-      cnt++;
-    }while( cnt<1000 && rx==SQLITE_OK && res==0 );
-    db->nextRowid = v;
-    if( rx==SQLITE_OK && res==0 ){
-      rc = SQLITE_FULL;
-      goto abort_due_to_error;
     }
   }
   VERIFY( NeedStack(p, p->tos+1); )
index d28efd4726fa033501cb8f7548b254e67ba03c6f..db73666af69cc5fc20054a8ff22c989c1b465e5f 100644 (file)
@@ -13,7 +13,7 @@
 # This file implements tests for the special processing associated
 # with INTEGER PRIMARY KEY columns.
 #
-# $Id: intpkey.test,v 1.7 2002/01/29 23:07:02 drh Exp $
+# $Id: intpkey.test,v 1.8 2002/02/19 22:42:06 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -111,19 +111,19 @@ do_test intpkey-1.10 {
 #
 do_test intpkey-1.11 {
   execsql {
-    UPDATE t1 SET a=7 WHERE b='one';
+    UPDATE t1 SET a=4 WHERE b='one';
     SELECT * FROM t1;
   }
-} {5 hello world 6 second entry 7 one two}
+} {4 one two 5 hello world 6 second entry}
 
 # Make sure SELECT statements are able to use the primary key column
 # as an index.
 #
 do_test intpkey-1.12 {
   execsql {
-    SELECT * FROM t1 WHERE a==7;
+    SELECT * FROM t1 WHERE a==4;
   }
-} {7 one two}
+} {4 one two}
 
 # Try to insert a non-integer value into the primary key field.  This
 # should result in a data type mismatch.
@@ -148,7 +148,7 @@ do_test intpkey-1.15 {
 } {0 {}}
 do_test intpkey-1.16 {
   execsql {SELECT * FROM t1}
-} {-3 y z 5 hello world 6 second entry 7 one two}
+} {-3 y z 4 one two 5 hello world 6 second entry}
 
 #### INDICES
 # Check to make sure indices work correctly with integer primary keys
@@ -190,35 +190,35 @@ do_test intpkey-2.3 {
   execsql {
     SELECT rowid, * FROM t1;
   }
-} {5 5 hello world 6 6 second entry 7 7 one two 8 8 y z}
+} {4 4 one two 5 5 hello world 6 6 second entry 8 8 y z}
 do_test intpkey-2.4 {
   execsql {
     SELECT rowid, * FROM t1 WHERE b<'second'
   }
-} {5 5 hello world 7 7 one two}
+} {5 5 hello world 4 4 one two}
 do_test intpkey-2.4.1 {
   execsql {
     SELECT rowid, * FROM t1 WHERE 'second'>b
   }
-} {5 5 hello world 7 7 one two}
+} {5 5 hello world 4 4 one two}
 do_test intpkey-2.4.2 {
   execsql {
     SELECT rowid, * FROM t1 WHERE 8>rowid AND 'second'>b
   }
-} {5 5 hello world 7 7 one two}
+} {4 4 one two 5 5 hello world}
 do_test intpkey-2.4.3 {
   execsql {
     SELECT rowid, * FROM t1 WHERE 8>rowid AND 'second'>b AND 0<rowid
   }
-} {5 5 hello world 7 7 one two}
+} {4 4 one two 5 5 hello world}
 do_test intpkey-2.5 {
   execsql {
     SELECT rowid, * FROM t1 WHERE b>'a'
   }
-} {5 5 hello world 7 7 one two 6 6 second entry 8 8 y z}
+} {5 5 hello world 4 4 one two 6 6 second entry 8 8 y z}
 do_test intpkey-2.6 {
   execsql {
-    DELETE FROM t1 WHERE rowid=7;
+    DELETE FROM t1 WHERE rowid=4;
     SELECT * FROM t1 WHERE b>'a';
   }
 } {5 hello world 6 second entry 8 y z}
diff --git a/test/minmax.test b/test/minmax.test
new file mode 100644 (file)
index 0000000..3639e79
--- /dev/null
@@ -0,0 +1,126 @@
+# 2001 September 15
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this file is testing SELECT statements that contain
+# aggregate min() and max() functions and which are handled as
+# as a special case.
+#
+# $Id: minmax.test,v 1.1 2002/02/19 22:42:06 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test minmax-1.0 {
+  execsql {
+    BEGIN;
+    CREATE TABLE t1(x, y);
+    INSERT INTO t1 VALUES(1,1);
+    INSERT INTO t1 VALUES(2,2);
+    INSERT INTO t1 VALUES(3,2);
+    INSERT INTO t1 VALUES(4,3);
+    INSERT INTO t1 VALUES(5,3);
+    INSERT INTO t1 VALUES(6,3);
+    INSERT INTO t1 VALUES(7,3);
+    INSERT INTO t1 VALUES(8,4);
+    INSERT INTO t1 VALUES(9,4);
+    INSERT INTO t1 VALUES(10,4);
+    INSERT INTO t1 VALUES(11,4);
+    INSERT INTO t1 VALUES(12,4);
+    INSERT INTO t1 VALUES(13,4);
+    INSERT INTO t1 VALUES(14,4);
+    INSERT INTO t1 VALUES(15,4);
+    INSERT INTO t1 VALUES(16,5);
+    INSERT INTO t1 VALUES(17,5);
+    INSERT INTO t1 VALUES(18,5);
+    INSERT INTO t1 VALUES(19,5);
+    INSERT INTO t1 VALUES(20,5);
+    COMMIT;
+    SELECT DISTINCT y FROM t1 ORDER BY y;
+  }
+} {1 2 3 4 5}
+
+do_test minmax-1.1 {
+  set sqlite_search_count 0
+  execsql {SELECT min(x) FROM t1}
+} {1}
+do_test minmax-1.2 {
+  set sqlite_search_count
+} {19}
+do_test minmax-1.3 {
+  set sqlite_search_count 0
+  execsql {SELECT max(x) FROM t1}
+} {20}
+do_test minmax-1.4 {
+  set sqlite_search_count
+} {19}
+do_test minmax-1.5 {
+  execsql {CREATE INDEX t1i1 ON t1(x)}
+  set sqlite_search_count 0
+  execsql {SELECT min(x) FROM t1}
+} {1}
+do_test minmax-1.6 {
+  set sqlite_search_count
+} {1}
+do_test minmax-1.7 {
+  set sqlite_search_count 0
+  execsql {SELECT max(x) FROM t1}
+} {20}
+do_test minmax-1.8 {
+  set sqlite_search_count
+} {1}
+do_test minmax-1.9 {
+  set sqlite_search_count 0
+  execsql {SELECT max(y) FROM t1}
+} {5}
+do_test minmax-1.10 {
+  set sqlite_search_count
+} {19}
+
+do_test minmax-2.0 {
+  execsql {
+    CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+    INSERT INTO t2 SELECT * FROM t1;
+  }
+  set sqlite_search_count 0
+  execsql {SELECT min(a) FROM t2}
+} {1}
+do_test minmax-2.1 {
+  set sqlite_search_count
+} {0}
+do_test minmax-2.2 {
+  set sqlite_search_count 0
+  execsql {SELECT max(a) FROM t2}
+} {20}
+do_test minmax-2.3 {
+  set sqlite_search_count
+} {0}
+
+do_test minmax-3.0 {
+  execsql {INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)}
+  set sqlite_search_count 0
+  execsql {SELECT max(a) FROM t2}
+} {21}
+do_test minmax-3.1 {
+  set sqlite_search_count
+} {0}
+do_test minmax-3.2 {
+  execsql {INSERT INTO t2 VALUES((SELECT max(a) FROM t2)+1,999)}
+  set sqlite_search_count 0
+  execsql {
+    SELECT b FROM t2 WHERE a=(SELECT max(a) FROM t2)
+  }
+} {999}
+do_test minmax-3.3 {
+  set sqlite_search_count
+} {0}
+
+
+finish_test
index edb9da22099c4f2e87a106fb8d7fe30a86512d26..35b820e49df9af6fc5b7853fdab2d8439128b670 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this file is testing the magic ROWID column that is
 # found on all tables.
 #
-# $Id: rowid.test,v 1.7 2002/01/04 03:09:30 drh Exp $
+# $Id: rowid.test,v 1.8 2002/02/19 22:42:06 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -155,18 +155,18 @@ do_test rowid-2.12 {
 do_test rowid-3.1 {
   execsql {
     CREATE TABLE t2(rowid int, x int, y int);
-    INSERT INTO t2 VALUES(1,2,3);
+    INSERT INTO t2 VALUES(0,2,3);
     INSERT INTO t2 VALUES(4,5,6);
     INSERT INTO t2 VALUES(7,8,9);
     SELECT * FROM t2 ORDER BY x;
   }
-} {1 2 3 4 5 6 7 8 9}
+} {0 2 3 4 5 6 7 8 9}
 do_test rowid-3.2 {
   execsql {SELECT * FROM t2 ORDER BY rowid}
-} {1 2 3 4 5 6 7 8 9}
+} {0 2 3 4 5 6 7 8 9}
 do_test rowid-3.3 {
   execsql {SELECT rowid, x, y FROM t2 ORDER BY rowid}
-} {1 2 3 4 5 6 7 8 9}
+} {0 2 3 4 5 6 7 8 9}
 do_test rowid-3.4 {
   set r1 [execsql {SELECT _rowid_, rowid FROM t2 ORDER BY rowid}]
   foreach {a b c d e f} $r1 {}
@@ -271,8 +271,9 @@ do_test rowid-6.1 {
   }
 } {1 2 3 4 5 6 7 8}
 do_test rowid-6.2 {
-  for {set ::norow 1} {[execsql {SELECT x FROM t1 WHERE rowid=10}]!=""} \
-    {incr ::norow} {}
+  for {set ::norow 1} {1} {incr ::norow} {
+    if {[execsql "SELECT x FROM t1 WHERE rowid=$::norow"]==""}  break
+  }
   execsql [subst {
     DELETE FROM t1 WHERE rowid=$::norow
   }]
@@ -283,5 +284,73 @@ do_test rowid-6.3 {
   }
 } {1 2 3 4 5 6 7 8}
 
+# Beginning with version 2.3.4, SQLite computes rowids of new rows by
+# finding the maximum current rowid and adding one.  It falls back to
+# the old random algorithm if the maximum rowid is the largest integer.
+# The following tests are for this new behavior.
+#
+do_test rowid-7.0 {
+  execsql {
+    DELETE FROM t1;
+    DROP TABLE t2;
+    DROP INDEX idxt1;
+    INSERT INTO t1 VALUES(1,2);
+    SELECT rowid, * FROM t1;
+  }
+} {1 1 2}
+do_test rowid-7.1 {
+  execsql {
+    INSERT INTO t1 VALUES(99,100);
+    SELECT rowid,* FROM t1
+  }
+} {1 1 2 2 99 100}
+do_test rowid-7.2 {
+  execsql {
+    CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+    INSERT INTO t2(b) VALUES(55);
+    SELECT * FROM t2;
+  }
+} {1 55}
+do_test rowid-7.3 {
+  execsql {
+    INSERT INTO t2(b) VALUES(66);
+    SELECT * FROM t2;
+  }
+} {1 55 2 66}
+do_test rowid-7.4 {
+  execsql {
+    INSERT INTO t2(a,b) VALUES(1000000,77);
+    INSERT INTO t2(b) VALUES(88);
+    SELECT * FROM t2;
+  }
+} {1 55 2 66 1000000 77 1000001 88}
+do_test rowid-7.5 {
+  execsql {
+    INSERT INTO t2(a,b) VALUES(2147483647,99);
+    INSERT INTO t2(b) VALUES(11);
+    SELECT b FROM t2 ORDER BY b;
+  }
+} {11 55 66 77 88 99}
+do_test rowid-7.6 {
+  execsql {
+    SELECT b FROM t2 WHERE a NOT IN(1,2,1000000,1000001,2147483647);
+  }
+} {11}
+do_test rowid-7.7 {
+  execsql {
+    INSERT INTO t2(b) VALUES(22);
+    INSERT INTO t2(b) VALUES(33);
+    INSERT INTO t2(b) VALUES(44);
+    INSERT INTO t2(b) VALUES(55);
+    SELECT b FROM t2 WHERE a NOT IN(1,2,1000000,1000001,2147483647) ORDER BY b;
+  }
+} {11 22 33 44 55}
+do_test rowid-7.8 {
+  execsql {
+    DELETE FROM t2 WHERE a!=2;
+    INSERT INTO t2(b) VALUES(111);
+    SELECT * FROM t2;
+  }
+} {2 66 3 111}
 
 finish_test
index 59eb5150f8fe8b36a2e0cf28a029eeee4a22f61f..c062470b4f69312ac73fd118ad682ce945270c1a 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this Tcl script to generate the sqlite.html file.
 #
-set rcsid {$Id: c_interface.tcl,v 1.23 2002/01/16 21:00:28 drh Exp $}
+set rcsid {$Id: c_interface.tcl,v 1.24 2002/02/19 22:42:06 drh Exp $}
 
 puts {<html>
 <head>
@@ -378,7 +378,7 @@ header file that comes in the source tree.</p>
 <p>Every row of an SQLite table has a unique integer key.  If the
 table has a column labeled INTEGER PRIMARY KEY, then that column
 servers as the key.  If there is no INTEGER PRIMARY KEY column then
-the key is a random integer.  The key for a row can be accessed in
+the key is a unique integer.  The key for a row can be accessed in
 a SELECT statement or used in a WHERE or ORDER BY clause using any
 of the names "ROWID", "OID", or "_ROWID_".</p>
 
index 968294a2e538c8deae948ab5fbd722d7031d0998..10a59b90792a188abad1e5db2c9191d12e64637b 100644 (file)
@@ -20,6 +20,9 @@ proc chng {date desc} {
 chng {2002 Feb * (2.3.4)} {
 <li>Change the name of the sanity_check PRAGMA to <b>integrity_check</b>
     and make it available in all compiles.</li>
+<li>SELECT min() or max() of an indexed column with no WHERE or GROUP BY
+    clause is handled as a special case which avoids a complete table scan.</li>
+<li>Automatically generated ROWIDs are now sequential.</li>
 }
 
 chng {2002 Feb 18 (2.3.3)} {
index af12e01ad505a8900cafcb0a22fcc4e24e233c46..e7df250872e1f9cd3e29cb927edeac3ab052c402 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this script to generated a faq.html output file
 #
-set rcsid {$Id: faq.tcl,v 1.7 2002/01/22 03:13:43 drh Exp $}
+set rcsid {$Id: faq.tcl,v 1.8 2002/02/19 22:42:06 drh Exp $}
 
 puts {<html>
 <head>
@@ -27,60 +27,39 @@ proc faq {question answer} {
 faq {
   How do I create an AUTOINCREMENT field.
 } {
-  SQLite does not support AUTOINCREMENT.  If you need a unique key for
-  a new entry in a table, you can create an auxiliary table
-  with a single entry that holds the next available value for that key.
-  Like this:
+  <p>Short answer: A column declared INTEGER PRIMARY KEY will
+  autoincrement.</p>
+
+  <p>Here is the long answer:
+  Beginning with version SQLite 2.3.4, If you declare a column of
+  a table to be INTEGER PRIMARY KEY, then whenever you insert a NULL
+  into that column of the table, the NULL is automatically converted
+  into an integer which is one greater than the largest value of that
+  column over all other rows in the table, or 1 if the table is empty.
+  For example, suppose you have a table like this:
 <blockquote><pre>
-CREATE TABLE counter(cnt);
-INSERT INTO counter VALUES(1);
+CREATE TABLE t1(
+  a INTEGER PRIMARY KEY,
+  b INTEGER
+);
 </pre></blockquote>
-  Once you have a counter set up, you can generate a unique key as follows:
+  <p>With this table, the statement</p>
 <blockquote><pre>
-BEGIN TRANSACTION;
-SELECT cnt FROM counter;
-UPDATE counter SET cnt=cnt+1;
-COMMIT;
+INSERT INTO t1 VALUES(NULL,123);
 </pre></blockquote>
-  There are other ways of simulating the effect of AUTOINCREMENT but
-  this approach seems to be the easiest and most efficient.
-
-  <p><i>New in SQLite version 2.2.0:</i>
-  If one of the columns in a table has type INTEGER PRIMARY KEY and
-  you do an INSERT on that table that does not specify a value for
-  the primary key, then a unique random number is inserted automatically
-  in that column.  This automatically generated key is random, not 
-  sequential, but you can still use it as a unique identifier.</p>
-
-  <p>Here is an example of how the INTEGER PRIMARY KEY feature can be
-  used:</p>
-
-  <blockquote><pre>
-CREATE TABLE ex2(
-  cnum INTEGER PRIMARY KEY,
-  name TEXT,
-  email TEXT
-);
-INSERT INTO ex2(name,email) VALUES('drh','drh@hwaci.com');
-INSERT INTO ex2(name,email) VALUES('alle','alle@hwaci.com');
-SELECT * FROM ex1;
+  <p>is logically equivalent to saying:</p>
+<blockquote><pre>
+INSERT INTO t1 VALUES((SELECT max(a) FROM t1)+1,123);
 </pre></blockquote>
-
-  <p>Notice that the primary key column <b>cnum</b> is not specified on
-  the INSERT statements.  The output of the SELECT on the last line will
-  be something like this:</p>
-
-  <blockquote>
-     1597027670|drh|drh@hwaci.com<br>
-     1597027853|alle|alle@hwaci.com
-  </blockquote>
-
-  <p>The randomly generated keys in this case are 1597027670 and
-  1597027853.  You will probably get different keys every time you
-  try this.  The keys will often be ascending, but this is not always
-  the case and you cannot count on that behavior.  The keys will never
-  be sequential.  If you need sequential keys, use the counter implemention
-  described first.</p>
+  <p>For SQLite version 2.2.0 through 2.3.3, if you insert a NULL into
+  an INTEGER PRIMARY KEY column, the NULL will be changed to a unique
+  integer, but it will a semi-random integer.  Unique keys generated this
+  way will not be sequential.  For SQLite version 2.3.4 and beyond, the
+  unique keys will be sequential until the largest key reaches a value
+  of 2147483647.  That is the largest 32-bit signed integer and cannot
+  be incremented, so subsequent insert attempts will revert to the
+  semi-random key generation algorithm of SQLite version 2.3.3 and
+  earlier.</p>
 
   <p>Beginning with version 2.2.3, there is a new API function named
   <b>sqlite_last_insert_rowid()</b> which will return the integer key
index aa14b0782efa230905f80711e4abdf1d98238009..174f48d85f97c116566578001dba48c64be72485 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this Tcl script to generate the sqlite.html file.
 #
-set rcsid {$Id: lang.tcl,v 1.25 2002/02/19 13:39:23 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.26 2002/02/19 22:42:06 drh Exp $}
 
 puts {<html>
 <head>
@@ -292,7 +292,7 @@ may only hold unique integer values.  (Except for this one case,
 SQLite ignores the datatype specification of columns and allows
 any kind of data to be put in a column regardless of its declared
 datatype.)  If a table does not have an INTEGER PRIMARY KEY column,
-then the B-Tree key will be a randomly generated integer.  The
+then the B-Tree key will be a automatically generated integer.  The
 B-Tree key for a row can always be accessed using one of the
 special names "<b>ROWID</b>", "<b>OID</b>", or "<b>_ROWID_</b>".
 This is true regardless of whether or not there is an INTEGER