]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Changes for exclusive access mode. There are still some bugs. (CVS 3712)
authordanielk1977 <danielk1977@noemail.net>
Sat, 24 Mar 2007 16:45:04 +0000 (16:45 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Sat, 24 Mar 2007 16:45:04 +0000 (16:45 +0000)
FossilOrigin-Name: b6c700370be29db2b974f9abd719c3e56abf8058

manifest
manifest.uuid
src/attach.c
src/pager.c
src/pager.h
src/pragma.c
src/sqliteInt.h
test/exclusive.test [new file with mode: 0644]
test/trans.test

index 5b8424da45dc6d0a480caf4e318c63beb58a0230..5159ea4ffe11a392b0708d2873fa139499295696 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Discard\sthe\scontents\sof\sthe\spager-cache\sonly\swhen\sthe\schange-counter\sindicates\sthat\sit\sis\sstale.\s(CVS\s3711)
-D 2007-03-23T18:12:07
+C Changes\sfor\sexclusive\saccess\smode.\sThere\sare\sstill\ssome\sbugs.\s(CVS\s3712)
+D 2007-03-24T16:45:05
 F Makefile.in 1fe3d0b46e40fd684e1e61f8e8056cefed16de9f
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -56,7 +56,7 @@ F sqlite3.def a96c1d0d39362b763d2ddba220a32da41a15c4b4
 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
 F src/alter.c 2c79ec40f65e33deaf90ca493422c74586e481a3
 F src/analyze.c 7d2b7ab9a9c2fd6e55700f69064dfdd3e36d7a8a
-F src/attach.c fd286a9140a2df84b1482f052b67ff5fad9569a1
+F src/attach.c 9b5a9c50fb92883e3404353b225674142da826cd
 F src/auth.c 902f4722661c796b97f007d9606bd7529c02597f
 F src/btree.c 4d94251b59fa33c888efc43881729f7d297899a4
 F src/btree.h 066444ee25bd6e6accb997bfd2cf5ace14dbcd00
@@ -86,10 +86,10 @@ F src/os_unix.c 4642f23ed0c1ae0f1440db1d2b4231348af69360
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
 F src/os_win.c 1d1d0989b0f235751504292c2f28e81044be0d70
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
-F src/pager.c da3c5e90b2c99bdba6129f568102c24cbd438452
-F src/pager.h 9f6b5ef42c761deec8a9b1966b32e9a9dc89a631
+F src/pager.c 9023042d50d961cfcaad4e9211eea711abb2b7f4
+F src/pager.h 3c16500c25051536e43fb19e246e58fc7cb51d9f
 F src/parse.y bcfe366c1fd61cfc40e5344eb69a31997a821af0
-F src/pragma.c a3fe1dacdbf320ad99d4125a60a5bce8f1808bc8
+F src/pragma.c 9cb8b94e7d38ba35a86037bd517d07ba9870b4b2
 F src/prepare.c 4e075fe28591b7d4ffbf818fb88a7e19bbe98065
 F src/printf.c aade23a789d7cc88b397ec0d33a0a01a33a7a9c1
 F src/random.c 6119474a6f6917f708c1dee25b9a8e519a620e88
@@ -98,7 +98,7 @@ F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
 F src/shell.c 3ae4654560e91220a95738a73d135d91d937cda1
 F src/sqlite.h.in 6b7383baf76070214f6381f603328ca9b22a7fae
 F src/sqlite3ext.h 011c75fd6459a61454514af07c7a4f1f5c767f27
-F src/sqliteInt.h 6e3ac7a1a8f51e24ce4784236aa957e9bd11217f
+F src/sqliteInt.h 5451308c885e8620e0d4764ae162da2566520073
 F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06
 F src/tclsqlite.c cd2b3b86ab07c0e0779f6c6e71e72c6c7dc1e704
 F src/test1.c b4ff8f82f84d2ccdf07a2db5acae7b47c12f60d7
@@ -199,6 +199,7 @@ F test/distinctagg.test 2b89d1c5220d966a30ba4b40430338669301188b
 F test/enc.test 7a03417a1051fe8bc6c7641cf4c8c3f7e0066d52
 F test/enc2.test 45710bacfa9df29720bc84c067dfdf8c8ddfb797
 F test/enc3.test 890508efff6677345e93bf2a8adb0489b30df030
+F test/exclusive.test 67a1bedd37b92785a0ba3b596401910713653b5e
 F test/expr.test c78843f730ccbe973d0c2ad1c99978f936893131
 F test/fkey1.test 153004438d51e6769fb1ce165f6313972d6263ce
 F test/format4.test bf3bed3b13c63abfb3cfec232597a319a31d0bcc
@@ -330,7 +331,7 @@ F test/tkt2192.test 480d0e017ddb01a46ee20809427370f343bb3c03
 F test/tkt2213.test 8cf7c446e1fcd0627fffe7fc19046eb24ac7333b
 F test/tkt2251.test 3f0549213386ed911715665a908ff2bb7a871002
 F test/trace.test 75ffc1b992c780d054748a656e3e7fd674f18567
-F test/trans.test 06bff0246886858793fca3748721936e2f65e3df
+F test/trans.test f4577bbefe1fb49e6c13f32923a8891250a9d628
 F test/trigger1.test 2c79e2bf76350811e362814e98779c120b6a9421
 F test/trigger2.test 33bf8ae9b788013be194efe5f66c04a202facbb4
 F test/trigger3.test 9102fd3933db294dc654b5aee9edfe9e94f2b9e2
@@ -437,7 +438,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 0fd9983a98d8d61654f252f1708a4d7232a96b53
-R ec284a76fe569250a4f6f20542fb07fe
+P 07b56965f3227c9f78680728b955395295c4aa49
+R 5306d65289bf66945a0a4cf7185afe17
 U danielk1977
-Z c13a7ab87a49cb94c1a31349a8579dca
+Z 4da3e2ad8e93e75be905d9a01fa04929
index 020cae72323f4255af8a6aba9c67ab5b9fc6bfed..3f6a74d77eb71a43dd9caa556d54ad72b132200c 100644 (file)
@@ -1 +1 @@
-07b56965f3227c9f78680728b955395295c4aa49
\ No newline at end of file
+b6c700370be29db2b974f9abd719c3e56abf8058
\ No newline at end of file
index 0a50b374d909ba7fc65d0cfebbba0ace3a09adc1..78cf0b4a18172e8cd9e49f17eb4fa1c042033324 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the ATTACH and DETACH commands.
 **
-** $Id: attach.c,v 1.54 2007/03/15 15:35:29 danielk1977 Exp $
+** $Id: attach.c,v 1.55 2007/03/24 16:45:05 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -133,6 +133,7 @@ static void attachFunc(
         "attached databases must use the same text encoding as main database");
       goto attach_error;
     }
+    sqlite3PagerLockingMode(sqlite3BtreePager(aNew->pBt), db->dfltLockMode);
   }
   aNew->zName = sqliteStrDup(zName);
   aNew->safety_level = 3;
index 2b43d474a5e17f63a751eda2caf3b4b9b21af605..6569ec4606834b82dcfc9162b9912cc94325285c 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.293 2007/03/23 18:12:07 danielk1977 Exp $
+** @(#) $Id: pager.c,v 1.294 2007/03/24 16:45:05 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -285,8 +285,10 @@ struct Pager {
   Pager *pNext;               /* Linked list of pagers in this thread */
 #endif
   char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
-  int doNotSync;              /* While true, do not spill the cache */
   u32 iChangeCount;           /* Db change-counter for which cache is valid */
+  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
+  u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
+  u8 changeCountDone;         /* Set after incrementing the change-counter */
 };
 
 /*
@@ -854,12 +856,15 @@ static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
 ** Unlock the database file.
 */
 static void pager_unlock(Pager *pPager){
-  if( !MEMDB ){
-    sqlite3OsUnlock(pPager->fd, NO_LOCK);
-    pPager->dbSize = -1;
-    IOTRACE(("UNLOCK %p\n", pPager))
+  if( !pPager->exclusiveMode ){
+    if( !MEMDB ){
+      sqlite3OsUnlock(pPager->fd, NO_LOCK);
+      pPager->dbSize = -1;
+      IOTRACE(("UNLOCK %p\n", pPager))
+    }
+    pPager->state = PAGER_UNLOCK;
+    pPager->changeCountDone = 0;
   }
-  pPager->state = PAGER_UNLOCK;
 }
 
 /*
@@ -873,7 +878,6 @@ static void pagerUnlockAndRollback(Pager *pPager){
     sqlite3PagerRollback(pPager);
   }
   pager_unlock(pPager);
-  assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
 }
 
 
@@ -902,28 +906,6 @@ static void pager_reset(Pager *pPager){
   pPager->nRef = 0;
 }
 
-/*
-** This function resets the various pager flags to their initial
-** state but does not discard the cached content.
-*/
-static void pagerSoftReset(Pager *pPager){
-  PgHdr *pPg;
-
-  assert(pPager->pStmt==0);
-  assert(pPager->nRef==0);
-  assert(pPager->pFirstSynced==pPager->pFirst);
-  assert(pPager->aInJournal==0);
-
-  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
-    assert( pPg->nRef==0 );
-    pPg->inJournal = 0;
-    pPg->inStmt = 0;
-    pPg->dirty = 0;
-    pPg->needSync = 0;
-    pPg->alwaysRollback = 0;
-  }
-}
-
 /*
 ** When this routine is called, the pager has the journal file open and
 ** a RESERVED or EXCLUSIVE lock on the database.  This routine releases
@@ -936,7 +918,7 @@ static void pagerSoftReset(Pager *pPager){
 */
 static int pager_unwritelock(Pager *pPager){
   PgHdr *pPg;
-  int rc;
+  int rc = SQLITE_OK;
   assert( !MEMDB );
   if( pPager->state<PAGER_RESERVED ){
     return SQLITE_OK;
@@ -947,15 +929,22 @@ static int pager_unwritelock(Pager *pPager){
     pPager->stmtOpen = 0;
   }
   if( pPager->journalOpen ){
-    sqlite3OsClose(&pPager->jfd);
-    pPager->journalOpen = 0;
-    sqlite3OsDelete(pPager->zJournal);
+    if( pPager->exclusiveMode ){
+      sqlite3OsTruncate(pPager->jfd, 0);
+      sqlite3OsSeek(pPager->jfd, 0);
+      pPager->journalOff = 0;
+    }else{
+      sqlite3OsClose(&pPager->jfd);
+      pPager->journalOpen = 0;
+      sqlite3OsDelete(pPager->zJournal);
+    }
     sqliteFree( pPager->aInJournal );
     pPager->aInJournal = 0;
     for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
       pPg->inJournal = 0;
       pPg->dirty = 0;
       pPg->needSync = 0;
+      pPg->alwaysRollback = 0;
 #ifdef SQLITE_CHECK_PAGES
       pPg->pageHash = pager_pagehash(pPg);
 #endif
@@ -967,9 +956,18 @@ static int pager_unwritelock(Pager *pPager){
     assert( pPager->aInJournal==0 );
     assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
   }
-  rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
-  pPager->state = PAGER_SHARED;
-  pPager->origDbSize = 0;
+  if( !pPager->exclusiveMode ){
+    rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
+    pPager->state = PAGER_SHARED;
+    pPager->origDbSize = 0;
+  }else{
+    sqlite3PagerPagecount(pPager);
+    pPager->origDbSize = pPager->dbSize;
+    pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
+    if( !pPager->aInJournal ){
+      rc = SQLITE_NOMEM;
+    }
+  }
   pPager->setMaster = 0;
   pPager->needSync = 0;
   pPager->pFirstSynced = pPager->pFirst;
@@ -1109,6 +1107,11 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
     pPg->pageHash = pager_pagehash(pPg);
 #endif
     CODEC1(pPager, pData, pPg->pgno, 3);
+
+    /* If this was page 1, then restore the value of Pager.iChangeCount */
+    if( pgno==1 ){
+      pPager->iChangeCount = retrieve32bits(pPg, 24);
+    }
   }
   return rc;
 }
@@ -1199,6 +1202,7 @@ delmaster_out:
   return rc;
 }
 
+#if 0
 /*
 ** Make every page in the cache agree with what is on disk.  In other words,
 ** reread the disk to reset the state of the cache.
@@ -1242,6 +1246,7 @@ static int pager_reload_cache(Pager *pPager){
   pPager->pDirty = 0;
   return rc;
 }
+#endif
 
 /*
 ** Truncate the main file of the given pager to the number of pages
@@ -2126,6 +2131,7 @@ int sqlite3PagerClose(Pager *pPager){
 
   disable_simulated_io_errors();
   pPager->errCode = 0;
+  pPager->exclusiveMode = 0;
   pager_reset(pPager);
   pagerUnlockAndRollback(pPager);
   enable_simulated_io_errors();
@@ -2734,8 +2740,6 @@ static int pagerSharedLock(Pager *pPager){
           pPager->nRef--;
           if( iChangeCount!=pPager->iChangeCount ){
             pager_reset(pPager);
-          }else{
-            pagerSoftReset(pPager);
           }
           pPager->iChangeCount = iChangeCount;
         }
@@ -3610,6 +3614,8 @@ int sqlite3PagerRollback(Pager *pPager){
       PgHistory *pHist;
       assert( !p->alwaysRollback );
       if( !p->dirty ){
+        assert( p->inJournal==0 );
+        assert( p->inStmt==0 );
         assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
         assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
         continue;
@@ -3890,6 +3896,7 @@ static int pager_incr_changecounter(Pager *pPager){
 
   /* Release the page reference. */
   sqlite3PagerUnref(pPgHdr);
+  pPager->changeCountDone = 1;
   return SQLITE_OK;
 }
 
@@ -4098,6 +4105,23 @@ void *sqlite3PagerGetExtra(DbPage *pPg){
   return (pPager?PGHDR_TO_EXTRA(pPg, pPager):0);
 }
 
+/*
+** Get/set the locking-mode for this pager. Parameter eMode must be one
+** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or 
+** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then
+** the locking-mode is set to the value specified.
+**
+** The returned value is either PAGER_LOCKINGMODE_NORMAL or
+** PAGER_LOCKINGMODE_EXCLUSIVE, indicating the current (possibly updated)
+** locking-mode.
+*/
+int sqlite3PagerLockingMode(Pager *pPager, int eMode){
+  if( eMode>=0 ){
+    pPager->exclusiveMode = eMode;
+  }
+  return (int)pPager->exclusiveMode;
+}
+
 #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
 /*
 ** Return the current state of the file lock for the given pager.
index 657f1806f94ddbe09118529d1dc9747484efbda2..3d8dbc5cce8fe9118a61f95879c292c30c168ecb 100644 (file)
@@ -13,7 +13,7 @@
 ** subsystem.  The page cache subsystem reads and writes a file a page
 ** at a time and provides a journal for rollback.
 **
-** @(#) $Id: pager.h,v 1.54 2007/03/19 17:44:27 danielk1977 Exp $
+** @(#) $Id: pager.h,v 1.55 2007/03/24 16:45:05 danielk1977 Exp $
 */
 
 #ifndef _PAGER_H_
@@ -69,6 +69,12 @@ typedef struct PgHdr DbPage;
 #define PAGER_OMIT_JOURNAL  0x0001    /* Do not use a rollback journal */
 #define PAGER_NO_READLOCK   0x0002    /* Omit readlocks on readonly files */
 
+/*
+** Valid values for the second argument to sqlite3PagerLockingMode().
+*/
+#define PAGER_LOCKINGMODE_QUERY      -1
+#define PAGER_LOCKINGMODE_NORMAL      0
+#define PAGER_LOCKINGMODE_EXCLUSIVE   1
 
 /*
 ** See source code comments for a detailed description of the following
@@ -119,6 +125,7 @@ int sqlite3PagerReleaseMemory(int);
 
 void *sqlite3PagerGetData(DbPage *); 
 void *sqlite3PagerGetExtra(DbPage *); 
+int sqlite3PagerLockingMode(Pager *, int);
 
 #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
 int sqlite3PagerLockstate(Pager*);
index c6af3857a867ec8e419ca498f42bd7c8a9ae0bd5..a257630cb3b8f28e7401d06491c15a86cdbf1ca7 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the PRAGMA command.
 **
-** $Id: pragma.c,v 1.129 2007/03/19 17:44:28 danielk1977 Exp $
+** $Id: pragma.c,v 1.130 2007/03/24 16:45:05 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -62,6 +62,17 @@ static int getBoolean(const char *z){
   return getSafetyLevel(z)&1;
 }
 
+/*
+** Interpret the given string as a locking mode value.
+*/
+static int getLockingMode(const char *z){
+  if( z ){
+    if( 0==sqlite3StrICmp(z, "exclusive") ) return PAGER_LOCKINGMODE_EXCLUSIVE;
+    if( 0==sqlite3StrICmp(z, "normal") ) return PAGER_LOCKINGMODE_NORMAL;
+  }
+  return PAGER_LOCKINGMODE_QUERY;
+}
+
 #ifndef SQLITE_OMIT_PAGER_PRAGMAS
 /*
 ** Interpret the given string as a temp db location. Return 1 for file
@@ -315,6 +326,53 @@ void sqlite3Pragma(
       sqlite3BtreeSetPageSize(pBt, atoi(zRight), -1);
     }
   }else
+
+  /*
+  **  PRAGMA [database.]locking_mode
+  **  PRAGMA [database.]locking_mode = (normal|exclusive)
+  */
+  if( sqlite3StrICmp(zLeft,"locking_mode")==0 ){
+    const char *zRet = "normal";
+    int eMode = getLockingMode(zRight);
+
+    if( pId2->n==0 && eMode==PAGER_LOCKINGMODE_QUERY ){
+      /* Simple "PRAGMA locking_mode;" statement. This is a query for
+      ** the current default locking mode (which may be different to
+      ** the locking-mode of the main database).
+      */
+      eMode = db->dfltLockMode;
+    }else{
+      Pager *pPager;
+      if( pId2->n==0 ){
+        /* This indicates that no database name was specified as part
+        ** of the PRAGMA command. In this case the locking-mode must be
+        ** set on all attached databases, as well as the main db file.
+        **
+        ** Also, the sqlite3.dfltLockMode variable is set so that
+        ** any subsequently attached databases also use the specified
+        ** locking mode.
+        */
+        int ii;
+        assert(pDb==&db->aDb[0]);
+        for(ii=2; ii<db->nDb; ii++){
+          pPager = sqlite3BtreePager(db->aDb[ii].pBt);
+          sqlite3PagerLockingMode(pPager, eMode);
+        }
+        db->dfltLockMode = eMode;
+      }
+      pPager = sqlite3BtreePager(pDb->pBt);
+      eMode = sqlite3PagerLockingMode(pPager, eMode);
+    }
+
+    assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE);
+    if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){
+      zRet = "exclusive";
+    }
+    sqlite3VdbeSetNumCols(v, 1);
+    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", P3_STATIC);
+    sqlite3VdbeOp3(v, OP_String8, 0, 0, zRet, 0);
+    sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+  }else
 #endif /* SQLITE_OMIT_PAGER_PRAGMAS */
 
   /*
index 075f2fa82ef6d8fb3b15c15c8d92e39a6c6712fb..50cdaccc642703ff9f3b7bf461fef80f230a690f 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.542 2007/03/14 15:37:04 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.543 2007/03/24 16:45:05 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -510,6 +510,7 @@ struct sqlite3 {
 #ifdef SQLITE_SSE
   sqlite3_stmt *pFetch;         /* Used by SSE to fetch stored statements */
 #endif
+  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
 };
 
 /*
diff --git a/test/exclusive.test b/test/exclusive.test
new file mode 100644 (file)
index 0000000..860fcfd
--- /dev/null
@@ -0,0 +1,340 @@
+# 2007 March 24
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# $Id: exclusive.test,v 1.1 2007/03/24 16:45:05 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!pager_pragmas} {
+  finish_test
+  return
+}
+
+file delete -force test2.db-journal
+file delete -force test2.db
+file delete -force test3.db-journal
+file delete -force test3.db
+file delete -force test4.db-journal
+file delete -force test4.db
+
+#----------------------------------------------------------------------
+# Test cases exclusive-1.X test the PRAGMA logic.
+#
+do_test exclusive-1.0 {
+  execsql {
+    pragma locking_mode;
+    pragma main.locking_mode;
+  } 
+} {normal normal}
+do_test exclusive-1.1 {
+  execsql {
+    pragma locking_mode = exclusive;
+  } 
+} {exclusive}
+do_test exclusive-1.2 {
+  execsql {
+    pragma locking_mode;
+    pragma main.locking_mode;
+  } 
+} {exclusive exclusive}
+do_test exclusive-1.3 {
+  execsql {
+    pragma locking_mode = normal;
+  } 
+} {normal}
+do_test exclusive-1.4 {
+  execsql {
+    pragma locking_mode;
+    pragma main.locking_mode;
+  } 
+} {normal normal}
+do_test exclusive-1.5 {
+  execsql {
+    pragma locking_mode = invalid;
+  } 
+} {normal}
+do_test exclusive-1.6 {
+  execsql {
+    pragma locking_mode;
+    pragma main.locking_mode;
+  } 
+} {normal normal}
+do_test exclusive-1.7 {
+  execsql {
+    pragma locking_mode = exclusive;
+    ATTACH 'test2.db' as aux;
+  }
+  execsql {
+    pragma main.locking_mode;
+    pragma aux.locking_mode;
+  }
+} {exclusive exclusive}
+do_test exclusive-1.8 {
+  execsql {
+    pragma main.locking_mode = normal;
+  }
+  execsql {
+    pragma main.locking_mode;
+    pragma aux.locking_mode;
+  }
+} {normal exclusive}
+do_test exclusive-1.9 {
+  execsql {
+    pragma locking_mode;
+  }
+} {exclusive}
+do_test exclusive-1.10 {
+  execsql {
+    ATTACH 'test3.db' as aux2;
+  }
+  execsql {
+    pragma main.locking_mode;
+    pragma aux.locking_mode;
+    pragma aux2.locking_mode;
+  }
+} {normal exclusive exclusive}
+do_test exclusive-1.11 {
+  execsql {
+    pragma aux.locking_mode = normal;
+  }
+  execsql {
+    pragma main.locking_mode;
+    pragma aux.locking_mode;
+    pragma aux2.locking_mode;
+  }
+} {normal normal exclusive}
+do_test exclusive-1.12 {
+  execsql {
+    pragma locking_mode = normal;
+  }
+  execsql {
+    pragma main.locking_mode;
+    pragma aux.locking_mode;
+    pragma aux2.locking_mode;
+  }
+} {normal normal normal}
+do_test exclusive-1.13 {
+  execsql {
+    ATTACH 'test4.db' as aux3;
+  }
+  execsql {
+    pragma main.locking_mode;
+    pragma aux.locking_mode;
+    pragma aux2.locking_mode;
+    pragma aux3.locking_mode;
+  }
+} {normal normal normal normal}
+
+do_test exclusive-1.99 {
+  execsql {
+    DETACH aux;
+    DETACH aux2;
+    DETACH aux3;
+  }
+} {}
+
+#----------------------------------------------------------------------
+# Test cases exclusive-2.X verify that connections in exclusive 
+# locking_mode do not relinquish locks.
+#
+do_test exclusive-2.0 {
+  execsql {
+    CREATE TABLE abc(a, b, c);
+    INSERT INTO abc VALUES(1, 2, 3);
+    PRAGMA locking_mode = exclusive;
+  }
+} {exclusive}
+do_test exclusive-2.1 {
+  sqlite3 db2 test.db
+  execsql {
+    INSERT INTO abc VALUES(4, 5, 6);
+    SELECT * FROM abc;
+  } db2
+} {1 2 3 4 5 6}
+do_test exclusive-2.2 {
+  # This causes connection 'db' (in exclusive mode) to establish 
+  # a shared-lock on the db. The other connection should now be
+  # locked out as a writer.
+  execsql {
+    SELECT * FROM abc;
+  } db
+} {1 2 3 4 5 6}
+do_test exclusive-2.4 {
+  execsql {
+    SELECT * FROM abc;
+  } db2
+} {1 2 3 4 5 6}
+do_test exclusive-2.5 {
+  catchsql {
+    INSERT INTO abc VALUES(7, 8, 9);
+  } db2
+} {1 {database is locked}}
+do_test exclusive-2.6 {
+  # Because connection 'db' only has a shared-lock, the other connection
+  # will be able to get a RESERVED, but will fail to upgrade to EXCLUSIVE.
+  execsql {
+    BEGIN;
+    INSERT INTO abc VALUES(7, 8, 9);
+  } db2
+  catchsql {
+    COMMIT
+  } db2
+} {1 {database is locked}}
+do_test exclusive-2.7 {
+  catchsql {
+    COMMIT
+  } db2
+} {1 {database is locked}}
+do_test exclusive-2.8 {
+  execsql {
+    ROLLBACK;
+  } db2
+} {}
+
+do_test exclusive-2.9 {
+  # Write the database to establish the exclusive lock with connection 'db.
+  execsql {
+    INSERT INTO abc VALUES(7, 8, 9);
+  } db
+  catchsql {
+    SELECT * FROM abc;
+  } db2
+} {1 {database is locked}}
+do_test exclusive-2.10 {
+  # Changing the locking-mode does not release any locks.
+  execsql {
+    PRAGMA locking_mode = normal;
+  } db
+  catchsql {
+    SELECT * FROM abc;
+  } db2
+} {1 {database is locked}}
+do_test exclusive-2.11 {
+  # After changing the locking mode, accessing the db releases locks.
+  execsql {
+    SELECT * FROM abc;
+  } db
+  execsql {
+    SELECT * FROM abc;
+  } db2
+} {1 2 3 4 5 6 7 8 9}
+db2 close
+
+#----------------------------------------------------------------------
+# Tests exclusive-3.X - test that a connection in exclusive mode 
+# truncates instead of deletes the journal file when committing 
+# a transaction.
+#
+proc filestate {fname} {
+  set exists 0
+  set content 0
+  if {[file exists $fname]} {
+    set exists 1
+    set content [expr {[file size $fname] > 0}]
+  }
+  list $exists $content
+}
+do_test exclusive-3.0 {
+  filestate test.db-journal
+} {0 0}
+do_test exclusive-3.1 {
+  execsql {
+    PRAGMA locking_mode = exclusive;
+    BEGIN;
+    DELETE FROM abc;
+  }
+  filestate test.db-journal
+} {1 1}
+do_test exclusive-3.2 {
+  execsql {
+    COMMIT;
+  }
+  filestate test.db-journal
+} {1 0}
+do_test exclusive-3.3 {
+  execsql {
+    INSERT INTO abc VALUES('A', 'B', 'C');
+    SELECT * FROM abc;
+  }
+} {A B C}
+do_test exclusive-3.4 {
+  execsql {
+    BEGIN;
+    UPDATE abc SET a = 1, b = 2, c = 3;
+    ROLLBACK;
+    SELECT * FROM abc;
+  }
+} {1 2 3}
+do_test exclusive-3.5 {
+  filestate test.db-journal
+} {1 0}
+do_test exclusive-3.6 {
+  execsql {
+    PRAGMA locking_mode = normal;
+    SELECT * FROM abc;
+  }
+  filestate test.db-journal
+} {0 0}
+
+# The following procedure computes a "signature" for table "t3".  If
+# T3 changes in any way, the signature should change.  
+#
+# This is used to test ROLLBACK.  We gather a signature for t3, then
+# make lots of changes to t3, then rollback and take another signature.
+# The two signatures should be the same.
+#
+proc signature {} {
+  return [db eval {SELECT count(*), md5sum(x) FROM t3}]
+}
+
+if 0 {
+
+do_test exclusive-4.0 {
+  execsql { PRAGMA default_cache_size=10; }
+  db close
+  sqlite3 db test.db
+  execsql { PRAGMA locking_mode = exclusive; }
+  execsql {
+    BEGIN;
+    CREATE TABLE t3(x TEXT);
+    INSERT INTO t3 VALUES(randstr(10,400));
+    INSERT INTO t3 VALUES(randstr(10,400));
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
+    COMMIT;
+    SELECT count(*) FROM t3;
+  }
+} {1024}
+set sig [signature]
+do_test exclusive-4.1 {
+  execsql {
+    BEGIN;
+    DELETE FROM t3 WHERE random()%10!=0;
+    INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+    INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
+    ROLLBACK;
+  }
+  signature
+} $sig
+
+}
+
+finish_test
+
index 8440ff3754156bdaf7cb025e7c6302804b86b7f0..14902cc7ef64087454b89978a71f831c12a99b02 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script is database locks.
 #
-# $Id: trans.test,v 1.32 2006/06/20 11:01:09 danielk1977 Exp $
+# $Id: trans.test,v 1.33 2007/03/24 16:45:05 danielk1977 Exp $
 
 
 set testdir [file dirname $argv0]
@@ -815,6 +815,7 @@ do_test trans-9.1 {
   }
   db close
   sqlite3 db test.db
+  # execsql { PRAGMA locking_mode = exclusive; }
   execsql {
     BEGIN;
     CREATE TABLE t3(x TEXT);