Fix for bug #15: Add the sqlite_changes() API function for retrieving the

number of rows that changed in the previous operation. (CVS 526)

FossilOrigin-Name: 6e71493b9dc77d508c3ce90562766789e87e6d80
This commit is contained in:
drh 2002-04-12 10:08:59 +00:00
parent b04a5d8768
commit c8d30ac109
15 changed files with 172 additions and 59 deletions

View File

@ -1,5 +1,5 @@
C Fix\sfor\sbug\s#16:\sCheck\sfor\sinvalid\sfunctions\sin\sthe\sVALUES\sclause\sof\san\nINSERT\sstatement.\s(CVS\s525) C Fix\sfor\sbug\s#15:\sAdd\sthe\ssqlite_changes()\sAPI\sfunction\sfor\sretrieving\sthe\nnumber\sof\srows\sthat\schanged\sin\sthe\sprevious\soperation.\s(CVS\s526)
D 2002-04-12T03:55:16 D 2002-04-12T10:08:59
F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d
F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296 F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@ -23,13 +23,13 @@ F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
F src/btree.c 7dd7ddc66459982dd0cb9800958c1f8d65a32d9f F src/btree.c 7dd7ddc66459982dd0cb9800958c1f8d65a32d9f
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
F src/build.c d01b81f41481e733e27ab2fa8e1bfcc64f24257d F src/build.c d01b81f41481e733e27ab2fa8e1bfcc64f24257d
F src/delete.c 577da499162291c1855f0b304b211bffcf9da945 F src/delete.c 6a6b8192cdff5e4b083da3bc63de099f3790d01f
F src/expr.c e7a1e22bc2ebcd789f0f8c0db544cf16ad664054 F src/expr.c e7a1e22bc2ebcd789f0f8c0db544cf16ad664054
F src/func.c dca9df811298cd0beb3724d40cee348e884352b2 F src/func.c dca9df811298cd0beb3724d40cee348e884352b2
F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892 F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
F src/insert.c 80105ff6a8bdabe8122948b0066fb1914f9b86c7 F src/insert.c 31233f44fc79edbb43523a830e54736a8e222ff4
F src/main.c b21019084b93fe685a8a25217d01f6958584ae9b F src/main.c df43fe585d2bfb925c837b6822783c0ee3dd6e1c
F src/md5.c b2b1a34fce66ceca97f4e0dabc20be8be7933c92 F src/md5.c b2b1a34fce66ceca97f4e0dabc20be8be7933c92
F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10 F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
@ -41,10 +41,10 @@ F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c 92aef3f69e90dc065d680d88b1f075409e9249bb F src/select.c 92aef3f69e90dc065d680d88b1f075409e9249bb
F src/shell.c 994ca7c8c40c40a95011812013fbbf9828f5a0e7 F src/shell.c 994ca7c8c40c40a95011812013fbbf9828f5a0e7
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 1dae50411aee9439860d7fbe315183c582d27197 F src/sqlite.h.in ffcacf73b5ed1a4939205d29a704a185758fa6a6
F src/sqliteInt.h 73e7d0f39a3c11d395fc422986959261bd193eba F src/sqliteInt.h e47ca9267a4c4a98e9f8d90c2df994a18f23d699
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c df847b71b28277f1cfa1ee1e3e51452ffe5a9a26 F src/tclsqlite.c c9e9039762d9866eae70bf782237d0206a13f57e
F src/test1.c d46ab7a82a9c16a3b1ee363cb4c0f98c5ff65743 F src/test1.c d46ab7a82a9c16a3b1ee363cb4c0f98c5ff65743
F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6 F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
@ -52,7 +52,7 @@ F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
F src/tokenize.c 5624d342601f616157ba266abccc1368a5afee70 F src/tokenize.c 5624d342601f616157ba266abccc1368a5afee70
F src/update.c 7dd714a6a7fa47f849ebb36b6d915974d6c6accb F src/update.c 7dd714a6a7fa47f849ebb36b6d915974d6c6accb
F src/util.c b34cd91387bbfdc79319ea451a7d120cef478120 F src/util.c b34cd91387bbfdc79319ea451a7d120cef478120
F src/vdbe.c ccc394cf72b5c43b71309fecc35cdf5cd252e154 F src/vdbe.c 9213ff1ab136eabcf3e8a58157765a01274bf8e9
F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc
F src/where.c 9d36f6c9fea4af71501770c13089f824cb9b033c F src/where.c 9d36f6c9fea4af71501770c13089f824cb9b033c
F test/all.test 6aa106eee4d7127afa5cee97c51a783a79694ead F test/all.test 6aa106eee4d7127afa5cee97c51a783a79694ead
@ -60,7 +60,7 @@ F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1 F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
F test/btree2.test e3b81ec33dc2f89b3e6087436dfe605b870c9080 F test/btree2.test e3b81ec33dc2f89b3e6087436dfe605b870c9080
F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895 F test/btree3.test 9caa9e22491dd8cd8aa36d7ac3b48b089817c895
F test/conflict.test 3383ac08da14cdba11bb4c9495583cf87f67afb1 F test/conflict.test 5149646703d3930c9111068b5cda7e2e938476e3
F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a
F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8 F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8
F test/expr.test 846795016b5993a7411f772eebe82ab67bd7230a F test/expr.test 846795016b5993a7411f772eebe82ab67bd7230a
@ -68,7 +68,7 @@ F test/func.test d34e461f0acb0cf2978a4b3a3e098460f2ea8fbc
F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
F test/index.test c8a471243bbf878974b99baf5badd59407237cf3 F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9 F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9
F test/insert2.test 2f02b1e0dbfba3e8c76496209be5f4010b584181 F test/insert2.test eb8481878a7f52ccb4e3346f87550f5afdd77f76
F test/intpkey.test 31b5f28b2c44273e6695cf36ab2e4133aee7753c F test/intpkey.test 31b5f28b2c44273e6695cf36ab2e4133aee7753c
F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a
@ -94,7 +94,7 @@ F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b
F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78 F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78
F test/table.test 17b0b6eafa3faaee5545b7a94e6c1ff73f0880f3 F test/table.test 17b0b6eafa3faaee5545b7a94e6c1ff73f0880f3
F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6 F test/tableapi.test 3c80421a889e1d106df16e5800fa787f0d2914a6
F test/tclsqlite.test ca8dd89b02ab68bd4540163c24551756a69f6783 F test/tclsqlite.test 79deeffd7cd637ca0f06c5dbbf2f44d272079533
F test/temptable.test 0e9934283259a5e637eec756a7eefd6964c0f79b F test/temptable.test 0e9934283259a5e637eec756a7eefd6964c0f79b
F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6 F test/tester.tcl dc1b56bd628b487e4d75bfd1e7480b5ed8810ac6
F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee F test/trans.test ae0b9a82d5d34122c3a3108781eb8d078091ccee
@ -115,7 +115,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4 F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
F www/c_interface.tcl 567cda531aac9d68a61ef02e26c6b202bd856db2 F www/c_interface.tcl 79f63ffa8c6e7adb9e3449ff325d093cfb0ce3e3
F www/changes.tcl 4083f03195f0ae36411e095a2d18e5fb4a3c40d9 F www/changes.tcl 4083f03195f0ae36411e095a2d18e5fb4a3c40d9
F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2 F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060 F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
@ -124,14 +124,14 @@ F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
F www/faq.tcl fb1e92e2f604546694f83a36d969492f52fb685d F www/faq.tcl fb1e92e2f604546694f83a36d969492f52fb685d
F www/formatchng.tcl 2ce21ff30663fad6618198fe747ce675df577590 F www/formatchng.tcl 2ce21ff30663fad6618198fe747ce675df577590
F www/index.tcl 2a9653ebeeaba3aca3401f476ba0e0e4acb40929 F www/index.tcl 2a9653ebeeaba3aca3401f476ba0e0e4acb40929
F www/lang.tcl 75615f1acd8a428d93546ad9edfbf8f78ee1e2df F www/lang.tcl 2d4654255ad1ec7f58d02dc41b59528c0ee6ea44
F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc
F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc
F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
P bb83642e9a6c1c9ade861618496933c9f922a8f8 P 43a77f019d34e1a6b3f502ad0ec31a00c8fdbe6e
R f474a54633adfc730f9670e6001e85d7 R f1c51fffab48448616c03a8f7db64a46
U drh U drh
Z ee5b859e30a1d5d36bceb3c81154e192 Z fa8a7c3395ff2734ddb7f18394f23f2a

View File

@ -1 +1 @@
43a77f019d34e1a6b3f502ad0ec31a00c8fdbe6e 6e71493b9dc77d508c3ce90562766789e87e6d80

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements. ** to handle DELETE FROM statements.
** **
** $Id: delete.c,v 1.29 2002/03/03 18:59:40 drh Exp $ ** $Id: delete.c,v 1.30 2002/04/12 10:08:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -183,7 +183,7 @@ void sqliteDeleteFrom(
} }
end = sqliteVdbeMakeLabel(v); end = sqliteVdbeMakeLabel(v);
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
sqliteGenerateRowDelete(v, pTab, base); sqliteGenerateRowDelete(v, pTab, base, 1);
sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeAddOp(v, OP_Goto, 0, addr);
sqliteVdbeResolveLabel(v, end); sqliteVdbeResolveLabel(v, end);
sqliteVdbeAddOp(v, OP_ListReset, 0, 0); sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
@ -229,11 +229,12 @@ delete_from_cleanup:
void sqliteGenerateRowDelete( void sqliteGenerateRowDelete(
Vdbe *v, /* Generate code into this VDBE */ Vdbe *v, /* Generate code into this VDBE */
Table *pTab, /* Table containing the row to be deleted */ Table *pTab, /* Table containing the row to be deleted */
int base /* Cursor number for the table */ int base, /* Cursor number for the table */
int count /* Increment the row change counter */
){ ){
sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
sqliteGenerateRowIndexDelete(v, pTab, base, 0); sqliteGenerateRowIndexDelete(v, pTab, base, 0);
sqliteVdbeAddOp(v, OP_Delete, base, 0); sqliteVdbeAddOp(v, OP_Delete, base, count);
} }
/* /*

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite. ** to handle INSERT statements in SQLite.
** **
** $Id: insert.c,v 1.51 2002/04/12 03:55:16 drh Exp $ ** $Id: insert.c,v 1.52 2002/04/12 10:08:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -526,7 +526,7 @@ void sqliteGenerateConstraintChecks(
break; break;
} }
case OE_Replace: { case OE_Replace: {
sqliteGenerateRowDelete(v, pTab, base); sqliteGenerateRowDelete(v, pTab, base, 0);
if( isUpdate ){ if( isUpdate ){
sqliteVdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1); sqliteVdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1);
sqliteVdbeAddOp(v, OP_MoveTo, base, 0); sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
@ -573,7 +573,7 @@ void sqliteCompleteInsertion(
sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0); sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0);
} }
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_PutIntKey, base, 0); sqliteVdbeAddOp(v, OP_PutIntKey, base, 1);
if( isUpdate && recnoChng ){ if( isUpdate && recnoChng ){
sqliteVdbeAddOp(v, OP_Pop, 1, 0); sqliteVdbeAddOp(v, OP_Pop, 1, 0);
} }

View File

@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be ** other files are for internal use by SQLite and should not be
** accessed by users of the library. ** accessed by users of the library.
** **
** $Id: main.c,v 1.68 2002/03/06 22:01:36 drh Exp $ ** $Id: main.c,v 1.69 2002/04/12 10:08:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "os.h" #include "os.h"
@ -421,6 +421,13 @@ int sqlite_last_insert_rowid(sqlite *db){
return db->lastRowid; return db->lastRowid;
} }
/*
** Return the number of changes in the most recent call to sqlite_exec().
*/
int sqlite_changes(sqlite *db){
return db->nChange;
}
/* /*
** Close an existing SQLite database ** Close an existing SQLite database
*/ */
@ -526,6 +533,8 @@ int sqlite_exec(
return rc; return rc;
} }
} }
if( db->recursionDepth==0 ){ db->nChange = 0; }
db->recursionDepth++;
memset(&sParse, 0, sizeof(sParse)); memset(&sParse, 0, sizeof(sParse));
sParse.db = db; sParse.db = db;
sParse.pBe = db->pBe; sParse.pBe = db->pBe;
@ -544,6 +553,7 @@ int sqlite_exec(
if( sParse.rc==SQLITE_SCHEMA ){ if( sParse.rc==SQLITE_SCHEMA ){
clearHashTable(db, 1); clearHashTable(db, 1);
} }
db->recursionDepth--;
return sParse.rc; return sParse.rc;
} }

View File

@ -12,7 +12,7 @@
** This header file defines the interface that the SQLite library ** This header file defines the interface that the SQLite library
** presents to client programs. ** presents to client programs.
** **
** @(#) $Id: sqlite.h.in,v 1.29 2002/03/08 02:12:00 drh Exp $ ** @(#) $Id: sqlite.h.in,v 1.30 2002/04/12 10:08:59 drh Exp $
*/ */
#ifndef _SQLITE_H_ #ifndef _SQLITE_H_
#define _SQLITE_H_ #define _SQLITE_H_
@ -173,6 +173,28 @@ int sqlite_exec(
*/ */
int sqlite_last_insert_rowid(sqlite*); int sqlite_last_insert_rowid(sqlite*);
/*
** This function returns the number of database rows that were changed
** (or inserted or deleted) by the most recent called sqlite_exec().
**
** All changes are counted, even if they were later undone by a
** ROLLBACK or ABORT. Except, changes associated with creating and
** dropping tables are not counted.
**
** If a callback invokes sqlite_exec() recursively, then the changes
** in the inner, recursive call are counted together with the changes
** in the outer call.
**
** SQLite implements the command "DELETE FROM table" without a WHERE clause
** by dropping and recreating the table. (This is much faster than going
** through and deleting individual elements form the table.) Because of
** this optimization, the change count for "DELETE FROM table" will be
** zero regardless of the number of elements that were originally in the
** table. To get an accurate count of the number of rows deleted, use
** "DELETE FROM table WHERE 1" instead.
*/
int sqlite_changes(sqlite*);
/* If the parameter to this routine is one of the return value constants /* If the parameter to this routine is one of the return value constants
** defined above, then this routine returns a constant text string which ** defined above, then this routine returns a constant text string which
** descripts (in English) the meaning of the return value. ** descripts (in English) the meaning of the return value.

View File

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** Internal interface definitions for SQLite.
** **
** @(#) $Id: sqliteInt.h,v 1.104 2002/03/12 23:10:05 drh Exp $ ** @(#) $Id: sqliteInt.h,v 1.105 2002/04/12 10:08:59 drh Exp $
*/ */
#include "sqlite.h" #include "sqlite.h"
#include "hash.h" #include "hash.h"
@ -167,6 +167,8 @@ struct sqlite {
int lastRowid; /* ROWID of most recent insert */ int lastRowid; /* ROWID of most recent insert */
int priorNewRowid; /* Last randomly generated ROWID */ int priorNewRowid; /* Last randomly generated ROWID */
int onError; /* Default conflict algorithm */ int onError; /* Default conflict algorithm */
int nChange; /* Number of rows changed */
int recursionDepth; /* Number of nested calls to sqlite_exec() */
}; };
/* /*
@ -630,7 +632,7 @@ void sqliteCommitTransaction(Parse*);
void sqliteRollbackTransaction(Parse*); void sqliteRollbackTransaction(Parse*);
char *sqlite_mprintf(const char *, ...); char *sqlite_mprintf(const char *, ...);
int sqliteExprIsConstant(Expr*); int sqliteExprIsConstant(Expr*);
void sqliteGenerateRowDelete(Vdbe*, Table*, int); void sqliteGenerateRowDelete(Vdbe*, Table*, int, int);
void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*); void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*);
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int); void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);

View File

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** A TCL Interface to SQLite ** A TCL Interface to SQLite
** **
** $Id: tclsqlite.c,v 1.30 2002/03/11 02:06:13 drh Exp $ ** $Id: tclsqlite.c,v 1.31 2002/04/12 10:08:59 drh Exp $
*/ */
#ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */
@ -268,10 +268,12 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
SqliteDb *pDb = (SqliteDb*)cd; SqliteDb *pDb = (SqliteDb*)cd;
int choice; int choice;
static char *DB_optStrs[] = { static char *DB_optStrs[] = {
"busy", "close", "complete", "eval", "last_insert_rowid", "timeout", 0 "busy", "changes", "close", "complete",
"eval", "last_insert_rowid", "timeout", 0
}; };
enum DB_opts { enum DB_opts {
DB_BUSY, DB_CLOSE, DB_COMPLETE, DB_EVAL, DB_LAST_INSERT_ROWID, DB_TIMEOUT DB_BUSY, DB_CHANGES, DB_CLOSE, DB_COMPLETE,
DB_EVAL, DB_LAST_INSERT_ROWID, DB_TIMEOUT
}; };
if( objc<2 ){ if( objc<2 ){
@ -320,6 +322,25 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
break; break;
} }
/*
** $db changes
**
** Return the number of rows that were modified, inserted, or deleted by
** the most recent "eval".
*/
case DB_CHANGES: {
Tcl_Obj *pResult;
int nChange;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 2, objv, "");
return TCL_ERROR;
}
nChange = sqlite_changes(pDb->db);
pResult = Tcl_GetObjResult(interp);
Tcl_SetIntObj(pResult, nChange);
break;
}
/* $db close /* $db close
** **
** Shutdown the database ** Shutdown the database

View File

@ -30,7 +30,7 @@
** But other routines are also provided to help in building up ** But other routines are also provided to help in building up
** a program instruction by instruction. ** a program instruction by instruction.
** **
** $Id: vdbe.c,v 1.137 2002/04/09 03:15:07 drh Exp $ ** $Id: vdbe.c,v 1.138 2002/04/12 10:09:00 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@ -3155,19 +3155,16 @@ case OP_NewRecno: {
** stack. The key is the next value down on the stack. The key must ** stack. The key is the next value down on the stack. The key must
** be an integer. The stack is popped twice by this instruction. ** be an integer. The stack is popped twice by this instruction.
** **
** If P2==1 then overwriting is prohibited. If a prior entry with ** If P2==1 then the row change count is incremented. If P2==0 the
** the same key exists, an SQLITE_CONSTRAINT exception is raised. ** row change count is unmodified.
*/ */
/* Opcode: PutStrKey P1 P2 * /* Opcode: PutStrKey P1 * *
** **
** Write an entry into the database file P1. A new entry is ** Write an entry into the database file P1. A new entry is
** created if it doesn't already exist or the data for an existing ** created if it doesn't already exist or the data for an existing
** entry is overwritten. The data is the value on the top of the ** entry is overwritten. The data is the value on the top of the
** stack. The key is the next value down on the stack. The key must ** stack. The key is the next value down on the stack. The key must
** be a string. The stack is popped twice by this instruction. ** be a string. The stack is popped twice by this instruction.
**
** If P2==1 then overwriting is prohibited. If a prior entry with
** the same key exists, an SQLITE_CONSTRAINT exception is raised.
*/ */
case OP_PutIntKey: case OP_PutIntKey:
case OP_PutStrKey: { case OP_PutStrKey: {
@ -3188,16 +3185,7 @@ case OP_PutStrKey: {
iKey = intToKey(aStack[nos].i); iKey = intToKey(aStack[nos].i);
zKey = (char*)&iKey; zKey = (char*)&iKey;
db->lastRowid = aStack[nos].i; db->lastRowid = aStack[nos].i;
} if( pOp->p2 ) db->nChange++;
if( pOp->p2 ){
int res;
rc = sqliteBtreeMoveto(p->aCsr[i].pCursor, zKey, nKey, &res);
if( res==0 && rc==SQLITE_OK ){
rc = SQLITE_CONSTRAINT;
}
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
} }
rc = sqliteBtreeInsert(p->aCsr[i].pCursor, zKey, nKey, rc = sqliteBtreeInsert(p->aCsr[i].pCursor, zKey, nKey,
zStack[tos], aStack[tos].n); zStack[tos], aStack[tos].n);
@ -3208,7 +3196,7 @@ case OP_PutStrKey: {
break; break;
} }
/* Opcode: Delete P1 * * /* Opcode: Delete P1 P2 *
** **
** Delete the record at which the P1 cursor is currently pointing. ** Delete the record at which the P1 cursor is currently pointing.
** **
@ -3216,12 +3204,16 @@ case OP_PutStrKey: {
** record in the table. If it is left pointing at the next record, then ** record in the table. If it is left pointing at the next record, then
** the next Next instruction will be a no-op. Hence it is OK to delete ** the next Next instruction will be a no-op. Hence it is OK to delete
** a record from within an Next loop. ** a record from within an Next loop.
**
** The row change counter is incremented if P2==1 and is unmodified
** if P2==0.
*/ */
case OP_Delete: { case OP_Delete: {
int i = pOp->p1; int i = pOp->p1;
if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){ if( VERIFY( i>=0 && i<p->nCursor && ) p->aCsr[i].pCursor!=0 ){
rc = sqliteBtreeDelete(p->aCsr[i].pCursor); rc = sqliteBtreeDelete(p->aCsr[i].pCursor);
} }
if( pOp->p2 ) db->nChange++;
break; break;
} }

View File

@ -13,7 +13,7 @@
# This file implements tests for the conflict resolution extension # This file implements tests for the conflict resolution extension
# to SQLite. # to SQLite.
# #
# $Id: conflict.test,v 1.10 2002/04/09 03:28:01 drh Exp $ # $Id: conflict.test,v 1.11 2002/04/12 10:09:00 drh Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@ -399,6 +399,9 @@ do_test conflict-7.2 {
UPDATE OR IGNORE t1 SET a=1000; UPDATE OR IGNORE t1 SET a=1000;
} }
} {1} } {1}
do_test conflict-7.2.1 {
db changes
} {1}
do_test conflict-7.3 { do_test conflict-7.3 {
execsql { execsql {
SELECT b FROM t1 WHERE a=1000; SELECT b FROM t1 WHERE a=1000;
@ -415,6 +418,9 @@ do_test conflict-7.5 {
UPDATE OR REPLACE t1 SET a=1001; UPDATE OR REPLACE t1 SET a=1001;
} }
} {50} } {50}
do_test conflict-7.5.1 {
db changes
} {50}
do_test conflict-7.6 { do_test conflict-7.6 {
execsql { execsql {
SELECT b FROM t1 WHERE a=1001; SELECT b FROM t1 WHERE a=1001;
@ -425,6 +431,9 @@ do_test conflict-7.7 {
SELECT count(*) FROM t1; SELECT count(*) FROM t1;
} }
} {1} } {1}
do_test conflict-7.7.1 {
db changes
} {0}
# Make sure the row count is right for rows that are ignored on # Make sure the row count is right for rows that are ignored on
# an insert. # an insert.
@ -438,31 +447,49 @@ do_test conflict-8.1 {
INSERT OR IGNORE INTO t1 VALUES(2,3); INSERT OR IGNORE INTO t1 VALUES(2,3);
} }
} {1} } {1}
do_test conflict-8.1.1 {
db changes
} {1}
do_test conflict-8.2 { do_test conflict-8.2 {
execsql { execsql {
INSERT OR IGNORE INTO t1 VALUES(2,4); INSERT OR IGNORE INTO t1 VALUES(2,4);
} }
} {0} } {0}
do_test conflict-8.2.1 {
db changes
} {0}
do_test conflict-8.3 { do_test conflict-8.3 {
execsql { execsql {
INSERT OR REPLACE INTO t1 VALUES(2,4); INSERT OR REPLACE INTO t1 VALUES(2,4);
} }
} {1} } {1}
do_test conflict-8.3.1 {
db changes
} {1}
do_test conflict-8.4 { do_test conflict-8.4 {
execsql { execsql {
INSERT OR IGNORE INTO t1 SELECT * FROM t1; INSERT OR IGNORE INTO t1 SELECT * FROM t1;
} }
} {0} } {0}
do_test conflict-8.4.1 {
db changes
} {0}
do_test conflict-8.5 { do_test conflict-8.5 {
execsql { execsql {
INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1; INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1;
} }
} {2} } {2}
do_test conflict-8.5.1 {
db changes
} {2}
do_test conflict-8.6 { do_test conflict-8.6 {
execsql { execsql {
INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1; INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1;
} }
} {3} } {3}
do_test conflict-8.6.1 {
db changes
} {3}
do_test insert-99.1 { do_test insert-99.1 {
set x [execsql {PRAGMA integrity_check}] set x [execsql {PRAGMA integrity_check}]

View File

@ -12,7 +12,7 @@
# focus of this file is testing the INSERT statement that takes is # focus of this file is testing the INSERT statement that takes is
# result from a SELECT. # result from a SELECT.
# #
# $Id: insert2.test,v 1.8 2002/03/03 02:49:52 drh Exp $ # $Id: insert2.test,v 1.9 2002/04/12 10:09:00 drh Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@ -38,6 +38,9 @@ do_test insert2-1.1.1 {
} }
} {6} } {6}
do_test insert2-1.1.2 { do_test insert2-1.1.2 {
db changes
} {6}
do_test insert2-1.1.3 {
execsql {SELECT * FROM t1 ORDER BY log} execsql {SELECT * FROM t1 ORDER BY log}
} {0 1 1 1 2 2 3 4 4 8 5 4} } {0 1 1 1 2 2 3 4 4 8 5 4}
@ -138,6 +141,11 @@ do_test insert2-3.2 {
INSERT INTO t4 VALUES(9,18); INSERT INTO t4 VALUES(9,18);
INSERT INTO t4 VALUES(10,20); INSERT INTO t4 VALUES(10,20);
COMMIT; COMMIT;
}
db changes
} {9}
do_test insert2-3.2.1 {
execsql {
SELECT count(*) FROM t4; SELECT count(*) FROM t4;
} }
} {10} } {10}
@ -182,5 +190,8 @@ do_test insert2-3.7 {
ROLLBACK; ROLLBACK;
} }
} {1} } {1}
do_test insert2-3.8 {
db changes
} {159}
finish_test finish_test

View File

@ -15,7 +15,7 @@
# interface is pretty well tested. This file contains some addition # interface is pretty well tested. This file contains some addition
# tests for fringe issues that the main test suite does not cover. # tests for fringe issues that the main test suite does not cover.
# #
# $Id: tclsqlite.test,v 1.5 2002/01/16 21:00:28 drh Exp $ # $Id: tclsqlite.test,v 1.6 2002/04/12 10:09:00 drh Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@ -29,7 +29,7 @@ do_test tcl-1.1 {
do_test tcl-1.2 { do_test tcl-1.2 {
set v [catch {db bogus} msg] set v [catch {db bogus} msg]
lappend v $msg lappend v $msg
} {1 {bad option "bogus": must be busy, close, complete, eval, last_insert_rowid, or timeout}} } {1 {bad option "bogus": must be busy, changes, close, complete, eval, last_insert_rowid, or timeout}}
do_test tcl-1.3 { do_test tcl-1.3 {
execsql {CREATE TABLE t1(a int, b int)} execsql {CREATE TABLE t1(a int, b int)}
execsql {INSERT INTO t1 VALUES(10,20)} execsql {INSERT INTO t1 VALUES(10,20)}

View File

@ -1,7 +1,7 @@
# #
# Run this Tcl script to generate the sqlite.html file. # Run this Tcl script to generate the sqlite.html file.
# #
set rcsid {$Id: c_interface.tcl,v 1.25 2002/03/04 02:26:17 drh Exp $} set rcsid {$Id: c_interface.tcl,v 1.26 2002/04/12 10:09:00 drh Exp $}
puts {<html> puts {<html>
<head> <head>
@ -307,6 +307,8 @@ useful interfaces. These extended routines are as follows:
<blockquote><pre> <blockquote><pre>
int sqlite_last_insert_rowid(sqlite*); int sqlite_last_insert_rowid(sqlite*);
int sqlite_changes(sqlite*);
int sqlite_get_table( int sqlite_get_table(
sqlite*, sqlite*,
char *sql, char *sql,
@ -389,6 +391,22 @@ the key is automatically generated. You can find the value of the key
for the most recent INSERT statement using the for the most recent INSERT statement using the
<b>sqlite_last_insert_rowid()</b> API function.</p> <b>sqlite_last_insert_rowid()</b> API function.</p>
<h2>The number of rows that changed</h2>
<p>The <b>sqlite_changes()</b> API function returns the number of rows
that were inserted, deleted, or modified during the most recent
<b>sqlite_exec()</b> call. The number reported includes any changes
that were later undo by a ROLLBACK or ABORT. But rows that are
deleted because of a DROP TABLE are <em>not</em> counted.</p>
<p>SQLite implements the command "<b>DELETE FROM table</b>" (without
a WHERE clause) by dropping the table then recreating it.
This is much faster than deleting the elements of the table individually.
But it also means that the value returned from <b>sqlite_changes()</b>
will be zero regardless of the number of elements that were originally
in the table. If an accurate count of the number of elements deleted
is necessary, use "<b>DELETE FROM table WHERE 1</b>" instead.</p>
<h2>Querying without using a callback function</h2> <h2>Querying without using a callback function</h2>
<p>The <b>sqlite_get_table()</b> function is a wrapper around <p>The <b>sqlite_get_table()</b> function is a wrapper around

View File

@ -1,7 +1,7 @@
# #
# Run this Tcl script to generate the sqlite.html file. # Run this Tcl script to generate the sqlite.html file.
# #
set rcsid {$Id: lang.tcl,v 1.31 2002/04/06 14:10:48 drh Exp $} set rcsid {$Id: lang.tcl,v 1.32 2002/04/12 10:09:00 drh Exp $}
puts {<html> puts {<html>
<head> <head>
@ -843,6 +843,8 @@ with caution.</p>
<p>When on, the COUNT_CHANGES pragma causes the callback function to <p>When on, the COUNT_CHANGES pragma causes the callback function to
be invoked once for each DELETE, INSERT, or UPDATE operation. The be invoked once for each DELETE, INSERT, or UPDATE operation. The
argument is the number of rows that were changed.</p> argument is the number of rows that were changed.</p>
<p>This pragma may be removed from future versions of SQLite.
Consider using the <b>sqlite_changes()</b> API function instead.</p></li>
<li><p><b>PRAGMA default_cache_size; <li><p><b>PRAGMA default_cache_size;
<br>PRAGMA default_cache_size = </b><i>Number-of-pages</i><b>;</b></p> <br>PRAGMA default_cache_size = </b><i>Number-of-pages</i><b>;</b></p>

View File

@ -1,7 +1,7 @@
# #
# Run this Tcl script to generate the tclsqlite.html file. # Run this Tcl script to generate the tclsqlite.html file.
# #
set rcsid {$Id: tclsqlite.tcl,v 1.6 2002/01/16 21:00:28 drh Exp $} set rcsid {$Id: tclsqlite.tcl,v 1.7 2002/04/12 10:09:00 drh Exp $}
puts {<html> puts {<html>
<head> <head>
@ -50,12 +50,13 @@ the database is stored.
<p> <p>
Once an SQLite database is open, it can be controlled using Once an SQLite database is open, it can be controlled using
methods of the <i>dbcmd</i>. There are currently 6 methods methods of the <i>dbcmd</i>. There are currently 7 methods
defined:</p> defined:</p>
<p> <p>
<ul> <ul>
<li> busy <li> busy
<li> changes
<li> close <li> close
<li> complete <li> complete
<li> eval <li> eval
@ -248,6 +249,12 @@ should return "1" if it wants SQLite to abandon the current operation.
<p>The "last_insert_rowid" method returns an integer which is the ROWID <p>The "last_insert_rowid" method returns an integer which is the ROWID
of the most recently inserted database row.</p> of the most recently inserted database row.</p>
<h2>The "changes" method</h2>
<p>The "changes" method returns an integer which is the number of rows
in the database that were inserted, deleted, and/or modified by the most
recent "eval" method.</p>
} }
puts { puts {