]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Added support for the COUNT_CHANGES pragma in order to help out the
authordrh <drh@noemail.net>
Mon, 15 Oct 2001 00:44:35 +0000 (00:44 +0000)
committerdrh <drh@noemail.net>
Mon, 15 Oct 2001 00:44:35 +0000 (00:44 +0000)
ODBC driver.  Fixed a but on count(*) when applied to empty tables. (CVS 289)

FossilOrigin-Name: 747bf1b30b74cfd0e9c27e7c0bc5172637f35520

19 files changed:
Makefile.template
VERSION
manifest
manifest.uuid
src/btree.c
src/build.c
src/delete.c
src/insert.c
src/os.c
src/select.c
src/sqliteInt.h
src/update.c
test/delete.test
test/insert2.test
test/select1.test
test/select5.test
test/update.test
www/changes.tcl
www/lang.tcl

index 7e45f4464eee2e7d23cd280f67bccdfedd35fcad..e9486371dca763aaebaaf93af2efc849393a6903 100644 (file)
@@ -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 2165f8f9b6a830a9d8dd3d2857d7fb86eba1d250..e01025862f79350522cba47477d9bb6c51729e12 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.0.4
+2.0.5
index 820c36221cfca7d3327c90c81c9769ad7e786523..d4e0a79cf6d2a185f65d1a905edeb21115952d08 100644 (file)
--- 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
index 55d1bcf60dd5cfdf025f7deaf8e0a9de3cf6c851..e8af732b9679fa374a1ff1a6864775ad423c2832 100644 (file)
@@ -1 +1 @@
-444447007a32f9ebd9e0714c6cbe0d9a63e193b1
\ No newline at end of file
+747bf1b30b74cfd0e9c27e7c0bc5172637f35520
\ No newline at end of file
index 761ccac948e36008cd5e793454782346f7609bb2..c17ef14049735d7066351bff8df58c564ff0ecf5 100644 (file)
@@ -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 ){
index 8f87dd4717dd2155f6acb1c84ff5a6a0f0b7a5cf..fc45a3441e9d1351f734a92f7f8c6a5531ee403c 100644 (file)
@@ -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 <ctype.h>
@@ -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;
index 6534f3070f9e479b742476a78bbe5fb6db9bb5ea..cd4694b618e0cfef63b88f52d538130bc3b186a9 100644 (file)
@@ -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);
index 489f7389ccb7a3a4f7441beb4212d127a9b29b90..8f93d5cebde8d5718696a0c9c689220f1b06e0b7 100644 (file)
@@ -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);
index f09aacc6305723698a0500185b6bd3e573ab82a0..862eb278b64c1c92b6a5367a6ee64a21cb39e8a6 100644 (file)
--- 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 thstat() 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
index ea265bad60519a65889aa98f873245284f5bb535..70e962cc4ee93775a2178c7a4360ae659989fc0b 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.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; i<pEList->nExpr; 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; i<pParse->nAgg; 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; i<pGroupBy->nExpr; i++){
         sqliteExprCode(pParse, pGroupBy->a[i].pExpr);
       }
       sqliteVdbeAddOp(v, OP_MakeKey, pGroupBy->nExpr, 0);
-      doFocus = 1;
-    }else{
-      doFocus = 0;
-      for(i=0; i<pParse->nAgg; 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; i<pParse->nAgg; i++){
         if( pParse->aAgg[i].isAgg ) continue;
index e23e2b1dcefb001955ea0f78679010d61f24d4ca..72d63abca66b59afb695cd21310e1ce679872ba2 100644 (file)
@@ -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
index f9f69fed5fd64c037973b3aec87d0dfd8ff45b01..f7061fce2ac65a044cfb56648f90d2885573a6c8 100644 (file)
@@ -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);
index 6ab2d3fec42938ad3382e00f3321fb0c12df4974..8fd27d92c5b69bab99bac917d1c22254ce9a1f52 100644 (file)
@@ -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}
+} {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 {
+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 {
index 59fb2e76e997f429b6d6e72daeb4d8574abf34a7..610e587511d23ae21c392723922ae4f2883cb766 100644 (file)
@@ -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}
index 7516aab77da393abd7ccf609c116cce957239e0b..d9fcb3db442f76316d0857879ee1f69862c89938 100644 (file)
@@ -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]
index 729268d2b978dea1ac002a5726bdc6cb41ef2064..a5095643cf90709a5e866829c476d4503f80ee5c 100644 (file)
@@ -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
index c389d20fb55e1b097c527e32b7b0294297eb5645..9ccef74991c25ec19eae1d154ce03d1c2243d818 100644 (file)
@@ -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}
 
index aecf8b62b6f160da4f4a25ed5512aa7c1e61d957..72285f6c381925eabcf057f1751f986b3000c47a 100644 (file)
@@ -17,6 +17,13 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
+chng {2001 Oct 14 (2.0.5)} {
+<li>Added the COUNT_CHANGES pragma.</li>
+<li>Changes to the FULL_COLUMN_NAMES pragma to help out the ODBC driver.</li>
+<li>Bug fix: "SELECT count(*)" was returning NULL for empty tables.
+    Now it returns 0.</li>
+}
+
 chng {2001 Oct 13 (2.0.4)} {
 <li>Bug fix: an abscure and relatively harmless bug was causing one of
     the tests to fail when gcc optimizations are turned on.  This release
index 8f622598056b65949dfdf3b3c26965deb6b4af9b..7e6a4c089bdad67001525e6bd2c591676aabc912 100644 (file)
@@ -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 {<html>
 <head>
@@ -631,6 +631,12 @@ with caution.</p>
     uses more memory, you can increase the cache size for a possible speed
     improvement.</p></li>
 
+<li><p><b>PRAGMA count_changes = ON;
+       <br>PRAGMA count_changes = OFF;</b></p>
+    <p>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.</p>
+
 <li><p><b>PRAGMA full_column_names = ON;
        <br>PRAGMA full_column_names = OFF;</b></p>
     <p>The column names reported in an SQLite callback are normally just
@@ -638,12 +644,32 @@ with caution.</p>
     is used.  But when full_column_names is turned on, column names are
     always reported as "TABLE.COLUMN" even for simple queries.</p></li>
 
-<li><p><b>PRAGMA vdbe_trace = ON;<br>PRAGMA vdbe_trace = OFF;</b></p>
-    <p>Turn tracing of the virtual database engine inside of the
-    SQLite library on and off.  This is used for debugging.</p></li>
+<li><p><b>PRAGMA index_info(</b><i>index-name</i><b>);</b></p>
+    <p>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.</p>
+
+<li><p><b>PRAGMA index_list(</b><i>table-name</i><b>);</b></p>
+    <p>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.</p>
 
 <li><p><b>PRAGMA parser_trace = ON;<br>PRAGMA parser_trace = OFF;</b></p>
     <p>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.
+    </p></li>
+
+<li><p><b>PRAGMA table_info(</b><i>table-name</i><b>);</b></p>
+    <p>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.</p>
+
+<li><p><b>PRAGMA vdbe_trace = ON;<br>PRAGMA vdbe_trace = OFF;</b></p>
+    <p>Turn tracing of the virtual database engine inside of the
     SQLite library on and off.  This is used for debugging.</p></li>
 </ul>