From 5cf8e8c7fa103aea2f10fc36a77cd4419c56bc5d Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 19 Feb 2002 22:42:05 +0000 Subject: [PATCH] New ROWIDs are numbered sequentially. (CVS 383) FossilOrigin-Name: 1686196a8aea326f616bc8205df99cd84d955ec4 --- manifest | 31 +++++------ manifest.uuid | 2 +- src/main.c | 4 +- src/select.c | 19 ++++--- src/sqliteInt.h | 4 +- src/vdbe.c | 67 ++++++++++++++++------- test/intpkey.test | 26 ++++----- test/minmax.test | 126 ++++++++++++++++++++++++++++++++++++++++++++ test/rowid.test | 83 ++++++++++++++++++++++++++--- www/c_interface.tcl | 4 +- www/changes.tcl | 3 ++ www/faq.tcl | 83 +++++++++++------------------ www/lang.tcl | 4 +- 13 files changed, 334 insertions(+), 122 deletions(-) create mode 100644 test/minmax.test diff --git a/manifest b/manifest index f80e6ba02c..9bc80123df 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index a91c486c4e..bf5e71a8c5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cc5abfe392bdb8c3ed00e0610bc2b41851bfc9d7 \ No newline at end of file +1686196a8aea326f616bc8205df99cd84d955ec4 \ No newline at end of file diff --git a/src/main.c b/src/main.c index 61450217e0..553f746834 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/select.c b/src/select.c index 036c35ec54..ef6cd216e8 100644 --- a/src/select.c +++ b/src/select.c @@ -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; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 1f3db95bf5..302a2b4949 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 */ }; diff --git a/src/vdbe.c b/src/vdbe.c index 788c5a9d22..f9f0885046 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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 @@ -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); ) diff --git a/test/intpkey.test b/test/intpkey.test index d28efd4726..db73666af6 100644 --- a/test/intpkey.test +++ b/test/intpkey.test @@ -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'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 index 0000000000..3639e793a7 --- /dev/null +++ b/test/minmax.test @@ -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 diff --git a/test/rowid.test b/test/rowid.test index edb9da2209..35b820e49d 100644 --- a/test/rowid.test +++ b/test/rowid.test @@ -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 diff --git a/www/c_interface.tcl b/www/c_interface.tcl index 59eb5150f8..c062470b4f 100644 --- a/www/c_interface.tcl +++ b/www/c_interface.tcl @@ -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 { @@ -378,7 +378,7 @@ header file that comes in the source tree.

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_".

diff --git a/www/changes.tcl b/www/changes.tcl index 968294a2e5..10a59b9079 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -20,6 +20,9 @@ proc chng {date desc} { chng {2002 Feb * (2.3.4)} {
  • Change the name of the sanity_check PRAGMA to integrity_check and make it available in all compiles.
  • +
  • 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.
  • +
  • Automatically generated ROWIDs are now sequential.
  • } chng {2002 Feb 18 (2.3.3)} { diff --git a/www/faq.tcl b/www/faq.tcl index af12e01ad5..e7df250872 100644 --- a/www/faq.tcl +++ b/www/faq.tcl @@ -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 { @@ -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: +

    Short answer: A column declared INTEGER PRIMARY KEY will + autoincrement.

    + +

    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:

    -CREATE TABLE counter(cnt);
    -INSERT INTO counter VALUES(1);
    -
    - Once you have a counter set up, you can generate a unique key as follows: -
    -BEGIN TRANSACTION;
    -SELECT cnt FROM counter;
    -UPDATE counter SET cnt=cnt+1;
    -COMMIT;
    -
    - There are other ways of simulating the effect of AUTOINCREMENT but - this approach seems to be the easiest and most efficient. - -

    New in SQLite version 2.2.0: - 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.

    - -

    Here is an example of how the INTEGER PRIMARY KEY feature can be - used:

    - -
    -CREATE TABLE ex2(
    -  cnum INTEGER PRIMARY KEY,
    -  name TEXT,
    -  email TEXT
    +CREATE TABLE t1(
    +  a INTEGER PRIMARY KEY,
    +  b INTEGER
     );
    -INSERT INTO ex2(name,email) VALUES('drh','drh@hwaci.com');
    -INSERT INTO ex2(name,email) VALUES('alle','alle@hwaci.com');
    -SELECT * FROM ex1;
     
    - -

    Notice that the primary key column cnum is not specified on - the INSERT statements. The output of the SELECT on the last line will - be something like this:

    - -
    - 1597027670|drh|drh@hwaci.com
    - 1597027853|alle|alle@hwaci.com -
    - -

    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.

    +

    With this table, the statement

    +
    +INSERT INTO t1 VALUES(NULL,123);
    +
    +

    is logically equivalent to saying:

    +
    +INSERT INTO t1 VALUES((SELECT max(a) FROM t1)+1,123);
    +
    +

    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.

    Beginning with version 2.2.3, there is a new API function named sqlite_last_insert_rowid() which will return the integer key diff --git a/www/lang.tcl b/www/lang.tcl index aa14b0782e..174f48d85f 100644 --- a/www/lang.tcl +++ b/www/lang.tcl @@ -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 { @@ -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 "ROWID", "OID", or "_ROWID_". This is true regardless of whether or not there is an INTEGER