]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Improve coverage of pager.c.
authordan <dan@noemail.net>
Wed, 11 Aug 2010 18:56:45 +0000 (18:56 +0000)
committerdan <dan@noemail.net>
Wed, 11 Aug 2010 18:56:45 +0000 (18:56 +0000)
FossilOrigin-Name: 2fa05d01b6b11788a5b73d203fcac9d4a4ba9fd8

manifest
manifest.uuid
src/pager.c
test/pager1.test
test/pager2.test
test/pagerfault.test

index 8dcf36d7e6de8d08d7f33c2ee297445c7149bf35..6f55fe208de8cd866df9fecbf8f76b8794d81988 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Update\san\sr-tree\sextension\stest\scase\sto\saccount\sfor\srecent\schanges\sto\sthe\squery\splanner.\sAlso\sfix\sa\scomment\sin\srtree.c.
-D 2010-08-11T12:26:46
+C Improve\scoverage\sof\spager.c.
+D 2010-08-11T18:56:46
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -156,7 +156,7 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
 F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e
 F src/os_unix.c 11194cbcf6a57456e58022dc537ab8c3497d9bb9
 F src/os_win.c 51cb62f76262d961ea4249489383d714501315a7
-F src/pager.c b3db762350ee71f5f8bde04f21ca2ffcccded483
+F src/pager.c 3fb56c4d03798058e5cf57b668468fa616e20bac
 F src/pager.h 80726162dc3942f59ab27b738fb667b9ba0a89d5
 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
@@ -539,10 +539,10 @@ F test/notify3.test d60923e186e0900f4812a845fcdfd8eea096e33a
 F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
 F test/null.test a8b09b8ed87852742343b33441a9240022108993
 F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec
-F test/pager1.test d8e4b2bc8164c920e6ea0572c9e13576d6e4f3fa
-F test/pager2.test f5c757c271ce642d36a393ecbfb3aef1c240dcef
+F test/pager1.test 20457c96ab1d4e876d335cfd6ddd0f539bda3f81
+F test/pager2.test 9edc1584cbc8ecd3b34dbcc8e86467bf05caa27f
 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
-F test/pagerfault.test c1d176326ce244db157ce9c3ba128be2a9b172d6
+F test/pagerfault.test 495ab0a0ed30aebe7e4278fcee148986085d4c8b
 F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401
 F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
 F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb
@@ -843,7 +843,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 7c674aaba52b00a78babc1e1ece7e5b652b65039
-R e8f0a89b3e6e46377528a5271511edf8
+P eaaca669a4afc83906806b22365b010c83bc3db8
+R 2966f30f98145ee75718d7c3465c6545
 U dan
-Z ddfd9f64a4ee25bfe95e56a61185d67f
+Z cd90e6256916f41a20463762a4c0c757
index 0f05938afec28450a85e511bb7fef39935859f2d..3bfa43329f4f2e324cc8b3198db448edb403099f 100644 (file)
@@ -1 +1 @@
-eaaca669a4afc83906806b22365b010c83bc3db8
\ No newline at end of file
+2fa05d01b6b11788a5b73d203fcac9d4a4ba9fd8
\ No newline at end of file
index 4d9be40eb8d69cd48dcbfedbcc69ba32d17daf1c..33a61de1111cfd7a37f0a5d07ce5db7a4d17354f 100644 (file)
@@ -273,10 +273,13 @@ int sqlite3PagerTrace=1;  /* True to enable tracing */
 **
 **  ERROR:
 **
-**    The ERROR state is entered when an IO, OOM or disk-full error 
-**    occurs at a point in the code that makes it difficult to be sure
-**    that the in-memory pager state (cache contents, db size etc.) are
-**    consistent with the contents of the file-system.
+**    The ERROR state is entered when an IO or disk-full error (including
+**    SQLITE_IOERR_NOMEM) occurs at a point in the code that makes it 
+**    difficult to be sure that the in-memory pager state (cache contents, 
+**    db size etc.) are consistent with the contents of the file-system.
+**
+**    Temporary pager files may enter the ERROR state, but in-memory pagers
+**    cannot.
 **
 **    For example, if an IO error occurs while performing a rollback, 
 **    the contents of the page-cache may be left in an inconsistent state.
@@ -323,6 +326,7 @@ int sqlite3PagerTrace=1;  /* True to enable tracing */
 **    * The Pager.errCode variable is set to something other than SQLITE_OK.
 **    * There are one or more outstanding references to pages (after the
 **      last reference is dropped the pager should move back to OPEN state).
+**    * The pager is not an in-memory pager.
 **    
 **
 ** Notes:
@@ -825,8 +829,22 @@ static int assert_pager_state(Pager *p){
   assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
   assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );
 
-  /* Check that MEMDB implies noSync. */
-  assert( !MEMDB || p->noSync );
+  /* Check that MEMDB implies noSync. And an in-memory journal. Since 
+  ** this means an in-memory pager performs no IO at all, it cannot encounter 
+  ** either SQLITE_IOERR or SQLITE_FULL during rollback or while finalizing 
+  ** a journal file. (although the in-memory journal implementation may 
+  ** return SQLITE_IOERR_NOMEM while the journal file is being written). It 
+  ** is therefore not possible for an in-memory pager to enter the ERROR 
+  ** state.
+  */
+  if( MEMDB ){
+    assert( p->noSync );
+    assert( p->journalMode==PAGER_JOURNALMODE_OFF 
+         || p->journalMode==PAGER_JOURNALMODE_MEMORY 
+    );
+    assert( p->eState!=PAGER_ERROR && p->eState!=PAGER_OPEN );
+    assert( pagerUseWal(p)==0 );
+  }
 
   /* If changeCountDone is set, a RESERVED lock or greater must be held
   ** on the file.
@@ -1759,7 +1777,8 @@ static void pager_unlock(Pager *pPager){
   ** it can safely move back to PAGER_OPEN state. This happens in both
   ** normal and exclusive-locking mode.
   */
-  if( pPager->errCode && !MEMDB ){
+  if( pPager->errCode ){
+    assert( !MEMDB );
     pager_reset(pPager);
     pPager->changeCountDone = pPager->tempFile;
     pPager->eState = PAGER_OPEN;
@@ -2567,13 +2586,10 @@ static int pager_playback(Pager *pPager, int isHot){
   char *zMaster = 0;       /* Name of master journal file if any */
   int needPagerReset;      /* True to reset page prior to first page rollback */
 
-  if( !isOpen(pPager->jfd) ){
-    return SQLITE_OK;
-  }
-
   /* Figure out how many records are in the journal.  Abort early if
   ** the journal is empty.
   */
+  assert( isOpen(pPager->jfd) );
   rc = sqlite3OsFileSize(pPager->jfd, &szJ);
   if( rc!=SQLITE_OK || szJ==0 ){
     goto end_playback;
@@ -3341,46 +3357,47 @@ void sqlite3PagerSetBusyhandler(
 ** then *pPageSize is set to the old, retained page size before returning.
 */
 int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){
-  int rc = pPager->errCode;
-
   /* It is not possible to do a full assert_pager_state() here, as this
   ** function may be called from within PagerOpen(), before the state
   ** of the Pager object is internally consistent.
+  **
+  ** At one point this function returned an error if the pager was in 
+  ** PAGER_ERROR state. But since PAGER_ERROR state guarantees that
+  ** there is at least one outstanding page reference, this function
+  ** is a no-op for that case anyhow.
   */
-  assert( rc==SQLITE_OK || pPager->eState==PAGER_ERROR );
 
-  if( rc==SQLITE_OK ){
-    u16 pageSize = *pPageSize;
-    assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
-    if( (pPager->memDb==0 || pPager->dbSize==0)
-     && sqlite3PcacheRefCount(pPager->pPCache)==0 
-     && pageSize && pageSize!=pPager->pageSize 
-    ){
-      char *pNew;                 /* New temp space */
-      i64 nByte = 0;
-      if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){
-        rc = sqlite3OsFileSize(pPager->fd, &nByte);
-        if( rc!=SQLITE_OK ) return rc;
-      }
-      pNew = (char *)sqlite3PageMalloc(pageSize);
-      if( !pNew ){
-        rc = SQLITE_NOMEM;
-      }else{
-        pager_reset(pPager);
-        pPager->dbSize = nByte/pageSize;
-        pPager->pageSize = pageSize;
-        sqlite3PageFree(pPager->pTmpSpace);
-        pPager->pTmpSpace = pNew;
-        sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
-      }
+  u16 pageSize = *pPageSize;
+  assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
+  if( (pPager->memDb==0 || pPager->dbSize==0)
+   && sqlite3PcacheRefCount(pPager->pPCache)==0 
+   && pageSize && pageSize!=pPager->pageSize 
+  ){
+    char *pNew;                 /* New temp space */
+    i64 nByte = 0;
+    if( pPager->eState>PAGER_OPEN && isOpen(pPager->fd) ){
+      int rc = sqlite3OsFileSize(pPager->fd, &nByte);
+      if( rc!=SQLITE_OK ) return rc;
+    }
+    pNew = (char *)sqlite3PageMalloc(pageSize);
+    if( !pNew ){
+      return SQLITE_NOMEM;
+    }else{
+      pager_reset(pPager);
+      pPager->dbSize = nByte/pageSize;
+      pPager->pageSize = pageSize;
+      sqlite3PageFree(pPager->pTmpSpace);
+      pPager->pTmpSpace = pNew;
+      sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
     }
-    *pPageSize = (u16)pPager->pageSize;
-    if( nReserve<0 ) nReserve = pPager->nReserve;
-    assert( nReserve>=0 && nReserve<1000 );
-    pPager->nReserve = (i16)nReserve;
-    pagerReportSize(pPager);
   }
-  return rc;
+
+  *pPageSize = (u16)pPager->pageSize;
+  if( nReserve<0 ) nReserve = pPager->nReserve;
+  assert( nReserve>=0 && nReserve<1000 );
+  pPager->nReserve = (i16)nReserve;
+  pagerReportSize(pPager);
+  return SQLITE_OK;
 }
 
 /*
@@ -4743,9 +4760,11 @@ int sqlite3PagerSharedLock(Pager *pPager){
     ** mode. Otherwise, the following function call is a no-op.
     */
     rc = pagerOpenWalIfPresent(pPager);
+    assert( pPager->pWal==0 || rc==SQLITE_OK );
   }
 
-  if( pagerUseWal(pPager) && rc==SQLITE_OK ){
+  if( pagerUseWal(pPager) ){
+    assert( rc==SQLITE_OK );
     rc = pagerBeginReadTransaction(pPager);
   }
 
@@ -4755,6 +4774,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
 
  failed:
   if( rc!=SQLITE_OK ){
+    assert( !MEMDB );
     pager_unlock(pPager);
     assert( pPager->eState==PAGER_OPEN );
   }else{
@@ -5080,7 +5100,7 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
   assert( pPager->eState>=PAGER_READER );
   pPager->subjInMemory = (u8)subjInMemory;
 
-  if( pPager->eState==PAGER_READER ){
+  if( ALWAYS(pPager->eState==PAGER_READER) ){
     assert( pPager->pInJournal==0 );
 
     if( pagerUseWal(pPager) ){
index 7f280027057b2cfced9f9c13b837d9dc60dbaa6c..330bb69a7f24b4b6f3d9f6b5ff6c54954c7d1ca1 100644 (file)
@@ -1062,6 +1062,10 @@ do_execsql_test pager1-6.8 {
 } {11}
 do_execsql_test pager1-6.9 { COMMIT } {}
 
+do_execsql_test pager1-6.10 { PRAGMA max_page_count = 10 } {10}
+do_execsql_test pager1-6.11 { SELECT * FROM t11 }          {1 2 3 4}
+do_execsql_test pager1-6.12 { PRAGMA max_page_count }      {11}
+
 
 #-------------------------------------------------------------------------
 # The following tests work with "PRAGMA journal_mode=TRUNCATE" and
@@ -1742,7 +1746,7 @@ do_multiclient_test tn {
     }
   } {1 2 3 4 5 6}
   do_test pager1-17.$tn.3.2 {
-    csql1 { INSERT INTO t1 VALUES(3, 4) }
+  csql1 { INSERT INTO t1 VALUES(3, 4) }
   } {1 {database is locked}}
   do_test pager1-17.$tn.3.3 { sql2 COMMIT } {}
 }
@@ -2232,6 +2236,7 @@ db close
 tv delete
 
 #-------------------------------------------------------------------------
+#
 do_test pager1.27.1 {
   faultsim_delete_and_reopen
   sqlite3_pager_refcounts db
@@ -2243,4 +2248,29 @@ do_test pager1.27.1 {
   execsql COMMIT
 } {}
 
+#-------------------------------------------------------------------------
+# Test that attempting to open a write-transaction with 
+# locking_mode=exclusive in WAL mode fails if there are other clients on 
+# the same database.
+#
+catch { db close }
+do_multiclient_test tn {
+  do_test pager1-28.$tn.1 {
+    sql1 { 
+      PRAGMA journal_mode = WAL;
+      CREATE TABLE t1(a, b);
+      INSERT INTO t1 VALUES('a', 'b');
+    }
+  } {wal}
+  do_test pager1-28.$tn.2 { sql2 { SELECT * FROM t1 } } {a b}
+
+  do_test pager1-28.$tn.3 { sql1 { PRAGMA locking_mode=exclusive } } {exclusive}
+  do_test pager1-28.$tn.4 { 
+    csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); }
+  } {1 {database is locked}}
+  code2 { db2 close ; sqlite3 db2 test.db }
+  do_test pager1-28.$tn.4 { 
+    sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT }
+  } {}
+}
 finish_test
index 977d45f91b4173170a76a80061c000cb800c2db4..780aec527412b57242718e3e7cf3b018fb38f00d 100644 (file)
@@ -116,4 +116,21 @@ foreach code [list {
 db close
 tv delete
 
+
+#-------------------------------------------------------------------------
+# Test a ROLLBACK with journal_mode=off.
+#
+breakpoint
+do_test pager2-2.1 {
+  faultsim_delete_and_reopen
+  execsql {
+    CREATE TABLE t1(a, b);
+    PRAGMA journal_mode = off;
+    BEGIN;
+      INSERT INTO t1 VALUES(1, 2);
+    ROLLBACK;
+    SELECT * FROM t1;
+  }
+} {off 1 2}
+
 finish_test
index f9c82ddb604fe95addf771425dda817073eefc19..363bc3b7fa5b7361897ac75c3645d499ba70599f 100644 (file)
@@ -52,7 +52,7 @@ do_faultsim_test pagerfault-1 -prep {
 } -body {
   execsql { SELECT count(*) FROM t1 }
 } -test {
-  faultsim_test_result {0 4}
+  faultsim_test_result {0 4} 
   faultsim_integrity_check
   if {[db one { SELECT count(*) FROM t1 }] != 4} {
     error "Database content appears incorrect"
@@ -1078,4 +1078,50 @@ do_faultsim_test pagerfault-22 -prep {
   faultsim_integrity_check
 }
 
+#-------------------------------------------------------------------------
+# Provoke an OOM error during a commit of multi-file transaction. One of
+# the databases written during the transaction is an in-memory database.
+# This test causes rollback of the in-memory database after CommitPhaseOne()
+# has successfully returned. i.e. the series of calls for the aborted commit 
+# is:
+#
+#   PagerCommitPhaseOne(<in-memory-db>)   ->   SQLITE_OK
+#   PagerCommitPhaseOne(<file-db>)        ->   SQLITE_IOERR
+#   PagerRollback(<in-memory-db>)
+#   PagerRollback(<file-db>)
+#
+do_faultsim_test pagerfault-23 -prep {
+  foreach f [glob -nocomplain test.db*] { file delete -force $f }
+  sqlite3 db :memory:
+  db eval { 
+    ATTACH 'test.db2' AS aux;
+    CREATE TABLE t1(a, b);
+    CREATE TABLE aux.t2(a, b);
+  }
+} -body {
+  execsql { 
+    BEGIN;
+      INSERT INTO t1 VALUES(1,2);
+      INSERT INTO t2 VALUES(3,4); 
+    COMMIT;
+  }
+} -test {
+  faultsim_test_result {0 {}}
+  faultsim_integrity_check
+}
+
+do_faultsim_test pagerfault-24 -prep {
+  faultsim_delete_and_reopen
+  db eval { PRAGMA temp_store = file }
+  execsql { CREATE TABLE x(a, b) }
+} -body {
+  execsql { CREATE TEMP TABLE t1(a, b) }
+} -test {
+  faultsim_test_result {0 {}} {1 {unable to open a temporary database file for storing temporary tables}}
+  set ic [db eval { PRAGMA temp.integrity_check }]
+  if {$ic != "ok"} { error "Integrity check: $ic" }
+}
+
+
+
 finish_test