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

     1  # 2010 April 13
     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 "blocking-checkpoint"
    13  # operations.
    14  #
    15  
    16  set testdir [file dirname $argv0]
    17  source $testdir/tester.tcl
    18  source $testdir/lock_common.tcl
    19  source $testdir/wal_common.tcl
    20  ifcapable !wal {finish_test ; return }
    21  do_not_use_codec
    22  
    23  set testprefix wal5
    24  
    25  proc db_page_count  {{file test.db}} { expr [file size $file] / 1024 }
    26  proc wal_page_count {{file test.db}} { wal_frame_count ${file}-wal 1024 }
    27  
    28  
    29  # A checkpoint may be requested either using the C API or by executing
    30  # an SQL PRAGMA command. To test both methods, all tests in this file are 
    31  # run twice - once using each method to request checkpoints.
    32  #
    33  foreach {testprefix do_wal_checkpoint} {
    34  
    35    wal5-pragma {
    36      proc do_wal_checkpoint { dbhandle args } {
    37        array set a $args
    38        foreach key [array names a] {
    39          if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" }
    40        }
    41  
    42        set sql "PRAGMA "
    43        if {[info exists a(-db)]} { append sql "$a(-db)." }
    44        append sql "wal_checkpoint"
    45        if {[info exists a(-mode)]} { append sql " = $a(-mode)" }
    46  
    47        uplevel [list $dbhandle eval $sql]
    48      }
    49    }
    50  
    51    wal5-capi {
    52      proc do_wal_checkpoint { dbhandle args } {
    53        set a(-mode) passive
    54        array set a $args
    55        foreach key [array names a] {
    56          if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" }
    57        }
    58  
    59        set vals {restart full truncate}
    60        if {[lsearch -exact $vals $a(-mode)]<0} { set a(-mode) passive }
    61  
    62        set cmd [list sqlite3_wal_checkpoint_v2 $dbhandle $a(-mode)]
    63        if {[info exists a(-db)]} { lappend sql $a(-db) }
    64  
    65        uplevel $cmd
    66      }
    67    }
    68  } {
    69  
    70    eval $do_wal_checkpoint
    71  
    72    do_multiclient_test tn {
    73  
    74      set ::nBusyHandler 0
    75      set ::busy_handler_script ""
    76      proc busyhandler {n} {
    77        incr ::nBusyHandler 
    78        eval $::busy_handler_script
    79        return 0
    80      }
    81  
    82      proc reopen_all {} {
    83        code1 {db close}
    84        code2 {db2 close}
    85        code3 {db3 close}
    86  
    87        code1 {sqlite3 db test.db}
    88        code2 {sqlite3 db2 test.db}
    89        code3 {sqlite3 db3 test.db}
    90  
    91        sql1  { PRAGMA synchronous = NORMAL }
    92        code1 { db busy busyhandler }
    93      }
    94  
    95      do_test 1.$tn.1 {
    96        reopen_all
    97        sql1 {
    98          PRAGMA page_size = 1024;
    99          PRAGMA auto_vacuum = 0;
   100          CREATE TABLE t1(x, y);
   101          PRAGMA journal_mode = WAL;
   102          INSERT INTO t1 VALUES(1, zeroblob(1200));
   103          INSERT INTO t1 VALUES(2, zeroblob(1200));
   104          INSERT INTO t1 VALUES(3, zeroblob(1200));
   105        }
   106        expr [file size test.db] / 1024
   107      } {2}
   108  
   109      # Have connection 2 grab a read-lock on the current snapshot.
   110      do_test 1.$tn.2 { sql2 { BEGIN; SELECT x FROM t1 } } {1 2 3}
   111  
   112      # Attempt a checkpoint.
   113      do_test 1.$tn.3 {
   114        code1 { do_wal_checkpoint db }
   115        list [db_page_count] [wal_page_count]
   116      } {5 9}
   117  
   118      # Write to the db again. The log cannot wrap because of the lock still
   119      # held by connection 2. The busy-handler has not yet been invoked.
   120      do_test 1.$tn.4 {
   121        sql1 { INSERT INTO t1 VALUES(4, zeroblob(1200)) }
   122        list [db_page_count] [wal_page_count] $::nBusyHandler
   123      } {5 12 0}
   124  
   125      # Now do a blocking-checkpoint. Set the busy-handler up so that connection
   126      # 2 releases its lock on the 6th invocation. The checkpointer should then
   127      # proceed to checkpoint the entire log file. Next write should go to the 
   128      # start of the log file.
   129      #
   130      set ::busy_handler_script { if {$n==5} { sql2 COMMIT } }
   131      do_test 1.$tn.5 {
   132        code1 { do_wal_checkpoint db -mode restart }
   133        list [db_page_count] [wal_page_count] $::nBusyHandler
   134      } {6 12 6}
   135      do_test 1.$tn.6 {
   136        set ::nBusyHandler 0
   137        sql1 { INSERT INTO t1 VALUES(5, zeroblob(1200)) }
   138        list [db_page_count] [wal_page_count] $::nBusyHandler
   139      } {6 12 0}
   140  
   141      do_test 1.$tn.7 {
   142        reopen_all
   143        list [db_page_count] [wal_page_count] $::nBusyHandler
   144      } [expr {[nonzero_reserved_bytes]?"/# # 0/":"7 0 0"}]
   145  
   146      do_test 1.$tn.8  { sql2 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5}
   147      do_test 1.$tn.9  {
   148        sql1 { INSERT INTO t1 VALUES(6, zeroblob(1200)) }
   149        list [db_page_count] [wal_page_count] $::nBusyHandler
   150      } [expr {[nonzero_reserved_bytes]?"/# # #/":"7 5 0"}]
   151      do_test 1.$tn.10 { sql3 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5 6}
   152  
   153      set ::busy_handler_script { 
   154        if {$n==5} { sql2 COMMIT } 
   155        if {$n==6} { set ::db_file_size [db_page_count] }
   156        if {$n==7} { sql3 COMMIT }
   157      }
   158      do_test 1.$tn.11 {
   159        code1 { do_wal_checkpoint db -mode restart }
   160        list [db_page_count] [wal_page_count] $::nBusyHandler
   161      } [expr {[nonzero_reserved_bytes]?"/# # #/":"10 5 8"}]
   162      do_test 1.$tn.12 { set ::db_file_size } 10
   163    }
   164  
   165    #-------------------------------------------------------------------------
   166    # This block of tests explores checkpoint operations on more than one 
   167    # database file.
   168    #
   169    proc setup_and_attach_aux {} {
   170      sql1 { ATTACH 'test.db2' AS aux }
   171      sql2 { ATTACH 'test.db2' AS aux }
   172      sql3 { ATTACH 'test.db2' AS aux }
   173      sql1 {
   174        PRAGMA aux.auto_vacuum = 0;
   175        PRAGMA main.auto_vacuum = 0;
   176        PRAGMA main.page_size=1024; PRAGMA main.journal_mode=WAL;
   177        PRAGMA aux.page_size=1024;  PRAGMA aux.journal_mode=WAL;
   178      }
   179    }
   180  
   181    proc file_page_counts {} {
   182      list [db_page_count  test.db ] \
   183           [wal_page_count test.db ] \
   184           [db_page_count  test.db2] \
   185           [wal_page_count test.db2]
   186    }
   187  
   188    # Test that executing "PRAGMA wal_checkpoint" checkpoints all attached
   189    # databases, not just the main db.  In capi mode, check that this is
   190    # true if a NULL pointer is passed to wal_checkpoint_v2() in place of a 
   191    # database name.
   192    do_multiclient_test tn {
   193      setup_and_attach_aux
   194      do_test 2.1.$tn.1 {
   195        sql1 {
   196          CREATE TABLE t1(a, b);
   197          INSERT INTO t1 VALUES(1, 2);
   198          CREATE TABLE aux.t2(a, b);
   199          INSERT INTO t2 VALUES(1, 2);
   200        }
   201      } {}
   202      do_test 2.2.$tn.2 { file_page_counts } {1 3 1 3}
   203      do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 3 3}
   204      do_test 2.1.$tn.4 { file_page_counts } {2 3 2 3}
   205    }
   206  
   207    do_multiclient_test tn {
   208      setup_and_attach_aux
   209      do_test 2.2.$tn.1 {
   210        execsql {
   211          CREATE TABLE t1(a, b);
   212          INSERT INTO t1 VALUES(1, 2);
   213          CREATE TABLE aux.t2(a, b);
   214          INSERT INTO t2 VALUES(1, 2);
   215          INSERT INTO t2 VALUES(3, 4);
   216        }
   217      } {}
   218      do_test 2.2.$tn.2 { file_page_counts } {1 3 1 4}
   219      do_test 2.2.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
   220      do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 3 3}
   221      do_test 2.2.$tn.5 { file_page_counts } {2 3 2 4}
   222    }
   223  
   224    do_multiclient_test tn {
   225      setup_and_attach_aux
   226      do_test 2.3.$tn.1 {
   227        execsql {
   228          CREATE TABLE t1(a, b);
   229          INSERT INTO t1 VALUES(1, 2);
   230          CREATE TABLE aux.t2(a, b);
   231          INSERT INTO t2 VALUES(1, 2);
   232        }
   233      } {}
   234      do_test 2.3.$tn.2 { file_page_counts } {1 3 1 3}
   235      do_test 2.3.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
   236      do_test 2.3.$tn.4 { sql1 { INSERT INTO t1 VALUES(3, 4) } } {}
   237      do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {}
   238      do_test 2.3.$tn.6 { file_page_counts } {1 4 1 4}
   239      do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 4 3}
   240  
   241      # The checkpoint above only writes page 1 of the db file. The other
   242      # page (page 2) is locked by the read-transaction opened by the
   243      # [sql2] commmand above. So normally, the db is 1 page in size here.
   244      # However, in mmap() mode, the db is pre-allocated to 2 pages at the
   245      # start of the checkpoint, even though page 2 cannot be written.
   246      set nDb 2
   247      if {[permutation]!="mmap"} {set nDb 1}
   248      ifcapable !mmap {set nDb 1}
   249      do_test 2.3.$tn.8 { file_page_counts } [list $nDb 4 2 4]
   250    }
   251  
   252    # Check that checkpoints block on the correct locks. And respond correctly
   253    # if they cannot obtain those locks. There are three locks that a checkpoint
   254    # may block on (in the following order):
   255    #
   256    #   1. The writer lock: FULL and RESTART checkpoints block until any writer
   257    #      process releases its lock.
   258    #
   259    #   2. Readers using part of the log file. FULL and RESTART checkpoints block
   260    #      until readers using part (but not all) of the log file have finished.
   261    #
   262    #   3. Readers using any of the log file. After copying data into the
   263    #      database file, RESTART checkpoints block until readers using any part
   264    #      of the log file have finished.
   265    #
   266    # This test case involves running a checkpoint while there exist other 
   267    # processes holding all three types of locks.
   268    #
   269    foreach {tn1 checkpoint busy_on ckpt_expected expected} {
   270      1   PASSIVE   -   {0 3 3}   -
   271      2   TYPO      -   {0 3 3}   -
   272  
   273      3   FULL      -   {0 4 4}   2
   274      4   FULL      1   {1 3 3}   1
   275      5   FULL      2   {1 4 3}   2
   276      6   FULL      3   {0 4 4}   2
   277  
   278      7   RESTART   -   {0 4 4}   3
   279      8   RESTART   1   {1 3 3}   1
   280      9   RESTART   2   {1 4 3}   2
   281      10  RESTART   3   {1 4 4}   3
   282  
   283      11  TRUNCATE  -   {0 0 0}   3
   284      12  TRUNCATE  1   {1 3 3}   1
   285      13  TRUNCATE  2   {1 4 3}   2
   286      14  TRUNCATE  3   {1 4 4}   3
   287  
   288    } {
   289      do_multiclient_test tn {
   290        setup_and_attach_aux
   291  
   292        proc busyhandler {x} {
   293          set ::max_busyhandler $x
   294          if {$::busy_on!="-" && $x==$::busy_on} { return 1 }
   295          switch -- $x {
   296            1 { sql2 "COMMIT ; BEGIN ; SELECT * FROM t1" }
   297            2 { sql3 "COMMIT" }
   298            3 { sql2 "COMMIT" }
   299          }
   300          return 0
   301        }
   302        set ::max_busyhandler -
   303  
   304        do_test 2.4.$tn1.$tn.1 {
   305          sql1 {
   306            CREATE TABLE t1(a, b);
   307            INSERT INTO t1 VALUES(1, 2);
   308          }
   309          sql2 { BEGIN; INSERT INTO t1 VALUES(3, 4) }
   310          sql3 { BEGIN; SELECT * FROM t1 }
   311        } {1 2}
   312  
   313        do_test 2.4.$tn1.$tn.2 {
   314          code1 { db busy busyhandler }
   315          code1 { do_wal_checkpoint db -mode [string tolower $checkpoint] }
   316        } $ckpt_expected
   317        do_test 2.4.$tn1.$tn.3 { set ::max_busyhandler } $expected
   318      }
   319    }
   320  
   321  
   322    do_multiclient_test tn {
   323  
   324      code1 $do_wal_checkpoint
   325      code2 $do_wal_checkpoint
   326      code3 $do_wal_checkpoint
   327      
   328      do_test 3.$tn.1 {
   329        sql1 {
   330          PRAGMA auto_vacuum = 0;
   331          PRAGMA journal_mode = WAL;
   332          PRAGMA synchronous = normal;
   333          CREATE TABLE t1(x, y);
   334        }
   335  
   336        sql2 { PRAGMA journal_mode }
   337        sql3 { PRAGMA journal_mode }
   338      } {wal}
   339  
   340      do_test 3.$tn.2 { code2 { do_wal_checkpoint db2 } } {0 2 2}
   341  
   342      do_test 3.$tn.3 { code2 { do_wal_checkpoint db2 } } {0 2 2}
   343  
   344      do_test 3.$tn.4 { code3 { do_wal_checkpoint db3 } } {0 2 2}
   345  
   346      code1 {db  close}
   347      code2 {db2 close}
   348      code3 {db3 close}
   349  
   350      code1 {sqlite3 db  test.db}
   351      code2 {sqlite3 db2 test.db}
   352      code3 {sqlite3 db3 test.db}
   353  
   354      do_test 3.$tn.5 { sql3 { PRAGMA journal_mode } } {wal}
   355  
   356      do_test 3.$tn.6 { code3 { do_wal_checkpoint db3 } } {0 0 0}
   357    }
   358  
   359    # Test SQLITE_CHECKPOINT_TRUNCATE.
   360    #
   361    do_multiclient_test tn {
   362  
   363      code1 $do_wal_checkpoint
   364      code2 $do_wal_checkpoint
   365      code3 $do_wal_checkpoint
   366  
   367      do_test 4.$tn.1 {
   368        sql1 {
   369          PRAGMA page_size = 1024;
   370          PRAGMA auto_vacuum = 0;
   371          PRAGMA journal_mode = WAL;
   372          PRAGMA synchronous = normal;
   373          CREATE TABLE t1(x, y);
   374          CREATE INDEX i1 ON t1(x, y);
   375          INSERT INTO t1 VALUES(1, 2);
   376          INSERT INTO t1 VALUES(3, 4);
   377        }
   378        file size test.db-wal
   379      } [wal_file_size 8 1024]
   380  
   381      do_test 4.$tn.2 { do_wal_checkpoint db -mode truncate } {0 0 0}
   382      do_test 4.$tn.3 { file size test.db-wal } 0
   383  
   384      do_test 4.$tn.4 {
   385        sql2 { SELECT * FROM t1 }
   386      } {1 2 3 4}
   387  
   388      do_test 4.$tn.5 {
   389        sql2 { INSERT INTO t1 VALUES('a', 'b') }
   390        file size test.db-wal
   391      } [wal_file_size 2 1024]
   392  
   393    }
   394    
   395    # Test that FULL, RESTART and TRUNCATE callbacks block on other clients
   396    # and truncate the wal file as required even if the entire wal file has
   397    # already been checkpointed when they are invoked.
   398    #
   399    do_multiclient_test tn {
   400  
   401      code1 $do_wal_checkpoint
   402      code2 $do_wal_checkpoint
   403      code3 $do_wal_checkpoint
   404  
   405      do_test 5.$tn.1 {
   406        sql1 {
   407          PRAGMA page_size = 1024;
   408          PRAGMA auto_vacuum = 0;
   409          PRAGMA journal_mode = WAL;
   410          PRAGMA synchronous = normal;
   411          CREATE TABLE t1(x, y);
   412          CREATE INDEX i1 ON t1(x, y);
   413          INSERT INTO t1 VALUES(1, 2);
   414          INSERT INTO t1 VALUES(3, 4);
   415          INSERT INTO t1 VALUES(5, 6);
   416        }
   417        file size test.db-wal
   418      } [wal_file_size 10 1024]
   419  
   420      do_test 5.$tn.2 { 
   421        sql2 { BEGIN; SELECT * FROM t1 }
   422      } {1 2 3 4 5 6}
   423  
   424      do_test 5.$tn.3 { do_wal_checkpoint db -mode passive } {0 10 10}
   425  
   426      do_test 5.$tn.4 { 
   427        sql3 { BEGIN; INSERT INTO t1 VALUES(7, 8); }
   428      } {}
   429  
   430      do_test 5.$tn.5 { do_wal_checkpoint db -mode passive  } {0 10 10}
   431      do_test 5.$tn.6 { do_wal_checkpoint db -mode full     } {1 10 10}
   432  
   433      do_test 5.$tn.7 { sql3 { ROLLBACK } } {}
   434  
   435      do_test 5.$tn.8 { do_wal_checkpoint db -mode full     } {0 10 10}
   436      do_test 5.$tn.9 { do_wal_checkpoint db -mode truncate } {1 10 10}
   437  
   438      do_test 5.$tn.10 { 
   439        file size test.db-wal
   440      } [wal_file_size 10 1024]
   441  
   442      proc xBusyHandler {n} { sql2 { COMMIT } ; return 0 }
   443      db busy xBusyHandler
   444  
   445      do_test 5.$tn.11 { do_wal_checkpoint db -mode truncate } {0 0 0}
   446      do_test 5.$tn.12 { file size test.db-wal } 0
   447  
   448      do_test 5.$tn.13 {
   449        sql1 {
   450          INSERT INTO t1 VALUES(7, 8);
   451          INSERT INTO t1 VALUES(9, 10);
   452          SELECT * FROM t1;
   453        }
   454      } {1 2 3 4 5 6 7 8 9 10}
   455  
   456      do_test 5.$tn.14 { 
   457        sql2 { BEGIN; SELECT * FROM t1 }
   458      } {1 2 3 4 5 6 7 8 9 10}
   459  
   460      proc xBusyHandler {n} { return 1 }
   461      do_test 5.$tn.15 { do_wal_checkpoint db -mode truncate } {1 4 4}
   462      do_test 5.$tn.16 { file size test.db-wal } [wal_file_size 4 1024]
   463  
   464      do_test 5.$tn.17 { do_wal_checkpoint db -mode restart } {1 4 4}
   465  
   466      proc xBusyHandler {n} { sql2 { COMMIT } ; return 0 }
   467      db busy xBusyHandler
   468      do_test 5.$tn.18 { do_wal_checkpoint db -mode restart } {0 4 4}
   469      do_test 5.$tn.19 { file size test.db-wal } [wal_file_size 4 1024]
   470  
   471      do_test 5.$tn.20 { do_wal_checkpoint db -mode truncate } {0 0 0}
   472      do_test 5.$tn.21 { file size test.db-wal } 0
   473    }
   474  
   475  }
   476  
   477  
   478  finish_test