]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Start all transactions and verify all schema cookies near the beginning of
authordrh <drh@noemail.net>
Wed, 9 Jun 2004 00:48:12 +0000 (00:48 +0000)
committerdrh <drh@noemail.net>
Wed, 9 Jun 2004 00:48:12 +0000 (00:48 +0000)
of each vdbe program. (CVS 1543)

FossilOrigin-Name: 1086196460e261718e78512d77e25dde021a117d

manifest
manifest.uuid
src/build.c
src/parse.y
src/pragma.c
src/sqliteInt.h
src/trigger.c
src/vdbe.c
src/vdbeaux.c
src/where.c
test/attach.test

index dc4d7729cc606b0b661bbe07f32e875d9c2409f9..47e49d1e42e66fb6dac72580d2b60b5064072aca 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Do\snot\srequire\sa\sRESERVED\slock\swhen\stransitioning\sfrom\sSHARED\sto\sEXCLUSIVE.\s(CVS\s1542)
-D 2004-06-08T00:47:47
+C Start\sall\stransactions\sand\sverify\sall\sschema\scookies\snear\sthe\sbeginning\sof\nof\seach\svdbe\sprogram.\s(CVS\s1543)
+D 2004-06-09T00:48:12
 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -27,7 +27,7 @@ F src/attach.c e76e4590ec5dd389e5646b171881b5243a6ef391
 F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79
 F src/btree.c edb38affc2e83f4299e49104cfe14e6570d8bd32
 F src/btree.h 589427ac13bb544d298cd99726e2572a6fe4bdaa
-F src/build.c 83303494ccad0ed1cea24f73c7db1f2669820ccd
+F src/build.c f720a2538af6b39a0edc9f62fad3a8c809d455b5
 F src/date.c 8e6fa3173386fb29fdef012ee08a853c1e9908b2
 F src/delete.c b30f08250c9ed53a25a13c7c04599c1e8753992d
 F src/encode.c a876af473d1d636faa3dca51c7571f2e007eea37
@@ -49,14 +49,14 @@ F src/os_win.c a13b85a0d4889e3d0b254ed2a61354acddc59fc4
 F src/os_win.h 004eec47b1780fcaf07420ddc2072294b698d48c
 F src/pager.c 3fddd1e5b3e449b19e4f762ab1f1d10786d56d28
 F src/pager.h 0c7b5ac45c69e690c45d160d03bdc8fbd2d4657b
-F src/parse.y 27c1ce09f9d309be91f9e537df2fb00892990af4
-F src/pragma.c 54b4d67fa81fd38b911aa3325348dcae9ceac5a4
+F src/parse.y 19972fbaa3440f511da36eae1d1fe536fe2c7805
+F src/pragma.c 9328a31c22615758077e8ded1126affe8f5e7fbe
 F src/printf.c 63b15f1ea9fe3daa066bb7430fd20d4a2d717dc8
 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
 F src/select.c 0ac0adeb2ae15255ac4399d9ee1b0d25a266a676
 F src/shell.c ca519519dcbbc582f6d88f7d0e7583b857fd3469
 F src/sqlite.h.in 577974e9a1b85815ccddfb5b858695b62000f595
-F src/sqliteInt.h 845d2a3ffdb9a9050a1b55044d4856227b649b84
+F src/sqliteInt.h 472033b41fe76cf0a9c39dda410cedbadeb61b03
 F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2
 F src/tclsqlite.c c4d549ad9f5941288247759ead89d9632254fdc3
 F src/test1.c 416a37411f2ca7947e6509c5b6ddc6dd86a1204e
@@ -65,20 +65,20 @@ F src/test3.c beafd0ccf7b9ae784744be1b1e66ffe8f64c25da
 F src/test4.c a921a69821fd30209589228e64f94e9f715b6fe2
 F src/test5.c 44178ce85c3afd2004ab4eeb5cfd7487116ce366
 F src/tokenize.c 183c5d7da11affab5d70d903d33409c8c0ce6c5b
-F src/trigger.c 532daca4972bbf1165bdeecf48d9949eee8c24c0
+F src/trigger.c 2c28bf37f21e1ca2fc39cd88db8dbe3ad6ac5419
 F src/update.c 259f06e7b22c684b2d3dda54a18185892d6e9573
 F src/utf.c c2c8e445bfea724f3502609d6389fe66651f02ab
 F src/util.c 8b3680271111bcdf5b395916b08b9a6684e0e73d
 F src/vacuum.c b921eb778842592e1fb48a9d4cef7e861103878f
-F src/vdbe.c 392c6b02c525ea12dff403ba4ceb42b0afcb42f5
+F src/vdbe.c 3ffa1effb57f861131f1abc6d4f14db81fad2ade
 F src/vdbe.h 46f74444a213129bc4b5ce40124dd8ed613b0cde
 F src/vdbeInt.h ab592f23ed5a1913f9a506bd7b76c5e39377942a
 F src/vdbeapi.c 4ac95766b0515538037a7aec172ed26142f97cf9
-F src/vdbeaux.c cd1be846336f039442503991fa2aba70f1708554
+F src/vdbeaux.c ab8c99fd5c94ff366f0db3801eb7a1d3f4026e23
 F src/vdbemem.c 5d029d83bc60eaf9c45837fcbc0b03348ec95d7a
-F src/where.c 444a7c3a8b1eb7bba072e489af628555d21d92a4
+F src/where.c ded00b92dcee77ebe358ff48f5ef05ee8e8ff163
 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
-F test/attach.test 1635022d7e1d95dc92fe381cc62f9bf25cb29d73
+F test/attach.test aed659e52635662bcd5069599aaca823533edf5a
 F test/attach2.test 2185dce04ef9ceb7b2d3df7d17fb2c3817028dea
 F test/attach3.test 8259ab833b5dcdf4acd75d9653f42f703ce2e013
 F test/auth.test 95809b8f6a9bec18b94d28cafd03fe27d2f8a9e9
@@ -215,7 +215,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248
 F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075
 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
-P 97aa54bb70715934e0af082d51b9b0f6bb847e8e
-R b382950b985a7f7c010fa51e63195e87
+P 4dfdea7373f3471d17498da3d6c3aaf926a72d4b
+R 6bf109928e5c77863915e3d9ce0daa3e
 U drh
-Z 979bdbef9456a09bba11f45e5a0188cd
+Z 588abfea4494baa41bec5420972c8c24
index fb0060ce47eb2348c0b8ecd0f6d92b4bf93db1c3..1d49d7cc41a3d328ef77092b07b09a9baef0f11c 100644 (file)
@@ -1 +1 @@
-4dfdea7373f3471d17498da3d6c3aaf926a72d4b
\ No newline at end of file
+1086196460e261718e78512d77e25dde021a117d
\ No newline at end of file
index df6bb336e80bbb394832333528df11dfa50379c4..00bf979b077bc6bc631900df09ddb2896d2b0648 100644 (file)
@@ -23,7 +23,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.209 2004/06/08 00:02:33 danielk1977 Exp $
+** $Id: build.c,v 1.210 2004/06/09 00:48:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -58,22 +58,44 @@ void sqlite3BeginParse(Parse *pParse, int explainFlag){
 
 /*
 ** This routine is called after a single SQL statement has been
-** parsed and we want to execute the VDBE code to implement 
-** that statement.  Prior action routines should have already
-** constructed VDBE code to do the work of the SQL statement.
-** This routine just has to execute the VDBE code.
+** parsed and a VDBE program to execute that statement has been
+** prepared.  This routine puts the finishing touches on the
+** VDBE program and resets the pParse structure for the next
+** parse.
 **
 ** Note that if an error occurred, it might be the case that
 ** no VDBE code was generated.
 */
-void sqlite3Exec(Parse *pParse){
-  sqlite *db = pParse->db;
-  Vdbe *v = pParse->pVdbe;
+void sqlite3FinishCoding(Parse *pParse){
+  sqlite *db;
+  Vdbe *v;
 
-  if( v==0 && (v = sqlite3GetVdbe(pParse))!=0 ){
+  if( sqlite3_malloc_failed ) return;
+
+  /* Begin by generating some termination code at the end of the
+  ** vdbe program
+  */
+  db = pParse->db;
+  v = sqlite3GetVdbe(pParse);
+  if( v ){
     sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+    if( pParse->cookieMask!=0 ){
+      u32 mask;
+      int iDb;
+      sqlite3VdbeChangeP2(v, pParse->cookieGoto, sqlite3VdbeCurrentAddr(v));
+      for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
+        if( (mask & pParse->cookieMask)==0 ) continue;
+        sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
+        if( iDb!=1 ){
+          sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
+        }
+      }
+      sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto+1);
+    }
   }
-  if( sqlite3_malloc_failed ) return;
+
+  /* Get the VDBE program ready for execution
+  */
   if( v && pParse->nErr==0 ){
     FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
     sqlite3VdbeTrace(v, trace);
@@ -88,6 +110,7 @@ void sqlite3Exec(Parse *pParse){
   pParse->nSet = 0;
   pParse->nAgg = 0;
   pParse->nVar = 0;
+  pParse->cookieMask = 0;
 }
 
 /*
@@ -2249,19 +2272,41 @@ void sqlite3RollbackTransaction(Parse *pParse){
 }
 
 /*
-** Generate VDBE code that will verify the schema cookie for all
-** named database files.
+** Generate VDBE code that will verify the schema cookie and start
+** a read-transaction for all named database files.
+**
+** It is important that all schema cookies be verified and all
+** read transactions be started before anything else happens in
+** the VDBE program.  But this routine can be called after much other
+** code has been generated.  So here is what we do:
+**
+** The first time this routine is called, we code an OP_Gosub that
+** will jump to a subroutine at the end of the program.  Then we
+** record every database that needs its schema verified in the
+** pParse->cookieMask field.  Later, after all other code has been
+** generated, the subroutine that does the cookie verifications and
+** starts the transactions will be coded and the OP_Gosub P2 value
+** will be made to point to that subroutine.  The generation of the
+** cookie verification subroutine code happens in sqlite3FinishCoding().
 */
 void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
-  sqlite *db = pParse->db;
-  Vdbe *v = sqlite3GetVdbe(pParse);
-  if( v==0 ) return;
+  sqlite *db;
+  Vdbe *v;
+  int mask;
+
+  v = sqlite3GetVdbe(pParse);
+  if( v==0 ) return;  /* This only happens if there was a prior error */
+  db = pParse->db;
   assert( iDb>=0 && iDb<db->nDb );
-  assert( db->aDb[iDb].pBt!=0 );
-  if( iDb!=1 && (iDb>63 || !(pParse->cookieMask & ((u64)1<<iDb))) ){
-    sqlite3VdbeAddOp(v, OP_Transaction, iDb, 0);
-    sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie);
-    pParse->cookieMask |= ((u64)1<<iDb);
+  assert( db->aDb[iDb].pBt!=0 || iDb==1 );
+  assert( iDb<32 );
+  if( pParse->cookieMask==0 ){
+    pParse->cookieGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
+  }
+  mask = 1<<iDb;
+  if( (pParse->cookieMask & mask)==0 ){
+    pParse->cookieMask |= mask;
+    pParse->cookieValue[iDb] = db->aDb[iDb].schema_cookie;
   }
 }
 
@@ -2287,11 +2332,8 @@ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
   sqlite *db = pParse->db;
   Vdbe *v = sqlite3GetVdbe(pParse);
   if( v==0 ) return;
-  sqlite3VdbeAddOp(v, OP_Transaction, iDb, 1);
-  if( (iDb>63 || !(pParse->cookieMask & ((u64)1<<iDb))) ){
-    sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, db->aDb[iDb].schema_cookie);
-    pParse->cookieMask |= ((u64)1<<iDb);
-  }
+  sqlite3CodeVerifySchema(pParse, iDb);
+  pParse->writeMask |= 1<<iDb;
   if( setStatement ){
     sqlite3VdbeAddOp(v, OP_Statement, iDb, 0);
   }
index 6e92a96fffbdffd86c2203b8d0b99211af4795ad..246cc89282f4f91e4015c2380c8f6ea294c4fd0f 100644 (file)
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.125 2004/05/31 08:55:34 danielk1977 Exp $
+** @(#) $Id: parse.y,v 1.126 2004/06/09 00:48:13 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -69,7 +69,7 @@ cmdlist ::= cmdlist ecmd.
 cmdlist ::= ecmd.
 ecmd ::= explain cmdx SEMI.
 ecmd ::= SEMI.
-cmdx ::= cmd.           { sqlite3Exec(pParse); }
+cmdx ::= cmd.           { sqlite3FinishCoding(pParse); }
 explain ::= EXPLAIN.    { sqlite3BeginParse(pParse, 1); }
 explain ::= .           { sqlite3BeginParse(pParse, 0); }
 
index 85bb495fdcc1f10f991448fab7c747b448dceb9d..14e287fee199af340d4ce42463f1241a333ee461 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the PRAGMA command.
 **
-** $Id: pragma.c,v 1.37 2004/06/07 07:52:18 danielk1977 Exp $
+** $Id: pragma.c,v 1.38 2004/06/09 00:48:13 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -599,6 +599,8 @@ void sqlite3Pragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
       HashElem *x;
       int cnt = 0;
 
+      sqlite3CodeVerifySchema(pParse, i);
+
       /* Do an integrity check of the B-Tree
       */
       for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
index bc73f021d222af3c567881b9f7d6fe9be1c941be..c738b4363e3a071755d95182826e873889d2304e 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.271 2004/06/07 07:52:18 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.272 2004/06/09 00:48:13 drh Exp $
 */
 #include "config.h"
 #include "sqlite3.h"
@@ -58,8 +58,9 @@
 /*
 ** The maximum number of attached databases.  This must be at least 2
 ** in order to support the main database file (0) and the file used to
-** hold temporary tables (1).  And it must be less than 256 because
-** an unsigned character is used to stored the database index.
+** hold temporary tables (1).  And it must be less than 32 because
+** we use a bitmask of databases with a u32 in places (for example
+** the Parse.cookieMask field).
 */
 #define MAX_ATTACHED 10
 
@@ -1005,7 +1006,10 @@ struct Parse {
   const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
   Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
   TriggerStack *trigStack;  /* Trigger actions being coded */
-  u64 cookieMask;      /* Bitmask of schema verified databases */
+  u32 cookieMask;      /* Bitmask of schema verified databases */
+  int cookieValue[MAX_ATTACHED+2];  /* Values of cookies to verify */
+  int cookieGoto;      /* Address of OP_Goto to cookie verifier subroutine */
+  u32 writeMask;       /* Start a write transaction on these databases */
 };
 
 /*
@@ -1202,7 +1206,7 @@ void sqlite3ErrorMsg(Parse*, const char*, ...);
 void sqlite3Dequote(char*);
 int sqlite3KeywordCode(const char*, int);
 int sqlite3RunParser(Parse*, const char*, char **);
-void sqlite3Exec(Parse*);
+void sqlite3FinishCoding(Parse*);
 Expr *sqlite3Expr(int, Expr*, Expr*, Token*);
 void sqlite3ExprSpan(Expr*,Token*,Token*);
 Expr *sqlite3ExprFunction(ExprList*, Token*);
index b40ae7d2fe06849e7d62f6725f914b8389fde6b4..38a573d9a1a56ac76720cba4367a96a9d73ebacd 100644 (file)
@@ -710,7 +710,6 @@ int sqlite3CodeRowTrigger(
 ){
   Trigger * pTrigger;
   TriggerStack * pTriggerStack;
-  u64 cookieMask = pParse->cookieMask;
 
   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
   assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER );
@@ -781,10 +780,5 @@ int sqlite3CodeRowTrigger(
     }
     pTrigger = pTrigger->pNext;
   }
-
-  pParse->cookieMask = cookieMask;
   return 0;
 }
-
-
-
index 848d7cccf88e8843a8ca56f26ce605c18f79ef60..e82a9852d2311933c9aad9dbc14f57b9ad11d027 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.359 2004/06/06 09:44:05 danielk1977 Exp $
+** $Id: vdbe.c,v 1.360 2004/06/09 00:48:14 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -2302,11 +2302,11 @@ case OP_AutoCommit: {
 ** started.  Index 0 is the main database file and index 1 is the
 ** file used for temporary tables.
 **
-** If P2 is non-zero, then a write-transaction is started.  A write lock is
+** If P2 is non-zero, then a write-transaction is started.  A RESERVED lock is
 ** obtained on the database file when a write-transaction is started.  No
-** other process can read or write the file while the transaction is
-** underway.  Starting a transaction also creates a rollback journal.  A
-** transaction must be started before any changes can be made to the
+** other process can start another write transaction while this transaction is
+** underway.  Starting a write transaction also creates a rollback journal. A
+** write transaction must be started before any changes can be made to the
 ** database.
 **
 ** If P2 is zero, then a read-lock is obtained on the database file.
index 3682320cd11547f0771b74746c4b3ceb81cdc46f..a168e49084d99b61139d8266a4f987e479589939 100644 (file)
@@ -654,7 +654,15 @@ void sqlite3VdbeMakeReady(
   sqlite3HashInit(&p->agg.hash, SQLITE_HASH_BINARY, 0);
   p->agg.pSearch = 0;
 #ifdef MEMORY_DEBUG
+  if( sqlite3OsFileExists("vdbe_explain") ){
+    int i;
+    printf("VDBE Program Listing:\n");
+    for(i=0; i<p->nOp; i++){
+      sqlite3VdbePrintOp(stdout, i, &p->aOp[i]);
+    }
+  }
   if( sqlite3OsFileExists("vdbe_trace") ){
+    printf("VDBE Execution Trace:\n");
     p->trace = stdout;
   }
 #endif
index 166e29927d24a31200081c47a539b4248938e02a..50733486e75d425eb67489db9cca346757dc87ab 100644 (file)
@@ -12,7 +12,7 @@
 ** This module contains C code that generates VDBE code used to process
 ** the WHERE clause of SQL statements.
 **
-** $Id: where.c,v 1.101 2004/05/29 11:24:50 danielk1977 Exp $
+** $Id: where.c,v 1.102 2004/06/09 00:48:15 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -671,6 +671,7 @@ WhereInfo *sqlite3WhereBegin(
 
   /* Open all tables in the pTabList and all indices used by those tables.
   */
+  sqlite3CodeVerifySchema(pParse, 1);  /* Inserts the cookie verifier Goto */
   for(i=0; i<pTabList->nSrc; i++){
     Table *pTab;
     Index *pIx;
@@ -680,7 +681,9 @@ WhereInfo *sqlite3WhereBegin(
     sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
     sqlite3VdbeAddOp(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum);
     sqlite3VdbeAddOp(v, OP_SetNumColumns, pTabList->a[i].iCursor, pTab->nCol);
-    sqlite3CodeVerifySchema(pParse, pTab->iDb);
+    if( pTab->tnum>1 ){
+      sqlite3CodeVerifySchema(pParse, pTab->iDb);
+    }
     if( (pIx = pWInfo->a[i].pIdx)!=0 ){
       sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);
       sqlite3VdbeOp3(v, OP_OpenRead, pWInfo->a[i].iCur, pIx->tnum,
index af3f529d6abb656732457e2d93d9682c46e90eb1..1c165cf9c28724327ab1d79b621c93d8df8d747b 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this script is testing the ATTACH and DETACH commands
 # and related functionality.
 #
-# $Id: attach.test,v 1.22 2004/06/07 01:52:15 drh Exp $
+# $Id: attach.test,v 1.23 2004/06/09 00:48:15 drh Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -314,7 +314,8 @@ do_test attach-3.3 {
   }
 } {0 {21 x 22 y}}
 
-# Even though main has a transaction, test2.db should not be locked.
+# Even though 'db' has started a transaction, it should not yet have
+# a lock on test2.db so 'db2' should be readable.
 do_test attach-3.4 {
   execsql BEGIN
   catchsql {
@@ -322,7 +323,8 @@ do_test attach-3.4 {
   } db2;
 } {0 {21 x 22 y}}
 
-# Reading from db2 should not lock test2.db
+# Reading from test2.db from db within a transaction should not
+# prevent test2.db from being read by db2.
 do_test attach-3.5 {
   execsql {SELECT * FROM t2}
   catchsql {
@@ -330,7 +332,8 @@ do_test attach-3.5 {
   } db2;
 } {0 {21 x 22 y}}
 
-# Making a change to db2 causes test2.ddb to become locked.
+# Making a change to test2.db through db  causes test2.db to get
+# a reserved lock.  It should still be accessible through db2.
 do_test attach-3.6 {
   execsql {
     UPDATE t2 SET x=x+1 WHERE x=50;
@@ -338,30 +341,42 @@ do_test attach-3.6 {
   catchsql {
     SELECT * FROM t2;
   } db2;
-} {1 {database is locked}}
+} {0 {21 x 22 y}}
 
 do_test attach-3.7 {
   execsql ROLLBACK
   execsql {SELECT * FROM t2} db2
 } {21 x 22 y}
+
+# Start transactions on both db and db2.  Once again, just because
+# we make a change to test2.db using db2, only a RESERVED lock is
+# obtained, so test2.db should still be readable using db.
+#
 do_test attach-3.8 {
   execsql BEGIN
   execsql BEGIN db2
   execsql {UPDATE t2 SET x=0 WHERE 0} db2
   catchsql {SELECT * FROM t2}
-} {1 {database is locked}}
+} {0 {21 x 22 y}}
+
+# It is also still accessible from db2.
 do_test attach-3.9 {
   catchsql {SELECT * FROM t2} db2
 } {0 {21 x 22 y}}
+
 do_test attach-3.10 {
   execsql {SELECT * FROM t1}
 } {1 2 3 4}
+
 do_test attach-3.11 {
   catchsql {UPDATE t1 SET a=a+1}
 } {0 {}}
 do_test attach-3.12 {
   execsql {SELECT * FROM t1}
 } {2 2 4 4}
+
+# db2 has a RESERVED lock on test2.db, so db cannot write to any tables
+# in test2.db.
 do_test attach-3.13 {
   catchsql {UPDATE t2 SET x=x+1 WHERE x=50}
 } {1 {database is locked}}
@@ -370,18 +385,18 @@ do_test attach-3.13 {
 # for a locked database.
 execsql {ROLLBACK}
 
+# db is able to reread its schema because db2 still only holds a
+# reserved lock.
 do_test attach-3.14 {
-  # Unable to reinitialize the schema tables because the aux database
-  # is still locked.
   catchsql {SELECT * FROM t1}
-} {1 {database is locked}}
+} {0 {1 2 3 4}}
 do_test attach-3.15 {
   execsql COMMIT db2
   execsql {SELECT * FROM t1}
 } {1 2 3 4}
 
 #set btree_trace 1
-#puts stderr "###################"; flush stderr
+
 # Ticket #323
 do_test attach-4.1 {
   execsql {DETACH db2}
@@ -442,6 +457,14 @@ do_test attach-4.7 {
     SELECT * FROM main.t4;
   }
 } {main.11}
+
+# This one is tricky.  On the UNION ALL select, we have to make sure
+# the schema for both main and db2 is valid before starting to execute
+# the first query of the UNION ALL.  If we wait to test the validity of
+# the schema for main until after the first query has run, that test will
+# fail and the query will abort but we will have already output some
+# results.  When the query is retried, the results will be repeated.
+#
 do_test attach-4.8 {
   execsql {
     ATTACH DATABASE 'test2.db' AS db2;
@@ -449,6 +472,7 @@ do_test attach-4.8 {
     SELECT * FROM db2.t4 UNION ALL SELECT * FROM main.t4;
   }
 } {db2.6 db2.13 main.11}
+
 do_test attach-4.9 {
   execsql {
     INSERT INTO main.t3 VALUES(15,16);