diff --git a/Makefile.template b/Makefile.template
index 7e45f4464e..e9486371dc 100644
--- a/Makefile.template
+++ b/Makefile.template
@@ -58,6 +58,7 @@ EXE =
#
TCC = gcc -O6
#TCC = gcc -g -O0 -Wall
+#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
#TCC = /opt/mingw/bin/i386-mingw32-gcc -O6
#TCC = /opt/ansic/bin/c89 -O +z -Wl,-a,archive
diff --git a/VERSION b/VERSION
index 2165f8f9b6..e01025862f 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.4
+2.0.5
diff --git a/manifest b/manifest
index 820c36221c..d4e0a79cf6 100644
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Version\s2.0.4\s(CVS\s466)
-D 2001-10-13T22:00:00
+C Added\ssupport\sfor\sthe\sCOUNT_CHANGES\spragma\sin\sorder\sto\shelp\sout\sthe\nODBC\sdriver.\s\sFixed\sa\sbut\son\scount(*)\swhen\sapplied\sto\sempty\stables.\s(CVS\s289)
+D 2001-10-15T00:44:35
F Makefile.in 6801df952cb1df64aa32e4de85fed24511d28efd
-F Makefile.template 582916b263aa40a70521dfb3d99d574028abd47b
+F Makefile.template 1fdb891f14083ee0b63cf7282f91529634438e7a
F README 93d2977cc5c6595c448de16bdefc312b9d401533
-F VERSION 930f436b9878b6b1a49f546e86a54c17df3e0ca9
+F VERSION e14d2010c343ae28a0dd038c3850eae3a88a9307
F aclocal.m4 11faa843caa38fd451bc6aeb43e248d1723a269d
F config.guess f38b1e93d1e0fa6f5a6913e9e7b12774b9232588
F config.log 6a73d03433669b10a3f0c221198c3f26b9413914
@@ -19,35 +19,35 @@ F libtool c56e618713c9510a103bda6b95f3ea3900dcacd6
F ltmain.sh e9ed72eb1d690f447c13945eaf69e28af531eda1
F publish.sh badcd69b8e3a8bc69b162c4c9d7c209b2a0b119e
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
-F src/btree.c 7e9c33a714ed1630562f89ad19847f5f28bd6d4d
+F src/btree.c 97653e88bc4b7396226b93c878b153c77f1d3d03
F src/btree.h 57d653ef5137b91f2a068aaf71a2905468dd2cb7
-F src/build.c 9c3e3634b20c358e538f33f5ae125667e65447b2
-F src/delete.c bed54503368e0976aa2e8487d8914e7b7fb63aae
+F src/build.c fe71d516148226bd6249403e82f8d07129206489
+F src/delete.c 6fe2191c49c4a31336e2fac11b3ad665ddcd4246
F src/expr.c c1381b8229a5573b0928ede962e45c1c49d067af
F src/hash.c b7ced0735287c142a3b2db46c3cae3e6826afb75
F src/hash.h a5f5b3ce2d086a172c5879b0b06a27a82eac9fac
-F src/insert.c ae283e85a301bb3cd6af955f62bde1ca4ba4b56d
+F src/insert.c b65c1d4b848e45d41e9dcccd2b226ca335de67b6
F src/main.c 9a18e97290d41844e8c12e021fb7c42948a19dc9
F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
-F src/os.c cece4ac6cabc9d377ef0a4ab4c16f6f0f6c84377
+F src/os.c 886bdd6c1dff71116f688e8b736d82e43e7ac9ed
F src/os.h bed702c9e3b768bc3cb1b12c90b83d099c1546be
F src/pager.c e2e189a15e230c60e811f5e2ab25e68ae41c90be
F src/pager.h a0d4c5ae271914aa07b62aee0707997d6932b6ca
F src/parse.y 148e4cd134d3cbd816dcb0df50e49e498faa6ba4
F src/printf.c b1e22a47be8cdf707815647239991e08e8cb69f9
F src/random.c 2a9cc2c9716d14815fd4c2accf89d87a1143e46b
-F src/select.c ff4dc2271bb6de7a94f22e651be4d29b4f24ff3f
+F src/select.c 75bb3ca7fd42f7c6d86fc565688e7834587a9f0d
F src/shell.c cb8c41f1b2173efd212dab3f35f1fc6bf32ead76
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in b95c161abf1d58bceb05290fa3f657d8f388fc11
-F src/sqliteInt.h 5d6a79c70bdd9f993958c9c4a7970079fda495dd
+F src/sqliteInt.h 04bfa79fcf6ade1961f6e3b9dc679a63be25cbd7
F src/table.c abd0adbe0fee39d995287b3bcccd908d174dfcac
F src/tclsqlite.c 765599686c19ed777ac379928d732c8bfc63ebac
F src/test1.c e4b31f62ea71963cbae44338acf477a04fc8fc49
F src/test2.c e9f99aa5ee73872819259d6612c11e55e1644321
F src/test3.c 4a0d7b882fdae731dbb759f512ad867122452f96
F src/tokenize.c c3fcb76a41a22803b6060bddb5fbadc80bbe309c
-F src/update.c 0b287faf0cc1d2bfa437f8a54061dd12ae6df91d
+F src/update.c c916182c6bfbc8a6f20c24920c4560fece6c9569
F src/util.c 4da3be37d0fd3c640d2d3033503768afdc8e5387
F src/vdbe.c 0f8ea6ca59f0899e9e0d71a81c0bf46110447cf6
F src/vdbe.h 86fc2ef42f48024c9a2e1b7fb01eda22b65a5295
@@ -57,13 +57,13 @@ F test/bigrow.test a35f2de9948b24e427fb292c35947795efe182d0
F test/btree.test 47952c7a0c22660566264c68c0664592b7da85ce
F test/btree2.test 20ce47ab804f15b6563736528bdd38aabe5193dc
F test/copy.test 768e6f1701a07d08090e1ca7f7dcce0a7a72b43e
-F test/delete.test 5ebb114582457428b3e0e30b21b477fedcb85609
+F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
F test/expr.test b4171c84b767f7b7e94dbce4824ba8e981a1c72f
F test/func.test fb0f44de6d8487359a4455accbae120bde267772
F test/in.test 9323681388be301dc73f370b4cd62c5a33f79d1e
F test/index.test 6076f29d09a4f26a2efa38b03b8cc338b8662f0e
F test/insert.test a5c122aa726f1cef6f07d6767e8fd6f220994c11
-F test/insert2.test 252d7130d8cc20f649b31a4f503cd87e660abda8
+F test/insert2.test d6901ca931e308fea7fca8c95ebe7dc957cc9fc2
F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
F test/lock.test 19593689260c419efe7ced55b1418653a4b7bcd1
F test/main.test 1626345b5f630c5398eede500d9354813b76b0fd
@@ -74,11 +74,11 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
F test/quick.test b6ec50f808efc06595fd324bf4f3fabadb9c7e9c
F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
F test/rowid.test 427bfbbe9684fe7a2f851aa05badaae6d4972ce8
-F test/select1.test b59e8c713277d7545f5b6e782e6223e51fea45a5
+F test/select1.test 5f47445fa3a033e02e1b07e4fcd4f142e5a46403
F test/select2.test f91c903e2bab0e9d45274855a981eebf846d5e32
F test/select3.test 5e1fe8e5a4e63fb2827ab3b89527e0fd4ae35259
F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228
-F test/select5.test 00a240e3311b6c4ff0f27a252ad33811211fa8d8
+F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac
F test/sort.test 462c1161eee1abaa7cc93990e0b34d5fdb70ce19
F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78
F test/table.test 3ef4254d62ece31a3872ab11cdaec846f6fa8fd1
@@ -88,7 +88,7 @@ F test/temptable.test 37acd9e39781c2ff7cff2ba741b6b27ce020a44a
F test/tester.tcl c7ddeebc14cc841abb37134cd5d40c1e3ad367c1
F test/trans.test 855337b8a178c73c433fcf8ee88e4b2f5efff0d9
F test/unique.test ef1f67607a7109e9c0842cd8557550fb121d7ec6
-F test/update.test b320ea22899e80b32b4d21c54591eb7a6ba4d6bd
+F test/update.test 8cf76467d46b1650539763c95d5208340c61d561
F test/vacuum.test 8acf8669f3b627e54149b25165b034aa06c2432e
F test/where.test 43d5ac94da3f3722375307f948884dc79b326a91
F tool/lemon.c 5533b63e5cdbb1efc939abac3c2f4f37ac839488
@@ -102,19 +102,19 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
F www/arch.tcl 03b521d252575f93b9c52f7c8b0007011512fcfb
F www/c_interface.tcl a59ee0835d1b33fcddab7d4fd65cf9e50f7d2dc7
-F www/changes.tcl 5407ebb20a046f1f12dd4b8acd23c135f8e45f91
+F www/changes.tcl 00cfa817042f33097616ff0de388e6503aab3968
F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e
F www/download.tcl 3e51c9ff1326b0a182846134987301310dff7d60
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
F www/index.tcl 68c815d64b35b2dcc4d4f6845827df71c6869f9f
-F www/lang.tcl 8cf5de0e7b5d038506cd0b8fd26567ba43777b16
+F www/lang.tcl 37d4a44bdafe42a270b9e28554651278086ce806
F www/mingw.tcl fc5f4ba9d336b6e8c97347cc6496d6162461ef60
F www/opcode.tcl 4365ad9798872491dbd7d3071510ebe461785ac3
F www/speed.tcl ab7d6d3bc898472bd94320a5d3c63de928d4804b
F www/sqlite.tcl 6a21242a272e9c0939a04419a51c3d50cae33e3e
F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa
F www/vdbe.tcl bb7d620995f0a987293e9d4fb6185a3b077e9b44
-P 26972afd645e21e0d16de9a0bb0d03754e909044
-R 4a79f4243a1c67e525408e6bb2f97ed5
+P 444447007a32f9ebd9e0714c6cbe0d9a63e193b1
+R 27dbd8ccaf5ffe5bb0031ee011cf1c46
U drh
-Z bda58aec2ebf892c3a56d60499b5a93d
+Z 7a6d7f82de1493c63c9271f60dfef892
diff --git a/manifest.uuid b/manifest.uuid
index 55d1bcf60d..e8af732b96 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-444447007a32f9ebd9e0714c6cbe0d9a63e193b1
\ No newline at end of file
+747bf1b30b74cfd0e9c27e7c0bc5172637f35520
\ No newline at end of file
diff --git a/src/btree.c b/src/btree.c
index 761ccac948..c17ef14049 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.33 2001/10/06 16:33:02 drh Exp $
+** $Id: btree.c,v 1.34 2001/10/15 00:44:35 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -597,8 +597,8 @@ static void pageDestructor(void *pData){
** until the first page is loaded.
**
** zFilename is the name of the database file. If zFilename is NULL
-** a new database with a random name is created. The database will be
-** destroyed when sqliteBtreeClose() is called.
+** a new database with a random name is created. This randomly named
+** database file will be deleted when sqliteBtreeClose() is called.
*/
int sqliteBtreeOpen(
const char *zFilename, /* Name of the file containing the BTree database */
@@ -807,6 +807,13 @@ int sqliteBtreeRollback(Btree *pBt){
** Create a new cursor for the BTree whose root is on the page
** iTable. The act of acquiring a cursor gets a read lock on
** the database file.
+**
+** If wrFlag==0, then the cursor can only be used for reading.
+** If wrFlag==1, then the cursor can be used for reading or writing.
+** A read/write cursor requires exclusive access to its table. There
+** cannot be two or more cursors open on the same table is any one of
+** cursors is a read/write cursor. But there can be two or more
+** read-only cursors open on the same table.
*/
int sqliteBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
int rc;
@@ -1357,6 +1364,7 @@ int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){
int sqliteBtreeNext(BtCursor *pCur, int *pRes){
int rc;
if( pCur->pPage==0 ){
+ if( pRes ) *pRes = 1;
return SQLITE_ABORT;
}
if( pCur->bSkipNext ){
diff --git a/src/build.c b/src/build.c
index 8f87dd4717..fc45a3441e 100644
--- a/src/build.c
+++ b/src/build.c
@@ -25,7 +25,7 @@
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.49 2001/10/13 02:59:09 drh Exp $
+** $Id: build.c,v 1.50 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -1441,6 +1441,14 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
}
}else
+ if( sqliteStrICmp(zLeft, "count_changes")==0 ){
+ if( getBoolean(zRight) ){
+ db->flags |= SQLITE_CountRows;
+ }else{
+ db->flags &= ~SQLITE_CountRows;
+ }
+ }else
+
if( sqliteStrICmp(zLeft, "table_info")==0 ){
Table *pTab;
Vdbe *v;
diff --git a/src/delete.c b/src/delete.c
index 6534f3070f..cd4694b618 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
-** $Id: delete.c,v 1.17 2001/10/13 01:06:48 drh Exp $
+** $Id: delete.c,v 1.18 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -33,6 +33,8 @@ void sqliteDeleteFrom(
Index *pIdx; /* For looping over indices of the table */
int base; /* Index of the first available table cursor */
sqlite *db; /* Main database structure */
+ int openOp; /* Opcode used to open a cursor to the table */
+
if( pParse->nErr || sqlite_malloc_failed ){
pTabList = 0;
@@ -86,11 +88,31 @@ void sqliteDeleteFrom(
pParse->schemaVerified = 1;
}
+ /* Initialize the counter of the number of rows deleted, if
+ ** we are counting rows.
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0);
+ }
/* Special case: A DELETE without a WHERE clause deletes everything.
** It is easier just to erase the whole table.
*/
if( pWhere==0 ){
+ if( db->flags & SQLITE_CountRows ){
+ /* If counting rows deleted, just count the total number of
+ ** entries in the table. */
+ int endOfLoop = sqliteVdbeMakeLabel(v);
+ int addr;
+ openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
+ sqliteVdbeAddOp(v, openOp, 0, pTab->tnum);
+ sqliteVdbeAddOp(v, OP_Rewind, 0, 0);
+ addr = sqliteVdbeAddOp(v, OP_Next, 0, endOfLoop);
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, addr);
+ sqliteVdbeResolveLabel(v, endOfLoop);
+ sqliteVdbeAddOp(v, OP_Close, 0, 0);
+ }
sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->isTemp);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pTab->isTemp);
@@ -101,8 +123,6 @@ void sqliteDeleteFrom(
** the table an pick which records to delete.
*/
else{
- int openOp;
-
/* Begin the database scan
*/
sqliteVdbeAddOp(v, OP_ListOpen, 0, 0);
@@ -112,6 +132,9 @@ void sqliteDeleteFrom(
/* Remember the key of every item to be deleted.
*/
sqliteVdbeAddOp(v, OP_ListWrite, 0, 0);
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
+ }
/* End the database scan loop.
*/
@@ -151,6 +174,15 @@ void sqliteDeleteFrom(
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}
+ /*
+ ** Return the number of rows that were deleted.
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_ColumnCount, 1, 0);
+ sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
+ sqliteVdbeChangeP3(v, -1, "rows deleted", P3_STATIC);
+ sqliteVdbeAddOp(v, OP_Callback, 1, 0);
+ }
delete_from_cleanup:
sqliteIdListDelete(pTabList);
diff --git a/src/insert.c b/src/insert.c
index 489f7389cc..8f93d5cebd 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
-** $Id: insert.c,v 1.23 2001/10/13 01:06:48 drh Exp $
+** $Id: insert.c,v 1.24 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -170,6 +170,9 @@ void sqliteInsert(
** and the loop is not used.
*/
if( srcTab>=0 ){
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */
+ }
sqliteVdbeAddOp(v, OP_Rewind, srcTab, 0);
iBreak = sqliteVdbeMakeLabel(v);
iCont = sqliteVdbeAddOp(v, OP_Next, srcTab, iBreak);
@@ -200,6 +203,7 @@ void sqliteInsert(
}
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_Put, base, 0);
+
/* Create appropriate entries for the new data row in all indices
** of the table.
@@ -230,6 +234,14 @@ void sqliteInsert(
sqliteVdbeAddOp(v, OP_PutIdx, idx+base, pIdx->isUnique);
}
+
+ /* If inserting from a SELECT, keep a count of the number of
+ ** rows inserted.
+ */
+ if( srcTab>=0 && (db->flags & SQLITE_CountRows)!=0 ){
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
+ }
+
/* The bottom of the loop, if the data source is a SELECT statement
*/
if( srcTab>=0 ){
@@ -241,6 +253,18 @@ void sqliteInsert(
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}
+ /*
+ ** Return the number of rows inserted.
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_ColumnCount, 1, 0);
+ sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
+ sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
+ if( srcTab<0 ){
+ sqliteVdbeAddOp(v, OP_Integer, 1, 0);
+ }
+ sqliteVdbeAddOp(v, OP_Callback, 1, 0);
+ }
insert_cleanup:
if( pList ) sqliteExprListDelete(pList);
diff --git a/src/os.c b/src/os.c
index f09aacc630..862eb278b6 100644
--- a/src/os.c
+++ b/src/os.c
@@ -68,7 +68,7 @@
** To work around the problem, SQLite has to manage file locks internally
** on its own. Whenever a new database is opened, we have to find the
** specific inode of the database file (the inode is determined by the
-** st_dev and st_ino fields of the stat structure the stat() fills in)
+** st_dev and st_ino fields of the stat structure that fstat() fills in)
** and check for locks already existing on that inode. When locks are
** created or removed, we have to look at our own internal record of the
** locks to see if another thread has previously set a lock on that same
diff --git a/src/select.c b/src/select.c
index ea265bad60..70e962cc4e 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.39 2001/10/13 01:06:48 drh Exp $
+** $Id: select.c,v 1.40 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -239,6 +239,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0);
for(i=0; inExpr; i++){
Expr *p;
+ int showFullNames;
if( pEList->a[i].zName ){
char *zName = pEList->a[i].zName;
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
@@ -247,17 +248,13 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
}
p = pEList->a[i].pExpr;
if( p==0 ) continue;
- if( p->span.z && p->span.z[0] ){
+ showFullNames = (pParse->db->flags & SQLITE_FullColNames)!=0;
+ if( p->span.z && p->span.z[0] && !showFullNames ){
int addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0);
sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
sqliteVdbeCompressSpace(v, addr);
- }else if( p->op!=TK_COLUMN || pTabList==0 ){
- char zName[30];
- sprintf(zName, "column%d", i+1);
- sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
- sqliteVdbeChangeP3(v, -1, zName, strlen(zName));
- }else{
- if( pTabList->nId>1 || (pParse->db->flags & SQLITE_FullColNames)!=0 ){
+ }else if( p->op==TK_COLUMN && pTabList ){
+ if( pTabList->nId>1 || showFullNames ){
char *zName = 0;
Table *pTab = pTabList->a[p->iTable].pTab;
char *zTab;
@@ -274,6 +271,16 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
sqliteVdbeChangeP3(v, -1, zName, P3_STATIC);
}
+ }else if( p->span.z && p->span.z[0] ){
+ int addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0);
+ sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
+ sqliteVdbeCompressSpace(v, addr);
+ }else{
+ char zName[30];
+ assert( p->op!=TK_COLUMN || pTabList==0 );
+ sprintf(zName, "column%d", i+1);
+ sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
+ sqliteVdbeChangeP3(v, -1, zName, strlen(zName));
}
}
}
@@ -867,6 +874,23 @@ int sqliteSelect(
*/
if( isAgg ){
sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg);
+ if( pGroupBy==0 ){
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ sqliteVdbeChangeP3(v, -1, "", P3_STATIC);
+ sqliteVdbeAddOp(v, OP_AggFocus, 0, 0);
+ for(i=0; inAgg; i++){
+ Expr *pE;
+ if( !pParse->aAgg[i].isAgg ) continue;
+ pE = pParse->aAgg[i].pExpr;
+ assert( pE==0 || pE->op==TK_AGG_FUNCTION );
+ assert( pE==0 || (pE->pList!=0 && pE->pList->nExpr==1) );
+ if( pE==0 || pE->iColumn==FN_Sum ){
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0);
+ sqliteVdbeAddOp(v, OP_AggSet, 0, i);
+ continue;
+ }
+ }
+ }
}
/* Initialize the memory cell to NULL
@@ -898,28 +922,13 @@ int sqliteSelect(
** processing.
*/
else{
- int doFocus;
if( pGroupBy ){
+ int lbl1;
for(i=0; inExpr; i++){
sqliteExprCode(pParse, pGroupBy->a[i].pExpr);
}
sqliteVdbeAddOp(v, OP_MakeKey, pGroupBy->nExpr, 0);
- doFocus = 1;
- }else{
- doFocus = 0;
- for(i=0; inAgg; i++){
- if( !pParse->aAgg[i].isAgg ){
- doFocus = 1;
- break;
- }
- }
- if( doFocus ){
- sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, -1, "", P3_STATIC);
- }
- }
- if( doFocus ){
- int lbl1 = sqliteVdbeMakeLabel(v);
+ lbl1 = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1);
for(i=0; inAgg; i++){
if( pParse->aAgg[i].isAgg ) continue;
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index e23e2b1dce..72d63abca6 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.61 2001/10/13 02:59:09 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.62 2001/10/15 00:44:36 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@@ -163,6 +163,9 @@ struct sqlite {
#define SQLITE_InTrans 0x00000008 /* True if in a transaction */
#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */
#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
+#define SQLITE_CountRows 0x00000040 /* Count rows changed by INSERT, */
+ /* DELETE, or UPDATE and return */
+ /* the count using a callback. */
/*
** Current file format version
diff --git a/src/update.c b/src/update.c
index f9f69fed5f..f7061fce2a 100644
--- a/src/update.c
+++ b/src/update.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
-** $Id: update.c,v 1.18 2001/10/13 01:06:48 drh Exp $
+** $Id: update.c,v 1.19 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -155,6 +155,12 @@ void sqliteUpdate(
*/
sqliteWhereEnd(pWInfo);
+ /* Initialize the count of updated rows
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_Integer, 0, 0);
+ }
+
/* Rewind the list of records that need to be updated and
** open every index that needs updating.
*/
@@ -216,6 +222,12 @@ void sqliteUpdate(
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_Put, base, 0);
+ /* Increment the count of rows affected by the update
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
+ }
+
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
*/
@@ -226,6 +238,16 @@ void sqliteUpdate(
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}
+ /*
+ ** Return the number of rows that were changed.
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqliteVdbeAddOp(v, OP_ColumnCount, 1, 0);
+ sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
+ sqliteVdbeChangeP3(v, -1, "rows updated", P3_STATIC);
+ sqliteVdbeAddOp(v, OP_Callback, 1, 0);
+ }
+
update_cleanup:
sqliteFree(apIdx);
sqliteFree(aXRef);
diff --git a/test/delete.test b/test/delete.test
index 6ab2d3fec4..8fd27d92c5 100644
--- a/test/delete.test
+++ b/test/delete.test
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the DELETE FROM statement.
#
-# $Id: delete.test,v 1.9 2001/09/16 00:13:28 drh Exp $
+# $Id: delete.test,v 1.10 2001/10/15 00:44:36 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -32,7 +32,7 @@ do_test delete-2.1 {
# Delete selected entries from a table with and without an index.
#
-do_test delete-3.1a {
+do_test delete-3.1.1 {
execsql {CREATE TABLE table1(f1 int, f2 int)}
execsql {INSERT INTO table1 VALUES(1,2)}
execsql {INSERT INTO table1 VALUES(2,4)}
@@ -40,17 +40,24 @@ do_test delete-3.1a {
execsql {INSERT INTO table1 VALUES(4,16)}
execsql {SELECT * FROM table1 ORDER BY f1}
} {1 2 2 4 3 8 4 16}
-do_test delete-3.1b {
+do_test delete-3.1.2 {
execsql {DELETE FROM table1 WHERE f1=3}
+} {}
+do_test delete-3.1.3 {
execsql {SELECT * FROM table1 ORDER BY f1}
} {1 2 2 4 4 16}
-do_test delete-3.1c {
+do_test delete-3.1.4 {
execsql {CREATE INDEX index1 ON table1(f1)}
+ execsql {PRAGMA count_changes=on}
execsql {DELETE FROM 'table1' WHERE f1=3}
+} {0}
+do_test delete-3.1.5 {
execsql {SELECT * FROM table1 ORDER BY f1}
} {1 2 2 4 4 16}
-do_test delete-3.1d {
+do_test delete-3.1.6 {
execsql {DELETE FROM table1 WHERE f1=2}
+} {1}
+do_test delete-3.1.7 {
execsql {SELECT * FROM table1 ORDER BY f1}
} {1 2 4 16}
@@ -69,14 +76,44 @@ do_test delete-4.2 {
# Lots of deletes
#
-do_test delete-5.1 {
+do_test delete-5.1.1 {
execsql {DELETE FROM table1}
+} {2}
+do_test delete-5.1.2 {
execsql {SELECT count(*) FROM table1}
-} {}
-do_test delete-5.2 {
+} {0}
+do_test delete-5.2.1 {
+ execsql {BEGIN TRANSACTION}
for {set i 1} {$i<=200} {incr i} {
execsql "INSERT INTO table1 VALUES($i,[expr {$i*$i}])"
}
+ execsql {COMMIT}
+ execsql {SELECT count(*) FROM table1}
+} {200}
+do_test delete-5.2.2 {
+ execsql {DELETE FROM table1}
+} {200}
+do_test delete-5.2.3 {
+ execsql {BEGIN TRANSACTION}
+ for {set i 1} {$i<=200} {incr i} {
+ execsql "INSERT INTO table1 VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {COMMIT}
+ execsql {SELECT count(*) FROM table1}
+} {200}
+do_test delete-5.2.4 {
+ execsql {PRAGMA count_changes=off}
+ execsql {DELETE FROM table1}
+} {}
+do_test delete-5.2.5 {
+ execsql {SELECT count(*) FROM table1}
+} {0}
+do_test delete-5.2.6 {
+ execsql {BEGIN TRANSACTION}
+ for {set i 1} {$i<=200} {incr i} {
+ execsql "INSERT INTO table1 VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {COMMIT}
execsql {SELECT count(*) FROM table1}
} {200}
do_test delete-5.3 {
diff --git a/test/insert2.test b/test/insert2.test
index 59fb2e76e9..610e587511 100644
--- a/test/insert2.test
+++ b/test/insert2.test
@@ -12,7 +12,7 @@
# focus of this file is testing the INSERT statement that takes is
# result from a SELECT.
#
-# $Id: insert2.test,v 1.4 2001/09/16 00:13:28 drh Exp $
+# $Id: insert2.test,v 1.5 2001/10/15 00:44:36 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -30,32 +30,43 @@ do_test insert2-1.0 {
# Insert into a new table from the old one.
#
-do_test insert2-1.1 {
+do_test insert2-1.1.1 {
execsql {
CREATE TABLE t1(log int, cnt int);
----vdbe-trace-on--
+ PRAGMA count_changes=on;
INSERT INTO t1 SELECT log, count(*) FROM d1 GROUP BY log;
}
+} {6}
+do_test insert2-1.1.2 {
execsql {SELECT * FROM t1 ORDER BY log}
} {0 1 1 1 2 2 3 4 4 8 5 4}
-do_test insert2-1.2 {
+do_test insert2-1.2.1 {
catch {execsql {DROP TABLE t1}}
execsql {
CREATE TABLE t1(log int, cnt int);
INSERT INTO t1
SELECT log, count(*) FROM d1 GROUP BY log
EXCEPT SELECT n-1,log FROM d1;
+ }
+} {4}
+do_test insert2-1.2.2 {
+ execsql {
SELECT * FROM t1 ORDER BY log;
}
} {0 1 3 4 4 8 5 4}
-do_test insert2-1.3 {
+do_test insert2-1.3.1 {
catch {execsql {DROP TABLE t1}}
execsql {
CREATE TABLE t1(log int, cnt int);
+ PRAGMA count_changes=off;
INSERT INTO t1
SELECT log, count(*) FROM d1 GROUP BY log
INTERSECT SELECT n-1,log FROM d1;
+ }
+} {}
+do_test insert2-1.3.2 {
+ execsql {
SELECT * FROM t1 ORDER BY log;
}
} {1 1 2 2}
diff --git a/test/select1.test b/test/select1.test
index 7516aab77d..d9fcb3db44 100644
--- a/test/select1.test
+++ b/test/select1.test
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the SELECT statement.
#
-# $Id: select1.test,v 1.11 2001/10/06 16:33:03 drh Exp $
+# $Id: select1.test,v 1.12 2001/10/15 00:44:36 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -242,13 +242,17 @@ do_test select1-6.1.1 {
execsql {PRAGMA full_column_names=on}
set v [catch {execsql2 {SELECT f1 FROM test1 ORDER BY f2}} msg]
lappend v $msg
-} {0 {f1 11 f1 33}}
+} {0 {test1.f1 11 test1.f1 33}}
do_test select1-6.1.2 {
+ set v [catch {execsql2 {SELECT f1 as 'f1' FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {f1 11 f1 33}}
+do_test select1-6.1.3 {
set v [catch {execsql2 {SELECT * FROM test1 WHERE f1==11}} msg]
execsql {PRAGMA full_column_names=off}
lappend v $msg
} {0 {test1.f1 11 test1.f2 22}}
-do_test select1-6.1.2 {
+do_test select1-6.1.4 {
set v [catch {execsql2 {SELECT * FROM test1 WHERE f1==11}} msg]
lappend v $msg
} {0 {f1 11 f2 22}}
@@ -276,6 +280,12 @@ do_test select1-6.5 {
set v [catch {execsql2 {SELECT test1.f1+F2 FROM test1 ORDER BY f2}} msg]
lappend v $msg
} {0 {test1.f1+F2 33 test1.f1+F2 77}}
+do_test select1-6.5.1 {
+ execsql2 {PRAGMA full_column_names=on}
+ set v [catch {execsql2 {SELECT test1.f1+F2 FROM test1 ORDER BY f2}} msg]
+ execsql2 {PRAGMA full_column_names=off}
+ lappend v $msg
+} {0 {test1.f1+F2 33 test1.f1+F2 77}}
do_test select1-6.6 {
set v [catch {execsql2 {SELECT test1.f1+F2, t1 FROM test1, test2
ORDER BY f2}} msg]
diff --git a/test/select5.test b/test/select5.test
index 729268d2b9..a5095643cf 100644
--- a/test/select5.test
+++ b/test/select5.test
@@ -12,7 +12,7 @@
# focus of this file is testing aggregate functions and the
# GROUP BY and HAVING clauses of SELECT statements.
#
-# $Id: select5.test,v 1.5 2001/09/16 00:13:28 drh Exp $
+# $Id: select5.test,v 1.6 2001/10/15 00:44:36 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -88,12 +88,32 @@ do_test select5-3.1 {
}
} {1 1 5 2 1 5 3 1 5}
-# Run the AVG() function when the count is zero.
+# Run various aggregate functions when the count is zero.
#
do_test select5-4.1 {
execsql {
SELECT avg(x) FROM t1 WHERE x>100
}
-} {}
+} {{}}
+do_test select5-4.2 {
+ execsql {
+ SELECT count(x) FROM t1 WHERE x>100
+ }
+} {0}
+do_test select5-4.3 {
+ execsql {
+ SELECT min(x) FROM t1 WHERE x>100
+ }
+} {{}}
+do_test select5-4.4 {
+ execsql {
+ SELECT max(x) FROM t1 WHERE x>100
+ }
+} {{}}
+do_test select5-4.5 {
+ execsql {
+ SELECT sum(x) FROM t1 WHERE x>100
+ }
+} {0}
finish_test
diff --git a/test/update.test b/test/update.test
index c389d20fb5..9ccef74991 100644
--- a/test/update.test
+++ b/test/update.test
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the UPDATE statement.
#
-# $Id: update.test,v 1.6 2001/09/16 00:13:28 drh Exp $
+# $Id: update.test,v 1.7 2001/10/15 00:44:36 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -61,25 +61,37 @@ do_test update-3.4 {
#
do_test update-3.5 {
execsql {UPDATE test1 SET f2=f2*3}
+} {}
+do_test update-3.6 {
execsql {SELECT * FROM test1 ORDER BY f1}
} {1 6 2 12 3 24 4 48 5 96 6 192 7 384 8 768 9 1536 10 3072}
-do_test update-3.6 {
+do_test update-3.7 {
+ execsql {PRAGMA count_changes=on}
execsql {UPDATE test1 SET f2=f2/3 WHERE f1<=5}
+} {5}
+do_test update-3.8 {
execsql {SELECT * FROM test1 ORDER BY f1}
} {1 2 2 4 3 8 4 16 5 32 6 192 7 384 8 768 9 1536 10 3072}
-do_test update-3.7 {
+do_test update-3.9 {
execsql {UPDATE test1 SET f2=f2/3 WHERE f1>5}
+} {5}
+do_test update-3.10 {
execsql {SELECT * FROM test1 ORDER BY f1}
} {1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024}
# Swap the values of f1 and f2 for all elements
#
-do_test update-3.8 {
+do_test update-3.11 {
execsql {UPDATE test1 SET F2=f1, F1=f2}
+} {10}
+do_test update-3.12 {
execsql {SELECT * FROM test1 ORDER BY F1}
} {2 1 4 2 8 3 16 4 32 5 64 6 128 7 256 8 512 9 1024 10}
-do_test update-3.9 {
+do_test update-3.13 {
+ execsql {PRAGMA count_changes=off}
execsql {UPDATE test1 SET F2=f1, F1=f2}
+} {}
+do_test update-3.14 {
execsql {SELECT * FROM test1 ORDER BY F1}
} {1 2 2 4 3 8 4 16 5 32 6 64 7 128 8 256 9 512 10 1024}
@@ -117,8 +129,16 @@ do_test update-4.5 {
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
do_test update-4.6 {
- execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
- execsql {SELECT * FROM test1 ORDER BY f1,f2}
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128;
+ }
+} {2}
+do_test update-4.7 {
+ execsql {
+ PRAGMA count_changes=off;
+ SELECT * FROM test1 ORDER BY f1,f2
+ }
} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
# Repeat the previous sequence of tests with an index.
@@ -154,34 +174,44 @@ do_test update-5.4.3 {
} {8 88 8 128 8 256 8 888}
do_test update-5.5 {
execsql {UPDATE test1 SET f1=f1-1 WHERE f1>100 and f2==128}
+} {}
+do_test update-5.5.1 {
execsql {SELECT * FROM test1 ORDER BY f1,f2}
} {6 64 8 88 8 128 8 256 8 888 9 512 10 1024 78 128 777 128}
-do_test update-5.5.1 {
+do_test update-5.5.2 {
execsql {SELECT * FROM test1 WHERE f1==78 ORDER BY f1,f2}
} {78 128}
-do_test update-5.5.2 {
+do_test update-5.5.3 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
-do_test update-5.5.3 {
+do_test update-5.5.4 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
-do_test update-5.5.4 {
+do_test update-5.5.5 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 128 8 256 8 888}
do_test update-5.6 {
- execsql {UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128}
- execsql {SELECT * FROM test1 ORDER BY f1,f2}
-} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE test1 SET f1=f1-1 WHERE f1<=100 and f2==128;
+ }
+} {2}
do_test update-5.6.1 {
+ execsql {
+ PRAGMA count_changes=off;
+ SELECT * FROM test1 ORDER BY f1,f2
+ }
+} {6 64 7 128 8 88 8 256 8 888 9 512 10 1024 77 128 777 128}
+do_test update-5.6.2 {
execsql {SELECT * FROM test1 WHERE f1==77 ORDER BY f1,f2}
} {77 128}
-do_test update-5.6.2 {
+do_test update-5.6.3 {
execsql {SELECT * FROM test1 WHERE f1==778 ORDER BY f1,f2}
} {}
-do_test update-5.6.3 {
+do_test update-5.6.4 {
execsql {SELECT * FROM test1 WHERE f1==777 ORDER BY f1,f2}
} {777 128}
-do_test update-5.6.4 {
+do_test update-5.6.5 {
execsql {SELECT * FROM test1 WHERE f1==8 ORDER BY f1,f2}
} {8 88 8 256 8 888}
diff --git a/www/changes.tcl b/www/changes.tcl
index aecf8b62b6..72285f6c38 100644
--- a/www/changes.tcl
+++ b/www/changes.tcl
@@ -17,6 +17,13 @@ proc chng {date desc} {
puts "
"
}
+chng {2001 Oct 14 (2.0.5)} {
+Added the COUNT_CHANGES pragma.
+Changes to the FULL_COLUMN_NAMES pragma to help out the ODBC driver.
+Bug fix: "SELECT count(*)" was returning NULL for empty tables.
+ Now it returns 0.
+}
+
chng {2001 Oct 13 (2.0.4)} {
Bug fix: an abscure and relatively harmless bug was causing one of
the tests to fail when gcc optimizations are turned on. This release
diff --git a/www/lang.tcl b/www/lang.tcl
index 8f62259805..7e6a4c089b 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.12 2001/10/13 02:59:10 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.13 2001/10/15 00:44:36 drh Exp $}
puts {
@@ -631,6 +631,12 @@ with caution.
uses more memory, you can increase the cache size for a possible speed
improvement.
+PRAGMA count_changes = ON;
+
PRAGMA count_changes = OFF;
+ When on, the COUNT_CHANGES pragma causes the callback function to
+ be invoked once for each DELETE, INSERT, or UPDATE operation. The
+ argument is the number of rows that were changed.
+
PRAGMA full_column_names = ON;
PRAGMA full_column_names = OFF;
The column names reported in an SQLite callback are normally just
@@ -638,12 +644,32 @@ with caution.
is used. But when full_column_names is turned on, column names are
always reported as "TABLE.COLUMN" even for simple queries.
-PRAGMA vdbe_trace = ON;
PRAGMA vdbe_trace = OFF;
- Turn tracing of the virtual database engine inside of the
- SQLite library on and off. This is used for debugging.
+PRAGMA index_info(index-name);
+ For each column that the named index references, invoke the
+ callback function
+ once with information about that column, including the column name,
+ and the column number.
+
+PRAGMA index_list(table-name);
+ For each index on the named table, invoke the callback function
+ once with information about that index. Arguments include the
+ index name and a flag to indicate whether or not the index must be
+ unique.
PRAGMA parser_trace = ON;
PRAGMA parser_trace = OFF;
Turn tracing of the SQL parser inside of the
+ SQLite library on and off. This is used for debugging.
+ This only works if the library is compiled without the NDEBUG macro.
+
+
+PRAGMA table_info(table-name);
+ For each column in the named table, invoke the callback function
+ once with information about that column, including the column name,
+ data type, whether or not the column can be NULL, and the default
+ value for the column.
+
+PRAGMA vdbe_trace = ON;
PRAGMA vdbe_trace = OFF;
+ Turn tracing of the virtual database engine inside of the
SQLite library on and off. This is used for debugging.