| # 2015 July 26 |
| # |
| # The author disclaims copyright to this source code. In place of |
| # a legal notice, here is a blessing: |
| # |
| # May you do good and not evil. |
| # May you find forgiveness for yourself and forgive others. |
| # May you share freely, never taking more than you give. |
| # |
| #*********************************************************************** |
| # |
| # Tests for transactions started with BEGIN CONCURRENT. The tests in this |
| # file focus on testing that deferred page allocation works properly. |
| # |
| |
| set testdir [file dirname $argv0] |
| source $testdir/tester.tcl |
| source $testdir/lock_common.tcl |
| set ::testprefix concurrent3 |
| |
| if {$AUTOVACUUM} { finish_test ; return } |
| ifcapable !concurrent { |
| finish_test |
| return |
| } |
| |
| db close |
| sqlite3_shutdown |
| test_sqlite3_log xLog |
| proc xLog {error_code msg} { |
| # puts "$error_code: $msg" |
| # Enable the previous for debugging |
| } |
| reset_db |
| |
| proc create_schema {} { |
| db eval { |
| PRAGMA journal_mode = wal; |
| |
| CREATE TABLE t1(x, y); |
| CREATE TABLE t2(x, y); |
| CREATE TABLE t3(x, y); |
| CREATE TABLE t4(x, y); |
| |
| CREATE INDEX i1 ON t1(y, x); |
| CREATE INDEX i2 ON t2(y, x); |
| CREATE INDEX i3 ON t3(y, x); |
| CREATE INDEX i4 ON t4(y, x); |
| } |
| } |
| |
| proc do_sql_op {iTbl iOp} { |
| set db "db$iTbl" |
| |
| switch $iOp { |
| "i" { |
| set sql " |
| WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<10) |
| INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM cnt; |
| " |
| } |
| |
| "d" { |
| set sql " |
| DELETE FROM t$iTbl WHERE rowid IN ( |
| SELECT rowid FROM t$iTbl ORDER BY 1 ASC LIMIT 10 |
| ) |
| " |
| } |
| |
| "D" { |
| set sql " |
| DELETE FROM t$iTbl WHERE rowid IN ( |
| SELECT rowid FROM t$iTbl o WHERE ( |
| SELECT count(*) FROM t$iTbl i WHERE i.rowid<o.rowid |
| ) % 2 |
| ) |
| " |
| } |
| |
| "I" { |
| set sql " |
| INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM t$iTbl; |
| " |
| } |
| |
| default { |
| error "bad iOp parameter: $iOp" |
| } |
| } |
| |
| $db eval $sql |
| } |
| |
| |
| set DBLIST {db1 db2 db3 db4} |
| |
| create_schema |
| foreach {tn oplist} { |
| 1 {1i 2i 3i 4i} |
| 2 {1iii 2iii 3iii 4iii} |
| 3 {1d 2d 3d 4d} |
| . ----------------------- |
| 4 {1i} |
| 5 {1d 2i} |
| . ----------------------- |
| 6 {1iii 2iii 3iii 4iii} |
| 7 {1di 2id 3iii 4ddd} |
| 8 {1iii 2iii 3iii 4iii} |
| 9 {1D 2II} |
| 10 {1I 2D 3I 4D} |
| 11 {1III 3dddddd 4III} |
| } { |
| if {[string range $oplist 0 0]=="-"} { |
| reset_db |
| create_schema |
| continue |
| } |
| foreach db $DBLIST { sqlite3 $db test.db } |
| |
| do_test 1.$tn { |
| foreach db $DBLIST { $db eval "BEGIN CONCURRENT" } |
| |
| foreach op $oplist { |
| set iTbl [string range $op 0 0] |
| foreach char [split [string range $op 1 end] {}] { |
| do_sql_op $iTbl $char |
| } |
| } |
| |
| foreach db $DBLIST { $db eval "COMMIT" } |
| db eval {PRAGMA integrity_check} |
| } {ok} |
| |
| foreach db $DBLIST { |
| $db close |
| } |
| } |
| |
| #------------------------------------------------------------------------- |
| # |
| proc create_schema2 {} { |
| db eval { |
| PRAGMA journal_mode = wal; |
| CREATE TABLE t1(x INTEGER PRIMARY KEY, y); |
| CREATE INDEX i1 ON t1(y); |
| } |
| } |
| |
| proc randint {nMax} { |
| db eval {SELECT abs(random() % $nMax)} |
| } |
| |
| proc do_sql_op2 {db iOp} { |
| switch -- $iOp { |
| i { |
| # Insert 1 rows. |
| set r [randint 1000000000] |
| set ::rows($r) 1 |
| #puts "insert row $r" |
| $db eval { INSERT OR IGNORE INTO t1 VALUES($r, randomblob(50)); } |
| } |
| |
| d { |
| # Insert 1 row |
| set keys [array names ::rows] |
| set r [randint [llength $keys]] |
| set rowid [lindex $keys $r] |
| $db eval { DELETE FROM t1 WHERE x=$rowid } |
| unset ::rows($rowid) |
| } |
| } |
| } |
| |
| foreach {tn nRepeat oplist} { |
| - - ---------------------------- |
| 1 100 { 1iiiiiiiiii } |
| 2 100 { 1i 2d } |
| 3 100 { 1d 2i } |
| 4 50 { 1d 2i 3d } |
| 5 500 { 1i 2i 3i 4i } |
| 6 500 { 1i 2d 3d 4d } |
| } { |
| if {[string range $oplist 0 0]=="-"} { |
| array unset rows |
| reset_db |
| create_schema2 |
| continue |
| } |
| |
| foreach db $DBLIST { |
| sqlite3 $db test.db |
| set stats($db,0) 0 |
| set stats($db,1) 0 |
| } |
| array unset used |
| |
| do_test 2.$tn { |
| |
| for {set i 0} {$i < $nRepeat} {incr i} { |
| foreach db $DBLIST { $db eval "BEGIN CONCURRENT" } |
| |
| foreach op $oplist { |
| set iDb [string range $op 0 0] |
| set used(db$iDb) 1 |
| foreach char [split [string range $op 1 end] {}] { |
| do_sql_op2 "db$iDb" $char |
| } |
| } |
| |
| foreach db $DBLIST { |
| set rc [catch { $db eval COMMIT } msg] |
| if {$rc} { $db eval ROLLBACK } |
| incr stats($db,$rc) |
| } |
| set res [db eval {PRAGMA integrity_check}] |
| if {$res != "ok"} { puts "after $db $rc: $res" ; after 1000000 } |
| } |
| } {} |
| |
| foreach db $DBLIST { |
| $db close |
| } |
| # foreach k [lsort [array names used]] { |
| # puts "$k: $stats($k,0) committed, $stats($k,1) rolled back" |
| # } |
| } |
| |
| |
| |
| finish_test |