]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
When the final connection disconnects from a wal mode database, check that the
authordan <dan@noemail.net>
Wed, 7 Feb 2018 16:14:41 +0000 (16:14 +0000)
committerdan <dan@noemail.net>
Wed, 7 Feb 2018 16:14:41 +0000 (16:14 +0000)
database file has not been moved or unlinked before deleting the wal and shm
files.

FossilOrigin-Name: 4761db83b6d3d57f281370899403c102e39ad0021d315dd6a6912d250436782a

manifest
manifest.uuid
src/pager.c
test/nockpt.test

index 48ba691a5ff89e85d9a14e1ce2aa8dc314e1fe6f..80740ef60b3b62cca1eb1d5a543da3fc0dff16cc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Adjust\sthe\sprevious\scheck-in,\swhich\smodified\sthe\sWin32\sVFS,\sso\sthat\sit\sworks\swith\sSQLITE_OMIT_WAL.
-D 2018-02-05T21:02:47.090
+C When\sthe\sfinal\sconnection\sdisconnects\sfrom\sa\swal\smode\sdatabase,\scheck\sthat\sthe\ndatabase\sfile\shas\snot\sbeen\smoved\sor\sunlinked\sbefore\sdeleting\sthe\swal\sand\sshm\nfiles.
+D 2018-02-07T16:14:41.573
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 7a3f714b4fcf793108042b7b0a5c720b0b310ec84314d61ba7f3f49f27e550ea
@@ -474,7 +474,7 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
 F src/os_unix.c ce491421b3a54b63094a155eeac668a3bc8e5b86a5a58551d906e5b5affb443f
 F src/os_win.c eb03c6d52f893bcd7fdd4c6006674c13c1b5e49543fec98d605201af2997171c
 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
-F src/pager.c cd194a8793ce061e184ddc369fadbc1020c6f431014d22093f6c5e55c9234033
+F src/pager.c a3834a40acc2f3ab247d846f850d8c9313587d9c99c57a4dc194f2d4d7bf9d15
 F src/pager.h 581698f2177e8bd4008fe4760898ce20b6133d1df22139b9101b5155f900df7a
 F src/parse.y 4e750e1b261ff9f1d0b6b5d40a829c66d691899f48953fde839d8b52d41aa148
 F src/pcache.c 7ae91a4557a43d77d449accbfdc68846e6516f8e2eda46e8bbe4536fb669b201
@@ -1094,7 +1094,7 @@ F test/multiplex4.test e8ae4c4bd70606a5727743241f13b5701990abe4
 F test/mutex1.test ea2cc74d97f077b9e74c84cbd024f14d79a8126f
 F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
 F test/nan.test 437d40e6d0778b050d7750726c0cbd2c9936b81962926e8f8c48ca698f00f4d1
-F test/nockpt.test 9a436a7213ba5ef7a32304998d386d3ea3f76c9d
+F test/nockpt.test d291d618c934a453683cb2eff95f633d406f7147fa0403e10055db19dcc3842a
 F test/nolock.test f196cf8b8fbea4e2ca345140a2b3f3b0da45c76e
 F test/normalize.test 501630ab49b0b26b65c74124bf03e3374c1b57fa97aae750f84803609141d167
 F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
@@ -1704,7 +1704,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 535ed0ac5e8728ec91fc0a4cb54b820923d161cfd4e0e6aed6df6cdae365bc7d
-R 610345e12e18345d79d861c16dfe01d9
-U mistachkin
-Z 7a54a037e46133481e56c9143fb3122e
+P 36c2e67e82626f8d0a187c6c286c133ed659889e3b577469261b9dcd3b3ab75b
+R ed31a2562d55c76884009a210d222cf0
+U dan
+Z 0e612251c296ff4ce7a47fbd872de74d
index 899bc00cb897a5dcfb409c668442d75b2da93865..f1767d6dc9da60332e73fcc063e82615b0d834b0 100644 (file)
@@ -1 +1 @@
-36c2e67e82626f8d0a187c6c286c133ed659889e3b577469261b9dcd3b3ab75b
\ No newline at end of file
+4761db83b6d3d57f281370899403c102e39ad0021d315dd6a6912d250436782a
\ No newline at end of file
index e4680bfbfed5098904aac60f95a54544a34ec80d..93a9b3bd3ee9da52c3fbde24fae40a71bad36193 100644 (file)
@@ -4102,6 +4102,30 @@ static void pagerFreeMapHdrs(Pager *pPager){
   }
 }
 
+/* Verify that the database file has not be deleted or renamed out from
+** under the pager.  Return SQLITE_OK if the database is still were it ought
+** to be on disk.  Return non-zero (SQLITE_READONLY_DBMOVED or some other error
+** code from sqlite3OsAccess()) if the database has gone missing.
+*/
+static int databaseIsUnmoved(Pager *pPager){
+  int bHasMoved = 0;
+  int rc;
+
+  if( pPager->tempFile ) return SQLITE_OK;
+  if( pPager->dbSize==0 ) return SQLITE_OK;
+  assert( pPager->zFilename && pPager->zFilename[0] );
+  rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
+  if( rc==SQLITE_NOTFOUND ){
+    /* If the HAS_MOVED file-control is unimplemented, assume that the file
+    ** has not been moved.  That is the historical behavior of SQLite: prior to
+    ** version 3.8.3, it never checked */
+    rc = SQLITE_OK;
+  }else if( rc==SQLITE_OK && bHasMoved ){
+    rc = SQLITE_READONLY_DBMOVED;
+  }
+  return rc;
+}
+
 
 /*
 ** Shutdown the page cache.  Free all memory and close all files.
@@ -4118,8 +4142,7 @@ static void pagerFreeMapHdrs(Pager *pPager){
 ** to the caller.
 */
 int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
-  u8 *pTmp = (u8 *)pPager->pTmpSpace;
-
+  u8 *pTmp = (u8*)pPager->pTmpSpace;
   assert( db || pagerUseWal(pPager)==0 );
   assert( assert_pager_state(pPager) );
   disable_simulated_io_errors();
@@ -4128,11 +4151,17 @@ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
   /* pPager->errCode = 0; */
   pPager->exclusiveMode = 0;
 #ifndef SQLITE_OMIT_WAL
-  assert( db || pPager->pWal==0 );
-  sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,
-      (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp)
-  );
-  pPager->pWal = 0;
+  {
+    u8 *a = 0;
+    assert( db || pPager->pWal==0 );
+    if( db && 0==(db->flags & SQLITE_NoCkptOnClose) 
+     && SQLITE_OK==databaseIsUnmoved(pPager)
+    ){
+      a = pTmp;
+    }
+    sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a);
+    pPager->pWal = 0;
+  }
 #endif
   pager_reset(pPager);
   if( MEMDB ){
@@ -4967,30 +4996,6 @@ act_like_temp_file:
 }
 
 
-/* Verify that the database file has not be deleted or renamed out from
-** under the pager.  Return SQLITE_OK if the database is still were it ought
-** to be on disk.  Return non-zero (SQLITE_READONLY_DBMOVED or some other error
-** code from sqlite3OsAccess()) if the database has gone missing.
-*/
-static int databaseIsUnmoved(Pager *pPager){
-  int bHasMoved = 0;
-  int rc;
-
-  if( pPager->tempFile ) return SQLITE_OK;
-  if( pPager->dbSize==0 ) return SQLITE_OK;
-  assert( pPager->zFilename && pPager->zFilename[0] );
-  rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
-  if( rc==SQLITE_NOTFOUND ){
-    /* If the HAS_MOVED file-control is unimplemented, assume that the file
-    ** has not been moved.  That is the historical behavior of SQLite: prior to
-    ** version 3.8.3, it never checked */
-    rc = SQLITE_OK;
-  }else if( rc==SQLITE_OK && bHasMoved ){
-    rc = SQLITE_READONLY_DBMOVED;
-  }
-  return rc;
-}
-
 
 /*
 ** This function is called after transitioning from PAGER_UNLOCK to
index bd3953f1ee634675d9983095ec8ec29003e91909..8f6b5e3be48374b9176606f4d42d629375f404f9 100644 (file)
@@ -61,6 +61,84 @@ do_test 1.14 { sqlite3_db_config db NO_CKPT_ON_CLOSE 1 } {1}
 do_execsql_test 1.14 { PRAGMA main.journal_mode = delete } {delete}
 do_test 1.15 { file exists test.db-wal } {0}
 
+#-------------------------------------------------------------------------
+# Test an unusual scenario:
+#
+#   1. A wal mode db is opened and written. Then sqlite3_close_v2() used
+#      to close the db handle while there is still an unfinalized
+#      statement (so the db handle stays open).
+#
+#   2. The db, wal and *-shm files are deleted from the file system.
+#
+#   3. Another connection creates a new wal mode db at the same file-system
+#      location as the previous one.
+#
+#   4. The statement left unfinalized in (1) is finalized.
+#
+# The test is to ensure that the connection left open in step (1) does
+# not try to delete the wal file from the file-system as part of step
+# 4.
+#
+reset_db
+db close
+
+# Open a connection on a wal database. Write to it a bit. Then prepare
+# a statement and call sqlite3_close_v2() (so that the statement handle
+# holds the db connection open).
+#
+set ::db1 [sqlite3_open_v2 test.db SQLITE_OPEN_READWRITE ""]
+do_test 2.0 {
+  lindex [
+    sqlite3_exec $::db1 {
+      PRAGMA journal_mode = wal;
+      CREATE TABLE t1(x PRIMARY KEY, y UNIQUE, z);
+      INSERT INTO t1 VALUES(1, 2, 3);
+      PRAGMA wal_checkpoint;
+    }] 0
+} {0}
+set ::stmt [sqlite3_prepare $::db1 "SELECT * FROM t1" -1 dummy]
+sqlite3_close_v2 $::db1
+
+# Delete the database, wal and shm files.
+#
+forcedelete test.db test.db-wal test.db-shm
+
+# Open and populate a new database file at the same file-system location
+# as the one just deleted. Contrive a partial checkpoint on it.
+#
+sqlite3 db  test.db
+sqlite3 db2 test.db
+do_execsql_test 2.1 {
+  PRAGMA journal_mode = wal;
+  CREATE TABLE y1(a PRIMARY KEY, b UNIQUE, c);
+  INSERT INTO y1 VALUES('a', 'b', 'c');
+  INSERT INTO y1 VALUES('d', 'e', 'f');
+} {wal}
+do_execsql_test -db db2 2.2 {
+  BEGIN;
+    SELECT * FROM y1;
+} {a b c d e f}
+do_execsql_test 2.3 {
+  UPDATE y1 SET c='g' WHERE a='d';
+  PRAGMA wal_checkpoint;
+} {0 11 10}
+do_execsql_test -db db2 2.4 {
+  COMMIT
+}
+
+# Finalize the statement handle, causing the first connection to be
+# closed. Test that this has not corrupted the database file by 
+# deleting the new wal file from the file-system. If it has, this
+# test should fail with an IO or corruption error.
+#
+do_test 2.5 {
+  sqlite3_finalize $::stmt
+  sqlite3 db3 test.db
+  execsql { 
+    PRAGMA integrity_check; 
+    SELECT * FROM y1;
+  } db3
+} {ok a b c d e g}
 
 
 finish_test