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 "

    $desc

" } +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.