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

     1  # 2015 December 7
     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 focus
    12  # of this file is the sqlite3_snapshot_xxx() APIs.
    13  #
    14  
    15  set testdir [file dirname $argv0]
    16  source $testdir/tester.tcl
    17  ifcapable !snapshot {finish_test; return}
    18  set testprefix snapshot
    19  
    20  # This test does not work with the inmemory_journal permutation. The reason
    21  # is that each connection opened as part of this permutation executes
    22  # "PRAGMA journal_mode=memory", which fails if the database is in wal mode
    23  # and there are one or more existing connections.
    24  if {[permutation]=="inmemory_journal"} {
    25    finish_test
    26    return
    27  }
    28  
    29  foreach {tn tcl} {
    30    1 {
    31      proc snapshot_get {DB DBNAME} {
    32        uplevel [list sqlite3_snapshot_get $DB $DBNAME]
    33      }
    34      proc snapshot_open {DB DBNAME SNAPSHOT} {
    35        uplevel [list sqlite3_snapshot_open $DB $DBNAME $SNAPSHOT]
    36      }
    37      proc snapshot_free {SNAPSHOT} {
    38        uplevel [list sqlite3_snapshot_free $SNAPSHOT]
    39      }
    40      proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
    41        uplevel [list sqlite3_snapshot_cmp $SNAPSHOT1 $SNAPSHOT2]
    42      }
    43    }
    44  
    45    2 {
    46      proc snapshot_get {DB DBNAME} {
    47        uplevel [list sqlite3_snapshot_get_blob $DB $DBNAME]
    48      }
    49      proc snapshot_open {DB DBNAME SNAPSHOT} {
    50        uplevel [list sqlite3_snapshot_open_blob $DB $DBNAME $SNAPSHOT]
    51      }
    52      proc snapshot_free {SNAPSHOT} {
    53      }
    54      proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
    55        uplevel [list sqlite3_snapshot_cmp_blob $SNAPSHOT1 $SNAPSHOT2]
    56      }
    57    }
    58  } {
    59  
    60    reset_db
    61    eval $tcl
    62  
    63    #-------------------------------------------------------------------------
    64    # Check some error conditions in snapshot_get(). It is an error if:
    65    #
    66    #  1) snapshot_get() is called on a non-WAL database, or
    67    #  2) there is an open write transaction on the database.
    68    #  3) the database handle is in auto-commit mode
    69    #
    70    do_execsql_test $tn.1.0 {
    71      CREATE TABLE t1(a, b);
    72      INSERT INTO t1 VALUES(1, 2);
    73      INSERT INTO t1 VALUES(3, 4);
    74    }
    75  
    76    do_test $tn.1.1.1 {
    77      execsql { BEGIN; SELECT * FROM t1; }
    78      list [catch { snapshot_get db main } msg] $msg
    79    } {1 SQLITE_ERROR}
    80    do_execsql_test $tn.1.1.2 COMMIT
    81  
    82    do_test $tn.1.2.1 {
    83      execsql {
    84        PRAGMA journal_mode = WAL;
    85        BEGIN;
    86          INSERT INTO t1 VALUES(5, 6);
    87          INSERT INTO t1 VALUES(7, 8);
    88      }
    89      list [catch { snapshot_get db main } msg] $msg
    90    } {1 SQLITE_ERROR}
    91    do_execsql_test $tn.1.2.2 COMMIT
    92  
    93    do_test $tn.1.3.1 {
    94      list [catch { snapshot_get db main } msg] $msg
    95    } {1 SQLITE_ERROR}
    96    do_test $tn.1.3.2 {
    97      db trans { set snap [snapshot_get db main] }
    98      snapshot_free $snap
    99    } {}
   100  
   101    #-------------------------------------------------------------------------
   102    # Check that a simple case works. Reuse the database created by the
   103    # block of tests above.
   104    #
   105    do_execsql_test $tn.2.1.0 {
   106      BEGIN;
   107        SELECT * FROM t1;
   108    } {1 2 3 4 5 6 7 8}
   109  
   110    do_test $tn.2.1.1 {
   111      set snapshot [snapshot_get db main]
   112      execsql {
   113        COMMIT;
   114        INSERT INTO t1 VALUES(9, 10);
   115        SELECT * FROM t1;
   116      }
   117    } {1 2 3 4 5 6 7 8 9 10}
   118  
   119    do_test $tn.2.1.2 {
   120      execsql BEGIN
   121      snapshot_open db main $snapshot
   122      execsql {
   123        SELECT * FROM t1;
   124      }
   125    } {1 2 3 4 5 6 7 8}
   126  
   127    do_test $tn.2.1.3 {
   128      snapshot_free $snapshot
   129      execsql COMMIT
   130    } {}
   131  
   132    do_test $tn.2.2.0 {
   133      sqlite3 db2 test.db
   134      execsql {
   135        BEGIN;
   136          SELECT * FROM t1;
   137      } db2
   138    } {1 2 3 4 5 6 7 8 9 10}
   139  
   140    do_test $tn.2.2.1 {
   141      set snapshot [snapshot_get db2 main]
   142      execsql {
   143        INSERT INTO t1 VALUES(11, 12);
   144        SELECT * FROM t1;
   145      }
   146    } {1 2 3 4 5 6 7 8 9 10 11 12}
   147  
   148    do_test $tn.2.2.2 {
   149      execsql BEGIN
   150      snapshot_open db main $snapshot
   151      execsql {
   152        SELECT * FROM t1;
   153      }
   154    } {1 2 3 4 5 6 7 8 9 10}
   155  
   156    do_test $tn.2.2.3 {
   157      snapshot_free $snapshot
   158      execsql COMMIT
   159      execsql COMMIT db2
   160      db2 close
   161    } {}
   162  
   163    do_test $tn.2.3.1 {
   164      execsql { DELETE FROM t1 WHERE a>6 }
   165      db trans { set snapshot [snapshot_get db main] }
   166      execsql {
   167        INSERT INTO t1 VALUES('a', 'b');
   168        INSERT INTO t1 VALUES('c', 'd');
   169        SELECT * FROM t1;
   170      }
   171    } {1 2 3 4 5 6 a b c d}
   172    do_test $tn.2.3.2 {
   173      execsql BEGIN
   174      snapshot_open db main $snapshot
   175      execsql { SELECT * FROM t1 }
   176    } {1 2 3 4 5 6}
   177  
   178    do_test $tn.2.3.3 {
   179      catchsql {
   180        INSERT INTO t1 VALUES('x','y')
   181      }
   182    } {1 {database is locked}}
   183    do_test $tn.2.3.4 {
   184      execsql COMMIT
   185      snapshot_free $snapshot
   186    } {}
   187  
   188    #-------------------------------------------------------------------------
   189    # Check some errors in snapshot_open(). It is an error if:
   190    #
   191    #   1) the db is in auto-commit mode,
   192    #   2) the db has an open (read or write) transaction,
   193    #   3) the db is not a wal database,
   194    #
   195    # Reuse the database created by earlier tests.
   196    #
   197    do_execsql_test $tn.3.0.0 {
   198      CREATE TABLE t2(x, y);
   199      INSERT INTO t2 VALUES('a', 'b');
   200      INSERT INTO t2 VALUES('c', 'd');
   201      BEGIN;
   202        SELECT * FROM t2;
   203    } {a b c d}
   204    do_test $tn.3.0.1 {
   205      set snapshot [snapshot_get db main]
   206      execsql { COMMIT }
   207      execsql { INSERT INTO t2 VALUES('e', 'f'); }
   208    } {}
   209  
   210    do_test $tn.3.1 {
   211      list [catch {snapshot_open db main $snapshot } msg] $msg
   212    } {1 SQLITE_ERROR}
   213  
   214    do_test $tn.3.2.1 {
   215      execsql {
   216        BEGIN;
   217          SELECT * FROM t2;
   218      }
   219    } {a b c d e f}
   220  
   221    # Update - it is no longer an error to have a read-transaction open, 
   222    # provided there are no active SELECT statements.
   223    do_test $tn.3.2.2a {
   224      db eval "SELECT * FROM t2" {
   225        set res [list [catch {snapshot_open db main $snapshot } msg] $msg]
   226        break
   227      }
   228      set res
   229    } {1 SQLITE_ERROR}
   230    do_test $tn.3.2.2b {
   231      snapshot_open db main $snapshot
   232    } {}
   233  
   234    do_test $tn.3.2.3 {
   235      execsql {
   236        COMMIT;
   237        BEGIN;
   238          INSERT INTO t2 VALUES('g', 'h');
   239      }
   240      list [catch {snapshot_open db main $snapshot } msg] $msg
   241    } {1 SQLITE_ERROR}
   242    do_execsql_test $tn.3.2.4 COMMIT
   243  
   244    do_test $tn.3.3.1a {
   245      execsql { PRAGMA journal_mode = DELETE }
   246      execsql { BEGIN }
   247      list [catch {snapshot_open db main $snapshot } msg] $msg
   248    } {1 SQLITE_ERROR}
   249  
   250    do_test $tn.3.3.1b {
   251      execsql { COMMIT ; BEGIN ; SELECT * FROM t2 }
   252      list [catch {snapshot_open db main $snapshot } msg] $msg
   253    } {1 SQLITE_ERROR}
   254  
   255    do_test $tn.$tn.3.3.2 {
   256      snapshot_free $snapshot
   257      execsql COMMIT
   258    } {}
   259  
   260    #-------------------------------------------------------------------------
   261    # Check that SQLITE_ERROR_SNAPSHOT is returned if the specified snapshot
   262    # no longer exists because the wal file has been checkpointed.
   263    #
   264    #   1. Reading a snapshot from the middle of a wal file is not possible
   265    #      after the wal file has been checkpointed.
   266    #
   267    #   2. That a snapshot from the end of a wal file can not be read once
   268    #      the wal file has been wrapped.
   269    #
   270    do_execsql_test $tn.4.1.0 {
   271      PRAGMA journal_mode = wal;
   272      CREATE TABLE t3(i, j);
   273      INSERT INTO t3 VALUES('o', 't');
   274      INSERT INTO t3 VALUES('t', 'f');
   275      BEGIN;
   276        SELECT * FROM t3;
   277    } {wal o t t f}
   278  
   279    do_test $tn.4.1.1 {
   280      set snapshot [snapshot_get db main]
   281      execsql COMMIT
   282    } {}
   283    do_test $tn.4.1.2 {
   284      execsql { 
   285        INSERT INTO t3 VALUES('f', 's'); 
   286        BEGIN;
   287      }
   288      snapshot_open db main $snapshot
   289      execsql { SELECT * FROM t3 }
   290    } {o t t f}
   291  
   292    do_test $tn.4.1.3 {
   293      execsql { 
   294        COMMIT;
   295        PRAGMA wal_checkpoint;
   296        BEGIN;
   297      }
   298      list [catch {snapshot_open db main $snapshot} msg] $msg
   299    } {1 SQLITE_ERROR_SNAPSHOT}
   300    do_test $tn.4.1.4 {
   301      snapshot_free $snapshot
   302      execsql COMMIT
   303    } {}
   304  
   305    do_test $tn.4.2.1 {
   306      execsql {
   307        INSERT INTO t3 VALUES('s', 'e');
   308        INSERT INTO t3 VALUES('n', 't');
   309        BEGIN;
   310          SELECT * FROM t3;
   311      }
   312    } {o t t f f s s e n t}
   313    do_test $tn.4.2.2 {
   314      set snapshot [snapshot_get db main]
   315      execsql {
   316        COMMIT;
   317        PRAGMA wal_checkpoint;
   318        BEGIN;
   319      }
   320      snapshot_open db main $snapshot
   321      execsql { SELECT * FROM t3 }
   322    } {o t t f f s s e n t}
   323    do_test $tn.4.2.3 {
   324      execsql {
   325        COMMIT;
   326        INSERT INTO t3 VALUES('e', 't');
   327        BEGIN;
   328      }
   329      list [catch {snapshot_open db main $snapshot} msg] $msg
   330    } {1 SQLITE_ERROR_SNAPSHOT}
   331    do_test $tn.4.2.4 {
   332      snapshot_free $snapshot
   333    } {}
   334  
   335    #-------------------------------------------------------------------------
   336    # Check that SQLITE_BUSY is returned if a checkpoint is running when
   337    # sqlite3_snapshot_open() is called.
   338    #
   339    reset_db
   340    db close
   341    testvfs tvfs
   342    sqlite3 db test.db -vfs tvfs
   343  
   344    do_execsql_test $tn.5.1 {
   345      PRAGMA journal_mode = wal;
   346      CREATE TABLE x1(x, xx, xxx);
   347      INSERT INTO x1 VALUES('z', 'zz', 'zzz');
   348      BEGIN;
   349        SELECT * FROM x1;
   350    } {wal z zz zzz}
   351  
   352    do_test $tn.5.2 {
   353      set ::snapshot [snapshot_get db main]
   354      sqlite3 db2 test.db -vfs tvfs
   355      execsql {
   356        INSERT INTO x1 VALUES('a', 'aa', 'aaa');
   357        COMMIT;
   358      }
   359    } {}
   360  
   361    set t53 0
   362    proc write_callback {args} {
   363      do_test $tn.5.3.[incr ::t53] {
   364        execsql BEGIN
   365        list [catch { snapshot_open db main $::snapshot } msg] $msg
   366      } {1 SQLITE_BUSY}
   367      catchsql COMMIT
   368    }
   369  
   370    tvfs filter xWrite
   371    tvfs script write_callback
   372    db2 eval { PRAGMA wal_checkpoint }
   373    db close
   374    db2 close
   375    tvfs delete
   376    snapshot_free $snapshot
   377  
   378    #-------------------------------------------------------------------------
   379    # Test that sqlite3_snapshot_get() may be called immediately after
   380    # "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
   381    # be called after opening the db handle and running the script
   382    # "PRAGMA user_version; BEGIN".
   383    reset_db
   384    do_execsql_test $tn.6.1 {
   385      PRAGMA journal_mode = wal;
   386      CREATE TABLE x1(x, xx, xxx);
   387      INSERT INTO x1 VALUES('z', 'zz', 'zzz');
   388      BEGIN;
   389        PRAGMA user_version;
   390    } {wal 0}
   391    do_test $tn.6.2 {
   392      set ::snapshot [snapshot_get db main]
   393      execsql {
   394        INSERT INTO x1 VALUES('a', 'aa', 'aaa');
   395        COMMIT;
   396      }
   397    } {}
   398    do_test $tn.6.3 {
   399      sqlite3 db2 test.db 
   400      db2 eval "PRAGMA user_version ; BEGIN"
   401      snapshot_open db2 main $::snapshot
   402      db2 eval { SELECT * FROM x1 }
   403    } {z zz zzz}
   404    do_test $tn.6.4 {
   405      db2 close
   406      sqlite3 db2 test.db 
   407      db2 eval "PRAGMA application_id"
   408      db2 eval "BEGIN"
   409      snapshot_open db2 main $::snapshot
   410      db2 eval { SELECT * FROM x1 }
   411    } {z zz zzz}
   412  
   413    do_test $tn.6.5 {
   414      db2 close
   415      sqlite3 db2 test.db 
   416      db2 eval "BEGIN"
   417      list [catch {snapshot_open db2 main $::snapshot} msg] $msg
   418    } {1 SQLITE_ERROR}
   419  
   420    snapshot_free $snapshot
   421  
   422    #-------------------------------------------------------------------------
   423    # The following tests investigate the sqlite3_snapshot_cmp() API.
   424    #
   425  
   426    # Compare snapshots $p1 and $p2, checking that the result is $r.
   427    #
   428    proc do_snapshot_cmp_test {tn p1 p2 r} {
   429      uplevel [list do_test $tn.1 [list snapshot_cmp $p1 $p2] $r]
   430      uplevel [list do_test $tn.2 [list snapshot_cmp $p2 $p1] [expr $r*-1]]
   431      uplevel [list do_test $tn.3 [list snapshot_cmp $p1 $p1] 0]
   432      uplevel [list do_test $tn.4 [list snapshot_cmp $p2 $p2] 0]
   433    }
   434  
   435    catch { db2 close }
   436    reset_db
   437  
   438    do_execsql_test $tn.7.1 {
   439      PRAGMA journal_mode = wal;
   440      CREATE TABLE t1(x);
   441    } wal
   442  
   443    do_test $tn.7.1.2 {
   444      execsql { BEGIN ; PRAGMA application_id }
   445      set p1 [snapshot_get db main]
   446      execsql {
   447        INSERT INTO t1 VALUES(10);
   448        COMMIT;
   449      }
   450      execsql { BEGIN ; PRAGMA application_id }
   451      set p2 [snapshot_get db main]
   452      execsql COMMIT
   453    } {}
   454  
   455    do_snapshot_cmp_test $tn.7.1.3 $p1 $p2 -1
   456    snapshot_free $p1
   457    snapshot_free $p2
   458  
   459    do_execsql_test $tn.7.2.1 {
   460      INSERT INTO t1 VALUES(11);
   461      INSERT INTO t1 VALUES(12);
   462      INSERT INTO t1 VALUES(13);
   463      BEGIN; 
   464        PRAGMA application_id;
   465    } {0}
   466    do_test $tn.7.2.2 {
   467      set p1 [snapshot_get db main]
   468      execsql {
   469        COMMIT;
   470        INSERT INTO t1 VALUES(14);
   471        PRAGMA wal_checkpoint;
   472        BEGIN;
   473          PRAGMA application_id;
   474      }
   475      set p2 [snapshot_get db main]
   476      execsql COMMIT
   477    } {}
   478  
   479    do_snapshot_cmp_test $tn.7.2.3 $p1 $p2 -1
   480    snapshot_free $p2
   481  
   482    do_test $tn.7.3.1 {
   483      execsql {
   484        INSERT INTO t1 VALUES(14);
   485        BEGIN;
   486          PRAGMA application_id;
   487      }
   488      set p2 [snapshot_get db main]
   489      execsql COMMIT
   490    } {}
   491  
   492    do_snapshot_cmp_test $tn.7.3.2 $p1 $p2 -1
   493    snapshot_free $p1
   494    snapshot_free $p2
   495  }
   496  
   497  finish_test