gitlab.com/CoiaPrant/sqlite3@v1.19.1/testdata/tcl/e_walckpt.test (about)

     1  # 2014 December 04
     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  #
    12  
    13  set testdir [file dirname $argv0]
    14  source $testdir/tester.tcl
    15  source $testdir/lock_common.tcl
    16  source $testdir/wal_common.tcl
    17  set testprefix e_walckpt
    18  
    19  # The following two commands are used to determine if any of the files
    20  # "test.db", "test.db2" and "test.db3" are modified by a test case.
    21  #
    22  # The [save_db_hashes] command saves a hash of the current contents of
    23  # all three files in global variables. The [compare_db_hashes] compares
    24  # the current contents with the saved hashes and returns a list of the
    25  # files that have changed.
    26  #
    27  proc save_db_hashes {} {
    28    global H
    29    foreach f {test.db test.db2 test.db3} {
    30      set H($f) 0
    31      catch { set H($f) [md5file $f] }
    32    }
    33  }
    34  proc compare_db_hashes {} {
    35    global H
    36    set ret [list]
    37    foreach f {test.db test.db2 test.db3} {
    38      set expect 0
    39      catch { set expect [md5file $f] }
    40      if {$H($f) != $expect} { lappend ret $f }
    41    }
    42    set ret
    43  }
    44  
    45  #-------------------------------------------------------------------------
    46  # All calls to the [sqlite3_wal_checkpoint_v2] command made within this
    47  # file use this wrapper. It's sole purpose is to throw an error if the
    48  # following requirement is violated:
    49  #
    50  # EVIDENCE-OF: R-60567-47780 Unless it returns SQLITE_MISUSE, the
    51  # sqlite3_wal_checkpoint_v2() interface sets the error information that
    52  # is queried by sqlite3_errcode() and sqlite3_errmsg().
    53  #
    54  proc wal_checkpoint_v2 {db args} {
    55    set rc [catch {
    56      uplevel sqlite3_wal_checkpoint_v2 $db $args
    57    } msg]
    58  
    59    set errcode "SQLITE_OK"
    60    if {$rc} {
    61      set errcode [lindex [split $msg " "] 0]
    62    } elseif { [lindex $msg 0] } {
    63      set errcode "SQLITE_BUSY"
    64    }
    65  
    66    if {$errcode != "SQLITE_MISUSE" && [sqlite3_errcode $db] != $errcode} {
    67      error "sqlite3_errcode mismatch! (1) $errcode!=[sqlite3_errcode $db]"
    68    }
    69  
    70    if {$rc==0} {
    71      return $msg
    72    } else {
    73      error $msg
    74    }
    75  }
    76  
    77  
    78  # The following tests are run 3 times, each using a different method of 
    79  # invoking a checkpoint:
    80  #
    81  #   1) Using sqlite3_wal_checkpoint_v2()
    82  #   2) Using "PRAGMA wal_checkpoint"
    83  #   3) Using sqlite3_wal_checkpoint() in place of checkpoint_v2(PASSIVE)
    84  #
    85  # Cases (2) and (3) are to show that the following statements are 
    86  # correct, respectively:
    87  #
    88  # EVIDENCE-OF: R-36706-10507 The PRAGMA wal_checkpoint command can be
    89  # used to invoke this interface from SQL.
    90  #
    91  # EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is
    92  # equivalent to
    93  # sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0).
    94  # 
    95  foreach {tn script} {
    96    1 {
    97      proc checkpoint {db mode args} {
    98        eval wal_checkpoint_v2 [list $db] [list $mode] $args
    99      }
   100    }
   101  
   102    2 {
   103      proc checkpoint {db mode args} {
   104        set sql "PRAGMA wal_checkpoint = $mode"
   105        if {[llength $args] && [lindex $args 0]!=""} {
   106          set sql "PRAGMA [lindex $args 0].wal_checkpoint = $mode"
   107        }
   108        set rc [catch { $db eval $sql } msg]
   109        if {$rc} {
   110          regsub {database} $msg {database:} msg
   111          error "[sqlite3_errcode $db] - $msg"
   112        }
   113        set msg
   114      }
   115    }
   116  
   117    3 {
   118      proc checkpoint {db mode args} {
   119        if {$mode == "passive"} {
   120          set rc [eval sqlite3_wal_checkpoint [list $db] $args]
   121          if {$rc != "SQLITE_OK"} {
   122            error "$rc - [sqlite3_errmsg $db]"
   123          }
   124        } else {
   125          eval wal_checkpoint_v2 [list $db] [list $mode] $args
   126        }
   127      }
   128    }
   129  
   130  } {
   131  
   132    eval $script
   133  
   134    reset_db
   135    forcedelete test.db2 test.db3 test.db4
   136    execsql {
   137      ATTACH 'test.db2' AS aux;
   138      ATTACH 'test.db3' AS aux2;
   139      ATTACH 'test.db4' AS aux3;
   140      CREATE TABLE t1(x);
   141      CREATE TABLE aux.t2(x);
   142      CREATE TABLE aux2.t3(x);
   143      CREATE TABLE aux3.t4(x);
   144      PRAGMA main.journal_mode = WAL;
   145      PRAGMA aux.journal_mode = WAL;
   146      PRAGMA aux2.journal_mode = WAL;
   147      /* Leave aux4 in rollback mode */
   148    }
   149  
   150    # EVIDENCE-OF: R-49787-09095 The sqlite3_wal_checkpoint_v2(D,X,M,L,C)
   151    # interface runs a checkpoint operation on database X of database
   152    # connection D in mode M. Status information is written back into
   153    # integers pointed to by L and C.
   154    #
   155    #     Tests 1, 2 and 3 below verify the "on database X" part of the
   156    #     above. Other parts of this requirement are tested below.
   157    #
   158    # EVIDENCE-OF: R-00653-06026 If parameter zDb is NULL or points to a
   159    # zero length string, then the specified operation is attempted on all
   160    # WAL databases attached to database connection db.
   161    #
   162    #     Tests 4 and 5 below test this.
   163    #
   164    foreach {tn2 zDb dblist} {
   165      1 main  test.db
   166      2 aux   test.db2
   167      3 aux2  test.db3
   168      4 ""    {test.db test.db2 test.db3}
   169      5 -     {test.db test.db2 test.db3}
   170      6 temp  {}
   171    } {
   172      do_test $tn.1.$tn2 {
   173        execsql {
   174          INSERT INTO t1 VALUES(1);
   175          INSERT INTO t2 VALUES(2);
   176          INSERT INTO t3 VALUES(3);
   177        }
   178        save_db_hashes
   179  
   180        if {$zDb == "-"} {
   181          checkpoint db passive
   182        } else {
   183          checkpoint db passive $zDb
   184        }
   185  
   186        compare_db_hashes
   187      } $dblist
   188    }
   189  
   190    # EVIDENCE-OF: R-38207-48996 If zDb is not NULL (or a zero length
   191    # string) and is not the name of any attached database, SQLITE_ERROR is
   192    # returned to the caller.
   193    do_test $tn.2.1 {
   194      list [catch { checkpoint db passive notadb } msg] $msg
   195    } {1 {SQLITE_ERROR - unknown database: notadb}}
   196  
   197    # EVIDENCE-OF: R-14303-42483 If database zDb is the name of an attached
   198    # database that is not in WAL mode, SQLITE_OK is returned and both
   199    # *pnLog and *pnCkpt set to -1.
   200    #
   201    if {$tn==3} {
   202      # With sqlite3_wal_checkpoint() the two output variables cannot be 
   203      # tested. So just test that no error is returned when attempting to
   204      # checkpoint a db in rollback mode.
   205      do_test $tn.2.2.a { checkpoint db passive aux3 } {}
   206    } else {
   207      do_test $tn.2.2.b { checkpoint db passive aux3 } {0 -1 -1}
   208    }
   209  
   210    # EVIDENCE-OF: R-62028-47212 All calls obtain an exclusive "checkpoint"
   211    # lock on the database file.
   212    db close
   213    testvfs tvfs
   214    tvfs filter xShmLock
   215    tvfs script filelock
   216    proc filelock {method file handle details} {
   217      # Test for an exclusive checkpoint lock. A checkpoint lock locks a
   218      # single byte starting at offset 1.
   219      if {$details == "1 1 lock exclusive"} { set ::seen_checkpoint_lock 1 }
   220    }
   221    sqlite3 db test.db -vfs tvfs
   222    do_test $tn.3.1 {
   223      execsql { INSERT INTO t1 VALUES('xyz') }
   224      unset -nocomplain ::seen_checkpoint_lock
   225      checkpoint db passive
   226      set ::seen_checkpoint_lock
   227    } {1}
   228    db close
   229    tvfs delete
   230    reset_db
   231  
   232  
   233   
   234  
   235    #-----------------------------------------------------------------------
   236    # EVIDENCE-OF: R-10421-19736 If any other process is running a
   237    # checkpoint operation at the same time, the lock cannot be obtained and
   238    # SQLITE_BUSY is returned.
   239    #
   240    # EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
   241    # it will not be invoked in this case.
   242    #
   243    testvfs tvfs
   244    tvfs filter xWrite
   245    sqlite3 db test.db -vfs tvfs
   246    sqlite3 db2 test.db -vfs tvfs
   247  
   248    do_test $tn.3.2.1 {
   249      db2 eval {
   250        PRAGMA auto_vacuum = 0;
   251        PRAGMA journal_mode = WAL;
   252        CREATE TABLE t1(x, y);
   253        INSERT INTO t1 VALUES(1,2);
   254        INSERT INTO t1 VALUES(3,4);
   255        INSERT INTO t1 VALUES(5,6);
   256      }
   257      file size test.db-wal
   258    } [wal_file_size 5 1024]
   259  
   260  
   261    # Connection [db] runs a checkpoint. During this checkpoint, each
   262    # time it calls xWrite() to write a page into the database file, we
   263    # attempt to start a checkpoint using [db2]. According to the 
   264    # first requirement being tested, this should return SQLITE_BUSY. According
   265    # to the second, the busy-handler belonging to [db2] should not be
   266    # invoked.
   267    #
   268    set ::write_count 0
   269    set ::write_errors [list]
   270    proc busy_callback {args} {
   271      lappend ::write_errors "busy handler called!"
   272    }
   273    proc write_callback {args} {
   274      set rc [catch {checkpoint db2 passive} msg]
   275      if {0==[regexp "database is locked" $msg] && $msg!="1 -1 -1"} {
   276        lappend ::write_errors "$rc $msg"
   277      } 
   278      incr ::write_count
   279    }
   280    db2 busy busy_callback
   281    tvfs script write_callback
   282  
   283    do_test $tn.3.2.2 {
   284      db eval {SELECT * FROM sqlite_master}
   285      checkpoint db full
   286      set ::write_count
   287    } {2}
   288  
   289    do_test $tn.3.2.3 {
   290      set ::write_errors
   291    } {}
   292  
   293    db close
   294    db2 close
   295    tvfs delete
   296  
   297    proc busy_handler {mode busy_handler_mode n} {
   298      incr ::busy_handler_counter
   299      switch -- $busy_handler_mode {
   300        1 {
   301          # Do nothing. Do not block.
   302          return 1
   303        }
   304  
   305        2 {
   306          # Close first the reader, then later the writer. Give up before
   307          # closing the [db6] reader.
   308          if {$n==5}  { catch {db2 eval commit} }
   309          if {$n==10} { catch {db3 eval commit} }
   310          if {$n==15} { return 1 }
   311          return 0
   312        }
   313  
   314        3 {
   315          # Close first the writer, then later the reader. And finally the 
   316          # [db6] reader.
   317          if {$n==5}  { catch {db2 eval commit} }
   318          if {$n==10} { catch {db3 eval commit} }
   319          if {$n==15} { catch {db6 eval commit} }
   320          return 0
   321        }
   322      }
   323    }
   324  
   325    foreach {mode busy_handler_mode} { 
   326      passive  1
   327      full     1       full     2       full    3
   328      restart  1       restart  2       restart  3
   329      truncate 1       truncate 2       truncate 3
   330    } {
   331      set tp "$tn.$mode.$busy_handler_mode"
   332  
   333      set ::sync_counter 0
   334  
   335      # Set up a callback function for xSync and xWrite calls made during
   336      # the checkpoint.
   337      #
   338      set ::checkpoint_ongoing 0
   339      proc tvfs_callback {method args} {
   340        if {$::checkpoint_ongoing==0} return
   341  
   342        set tail [file tail [lindex $args 0]]
   343        if {$method == "xSync" && $tail == "test.db"} {
   344          incr ::sync_counter
   345        }
   346        if {$method == "xWrite" && $tail=="test.db"} {
   347          if {$::write_ok < 0} {
   348            set ::write_ok [expr ![catch {db5 eval { BEGIN IMMEDIATE }}]]
   349            catch { db5 eval ROLLBACK }
   350          }
   351          if {$::read_ok < 0} {
   352            set ::read_ok [expr ![catch {db5 eval { SELECT * FROM t1 }}]]
   353          }
   354  
   355          # If one has not already been opened, open a read-transaction using
   356          # connection [db6]
   357          catch { db6 eval { BEGIN ; SELECT * FROM sqlite_master } } msg
   358        }
   359        if {$method == "xShmLock" } {
   360          set details [lindex $args 2]
   361          if {$details == "0 1 lock exclusive"} { set ::seen_writer_lock 1 }
   362        }
   363      }
   364  
   365      catch { db close }
   366      forcedelete test.db
   367      testvfs tvfs
   368      sqlite3 db test.db -vfs tvfs
   369      #tvfs filter xSync
   370      tvfs script tvfs_callback
   371  
   372      do_execsql_test $tp.0 {
   373        CREATE TABLE t1(a, b);
   374        CREATE TABLE t2(a, b);
   375        PRAGMA journal_mode = wal;
   376        INSERT INTO t1 VALUES(1, 2);
   377        INSERT INTO t1 VALUES(3, 4);
   378        INSERT INTO t1 VALUES(5, 6);
   379      } {wal}
   380  
   381      # Open a reader on the current database snapshot.
   382      do_test $tp.1 {
   383        sqlite3 db2 test.db -vfs tvfs
   384        execsql {
   385          BEGIN;
   386            SELECT * FROM t1 UNION ALL SELECT * FROM t2;
   387        } db2
   388      } {1 2 3 4 5 6}
   389  
   390      # Open a writer. Write a transaction. Then begin, but do not commit,
   391      # a second transaction.
   392      do_test $tp.2 {
   393        sqlite3 db3 test.db -vfs tvfs
   394        execsql {
   395          INSERT INTO t2 VALUES(7, 8);
   396          BEGIN;
   397            INSERT INTO t2 VALUES(9, 10);
   398            SELECT * FROM t1 UNION ALL SELECT * FROM t2;
   399        } db3
   400      } {1 2 3 4 5 6 7 8 9 10}
   401  
   402      sqlite3 db5 test.db -vfs tvfs
   403      sqlite3 db6 test.db -vfs tvfs
   404  
   405      # Register a busy-handler with connection [db].
   406      #
   407      db busy [list busy_handler $mode $busy_handler_mode]
   408      set ::sync_counter 0
   409      set ::busy_handler_counter 0
   410      set ::read_ok -1
   411      set ::write_ok -1
   412      set ::seen_writer_lock 0
   413      
   414      set ::checkpoint_ongoing 1
   415      do_test $tp.3 {
   416        checkpoint db $mode main
   417        set {} {}
   418      } {}
   419      set ::checkpoint_ongoing 0
   420      set ::did_restart_blocking [expr {[catch {db6 eval commit}]}]
   421  
   422      if { $mode=="passive" } {
   423        # EVIDENCE-OF: R-16333-64433 Checkpoint as many frames as possible
   424        # without waiting for any database readers or writers to finish, then
   425        # sync the database file if all frames in the log were checkpointed.
   426        #
   427        #   "As many frames as possible" means all but the last two transactions
   428        #   (the two that write to table t2, of which the scond is unfinished).
   429        #   So copying the db file only we see the t1 change, but not the t2
   430        #   modifications.
   431        #
   432        #   The busy handler is not invoked (see below) and the db reader and
   433        #   writer are still active - so the checkpointer did not wait for either
   434        #   readers or writers. As a result the checkpoint was not finished and
   435        #   so the db file is not synced.
   436        #
   437        # EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
   438        # in the SQLITE_CHECKPOINT_PASSIVE mode.
   439        #
   440        #   It's not. Test case "$tp.6".
   441        #
   442        do_test $tp.4 {
   443          forcecopy test.db abc.db
   444          sqlite3 db4 abc.db
   445          db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
   446        } {1 2 3 4 5 6}
   447        do_test $tp.5 { set ::sync_counter } 0
   448        do_test $tp.6 { set ::busy_handler_counter } 0
   449        db4 close
   450    
   451        db2 eval COMMIT
   452        db3 eval COMMIT
   453    
   454        # EVIDENCE-OF: R-65499-53765 On the other hand, passive mode might leave
   455        # the checkpoint unfinished if there are concurrent readers or writers.
   456        #
   457        #   The reader and writer have now dropped their locks. And so a 
   458        #   checkpoint now is able to checkpoint more frames. Showing that the
   459        #   attempt above was left "unfinished".
   460        #
   461        #   Also, because the checkpoint finishes this time, the db is synced.
   462        #   Which is part of R-16333-64433 above.
   463        #
   464        set ::checkpoint_ongoing 1
   465        do_test $tp.7 {
   466          checkpoint db $mode main
   467          forcecopy test.db abc.db
   468          sqlite3 db4 abc.db
   469          db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
   470        } {1 2 3 4 5 6 7 8 9 10}
   471        set ::checkpoint_ongoing 0
   472        do_test $tp.7 { set ::sync_counter } 1
   473        do_test $tp.8 { set ::busy_handler_counter } 0
   474        db4 close
   475      }
   476  
   477      if { $mode=="full" || $mode=="restart" || $mode=="truncate" } {
   478  
   479        # EVIDENCE-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
   480        # TRUNCATE modes also obtain the exclusive "writer" lock on the 
   481        # database file.
   482        #
   483        #   Or at least attempts to obtain.
   484        #
   485        do_test $tp.9 {
   486          set ::seen_writer_lock
   487        } {1}
   488  
   489        if {$busy_handler_mode==2 || $busy_handler_mode==3} {
   490          # EVIDENCE-OF: R-59171-47567 This mode blocks (it invokes the
   491          # busy-handler callback) until there is no database writer and all
   492          # readers are reading from the most recent database snapshot.
   493          #
   494          #   The test below shows that both the reader and writer have 
   495          #   finished:
   496          #
   497          #   Also restated by the following two. That both busy_handler_mode
   498          #   values 2 and 3 work show that both of the following are true - as
   499          #   they release the reader and writer transactions in different
   500          #   orders.
   501          #
   502          # EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
   503          # immediately, and a busy-handler is configured, it is invoked and the
   504          # writer lock retried until either the busy-handler returns 0 or the
   505          # lock is successfully obtained.
   506          #
   507          # EVIDENCE-OF: R-48107-00250 The busy-handler is also invoked while
   508          # waiting for database readers as described above.
   509          #
   510          do_test $tp.7 {
   511            list [catchsql COMMIT db2] [catchsql COMMIT db3]
   512          } [list                                             \
   513              {1 {cannot commit - no transaction is active}}  \
   514              {1 {cannot commit - no transaction is active}}  \
   515          ]
   516  
   517          # EVIDENCE-OF: R-29177-48281 It then checkpoints all frames in the log
   518          # file and syncs the database file.
   519          #
   520          do_test $tp.8 {
   521            forcecopy test.db abc.db
   522            sqlite3 db4 abc.db
   523            db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
   524          } {1 2 3 4 5 6 7 8 9 10}
   525          do_test $tp.9 { set ::sync_counter } 1
   526          db4 close
   527  
   528          # EVIDENCE-OF: R-51867-44713 This mode blocks new database writers
   529          # while it is pending, but new database readers are allowed to continue
   530          # unimpeded.
   531          #
   532          # EVIDENCE-OF: R-47276-58266 Like SQLITE_CHECKPOINT_FULL, this mode
   533          # blocks new database writer attempts while it is pending, but does not
   534          # impede readers.
   535          #
   536          #   The first of the above two refers to "full" mode. The second
   537          #   to "restart".
   538          #
   539          do_test $tp.10.1 {
   540            list $::write_ok $::read_ok
   541          } {0 1}
   542  
   543          # EVIDENCE-OF: R-12410-31217 This mode works the same way as
   544          # SQLITE_CHECKPOINT_FULL with the addition that after checkpointing the
   545          # log file it blocks (calls the busy-handler callback) until all
   546          # readers are reading from the database file only.
   547          #
   548          #     The stuff above passed, so the first part of this requirement
   549          #     is met. The second part is tested below. If the checkpoint mode
   550          #     was "restart" or "truncate", then the busy-handler will have
   551          #     been called to block on wal-file readers.
   552          #
   553          do_test $tp.11 {
   554            set ::did_restart_blocking
   555          } [expr {($mode=="restart"||$mode=="truncate")&&$busy_handler_mode==3}]
   556  
   557          # EVIDENCE-OF: R-44699-57140 This mode works the same way as
   558          # SQLITE_CHECKPOINT_RESTART with the addition that it also truncates
   559          # the log file to zero bytes just prior to a successful return.
   560          if {$mode=="truncate" && $busy_handler_mode==3} {
   561            do_test $tp.12 {
   562              file size test.db-wal
   563            } 0
   564          }
   565        } elseif {$busy_handler_mode==1} {
   566  
   567          # EVIDENCE-OF: R-34519-06271 SQLITE_BUSY is returned in this case.
   568          if {$tn!=2} {
   569            # ($tn==2) is the loop that uses "PRAGMA wal_checkpoint"
   570            do_test $tp.13 { sqlite3_errcode db } {SQLITE_BUSY}
   571          }
   572  
   573          # EVIDENCE-OF: R-49155-63541 If the busy-handler returns 0 before the
   574          # writer lock is obtained or while waiting for database readers, the
   575          # checkpoint operation proceeds from that point in the same way as
   576          # SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
   577          # without blocking any further.
   578          do_test $tp.14 {
   579            forcecopy test.db abc.db
   580              sqlite3 db4 abc.db
   581              db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 }
   582          } {1 2 3 4 5 6}
   583          do_test $tp.15 { set ::sync_counter } 0
   584          do_test $tp.16 { set ::busy_handler_counter } 1
   585          db4 close
   586        }
   587      }
   588  
   589      db2 close
   590      db3 close
   591      db5 close
   592      db6 close
   593    }
   594  
   595    db close
   596    tvfs delete
   597  }
   598  
   599  #-----------------------------------------------------------------------
   600  # EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
   601  # mode:
   602  #
   603  #   Valid checkpoint modes are 0, 1, 2 and 3.
   604  #
   605  sqlite3 db test.db
   606  foreach {tn mode res} {
   607    0 -1001    {1 {SQLITE_MISUSE - not an error}}
   608    1 -1       {1 {SQLITE_MISUSE - not an error}}
   609    2  0       {0 {0 -1 -1}}
   610    3  1       {0 {0 -1 -1}}
   611    4  2       {0 {0 -1 -1}}
   612    5  3       {0 {0 -1 -1}}
   613    6  4       {1 {SQLITE_MISUSE - not an error}}
   614    7  114     {1 {SQLITE_MISUSE - not an error}}
   615    8  1000000 {1 {SQLITE_MISUSE - not an error}}
   616  } {
   617    do_test 4.$tn {
   618      list [catch "wal_checkpoint_v2 db $mode" msg] $msg
   619    } $res
   620  }
   621  db close
   622  
   623  foreach tn {1 2 3} {
   624    forcedelete test.db test.db2 test.db3
   625    testvfs tvfs
   626  
   627    sqlite3 db test.db -vfs tvfs
   628    execsql {
   629      ATTACH 'test.db2' AS aux2;
   630      ATTACH 'test.db3' AS aux3;
   631      PRAGMA main.journal_mode = WAL;
   632      PRAGMA aux2.journal_mode = WAL;
   633      PRAGMA aux3.journal_mode = WAL;
   634  
   635      CREATE TABLE main.t1(x,y);
   636      CREATE TABLE aux2.t2(x,y);
   637      CREATE TABLE aux3.t3(x,y);
   638  
   639      INSERT INTO t1 VALUES('a', 'b');
   640      INSERT INTO t2 VALUES('a', 'b');
   641      INSERT INTO t3 VALUES('a', 'b');
   642    }
   643    sqlite3 db2 test.db2 -vfs tvfs
   644  
   645    switch -- $tn {
   646      1 {
   647        # EVIDENCE-OF: R-41299-52117 If no error (SQLITE_BUSY or otherwise) is
   648        # encountered while processing the attached databases, SQLITE_OK is
   649        # returned.
   650        do_test 5.$tn.1 {
   651          lindex [wal_checkpoint_v2 db truncate] 0
   652        } {0}    ;# 0 -> SQLITE_OK
   653        do_test 5.$tn.2 {
   654          list [expr [file size test.db-wal]==0]  \
   655               [expr [file size test.db2-wal]==0] \
   656               [expr [file size test.db3-wal]==0]
   657        } {1 1 1}
   658      }
   659  
   660      2 {
   661        # EVIDENCE-OF: R-38578-34175 If an SQLITE_BUSY error is encountered when
   662        # processing one or more of the attached WAL databases, the operation is
   663        # still attempted on any remaining attached databases and SQLITE_BUSY is
   664        # returned at the end.
   665        db2 eval { BEGIN; INSERT INTO t2 VALUES('d', 'e'); }
   666        do_test 5.$tn.1 {
   667          lindex [wal_checkpoint_v2 db truncate] 0
   668        } {1}    ;# 1 -> SQLITE_BUSY
   669        do_test 5.$tn.2 {
   670          list [expr [file size test.db-wal]==0]  \
   671               [expr [file size test.db2-wal]==0] \
   672               [expr [file size test.db3-wal]==0]
   673        } {1 0 1}
   674        db2 eval ROLLBACK
   675      }
   676  
   677      3 {
   678        # EVIDENCE-OF: R-38049-07913 If any other error occurs while processing
   679        # an attached database, processing is abandoned and the error code is
   680        # returned to the caller immediately.
   681        tvfs filter xWrite
   682        tvfs script inject_ioerr
   683        proc inject_ioerr {method file args} {
   684          if {[file tail $file]=="test.db2"} {
   685            return "SQLITE_IOERR"
   686          }
   687          return 0
   688        }
   689        do_test 5.$tn.1 {
   690          list [catch { wal_checkpoint_v2 db truncate } msg] $msg
   691        } {1 {SQLITE_IOERR - disk I/O error}}
   692        do_test 5.$tn.2 {
   693          list [expr [file size test.db-wal]==0]  \
   694               [expr [file size test.db2-wal]==0] \
   695               [expr [file size test.db3-wal]==0]
   696        } {1 0 0}
   697        tvfs script ""
   698      }
   699    }
   700  
   701    db close
   702    db2 close
   703  }
   704  
   705  reset_db
   706  sqlite3 db2 test.db
   707  
   708  do_test 6.1 {
   709    execsql {
   710      PRAGMA auto_vacuum = 0; 
   711      PRAGMA journal_mode = WAL;
   712      CREATE TABLE t1(a, b);
   713      INSERT INTO t1 VALUES(1, 2);
   714    }
   715    file size test.db-wal
   716  } [wal_file_size 3 1024]
   717  
   718  do_test 6.2 {
   719    db2 eval { BEGIN; SELECT * FROM t1; }
   720    db  eval { INSERT INTO t1 VALUES(3, 4) }
   721    file size test.db-wal
   722  } [wal_file_size 4 1024]
   723  
   724  #   At this point the log file contains 4 frames. 3 of which it should
   725  #   be possible to checkpoint.
   726  #
   727  # EVIDENCE-OF: R-16642-42503 If pnLog is not NULL, then *pnLog is set to
   728  # the total number of frames in the log file or to -1 if the checkpoint
   729  # could not run because of an error or because the database is not in
   730  # WAL mode.
   731  #
   732  # EVIDENCE-OF: R-10514-25250 If pnCkpt is not NULL,then *pnCkpt is set
   733  # to the total number of checkpointed frames in the log file (including
   734  # any that were already checkpointed before the function was called) or
   735  # to -1 if the checkpoint could not run due to an error or because the
   736  # database is not in WAL mode.
   737  #
   738  do_test 6.4 {
   739    lrange [wal_checkpoint_v2 db passive] 1 2
   740  } {4 3} 
   741  
   742  # EVIDENCE-OF: R-37257-17813 Note that upon successful completion of an
   743  # SQLITE_CHECKPOINT_TRUNCATE, the log file will have been truncated to
   744  # zero bytes and so both *pnLog and *pnCkpt will be set to zero.
   745  #
   746  do_test 6.5 {
   747    db2 eval COMMIT
   748    wal_checkpoint_v2 db truncate
   749  } {0 0 0}
   750  
   751  
   752  
   753  finish_test