]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Check that the first byte of a potentially hot journal file is non-zero before beginn...
authordanielk1977 <danielk1977@noemail.net>
Thu, 26 Mar 2009 17:13:06 +0000 (17:13 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 26 Mar 2009 17:13:06 +0000 (17:13 +0000)
FossilOrigin-Name: 80241a050296067937d0b0529fdf0c347358f86c

manifest
manifest.uuid
src/pager.c
test/jrnlmode2.test [new file with mode: 0644]
test/tester.tcl

index 2dd07b2788f85846355934cc72f49af0d3069dbf..48df9da915a87ff5467ab7b32e4a32b614c9c9fc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Prevent\sthe\stcl\stest\ssuite\sfrom\sthrowing\san\sexception\swhen\sa\sthreadsafe\sSQLite\sis\slinked\sagainst\sa\snon-threadsafe\sTcl\sfor\stesting.\sPrint\sa\swarning\sinstead.\sTicket\s#3753.\s(CVS\s6389)
-D 2009-03-26T14:48:07
+C Check\sthat\sthe\sfirst\sbyte\sof\sa\spotentially\shot\sjournal\sfile\sis\snon-zero\sbefore\sbeginning\shot-journal\srollback.\sFix\sfor\s#3751\sand\s#3745.\s(CVS\s6390)
+D 2009-03-26T17:13:06
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 583e87706abc3026960ed759aff6371faf84c211
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -143,7 +143,7 @@ F src/os_common.h 8c61457df58f1a4bd5f5adc3e90e01b37bf7afbc
 F src/os_os2.c bed77dc26e3a95ce4a204936b9a1ca6fe612fcc5
 F src/os_unix.c 7bf3ebde13154a97f797df1551d19cf0d2785c15
 F src/os_win.c 40636702058ed4dcd35d68151bfab56d4997cdc1
-F src/pager.c da7ffd068347cdc88b477875585d53dcf2bdc4a7
+F src/pager.c 6a932669c9cd779aa03f111f1a89bc1bcf7a6465
 F src/pager.h 0c9f3520c00d8a3b8e792ca56c9a11b6b02b4b0f
 F src/parse.y b9ba0946a13e9f32a96044e64a3e8780269b08b0
 F src/pcache.c fcf7738c83c4d3e9d45836b2334c8a368cc41274
@@ -421,6 +421,7 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
 F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
 F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19
 F test/jrnlmode.test 32e79fa272e78ff3b734aac08db886f98f84165a
+F test/jrnlmode2.test bd846e10d463c0b6f8bded674cb07f94a0277097
 F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05
 F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51
 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
@@ -556,7 +557,7 @@ F test/tclsqlite.test 8b1150d0486c4848c70d96422513a91c5342be0e
 F test/tempdb.test b88ac8a19823cf771d742bf61eef93ef337c06b1
 F test/temptable.test 5d8ca46be28cc06c887c5a77df650843b7edbae1
 F test/temptrigger.test 03093be9967942623232dfdf2a63b832d4e0e4fa
-F test/tester.tcl 66546f6766029384360b24cacb3896376c5f5f69
+F test/tester.tcl ebaa00336761e1b97f56a8376178289097db43ab
 F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
 F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
 F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@@ -709,7 +710,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P e99e28efbbfff3e46eec0f90d566991de4406fd8
-R 1f72d75124ff1aa9ba3be363b671b72e
+P a22e7c818b2227a4c80ad84b299d11f365e3b17d
+R 4ee0c36ed6c98bbdc0cb2efb14dfd44e
 U danielk1977
-Z 8663270a3bb5409566dc3bce815cb5b0
+Z 9399bffef7da619c63a0329f4e82af23
index 8bda3a8dfe5df394738165daf97c2e548b71010f..ce957cb9fad761a78c0ce61d980b263b9fd86215 100644 (file)
@@ -1 +1 @@
-a22e7c818b2227a4c80ad84b299d11f365e3b17d
\ No newline at end of file
+80241a050296067937d0b0529fdf0c347358f86c
\ No newline at end of file
index 335ec21e5172622f98acae111d12ffe93c447c9e..e17bcf5bdb454e2f5a103ca0ce41976fd98f4d2a 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.572 2009/03/23 04:33:33 danielk1977 Exp $
+** @(#) $Id: pager.c,v 1.573 2009/03/26 17:13:06 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -3329,11 +3329,12 @@ int sqlite3PagerOpen(
 ** PAGER_SHARED state. It tests if there is a hot journal present in
 ** the file-system for the given pager. A hot journal is one that 
 ** needs to be played back. According to this function, a hot-journal
-** file exists if the following three criteria are met:
+** file exists if the following criteria are met:
 **
 **   * The journal file exists in the file system, and
 **   * No process holds a RESERVED or greater lock on the database file, and
-**   * The database file itself is greater than 0 bytes in size.
+**   * The database file itself is greater than 0 bytes in size, and
+**   * The first byte of the journal file exists and is not 0x00.
 **
 ** If the current size of the database file is 0 but a journal file
 ** exists, that is probably an old journal left over from a prior
@@ -3341,14 +3342,12 @@ int sqlite3PagerOpen(
 ** just deleted using OsDelete, *pExists is set to 0 and SQLITE_OK
 ** is returned.
 **
-** This routine does not open the journal file to examine its
-** content.  Hence, the journal might contain the name of a master
-** journal file that has been deleted, and hence not be hot.  Or
-** the header of the journal might be zeroed out.  This routine
-** does not discover these cases of a non-hot journal - if the
-** journal file exists and is not empty this routine assumes it
-** is hot.  The pager_playback() routine will discover that the
-** journal file is not really hot and will no-op.
+** This routine does not check if there is a master journal filename
+** at the end of the file. If there is, and that master journal file
+** does not exist, then the journal file is not really hot. In this
+** case this routine will return a false-positive. The pager_playback()
+** routine will discover that the journal file is not really hot and 
+** will not roll it back. 
 **
 ** If a hot-journal file is found to exist, *pExists is set to 1 and 
 ** SQLITE_OK returned. If no hot-journal file is present, *pExists is
@@ -3359,29 +3358,52 @@ int sqlite3PagerOpen(
 static int hasHotJournal(Pager *pPager, int *pExists){
   sqlite3_vfs * const pVfs = pPager->pVfs;
   int rc;                       /* Return code */
-  int exists = 0;               /* True if a journal file is present */
-  int locked = 0;               /* True if some process holds a RESERVED lock */
+  int exists;                   /* True if a journal file is present */
 
   assert( pPager!=0 );
   assert( pPager->useJournal );
   assert( isOpen(pPager->fd) );
+  assert( !isOpen(pPager->jfd) );
 
   *pExists = 0;
   rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
   if( rc==SQLITE_OK && exists ){
+    int locked;                 /* True if some process holds a RESERVED lock */
     rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
     if( rc==SQLITE_OK && !locked ){
       int nPage;
+
+      /* Check the size of the database file. If it consists of 0 pages,
+      ** then delete the journal file. See the header comment above for 
+      ** the reasoning here.
+      */
       rc = sqlite3PagerPagecount(pPager, &nPage);
       if( rc==SQLITE_OK ){
-       if( nPage==0 ){
-          sqlite3OsDelete(pVfs, pPager->zJournal, 0);
+        if( nPage==0 ){
+          rc = sqlite3OsDelete(pVfs, pPager->zJournal, 0);
         }else{
-          *pExists = 1;
+          /* The journal file exists and no other connection has a reserved
+          ** or greater lock on the database file. Now check that there is
+          ** at least one non-zero bytes at the start of the journal file.
+          ** If there is, then we consider this journal to be hot. If not, 
+          ** it can be ignored.
+          */
+          int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
+          rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
+          if( rc==SQLITE_OK ){
+            u8 first = 0;
+            rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
+            if( rc==SQLITE_IOERR_SHORT_READ ){
+              rc = SQLITE_OK;
+            }
+            sqlite3OsClose(pPager->jfd);
+            *pExists = (first!=0);
+          }
         }
       }
     }
   }
+
   return rc;
 }
 
@@ -3927,7 +3949,7 @@ static int pager_open_journal(Pager *pPager){
       sqlite3MemJournalOpen(pPager->jfd);
     }else{
       const int flags =                   /* VFS flags to open journal file */
-        SQLITE_OPEN_READWRITE|SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_CREATE|
+        SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
         (pPager->tempFile ? 
           (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
           (SQLITE_OPEN_MAIN_JOURNAL)
diff --git a/test/jrnlmode2.test b/test/jrnlmode2.test
new file mode 100644 (file)
index 0000000..d0f3451
--- /dev/null
@@ -0,0 +1,100 @@
+# 2009 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.
+#
+#***********************************************************************
+#
+# $Id: jrnlmode2.test,v 1.5 2009/03/26 17:13:06 danielk1977 Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!pager_pragmas} {
+  finish_test
+  return
+}
+
+#-------------------------------------------------------------------------
+# Test overview:
+#
+#   jrnlmode2-1.*: Demonstrate bug #3745
+#   jrnlmode2-2.*: Demonstrate bug #3751
+#
+
+do_test jrnlmode2-1.1 {
+  execsql {
+    PRAGMA journal_mode = persist;
+    CREATE TABLE t1(a, b);
+    INSERT INTO t1 VALUES(1, 2);
+  }
+} {persist}
+
+do_test jrnlmode2-1.2 {
+  file exists test.db-journal
+} {1}
+
+do_test jrnlmode2-1.3 {
+  sqlite3 db2 test.db
+  execsql { SELECT * FROM t1 } db2
+} {1 2}
+
+do_test jrnlmode2-1.4 {
+  execsql {
+    INSERT INTO t1 VALUES(3, 4);
+    BEGIN;
+    SELECT * FROM t1;
+  }
+  execsql { PRAGMA lock_status }
+} {main shared temp closed}
+
+do_test jrnlmode2-1.5 {
+  file exists test.db-journal
+} {1}
+
+do_test jrnlmode2-1.6 {
+  catchsql { SELECT * FROM t1 } db2
+} {0 {1 2 3 4}}
+
+do_test jrnlmode2-1.7 {
+  execsql { COMMIT }
+  catchsql { SELECT * FROM t1 } db2
+} {0 {1 2 3 4}}
+
+
+
+do_test jrnlmode2-2.1 {
+  db2 close
+  execsql { PRAGMA journal_mode = truncate }
+  execsql { INSERT INTO t1 VALUES(5, 6) }
+} {}
+
+do_test jrnlmode2-2.2 {
+  file exists test.db-journal
+} {1}
+
+do_test jrnlmode2-2.3 {
+  file size test.db-journal
+} {0}
+
+do_test jrnlmode2-2.4 {
+  sqlite3 db2 test.db -readonly 1
+  catchsql { SELECT * FROM t1 } db2
+} {0 {1 2 3 4 5 6}}
+
+do_test jrnlmode2-2.5 {
+  file delete test.db-journal
+} {}
+
+do_test jrnlmode2-2.6 {
+  sqlite3 db2 test.db -readonly 1
+  catchsql { SELECT * FROM t1 } db2
+} {0 {1 2 3 4 5 6}}
+
+catch { db2 close }
+finish_test
+
index 046fb2ed36385f097048ad665a361411fb409fad..d3624cda7e255a0a1acbaddf366b9efec1e7d3d9 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements some common TCL routines used for regression
 # testing the SQLite library
 #
-# $Id: tester.tcl,v 1.140 2009/02/19 14:39:25 danielk1977 Exp $
+# $Id: tester.tcl,v 1.141 2009/03/26 17:13:06 danielk1977 Exp $
 
 #
 # What for user input before continuing.  This gives an opportunity
@@ -290,6 +290,7 @@ proc finalize_testing {} {
   if {$nErr>0} {
     puts "Failures on these tests: $::failList"
   }
+  run_thread_tests 1
   if {[llength $omitList]>0} {
     puts "Omitted test cases:"
     set prec {}
@@ -959,3 +960,5 @@ proc copy_file {from to} {
 # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
 # to non-zero, then set the global variable $AUTOVACUUM to 1.
 set AUTOVACUUM $sqlite_options(default_autovacuum)
+
+source $testdir/thread_common.tcl