From 414834686c7cafc61ed50352553417d880144fcf Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Sat, 24 Mar 2007 16:45:04 +0000 Subject: [PATCH] Changes for exclusive access mode. There are still some bugs. (CVS 3712) FossilOrigin-Name: b6c700370be29db2b974f9abd719c3e56abf8058 --- manifest | 23 +-- manifest.uuid | 2 +- src/attach.c | 3 +- src/pager.c | 102 ++++++++----- src/pager.h | 9 +- src/pragma.c | 60 +++++++- src/sqliteInt.h | 3 +- test/exclusive.test | 340 ++++++++++++++++++++++++++++++++++++++++++++ test/trans.test | 3 +- 9 files changed, 489 insertions(+), 56 deletions(-) create mode 100644 test/exclusive.test diff --git a/manifest b/manifest index 5b8424da45..5159ea4ffe 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 020cae7232..3f6a74d77e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -07b56965f3227c9f78680728b955395295c4aa49 \ No newline at end of file +b6c700370be29db2b974f9abd719c3e56abf8058 \ No newline at end of file diff --git a/src/attach.c b/src/attach.c index 0a50b374d9..78cf0b4a18 100644 --- a/src/attach.c +++ b/src/attach.c @@ -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; diff --git a/src/pager.c b/src/pager.c index 2b43d474a5..6569ec4606 100644 --- a/src/pager.c +++ b/src/pager.c @@ -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->statestmtOpen = 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. diff --git a/src/pager.h b/src/pager.h index 657f1806f9..3d8dbc5cce 100644 --- a/src/pager.h +++ b/src/pager.h @@ -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*); diff --git a/src/pragma.c b/src/pragma.c index c6af3857a8..a257630cb3 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -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; iinDb; 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 */ /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 075f2fa82e..50cdaccc64 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 index 0000000000..860fcfdd0a --- /dev/null +++ b/test/exclusive.test @@ -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 + diff --git a/test/trans.test b/test/trans.test index 8440ff3754..14902cc7ef 100644 --- a/test/trans.test +++ b/test/trans.test @@ -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);