modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/session/sessionB.test (about)

     1  # 2014 August 16
     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  # This file implements regression tests for sessions SQLite extension.
    13  # Specifically, this file contains tests for "patchset" changes.
    14  #
    15  
    16  if {![info exists testdir]} {
    17    set testdir [file join [file dirname [info script]] .. .. test]
    18  } 
    19  source [file join [file dirname [info script]] session_common.tcl]
    20  source $testdir/tester.tcl
    21  ifcapable !session {finish_test; return}
    22  
    23  set testprefix sessionB
    24  
    25  #
    26  # 1.*: Test that the blobs returned by the session_patchset() API are 
    27  #      as expected. Also the sqlite3_changeset_iter functions.
    28  #
    29  # 2.*: Test that patchset blobs are handled by sqlite3changeset_apply().
    30  #
    31  # 3.*: Test that sqlite3changeset_invert() works with patchset blobs. 
    32  #      Correct behaviour is to return SQLITE_CORRUPT.
    33  
    34  proc do_sql2patchset_test {tn sql res} {
    35    sqlite3session S db main
    36    S attach *
    37    execsql $sql
    38    uplevel [list do_patchset_test $tn S $res]
    39    S delete
    40  }
    41  
    42  #-------------------------------------------------------------------------
    43  # Run simple tests of the _patchset() API.
    44  #
    45  do_execsql_test 1.0 {
    46    CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, a));
    47    INSERT INTO t1 VALUES(1, 2, 3, 4);
    48    INSERT INTO t1 VALUES(5, 6, 7, 8);
    49    INSERT INTO t1 VALUES(9, 10, 11, 12);
    50  }
    51  
    52  do_test 1.1 {
    53    sqlite3session S db main
    54    S attach t1
    55    execsql {
    56      INSERT INTO t1 VALUES('w', 'x', 'y', 'z');
    57      DELETE FROM t1 WHERE d=4;
    58      UPDATE t1 SET c = 14 WHERE a=5;
    59    }
    60  } {}
    61  
    62  do_patchset_test 1.2 S {
    63    {UPDATE t1 0 X..X {i 5 {} {} {} {} i 8} {{} {} {} {} i 14 {} {}}}
    64    {INSERT t1 0 X..X {} {t w t x t y t z}}
    65    {DELETE t1 0 X..X {i 1 {} {} {} {} i 4} {}}
    66  }
    67  
    68  do_test 1.3 {
    69    S delete
    70  } {}
    71  
    72  do_sql2patchset_test 1.4 {
    73    DELETE FROM t1;
    74  } {
    75    {DELETE t1 0 X..X {i 5 {} {} {} {} i 8} {}}
    76    {DELETE t1 0 X..X {t w {} {} {} {} t z} {}}
    77    {DELETE t1 0 X..X {i 9 {} {} {} {} i 12} {}}
    78  }
    79  
    80  do_sql2patchset_test 1.5 {
    81    INSERT INTO t1 VALUES(X'61626364', NULL, NULL, 4.2);
    82    INSERT INTO t1 VALUES(4.2, NULL, NULL, X'61626364');
    83  } {
    84    {INSERT t1 0 X..X {} {f 4.2 n {} n {} b abcd}} 
    85    {INSERT t1 0 X..X {} {b abcd n {} n {} f 4.2}}
    86  }
    87  
    88  do_sql2patchset_test 1.6 {
    89    UPDATE t1 SET b=45 WHERE typeof(a)=='blob';
    90    UPDATE t1 SET c='zzzz' WHERE typeof(a)!='blob';
    91  } {
    92    {UPDATE t1 0 X..X {f 4.2 {} {} {} {} b abcd} {{} {} {} {} t zzzz {} {}}}
    93    {UPDATE t1 0 X..X {b abcd {} {} {} {} f 4.2} {{} {} i 45 {} {} {} {}}}
    94  }
    95  
    96  do_sql2patchset_test 1.7 {
    97    UPDATE t1 SET b='xyz' WHERE typeof(a)=='blob';
    98    UPDATE t1 SET c='xyz' WHERE typeof(a)!='blob';
    99    UPDATE t1 SET b=45 WHERE typeof(a)=='blob';
   100    UPDATE t1 SET c='zzzz' WHERE typeof(a)!='blob';
   101  } {
   102  }
   103  
   104  do_sql2patchset_test 1.8 {
   105    DELETE FROM t1;
   106  } {
   107    {DELETE t1 0 X..X {f 4.2 {} {} {} {} b abcd} {}} 
   108    {DELETE t1 0 X..X {b abcd {} {} {} {} f 4.2} {}}
   109  }
   110  
   111  #-------------------------------------------------------------------------
   112  # Run simple tests of _apply() with patchset objects.
   113  #
   114  reset_db
   115  
   116  proc noop {args} { error $args }
   117  proc exec_rollback_replay {sql} {
   118    sqlite3session S db main
   119    S attach *
   120    execsql BEGIN
   121    execsql $sql
   122    set patchset [S patchset]
   123    S delete
   124    execsql ROLLBACK
   125    sqlite3changeset_apply db $patchset noop
   126  }
   127  
   128  do_execsql_test 2.0 {
   129    CREATE TABLE t2(a, b, c, d, PRIMARY KEY(b,c));
   130    CREATE TABLE t3(w, x, y, z, PRIMARY KEY(w));
   131  }
   132  
   133  do_test 2.1 {
   134    exec_rollback_replay {
   135      INSERT INTO t2 VALUES(1, 2, 3, 4);
   136      INSERT INTO t2 VALUES('w', 'x', 'y', 'z');
   137    }
   138    execsql { SELECT * FROM t2 }
   139  } {1 2 3 4 w x y z}
   140  
   141  do_test 2.2 {
   142    exec_rollback_replay {
   143      DELETE FROM t2 WHERE a=1;
   144      UPDATE t2 SET d = 'a';
   145    }
   146    execsql { SELECT * FROM t2 }
   147  } {w x y a}
   148  
   149  #-------------------------------------------------------------------------
   150  # sqlite3changeset_invert()
   151  #
   152  reset_db
   153  
   154  do_execsql_test 3.1 { CREATE TABLE t1(x PRIMARY KEY, y) }
   155  do_test 3.2 {
   156    sqlite3session S db main
   157    S attach *
   158    execsql { INSERT INTO t1 VALUES(1, 2) }
   159    set patchset [S patchset]
   160    S delete
   161    list [catch { sqlite3changeset_invert $patchset } msg] [set msg]
   162  } {1 SQLITE_CORRUPT}
   163  
   164  
   165  #-------------------------------------------------------------------------
   166  # sqlite3changeset_concat()
   167  #
   168  reset_db
   169  
   170  proc do_patchconcat_test {tn args} {
   171    set bRevert 0
   172    if {[lindex $args 0] == "-revert"} {
   173      set bRevert 1
   174      set args [lrange $args 1 end]
   175    }
   176    set nSql [expr [llength $args]-1]
   177    set res [lindex $args $nSql]
   178    set patchlist [list]
   179  
   180    execsql BEGIN
   181    if {$bRevert} { execsql { SAVEPOINT x } }
   182    foreach sql [lrange $args 0 end-1] {
   183      sqlite3session S db main
   184      S attach *
   185      execsql $sql
   186      lappend patchlist [S patchset]
   187      S delete
   188      if {$bRevert} { execsql { ROLLBACK TO x } }
   189    }
   190    execsql ROLLBACK
   191  
   192    set patch [lindex $patchlist 0]
   193    foreach p [lrange $patchlist 1 end] {
   194      set patch [sqlite3changeset_concat $patch $p]
   195    }
   196  
   197    set x [list]
   198    sqlite3session_foreach c $patch { lappend x $c }
   199  
   200    uplevel [list do_test $tn [list set {} $x] [list {*}$res]]
   201  }
   202  
   203  do_execsql_test 4.1.1 {
   204    CREATE TABLE t1(x PRIMARY KEY, y, z);
   205  }
   206  do_patchconcat_test 4.1.2 {
   207    INSERT INTO t1 VALUES(1, 2, 3);
   208  } {
   209    INSERT INTO t1 VALUES(4, 5, 6);
   210  } {
   211    {INSERT t1 0 X.. {} {i 1 i 2 i 3}} 
   212    {INSERT t1 0 X.. {} {i 4 i 5 i 6}}
   213  }
   214  
   215  do_execsql_test 4.2.1 {
   216    INSERT INTO t1 VALUES(1, 2, 3);
   217    INSERT INTO t1 VALUES(4, 5, 6);
   218  }
   219  
   220  do_patchconcat_test 4.2.2 {
   221    UPDATE t1 SET z = 'abc' WHERE x=1
   222  } {
   223    UPDATE t1 SET z = 'def' WHERE x=4
   224  } {
   225    {UPDATE t1 0 X.. {i 1 {} {} {} {}} {{} {} {} {} t abc}} 
   226    {UPDATE t1 0 X.. {i 4 {} {} {} {}} {{} {} {} {} t def}}
   227  }
   228  
   229  do_patchconcat_test 4.2.3 {
   230    DELETE FROM t1 WHERE x=1;
   231  } {
   232    DELETE FROM t1 WHERE x=4;
   233  } {
   234    {DELETE t1 0 X.. {i 1 {} {} {} {}} {}} 
   235    {DELETE t1 0 X.. {i 4 {} {} {} {}} {}}
   236  }
   237  
   238  
   239  do_execsql_test 4.3.1 {
   240    CREATE TABLE t2(a, b, c, d, PRIMARY KEY(c, b));
   241    INSERT INTO t2 VALUES('.', 1, 1, '.');
   242    INSERT INTO t2 VALUES('.', 1, 2, '.');
   243    INSERT INTO t2 VALUES('.', 2, 1, '.');
   244    INSERT INTO t2 VALUES('.', 2, 2, '.');
   245  }
   246  
   247  # INSERT + INSERT 
   248  do_patchconcat_test 4.3.2 -revert {
   249    INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
   250  } {
   251    INSERT INTO t2 VALUES('b', 'a', 'a', 'b');
   252  } {
   253    {INSERT t2 0 .XX. {} {t a t a t a t a}}
   254  }
   255  
   256  # INSERT + DELETE 
   257  do_patchconcat_test 4.3.3 {
   258    INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
   259  } {
   260    DELETE FROM t2 WHERE c = 'a';
   261  } {
   262  }
   263  
   264  # INSERT + UPDATE
   265  do_patchconcat_test 4.3.4 {
   266    INSERT INTO t2 VALUES('a', 'a', 'a', 'a');
   267  } {
   268    UPDATE t2 SET d = 'b' WHERE c='a';
   269  } {
   270    {INSERT t2 0 .XX. {} {t a t a t a t b}}
   271  }
   272  
   273  # UPDATE + UPDATE
   274  do_patchconcat_test 4.3.5 {
   275    UPDATE t2 SET a = 'a' WHERE c=1 AND b=2;
   276  } {
   277    UPDATE t2 SET d = 'd' WHERE c=1 AND b=2;
   278  } {
   279    {UPDATE t2 0 .XX. {{} {} i 2 i 1 {} {}} {t a {} {} {} {} t d}}
   280  }
   281  
   282  # UPDATE + DELETE
   283  do_patchconcat_test 4.3.6 {
   284    UPDATE t2 SET a = 'a' WHERE c=1 AND b=2;
   285  } {
   286    DELETE FROM t2 WHERE c=1 AND b=2;
   287  } {
   288    {DELETE t2 0 .XX. {{} {} i 2 i 1 {} {}} {}}
   289  }
   290  
   291  # DELETE + INSERT
   292  do_patchconcat_test 4.3.7 {
   293    DELETE FROM t2 WHERE b=1;
   294  } {
   295    INSERT INTO t2 VALUES('x', 1, 2, '.');
   296  } {
   297    {DELETE t2 0 .XX. {{} {} i 1 i 1 {} {}} {}} 
   298    {UPDATE t2 0 .XX. {{} {} i 1 i 2 {} {}} {t x {} {} {} {} t .}}
   299  }
   300  
   301  # DELETE + UPDATE
   302  do_patchconcat_test 4.3.8 -revert {
   303    DELETE FROM t2 WHERE b=1 AND c=2;
   304  } {
   305    UPDATE t2 SET a=5 WHERE b=1 AND c=2;
   306  } {
   307    {DELETE t2 0 .XX. {{} {} i 1 i 2 {} {}} {}} 
   308  }
   309  
   310  # DELETE + UPDATE
   311  do_patchconcat_test 4.3.9 -revert {
   312    DELETE FROM t2 WHERE b=1 AND c=2;
   313  } {
   314    DELETE FROM t2 WHERE b=1;
   315  } {
   316    {DELETE t2 0 .XX. {{} {} i 1 i 1 {} {}} {}} 
   317    {DELETE t2 0 .XX. {{} {} i 1 i 2 {} {}} {}} 
   318  }
   319  
   320  #-------------------------------------------------------------------------
   321  # More rigorous testing of the _patchset(), _apply and _concat() APIs.
   322  #
   323  # The inputs to each test are a populate database and a list of DML 
   324  # statements. This test determines that the final database is the same
   325  # if:
   326  # 
   327  #   1) the statements are executed directly on the database.
   328  #
   329  #   2) a single patchset is collected while executing the statements and
   330  #      then applied to a copy of the original database file.
   331  #
   332  #   3) individual patchsets are collected for statement while executing
   333  #      them and concatenated together before being applied to a copy of
   334  #      the original database. The concatenation is done in a couple of
   335  #      different ways - linear, pairwise etc.
   336  #
   337  # All tests, as it happens, are run with both changesets and patchsets.
   338  # But the focus is on patchset capabilities.
   339  #
   340  
   341  # Return a checksum of the contents of the database file. Implicit IPK
   342  # columns are not included in the checksum - just modifying rowids does
   343  # not change the database checksum.
   344  #
   345  proc databasecksum {db} {
   346    set alltab [$db eval {SELECT name FROM sqlite_master WHERE type='table'}]
   347    foreach tab $alltab {
   348      $db eval "SELECT * FROM $tab LIMIT 1" res { }
   349      set slist [list]
   350      foreach col [lsort $res(*)] {
   351        lappend slist "quote($col)"
   352      }
   353      set sql "SELECT [join $slist ,] FROM $tab"
   354      append txt "[lsort [$db eval $sql]]\n"
   355    }
   356    return [md5 $txt]
   357  }
   358  
   359  proc do_patchset_test {tn tstcmd lSql} {
   360    if {$tstcmd != "patchset" && $tstcmd != "changeset"} {
   361      error "have $tstcmd: must be patchset or changeset"
   362    }
   363  
   364    foreach fname {test.db2 test.db3 test.db4 test.db5} {
   365      forcedelete $fname
   366      forcecopy test.db $fname
   367    }
   368  
   369    # Execute the SQL statements on [db]. Collect a patchset for each 
   370    # individual statement, as well as a single patchset for the entire 
   371    # operation.
   372    sqlite3session S db main
   373    S attach *
   374    foreach sql $lSql { 
   375      sqlite3session T db main
   376      T attach *
   377      db eval $sql 
   378      lappend lPatch [T $tstcmd]
   379      T delete
   380    }
   381    set patchset [S $tstcmd]
   382    S delete
   383  
   384    # Calculate a checksum for the final database.
   385    set cksum [databasecksum db]
   386  
   387    # 1. Apply the single large patchset to test.db2
   388    sqlite3 db2 test.db2
   389    sqlite3changeset_apply db2 $patchset noop
   390    uplevel [list do_test $tn.1 { databasecksum db2 } $cksum ]
   391    db2 close
   392    
   393    # 2. Apply each of the single-statement patchsets to test.db3
   394    sqlite3 db2 test.db3
   395    foreach p $lPatch {
   396      sqlite3changeset_apply db2 $p noop
   397    }
   398    uplevel [list do_test $tn.2 { databasecksum db2 } $cksum ]
   399    db2 close
   400  
   401    # 3. Concatenate all single-statement patchsets into a single large
   402    #    patchset, then apply it to test.db4.
   403    #
   404    sqlite3 db2 test.db4
   405    set big ""
   406    foreach p $lPatch {
   407      set big [sqlite3changeset_concat $big $p]
   408    }
   409    sqlite3changeset_apply db2 $big noop
   410    uplevel [list do_test $tn.3 { databasecksum db2 } $cksum ]
   411    db2 close
   412  
   413    # 4. Concatenate all single-statement patchsets pairwise into a single
   414    #    large patchset, then apply it to test.db5. Pairwise concatenation:
   415    #
   416    #         a b c d e f g h i j k
   417    #      -> {a b} {c d} {e f} {g h} {i j} k
   418    #      -> {a b c d} {e f g h} {i j k}
   419    #      -> {a b c d e f g h} {i j k}
   420    #      -> {a b c d e f g h i j k}
   421    #      -> APPLY!
   422    #
   423    sqlite3 db2 test.db5
   424    set L $lPatch
   425    while {[llength $L] > 1} {
   426      set O [list]
   427      for {set i 0} {$i < [llength $L]} {incr i 2} {
   428        if {$i==[llength $L]-1} {
   429          lappend O [lindex $L $i]
   430        } else {
   431          set i1 [expr $i+1]
   432          lappend O [sqlite3changeset_concat [lindex $L $i] [lindex $L $i1]]
   433        }
   434      }
   435      set L $O
   436    }
   437    sqlite3changeset_apply db2 [lindex $L 0] noop
   438    uplevel [list do_test $tn.4 { databasecksum db2 } $cksum ]
   439    db2 close
   440  }
   441  
   442  proc do_patchset_changeset_test {tn initsql args} {
   443    foreach tstcmd {patchset changeset} {
   444      reset_db
   445      execsql $initsql
   446      set x 0
   447      foreach sql $args {
   448        incr x
   449        set lSql [split $sql ";"]
   450        uplevel [list do_patchset_test $tn.$tstcmd.$x $tstcmd $lSql]
   451      }
   452    }
   453  }
   454  
   455  do_patchset_changeset_test 5.1 {
   456    CREATE TABLE t1(a PRIMARY KEY, b, c);
   457    INSERT INTO t1 VALUES(1, 2, 3);
   458  } {
   459    INSERT INTO t1 VALUES(4, 5, 6);
   460    DELETE FROM t1 WHERE a=1;
   461  } {
   462    INSERT INTO t1 VALUES(7, 8, 9);
   463    UPDATE t1 SET c = 5;
   464    INSERT INTO t1 VALUES(10, 11, 12);
   465    UPDATE t1 SET c = 6;
   466    INSERT INTO t1 VALUES(13, 14, 15);
   467  } {
   468    UPDATE t1 SET c=c+1;
   469    DELETE FROM t1 WHERE (a%2);
   470  } 
   471  
   472  do_patchset_changeset_test 5.2 {
   473    CREATE TABLE t1(a PRIMARY KEY, b, c);
   474    CREATE TABLE t2(a, b, c, d, PRIMARY KEY(c, b));
   475  } {
   476    INSERT INTO t1 VALUES(x'00', 0, 'zero');
   477    INSERT INTO t1 VALUES(x'01', 1, 'one');
   478    INSERT INTO t1 VALUES(x'02', 4, 'four');
   479    INSERT INTO t1 VALUES(x'03', 9, 'nine');
   480    INSERT INTO t1 VALUES(x'04', 16, 'sixteen');
   481    INSERT INTO t1 VALUES(x'05', 25, 'twenty-five');
   482  } {
   483    UPDATE t1 SET a = b WHERE b<=4;
   484    INSERT INTO t2 SELECT NULL, * FROM t1;
   485    DELETE FROM t1 WHERE b=25;
   486  } {
   487    DELETE FROM t2;
   488    INSERT INTO t2 SELECT NULL, * FROM t1;
   489    DELETE FROM t1;
   490    INSERT INTO t1 SELECT b, c, d FROM t2;
   491    UPDATE t1 SET b = b+1;
   492    UPDATE t1 SET b = b+1;
   493    UPDATE t1 SET b = b+1;
   494  }
   495  
   496  set initsql { CREATE TABLE t1(a, b, c, PRIMARY KEY(c, b)); }
   497  for {set i 0} {$i < 1000} {incr i} {
   498    append insert "INSERT INTO t1 VALUES($i, $i, $i);"
   499    append delete "DELETE FROM t1 WHERE b=$i;"
   500  }
   501  do_patchset_changeset_test 5.3 \
   502    $initsql $insert $delete     \
   503    $insert $delete              \
   504    "$insert $delete"            \
   505    $delete
   506  
   507  
   508  finish_test