]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the ability to read read-only WAL-mode database when -shm is present,
authordrh <>
Thu, 20 Jan 2022 12:36:00 +0000 (12:36 +0000)
committerdrh <>
Thu, 20 Jan 2022 12:36:00 +0000 (12:36 +0000)
([00ec95fcd02bb415|check-in 00ec95fcd02bb415]) so that it works
for the case of 64K page size.

FossilOrigin-Name: 2cc15e2f2918114dc8ab86774299511adceac8e9a4f129aa413263d8705b0c3a

manifest
manifest.uuid
src/wal.c
test/walro2.test

index 0f6edab17d465caf3c0cafbfdf7c94a36780fe0d..4b09be5ba43f7745ee69f8bfd9088e11700a6d03 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Allow\san\s"IntReal"\svalue\sto\scount\sas\sa\sREAL\swhen\schecking\stypes\sfor\ninsertion\sinto\sa\sgenerated\scolumn\son\sa\sSTRICT\stable.\n[forum:/forumpost/fa012c77796d9399|Forum\spost\sfa012c77796d9399].
-D 2022-01-17T23:53:22.530
+C Fix\sthe\sability\sto\sread\sread-only\sWAL-mode\sdatabase\swhen\s-shm\sis\spresent,\n([00ec95fcd02bb415|check-in\s00ec95fcd02bb415])\sso\sthat\sit\sworks\nfor\sthe\scase\sof\s64K\spage\ssize.
+D 2022-01-20T12:36:00.363
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -635,7 +635,7 @@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf8
 F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
 F src/vtab.c 9d5c3f49d3a6959b6eef287bb8fa773563102a80a835c3314c57144412709e78
 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
-F src/wal.c ed0398a7adf02c31e34aada42cc86c58f413a7afe5f741a5d373ad087abde028
+F src/wal.c c55ef660f9ee91e1331ced61f312aae81179ff9ec8632639136bcb252ce86e1b
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
 F src/where.c de0d4ff409c7b62a8803f9f267cc2c7fedddbc00de9ab7b5382c507383c18665
@@ -1751,7 +1751,7 @@ F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
 F test/walprotocol.test 1b3f922125e341703f6e946d77fdc564d38fb3e07a9385cfdc6c99cac1ecf878
 F test/walprotocol2.test 7d3b6b4bf0b12f8007121b1e6ef714bc99101fb3b48e46371df1db868eebc131
 F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20
-F test/walro2.test 0e79dd15cbdb4f482c01ea248373669c732414a726b357d04846a816afafb768
+F test/walro2.test 33955a6fd874dd9724005e17f77fef89d334b3171454a1256fe4941a96766cdc
 F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cfd51af68
 F test/walsetlk.test 3185bebc90557e0d611442c8d64f7a0cb7b06f8e156eea37a4a7358f722715be
 F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417
@@ -1936,9 +1936,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5d17a0
-Q +1ec44d55da2ced1a1b0b78b489caff628652464f5709ee827e35409eb20ea794
-R 1e5db94997199ebe827df82b2178e3d9
+P b6fb223d90f453d67fcfc71c973c9fa0b6ad2a8826f59a25435ccf72d2f6fbc6
+Q +f426874e005e3c23e8a00083b7c201408e072bca413e52bfc436da6483afb0cd
+R 348a066bda3a4c63d3a06b50a85b4b6a
 U drh
-Z 2cf0f43a19a814e8bddd48c1cf5ad151
+Z 97cf19dd4d5f8b029f3590616d697ff0
 # Remove this line to create a well-formed Fossil manifest.
index ec682ceb1e552b6558eb697aa8521477d4255373..0097d7db0f69c72156b40435ada11c2255830634 100644 (file)
@@ -1 +1 @@
-b6fb223d90f453d67fcfc71c973c9fa0b6ad2a8826f59a25435ccf72d2f6fbc6
\ No newline at end of file
+2cc15e2f2918114dc8ab86774299511adceac8e9a4f129aa413263d8705b0c3a
\ No newline at end of file
index a0ec91965caf1b25b0bddabac00c1161fc250249..37930f89e228da48b76925bc4081f2aa8111254c 100644 (file)
--- a/src/wal.c
+++ b/src/wal.c
@@ -2520,6 +2520,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
   volatile void *pDummy;          /* Dummy argument for xShmMap */
   int rc;                         /* Return code */
   u32 aSaveCksum[2];              /* Saved copy of pWal->hdr.aFrameCksum */
+  int szPage;                     /* Page size */
 
   assert( pWal->bShmUnreliable );
   assert( pWal->readOnly & WAL_SHM_RDONLY );
@@ -2603,7 +2604,8 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
   }
 
   /* Allocate a buffer to read frames into */
-  szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE;
+  szPage = walPagesize(pWal);
+  szFrame = szPage + WAL_FRAME_HDRSIZE;
   aFrame = (u8 *)sqlite3_malloc64(szFrame);
   if( aFrame==0 ){
     rc = SQLITE_NOMEM_BKPT;
@@ -2617,7 +2619,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
   ** the caller.  */
   aSaveCksum[0] = pWal->hdr.aFrameCksum[0];
   aSaveCksum[1] = pWal->hdr.aFrameCksum[1];
-  for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); 
+  for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, szPage); 
       iOffset+szFrame<=szWal; 
       iOffset+=szFrame
   ){
index 34408c16954ce6d0f652ee7cb96d4605ce59584a..1eb9d1c3ee32ae10ec7f731027626164e82deda8 100644 (file)
@@ -50,357 +50,362 @@ do_execsql_test 0.0 {
   CREATE TABLE t1(x);
 } {wal}
 set MINSHMSZ [file size test.db-shm]
+set dfltpgsz [db one {PRAGMA page_size}]
 
 foreach bZeroShm {0 1} {
-set TN [expr $bZeroShm+1]
-do_multiclient_test tn {
-  
-  # Close all connections and delete the database.
-  #
-  code1 { db close  }
-  code2 { db2 close }
-  code3 { db3 close }
-  forcedelete test.db
-  
-  # Do not run tests with the connections in the same process.
-  #
-  if {$tn==2} continue
-
-  foreach c {code1 code2 code3} {
-    $c {
-      sqlite3_shutdown
-      sqlite3_config_uri 1
-    }
-  }
-
-  do_test $TN.1.1 {
-    code2 { sqlite3 db2 test.db }
-    sql2 { 
-      CREATE TABLE t1(x, y);
-      PRAGMA journal_mode = WAL;
-      INSERT INTO t1 VALUES('a', 'b');
-      INSERT INTO t1 VALUES('c', 'd');
-    }
-    file exists test.db-shm
-  } {1}
-
-  do_test $TN.1.2.1 {
-    copy_to_test2 $bZeroShm
-    code1 {
-      sqlite3 db file:test.db2?readonly_shm=1
-    }
-
-    sql1 { SELECT * FROM t1 }
-  } {a b c d}
-  do_test $TN.1.2.2 {
-    sql1 { SELECT * FROM t1 }
-  } {a b c d}
-
-  do_test $TN.1.3.1 {
-    code3 { sqlite3 db3 test.db2 }
-    sql3 { SELECT * FROM t1 }
-  } {a b c d}
-
-  do_test $TN.1.3.2 {
-    sql1 { SELECT * FROM t1 }
-  } {a b c d}
-
-  code1 { db close  }
-  code2 { db2 close }
-  code3 { db3 close }
-
-  do_test $TN.2.1 {
-    code2 { sqlite3 db2 test.db }
-    sql2 { 
-      INSERT INTO t1 VALUES('e', 'f');
-      INSERT INTO t1 VALUES('g', 'h');
-    }
-    file exists test.db-shm
-  } {1}
-
-  do_test $TN.2.2 {
-    copy_to_test2 $bZeroShm
-    code1 {
-      sqlite3 db file:test.db2?readonly_shm=1
-    }
-    sql1 { 
-      BEGIN;
-      SELECT * FROM t1;
-    }
-  } {a b c d e f g h}
-
-  do_test $TN.2.3.1 {
-    code3 { sqlite3 db3 test.db2 }
-    sql3 { SELECT * FROM t1 }
-  } {a b c d e f g h}
-  do_test $TN.2.3.2 {
-    sql3 { INSERT INTO t1 VALUES('i', 'j') }
-    code3 { db3 close }
-    sql1 { COMMIT } 
-  } {}
-  do_test $TN.2.3.3 {
-    sql1 { SELECT * FROM t1 }
-  } {a b c d e f g h i j}
-
-
-  #-----------------------------------------------------------------------
-  # 3.1.*: That a readonly_shm connection can read a database file if both
-  #        the *-wal and *-shm files are zero bytes in size.
-  #
-  # 3.2.*: That it flushes the cache if, between transactions on a db with a
-  #        zero byte *-wal file, some other connection modifies the db, then
-  #        does "PRAGMA wal_checkpoint=truncate" to truncate the wal file
-  #        back to zero bytes in size.
-  #
-  # 3.3.*: That, if between transactions some other process wraps the wal
-  #        file, the readonly_shm client reruns recovery.
-  #
-  catch { code1 { db close } }
-  catch { code2 { db2 close } }
-  catch { code3 { db3 close } }
-  do_test $TN.3.1.0 {
-    list [file exists test.db-wal] [file exists test.db-shm]
-  } {0 0}
-  do_test $TN.3.1.1 {
-    close [open test.db-wal w]
-    close [open test.db-shm w]
-    code1 {
-      sqlite3 db file:test.db?readonly_shm=1
-    }
-    sql1 { SELECT * FROM t1 }
-  } {a b c d e f g h}
-
-  do_test $TN.3.2.0 {
-    list [file size test.db-wal] [file size test.db-shm]
-  } {0 0}
-  do_test $TN.3.2.1 {
-    code2 { sqlite3 db2 test.db }
-    sql2 { INSERT INTO t1 VALUES(1, 2) ; PRAGMA wal_checkpoint=truncate }
-    code2 { db2 close }
-    sql1 { SELECT * FROM t1 }
-  } {a b c d e f g h 1 2}
-  do_test $TN.3.2.2 {
-    list [file size test.db-wal] [file size test.db-shm]
-  } [list 0 $MINSHMSZ]
-
-  do_test $TN.3.3.0 {
-    code2 { sqlite3 db2 test.db }
-    sql2 { 
-      INSERT INTO t1 VALUES(3, 4);
-      INSERT INTO t1 VALUES(5, 6);
-      INSERT INTO t1 VALUES(7, 8);
-      INSERT INTO t1 VALUES(9, 10);
-    }
-    code2 { db2 close }
-    code1 { db close }
-    list [file size test.db-wal] [file size test.db-shm]
-  } [list [wal_file_size 4 1024] $MINSHMSZ]
-  do_test $TN.3.3.1 {
-    code1 { sqlite3 db file:test.db?readonly_shm=1 }
-    sql1 { SELECT * FROM t1 }
-  } {a b c d e f g h 1 2 3 4 5 6 7 8 9 10}
-  do_test $TN.3.3.2 {
-    code2 { sqlite3 db2 test.db }
-    sql2 { 
-      PRAGMA wal_checkpoint; 
-      DELETE FROM t1;
-      INSERT INTO t1 VALUES('i', 'ii');
-    }
-    code2 { db2 close }
-    list [file size test.db-wal] [file size test.db-shm]
-  } [list [wal_file_size 4 1024] $MINSHMSZ]
-  do_test $TN.3.3.3 {
-    sql1 { SELECT * FROM t1 }
-  } {i ii}
-
-  #-----------------------------------------------------------------------
-  #
-  #
-  catch { code1 { db close } }
-  catch { code2 { db2 close } }
-  catch { code3 { db3 close } }
-
-  do_test $TN.4.0 {
-    code1 { forcedelete test.db }
-    code1 { sqlite3 db test.db }
-    sql1 {
-      PRAGMA journal_mode = wal;
-      CREATE TABLE t1(x);
-      INSERT INTO t1 VALUES('hello');
-      INSERT INTO t1 VALUES('world');
-    }
-
-    copy_to_test2 $bZeroShm
-
-    code1 { db close }
-  } {}
-
-  do_test $TN.4.1.1 {
-    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
-    sql2 { SELECT * FROM t1 }
-  } {hello world}
-
-  do_test $TN.4.1.2 {
-    code3 { sqlite3 db3 test.db2 }
-    sql3 {
-      INSERT INTO t1 VALUES('!');
-      PRAGMA wal_checkpoint = truncate;
-    }
-    code3 { db3 close }
-  } {}
-  do_test $TN.4.1.3 {
-    sql2 { SELECT * FROM t1 }
-  } {hello world !}
-
-  catch { code1 { db close } }
-  catch { code2 { db2 close } }
-  catch { code3 { db3 close } }
-
-  do_test $TN.4.2.1 {
-    code1 { sqlite3 db test.db }
-    sql1 {
-      INSERT INTO t1 VALUES('!');
-      INSERT INTO t1 VALUES('!');
-
-      PRAGMA cache_size = 10;
-      CREATE TABLE t2(x);
-
-      BEGIN;
-        WITH s(i) AS (
-          SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
-          )
-        INSERT INTO t2 SELECT randomblob(500) FROM s;
-        SELECT count(*) FROM t2;
-    } 
-  } {500}
-  set sz [file size test.db-wal]
-  do_test $TN.4.2.2.(sz=$sz) {
-    expr {$sz>400000}
-  } {1}
-  do_test $TN.4.2.4 {
-    file_control_persist_wal db 1; db close
-
-    copy_to_test2 $bZeroShm
-    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
-    sql2 {
-      SELECT * FROM t1;
-      SELECT count(*) FROM t2;
-    }
-  } {hello world ! ! 0}
-
-  #-----------------------------------------------------------------------
-  #
-  #
-  catch { code1 { db close } }
-  catch { code2 { db2 close } }
-  catch { code3 { db3 close } }
-
-  do_test $TN.5.0 {
-    code1 { forcedelete test.db }
-    code1 { sqlite3 db test.db }
-    sql1 {
-      PRAGMA journal_mode = wal;
-      CREATE TABLE t1(x);
-      INSERT INTO t1 VALUES('hello');
-      INSERT INTO t1 VALUES('world');
-      INSERT INTO t1 VALUES('!');
-      INSERT INTO t1 VALUES('world');
-      INSERT INTO t1 VALUES('hello');
-    }
-
-    copy_to_test2 $bZeroShm
+  for {set pgsz 512} {$pgsz<=65536} {set pgsz [expr {$pgsz*2}]} {
+    set TN [expr $bZeroShm+1]-$pgsz
+    do_multiclient_test tn {
+      
+      # Close all connections and delete the database.
+      #
+      code1 { db close  }
+      code2 { db2 close }
+      code3 { db3 close }
+      forcedelete test.db
+      
+      # Do not run tests with the connections in the same process.
+      #
+      if {$tn==2} continue
     
-    code1 { db close }
-  } {}
-
-  do_test $TN.5.1 {
-    code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
-    sql2 {
-      SELECT * FROM t1;
-    }
-  } {hello world ! world hello}
-
-  do_test $TN.5.2 {
-    code1 {
-      proc handle_read {op args} {
-        if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
-          set ::res2 [sql2 { SELECT * FROM t1 }]
+      foreach c {code1 code2 code3} {
+        $c {
+          sqlite3_shutdown
+          sqlite3_config_uri 1
         }
-        puts "$msg xRead $args"
-        return "SQLITE_OK"
       }
-      testvfs tvfs -fullshm 1
-
-      sqlite3 db file:test.db2?vfs=tvfs
-      db eval { SELECT * FROM sqlite_master }
-
-      tvfs filter xRead
-      tvfs script handle_read
-    }
-    sql1 {
-      PRAGMA wal_checkpoint = truncate;
-    }
-    code1 { set ::res2 }
-  } {hello world ! world hello}
-
-  do_test $TN.5.3 {
-    code1 { db close }
-    code1 { tvfs delete }
-  } {}
-
-  #-----------------------------------------------------------------------
-  #
-  #
-  catch { code1 { db close } }
-  catch { code2 { db2 close } }
-  catch { code3 { db3 close } }
-
-  do_test $TN.6.1 {
-    code1 { forcedelete test.db }
-    code1 { sqlite3 db test.db }
-    sql1 {
-      PRAGMA journal_mode = wal;
-      CREATE TABLE t1(x);
-      INSERT INTO t1 VALUES('hello');
-      INSERT INTO t1 VALUES('world');
-      INSERT INTO t1 VALUES('!');
-      INSERT INTO t1 VALUES('world');
-      INSERT INTO t1 VALUES('hello');
-    }
-
-    copy_to_test2 $bZeroShm
     
-    code1 { db close }
-  } {}
-
-  do_test $TN.6.2 {
-    code1 {
-      set ::nRem 5
-      proc handle_read {op args} {
-        if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
-          incr ::nRem -1
-          if {$::nRem==0} {
-            code2 { sqlite3 db2 test.db2 }
-            sql2  { PRAGMA wal_checkpoint = truncate }
+      do_test $TN.1.1 {
+        code2 { sqlite3 db2 test.db }
+        sql2 "PRAGMA page_size=$::pgsz"
+        sql2 {
+          CREATE TABLE t1(x, y);
+          PRAGMA journal_mode = WAL;
+          INSERT INTO t1 VALUES('a', 'b');
+          INSERT INTO t1 VALUES('c', 'd');
+        }
+        file exists test.db-shm
+      } {1}
+    
+      do_test $TN.1.2.1 {
+        copy_to_test2 $bZeroShm
+        code1 {
+          sqlite3 db file:test.db2?readonly_shm=1
+        }
+    
+        sql1 { SELECT * FROM t1 }
+      } {a b c d}
+      do_test $TN.1.2.2 {
+        sql1 { SELECT * FROM t1 }
+      } {a b c d}
+    
+      do_test $TN.1.3.1 {
+        code3 { sqlite3 db3 test.db2 }
+        sql3 { SELECT * FROM t1 }
+      } {a b c d}
+    
+      do_test $TN.1.3.2 {
+        sql1 { SELECT * FROM t1 }
+      } {a b c d}
+    
+      code1 { db close  }
+      code2 { db2 close }
+      code3 { db3 close }
+    
+      do_test $TN.2.1 {
+        code2 { sqlite3 db2 test.db }
+        sql2 "PRAGMA page_size=$::pgsz;"
+        sql2 { 
+          INSERT INTO t1 VALUES('e', 'f');
+          INSERT INTO t1 VALUES('g', 'h');
+        }
+        file exists test.db-shm
+      } {1}
+    
+      do_test $TN.2.2 {
+        copy_to_test2 $bZeroShm
+        code1 {
+          sqlite3 db file:test.db2?readonly_shm=1
+        }
+        sql1 { 
+          BEGIN;
+          SELECT * FROM t1;
+        }
+      } {a b c d e f g h}
+    
+      do_test $TN.2.3.1 {
+        code3 { sqlite3 db3 test.db2 }
+        sql3 { SELECT * FROM t1 }
+      } {a b c d e f g h}
+      do_test $TN.2.3.2 {
+        sql3 { INSERT INTO t1 VALUES('i', 'j') }
+        code3 { db3 close }
+        sql1 { COMMIT } 
+      } {}
+      do_test $TN.2.3.3 {
+        sql1 { SELECT * FROM t1 }
+      } {a b c d e f g h i j}
+    
+    
+      #-----------------------------------------------------------------------
+      # 3.1.*: That a readonly_shm connection can read a database file if both
+      #        the *-wal and *-shm files are zero bytes in size.
+      #
+      # 3.2.*: That it flushes the cache if, between transactions on a db with a
+      #        zero byte *-wal file, some other connection modifies the db, then
+      #        does "PRAGMA wal_checkpoint=truncate" to truncate the wal file
+      #        back to zero bytes in size.
+      #
+      # 3.3.*: That, if between transactions some other process wraps the wal
+      #        file, the readonly_shm client reruns recovery.
+      #
+      catch { code1 { db close } }
+      catch { code2 { db2 close } }
+      catch { code3 { db3 close } }
+      do_test $TN.3.1.0 {
+        list [file exists test.db-wal] [file exists test.db-shm]
+      } {0 0}
+      do_test $TN.3.1.1 {
+        close [open test.db-wal w]
+        close [open test.db-shm w]
+        code1 {
+          sqlite3 db file:test.db?readonly_shm=1
+        }
+        sql1 { SELECT * FROM t1 }
+      } {a b c d e f g h}
+    
+      do_test $TN.3.2.0 {
+        list [file size test.db-wal] [file size test.db-shm]
+      } {0 0}
+      do_test $TN.3.2.1 {
+        code2 { sqlite3 db2 test.db }
+        sql2 { INSERT INTO t1 VALUES(1, 2) ; PRAGMA wal_checkpoint=truncate }
+        code2 { db2 close }
+        sql1 { SELECT * FROM t1 }
+      } {a b c d e f g h 1 2}
+      if {$pgsz!=$dfltpgsz} continue
+      do_test $TN.3.2.2 {
+        list [file size test.db-wal] [file size test.db-shm]
+      } [list 0 $MINSHMSZ]
+      do_test $TN.3.3.0 {
+        code2 { sqlite3 db2 test.db }
+        sql2 { 
+          INSERT INTO t1 VALUES(3, 4);
+          INSERT INTO t1 VALUES(5, 6);
+          INSERT INTO t1 VALUES(7, 8);
+          INSERT INTO t1 VALUES(9, 10);
+        }
+        code2 { db2 close }
+        code1 { db close }
+        list [file size test.db-wal] [file size test.db-shm]
+      } [list [wal_file_size 4 1024] $MINSHMSZ]
+      do_test $TN.3.3.1 {
+        code1 { sqlite3 db file:test.db?readonly_shm=1 }
+        sql1 { SELECT * FROM t1 }
+      } {a b c d e f g h 1 2 3 4 5 6 7 8 9 10}
+      do_test $TN.3.3.2 {
+        code2 { sqlite3 db2 test.db }
+        sql2 { 
+          PRAGMA wal_checkpoint; 
+          DELETE FROM t1;
+          INSERT INTO t1 VALUES('i', 'ii');
+        }
+        code2 { db2 close }
+        list [file size test.db-wal] [file size test.db-shm]
+      } [list [wal_file_size 4 1024] $MINSHMSZ]
+      do_test $TN.3.3.3 {
+        sql1 { SELECT * FROM t1 }
+      } {i ii}
+    
+      #-----------------------------------------------------------------------
+      #
+      #
+      catch { code1 { db close } }
+      catch { code2 { db2 close } }
+      catch { code3 { db3 close } }
+    
+      do_test $TN.4.0 {
+        code1 { forcedelete test.db }
+        code1 { sqlite3 db test.db }
+        sql1 {
+          PRAGMA journal_mode = wal;
+          CREATE TABLE t1(x);
+          INSERT INTO t1 VALUES('hello');
+          INSERT INTO t1 VALUES('world');
+        }
+    
+        copy_to_test2 $bZeroShm
+    
+        code1 { db close }
+      } {}
+    
+      do_test $TN.4.1.1 {
+        code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
+        sql2 { SELECT * FROM t1 }
+      } {hello world}
+    
+      do_test $TN.4.1.2 {
+        code3 { sqlite3 db3 test.db2 }
+        sql3 {
+          INSERT INTO t1 VALUES('!');
+          PRAGMA wal_checkpoint = truncate;
+        }
+        code3 { db3 close }
+      } {}
+      do_test $TN.4.1.3 {
+        sql2 { SELECT * FROM t1 }
+      } {hello world !}
+    
+      catch { code1 { db close } }
+      catch { code2 { db2 close } }
+      catch { code3 { db3 close } }
+    
+      do_test $TN.4.2.1 {
+        code1 { sqlite3 db test.db }
+        sql1 {
+          INSERT INTO t1 VALUES('!');
+          INSERT INTO t1 VALUES('!');
+    
+          PRAGMA cache_size = 10;
+          CREATE TABLE t2(x);
+    
+          BEGIN;
+            WITH s(i) AS (
+              SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500
+              )
+            INSERT INTO t2 SELECT randomblob(500) FROM s;
+            SELECT count(*) FROM t2;
+        } 
+      } {500}
+      set sz [file size test.db-wal]
+      do_test $TN.4.2.2.(sz=$sz) {
+        expr {$sz>400000}
+      } {1}
+      do_test $TN.4.2.4 {
+        file_control_persist_wal db 1; db close
+    
+        copy_to_test2 $bZeroShm
+        code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
+        sql2 {
+          SELECT * FROM t1;
+          SELECT count(*) FROM t2;
+        }
+      } {hello world ! ! 0}
+    
+      #-----------------------------------------------------------------------
+      #
+      #
+      catch { code1 { db close } }
+      catch { code2 { db2 close } }
+      catch { code3 { db3 close } }
+    
+      do_test $TN.5.0 {
+        code1 { forcedelete test.db }
+        code1 { sqlite3 db test.db }
+        sql1 {
+          PRAGMA journal_mode = wal;
+          CREATE TABLE t1(x);
+          INSERT INTO t1 VALUES('hello');
+          INSERT INTO t1 VALUES('world');
+          INSERT INTO t1 VALUES('!');
+          INSERT INTO t1 VALUES('world');
+          INSERT INTO t1 VALUES('hello');
+        }
+    
+        copy_to_test2 $bZeroShm
+        
+        code1 { db close }
+      } {}
+    
+      do_test $TN.5.1 {
+        code2 { sqlite3 db2 file:test.db2?readonly_shm=1 }
+        sql2 {
+          SELECT * FROM t1;
+        }
+      } {hello world ! world hello}
+    
+      do_test $TN.5.2 {
+        code1 {
+          proc handle_read {op args} {
+            if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
+              set ::res2 [sql2 { SELECT * FROM t1 }]
+            }
+            puts "$msg xRead $args"
+            return "SQLITE_OK"
           }
+          testvfs tvfs -fullshm 1
+    
+          sqlite3 db file:test.db2?vfs=tvfs
+          db eval { SELECT * FROM sqlite_master }
+    
+          tvfs filter xRead
+          tvfs script handle_read
         }
-        return "SQLITE_OK"
-      }
-      testvfs tvfs -fullshm 1
-
-      tvfs filter xRead
-      tvfs script handle_read
-
-      sqlite3 db file:test.db2?readonly_shm=1&vfs=tvfs
-      db eval { SELECT * FROM t1 }
+        sql1 {
+          PRAGMA wal_checkpoint = truncate;
+        }
+        code1 { set ::res2 }
+      } {hello world ! world hello}
+    
+      do_test $TN.5.3 {
+        code1 { db close }
+        code1 { tvfs delete }
+      } {}
+    
+      #-----------------------------------------------------------------------
+      #
+      #
+      catch { code1 { db close } }
+      catch { code2 { db2 close } }
+      catch { code3 { db3 close } }
+    
+      do_test $TN.6.1 {
+        code1 { forcedelete test.db }
+        code1 { sqlite3 db test.db }
+        sql1 {
+          PRAGMA journal_mode = wal;
+          CREATE TABLE t1(x);
+          INSERT INTO t1 VALUES('hello');
+          INSERT INTO t1 VALUES('world');
+          INSERT INTO t1 VALUES('!');
+          INSERT INTO t1 VALUES('world');
+          INSERT INTO t1 VALUES('hello');
+        }
+    
+        copy_to_test2 $bZeroShm
+        
+        code1 { db close }
+      } {}
+    
+      do_test $TN.6.2 {
+        code1 {
+          set ::nRem 5
+          proc handle_read {op args} {
+            if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} {
+              incr ::nRem -1
+              if {$::nRem==0} {
+                code2 { sqlite3 db2 test.db2 }
+                sql2  { PRAGMA wal_checkpoint = truncate }
+              }
+            }
+            return "SQLITE_OK"
+          }
+          testvfs tvfs -fullshm 1
+    
+          tvfs filter xRead
+          tvfs script handle_read
+    
+          sqlite3 db file:test.db2?readonly_shm=1&vfs=tvfs
+          db eval { SELECT * FROM t1 }
+        }
+      } {hello world ! world hello}
+    
+      do_test $TN.6.3 {
+        code1 { db close }
+        code1 { tvfs delete }
+      } {}
     }
-  } {hello world ! world hello}
-
-  do_test $TN.6.3 {
-    code1 { db close }
-    code1 { tvfs delete }
-  } {}
-}
+  } ;# for pgsz
 } ;# foreach bZeroShm
 
 finish_test