github.com/jdgcs/sqlite3@v1.12.1-0.20210908114423-bc5f96e4dd51/testdata/tcl/walvfs.test (about)

     1  # 2018 December 23
     2  #
     3  # The author disclaims copyright to this source code.  In place of
     4  # a legal notice, here is a blessing:
     5  #
     6  #    May you do good and not evil.
     7  #    May you find forgiveness for yourself and forgive others.
     8  #    May you share freely, never taking more than you give.
     9  #
    10  #***********************************************************************
    11  # This file implements regression tests for SQLite library.  The
    12  # focus of this file is testing the operation of the library in
    13  # "PRAGMA journal_mode=WAL" mode.
    14  #
    15  
    16  set testdir [file dirname $argv0]
    17  source $testdir/tester.tcl
    18  source $testdir/lock_common.tcl
    19  source $testdir/malloc_common.tcl
    20  source $testdir/wal_common.tcl
    21  set testprefix walvfs
    22  
    23  ifcapable !wal {finish_test ; return }
    24  
    25  db close
    26  testvfs tvfs 
    27  tvfs script xSync
    28  tvfs filter xSync
    29  set ::sync_count 0
    30  proc xSync {method file args} {
    31    if {[file tail $file]=="test.db-wal"} {
    32      incr ::sync_count
    33    }
    34  }
    35  
    36  
    37  #-------------------------------------------------------------------------
    38  # Test that if IOCAP_SEQUENTIAL is set, the wal-header is not synced to
    39  # disk immediately after it is written.
    40  #
    41  sqlite3 db test.db -vfs tvfs
    42  do_execsql_test 1.0 {
    43    PRAGMA auto_vacuum = 0;
    44    PRAGMA journal_mode = wal;
    45    PRAGMA synchronous = normal;
    46    CREATE TABLE t1(a, b, c);
    47    INSERT INTO t1 VALUES(1, 2, 3);
    48    INSERT INTO t1 VALUES(4, 5, 6);
    49    INSERT INTO t1 VALUES(7, 8, 9);
    50    PRAGMA wal_checkpoint;
    51  } {wal 0 5 5}
    52  
    53  set ::sync_count 0
    54  do_test 1.1 {
    55    execsql { INSERT INTO t1 VALUES(10, 11, 12) }
    56    set ::sync_count
    57  } 1
    58  
    59  db close
    60  tvfs devchar sequential
    61  sqlite3 db test.db -vfs tvfs
    62  do_execsql_test 1.2 {
    63    PRAGMA synchronous = normal;
    64    INSERT INTO t1 VALUES(13, 14, 15);
    65    INSERT INTO t1 VALUES(16, 17, 18);
    66    PRAGMA wal_checkpoint;
    67  } {0 4 4}
    68  
    69  set ::sync_count 0
    70  do_test 1.3 {
    71    execsql { INSERT INTO t1 VALUES(10, 11, 12) }
    72    set ::sync_count
    73  } 0
    74  
    75  #-------------------------------------------------------------------------
    76  # Test that "PRAGMA journal_size_limit" works in wal mode.
    77  #
    78  reset_db
    79  do_execsql_test 2.0 {
    80    PRAGMA journal_size_limit = 10000;
    81    CREATE TABLE t1(x);
    82    PRAGMA journal_mode = wal;
    83    WITH s(i) AS (
    84      SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
    85    )
    86    INSERT INTO t1 SELECT randomblob(750) FROM s;
    87  } {10000 wal}
    88  do_test 2.1 {
    89    expr [file size test.db-wal]>12000
    90  } {1}
    91  do_test 2.2 {
    92    execsql {
    93      PRAGMA wal_checkpoint;
    94      INSERT INTO t1 VALUES(randomblob(750));
    95    }
    96    file size test.db-wal
    97  } {10000}
    98  do_test 2.3 {
    99    execsql {
   100      PRAGMA journal_size_limit = 8000;
   101      PRAGMA wal_checkpoint;
   102      INSERT INTO t1 VALUES(randomblob(750));
   103    }
   104    file size test.db-wal
   105  } {8000}
   106  
   107  #-------------------------------------------------------------------------
   108  # Test that a checkpoint may be interrupted using sqlite3_interrupt().
   109  # And that the error code is SQLITE_NOMEM, not SQLITE_INTERRUPT, if
   110  # an OOM error occurs just before the sqlite3_interrupt() call.
   111  #
   112  reset_db
   113  db close
   114  sqlite3 db test.db -vfs tvfs
   115  tvfs filter {}
   116  
   117  do_execsql_test 3.0 {
   118    CREATE TABLE t1(x);
   119    PRAGMA journal_mode = wal;
   120    WITH s(i) AS (
   121      SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
   122    )
   123    INSERT INTO t1 SELECT randomblob(750) FROM s;
   124  } {wal}
   125  
   126  tvfs filter xWrite
   127  tvfs script xWrite
   128  set ::cnt 2
   129  proc xWrite {method file args} {
   130    if {[file tail $file]=="test.db"} {
   131      incr ::cnt -1
   132      if {$::cnt==0} {
   133        sqlite3_interrupt db
   134      }
   135    }
   136    return SQLITE_OK
   137  }
   138  
   139  do_catchsql_test 3.1 {
   140    PRAGMA wal_checkpoint
   141  } {1 interrupted}
   142  
   143  set ::cnt 2
   144  proc xWrite {method file args} {
   145    if {[file tail $file]=="test.db"} {
   146      incr ::cnt -1
   147      if {$::cnt==0} {
   148        sqlite3_memdebug_fail 1 -repeat 0
   149        # For this test to pass, the following statement must call malloc() at 
   150        # least once. Even if the lookaside is enabled.
   151        set ::xwrite_stmt_res [catchsql { SELECT hex(randomblob(4000)) }]
   152        sqlite3_interrupt db
   153      }
   154    }
   155    return SQLITE_OK
   156  }
   157  
   158  set ::xwrite_stmt_res ""
   159  do_catchsql_test 3.2 {
   160    PRAGMA wal_checkpoint
   161  } {1 {out of memory}}
   162  do_test 3.2.2 {
   163    set ::xwrite_stmt_res
   164  } {1 {out of memory}}
   165  unset ::xwrite_stmt_res
   166  
   167  #-------------------------------------------------------------------------
   168  #
   169  reset_db
   170  db close
   171  do_test 4.0 {
   172    sqlite3 db test.db -vfs tvfs
   173    execsql {
   174      CREATE TABLE t1(x);
   175      PRAGMA journal_mode = wal;
   176      WITH s(i) AS (
   177          SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
   178      )
   179      INSERT INTO t1 SELECT randomblob(750) FROM s;
   180    } db
   181  } {wal}
   182  db close
   183  
   184  tvfs filter xShmMap
   185  tvfs script xShmMap
   186  proc xShmMap {method file args} { 
   187    return SQLITE_READONLY 
   188  }
   189  sqlite3 db test.db -vfs tvfs
   190  do_catchsql_test 4.1 {
   191    SELECT count(*) FROM t1
   192  } {1 {attempt to write a readonly database}}
   193  
   194  set ::cnt 5
   195  tvfs filter {xShmMap xShmLock}
   196  proc xShmMap {method file name args} { 
   197    switch -- $method {
   198      xShmMap {  return SQLITE_READONLY }
   199      xShmLock {
   200        if {$args == "{0 1 lock shared}"} {
   201          incr ::cnt -1
   202          if {$::cnt>0} { return SQLITE_BUSY }
   203        }
   204      }
   205    }
   206    return SQLITE_OK
   207  }
   208  do_catchsql_test 4.2 {
   209    SELECT count(*) FROM t1
   210  } {1 {attempt to write a readonly database}}
   211  
   212  #-------------------------------------------------------------------------
   213  #
   214  reset_db
   215  db close 
   216  sqlite3 db test.db -vfs tvfs
   217  tvfs filter {}
   218  do_execsql_test 5.0 {
   219    PRAGMA auto_vacuum = 0;
   220    PRAGMA page_size = 1024;
   221    CREATE TABLE t1(x);
   222    PRAGMA journal_mode = wal;
   223    WITH s(i) AS (
   224        SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
   225    )
   226    INSERT INTO t1 SELECT randomblob(750) FROM s;
   227  } {wal}
   228  
   229  do_execsql_test 5.1 {
   230    SELECT count(*) FROM t1
   231  } {20}
   232  
   233  do_test 5.2 {
   234    vfs_set_readmark db main 1 100
   235    vfs_set_readmark db main 2 100
   236    vfs_set_readmark db main 3 100
   237    vfs_set_readmark db main 4 100
   238  } {100}
   239  
   240  do_execsql_test 5.3 {
   241    SELECT count(*) FROM t1
   242  } {20}
   243  
   244  do_test 5.3 {
   245    list [vfs_set_readmark db main 1] \
   246         [vfs_set_readmark db main 2] \
   247         [vfs_set_readmark db main 3] \
   248         [vfs_set_readmark db main 4] 
   249  } {24 100 100 100}
   250  
   251  tvfs script xShmLock
   252  tvfs filter xShmLock
   253  set ::cnt 20
   254  proc xShmLock {args} {
   255    incr ::cnt -1
   256    if {$::cnt>0} { return SQLITE_BUSY }
   257    return SQLITE_OK
   258  }
   259  
   260  do_test 5.4 {
   261    vfs_set_readmark db main 1 100
   262    execsql { SELECT count(*) FROM t1 }
   263  } {20}
   264  
   265  vfs_set_readmark db main 1 100
   266  vfs_set_readmark db main 2 100
   267  vfs_set_readmark db main 3 100
   268  vfs_set_readmark db main 4 100
   269  
   270  tvfs script xShmMapLock
   271  tvfs filter {xShmLock xShmMap}
   272  proc xShmMapLock {method args} {
   273    if {$method=="xShmMap"} {
   274      return "SQLITE_READONLY"
   275    }
   276    return SQLITE_BUSY
   277  }
   278  
   279  sqlite3 db2 test.db -vfs tvfs
   280  breakpoint
   281  do_test 5.5 {
   282    list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
   283  } {1 {attempt to write a readonly database}}
   284  
   285  tvfs filter {}
   286  vfs_set_readmark db main 1 1
   287  
   288  do_test 5.6 {
   289    list [catch { execsql { SELECT count(*) FROM t1 } db2 } msg] $msg
   290  } {0 20}
   291  db2 close
   292  db close
   293  
   294  #-------------------------------------------------------------------------
   295  # Cause an SQLITE_PROTOCOL while attempting to restart the wal file.
   296  #
   297  reset_db
   298  tvfs filter {}
   299  db close
   300  sqlite3 db test.db -vfs tvfs
   301  do_execsql_test 6.0 {
   302    PRAGMA auto_vacuum = 0;
   303    PRAGMA page_size = 1024;
   304    CREATE TABLE t1(x);
   305    PRAGMA journal_mode = wal;
   306    WITH s(i) AS (
   307        SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
   308    )
   309    INSERT INTO t1 SELECT randomblob(750) FROM s;
   310  } {wal}
   311  
   312  do_test 6.1 {
   313    execsql { PRAGMA wal_checkpoint } 
   314    set {} {}
   315  } {}
   316  
   317  tvfs filter xShmLock
   318  tvfs script xShmLock
   319  set ::flag 0
   320  proc xShmLock {method file handle spec} {
   321    if {$::flag && [lrange $spec 2 end]=="lock shared"} {
   322      return SQLITE_BUSY
   323    }
   324    if {$spec=="3 1 unlock shared"} {
   325      set ::flag 1
   326    }
   327    return SQLITE_OK
   328  }
   329  
   330  puts "# WARNING: This next test takes around 12 seconds"
   331  do_catchsql_test 6.2 {
   332    INSERT INTO t1 VALUES(1);
   333  } {1 {locking protocol}}
   334  
   335  #-------------------------------------------------------------------------
   336  # Check that a checkpoint fails if it cannot get the CHECKPOINTER lock
   337  #
   338  reset_db
   339  tvfs filter {}
   340  db close
   341  sqlite3 db test.db -vfs tvfs
   342  do_execsql_test 7.0 {
   343    PRAGMA auto_vacuum = 0;
   344    PRAGMA page_size = 1024;
   345    CREATE TABLE t1(x);
   346    PRAGMA journal_mode = wal;
   347    WITH s(i) AS (
   348        SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20
   349    )
   350    INSERT INTO t1 SELECT randomblob(750) FROM s;
   351  } {wal}
   352  
   353  tvfs script xShmLock
   354  tvfs filter xShmLock
   355  proc xShmLock {method file handle spec} {
   356    if {$spec=="1 1 lock exclusive"} {
   357      return SQLITE_BUSY
   358    }
   359    return SQLITE_OK
   360  }
   361  
   362  do_execsql_test 7.1 {
   363    PRAGMA wal_checkpoint
   364  } {1 -1 -1}
   365  
   366  #-------------------------------------------------------------------------
   367  # Check that the page cache is correctly flushed if a checkpointer using
   368  # a version 2 VFS makes a checkpoint with an out-of-date cache.
   369  #
   370  reset_db
   371  testvfs tvfs2 -iversion 2
   372  db close
   373  sqlite3 db test.db -vfs tvfs2
   374  do_execsql_test 8.0 {
   375    PRAGMA auto_vacuum = 0;
   376    PRAGMA page_size = 1024;
   377    CREATE TABLE t1(x);
   378    PRAGMA journal_mode = wal;
   379    WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
   380    INSERT INTO t1 SELECT randomblob(75) FROM s;
   381  } {wal}
   382  
   383  do_execsql_test 8.1 { SELECT count(*) FROM t1 } {20}
   384  
   385  do_test 8.2 {
   386    sqlite3 db2 test.db -vfs tvfs2
   387    execsql {
   388      INSERT INTO t1 VALUES(randomblob(75));
   389    } db2
   390    db2 close
   391  } {}
   392  
   393  do_execsql_test 8.3 { 
   394    PRAGMA wal_checkpoint;
   395    SELECT count(*) FROM t1 
   396  } {0 5 5 21}
   397  db close
   398  tvfs2 delete
   399  
   400  #-------------------------------------------------------------------------
   401  reset_db
   402  db close
   403  sqlite3 db test.db -vfs tvfs
   404  do_execsql_test 9.0 {
   405    PRAGMA auto_vacuum = 0;
   406    PRAGMA page_size = 1024;
   407    CREATE TABLE t1(x);
   408    PRAGMA journal_mode = wal;
   409    WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 20 )
   410    INSERT INTO t1 SELECT randomblob(75) FROM s;
   411  } {wal}
   412  
   413  sqlite3 db2 test.db -vfs tvfs
   414  tvfs filter {xShmMap xShmLock}
   415  tvfs script xShmMap
   416  proc xShmMap {method file handle args} {
   417    switch -- $method {
   418      xShmMap {
   419        return "SQLITE_READONLY_CANTINIT"
   420      }
   421      xShmLock {
   422        if {$args=="{3 1 lock shared}"} {
   423          return "SQLITE_IOERR"
   424        }
   425      }
   426    }
   427  }
   428  
   429  do_test 9.1 {
   430    catchsql { SELECT count(*) FROM t1 } db2
   431  } {1 {disk I/O error}}
   432  
   433  db close
   434  db2 close
   435  tvfs delete
   436  finish_test