mirror of
				https://github.com/tursodatabase/libsql.git
				synced 2025-10-31 16:56:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			172 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| # 2020 June 30
 | |
| #
 | |
| # 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.
 | |
| #
 | |
| #***********************************************************************
 | |
| # This file test the busy handler
 | |
| #
 | |
| # TESTRUNNER: slow
 | |
| 
 | |
| 
 | |
| set testdir [file dirname $argv0]
 | |
| source $testdir/tester.tcl
 | |
| source $testdir/lock_common.tcl
 | |
| set testprefix busy2
 | |
| 
 | |
| do_multiclient_test tn {
 | |
|   do_test 1.$tn.0 {
 | |
|     sql2 {
 | |
|       CREATE TABLE t1(a, b);
 | |
|       PRAGMA journal_mode = wal;
 | |
|       INSERT INTO t1 VALUES('A', 'B');
 | |
|     }
 | |
|   } {wal}
 | |
| 
 | |
|   do_test 1.$tn.1 {
 | |
|     code1 { db timeout 1000 }
 | |
|     sql1 { SELECT * FROM t1 }
 | |
|   } {A B}
 | |
| 
 | |
|   do_test 1.$tn.2 {
 | |
|     sql2 {
 | |
|       BEGIN;
 | |
|         INSERT INTO t1 VALUES('C', 'D');
 | |
|     }
 | |
|   } {}
 | |
| 
 | |
|   do_test 1.$tn.3 {
 | |
|     set us [lindex [time { catch { sql1 { BEGIN EXCLUSIVE } } }] 0]
 | |
|     expr {$us>950000 && $us<1500000}
 | |
|   } {1}
 | |
| 
 | |
|   do_test 1.$tn.4 {
 | |
|     sql2 {
 | |
|       COMMIT
 | |
|     }
 | |
|   } {}
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------
 | |
| 
 | |
| do_multiclient_test tn {
 | |
|   # Make the db a WAL mode db. And add a table and a row to it. Then open
 | |
|   # a second connection within process 1. Process 1 now has connections
 | |
|   # [db] and [db1.2], process 2 has connection [db2] only.
 | |
|   #
 | |
|   # Configure all connections to use a 1000 ms timeout.
 | |
|   #
 | |
|   do_test 2.$tn.0 {
 | |
|     code1 {
 | |
|       sqlite3 db1.2 test.db
 | |
|     }
 | |
|     sql1 {
 | |
|       PRAGMA auto_vacuum = off;
 | |
|       PRAGMA journal_mode = wal;
 | |
|       CREATE TABLE t1(a, b);
 | |
|       INSERT INTO t1 VALUES(1, 2);
 | |
|     }
 | |
|     code2 {
 | |
|       db2 timeout 1000
 | |
|     }
 | |
|     code1 {
 | |
|       db1.2 timeout 1000
 | |
|       db    timeout 1000
 | |
|       db1.2 eval {SELECT * FROM t1}
 | |
|     }
 | |
|   } {1 2}
 | |
| 
 | |
|   # Take a read lock with [db] in process 1.
 | |
|   #
 | |
|   do_test 2.$tn.1 {
 | |
|     sql1 {
 | |
|       BEGIN;
 | |
|         SELECT * FROM t1;
 | |
|     }
 | |
|   } {1 2}
 | |
| 
 | |
|   # Insert a row using [db2] in process 2. Then try a passive checkpoint.
 | |
|   # It fails to checkpoint the final frame (due to the readlock taken by
 | |
|   # [db]), and returns in less than 250ms.
 | |
|   do_test 2.$tn.2 {
 | |
|     sql2 { INSERT INTO t1 VALUES(3, 4) }
 | |
|     set us [lindex [time {
 | |
|       set res [code2 { db2 eval { PRAGMA wal_checkpoint } }]
 | |
|     }] 0]
 | |
|     list [expr $us < 250000] $res
 | |
|   } {1 {0 4 3}}
 | |
| 
 | |
|   # Now try a FULL checkpoint with [db2]. It returns SQLITE_BUSY. And takes
 | |
|   # over 950ms to do so.
 | |
|   do_test 2.$tn.3 {
 | |
|     set us [lindex [time {
 | |
|       set res [code2 { db2 eval { PRAGMA wal_checkpoint = FULL } }]
 | |
|     }] 0]
 | |
|     list [expr $us > 950000] $res
 | |
|   } {1 {1 4 3}}
 | |
| 
 | |
|   # Passive checkpoint with [db1.2] (process 1). No SQLITE_BUSY, returns
 | |
|   # in under 250ms.
 | |
|   do_test 2.$tn.4 {
 | |
|     set us [lindex [time {
 | |
|       set res [code1 { db1.2 eval { PRAGMA wal_checkpoint } }]
 | |
|     }] 0]
 | |
|     list [expr $us < 250000] $res
 | |
|   } {1 {0 4 3}}
 | |
| 
 | |
|   # Full checkpoint with [db1.2] (process 1). SQLITE_BUSY returned in
 | |
|   # a bit over 950ms.
 | |
|   do_test 2.$tn.5 {
 | |
|     set us [lindex [time {
 | |
|       set res [code1 { db1.2 eval { PRAGMA wal_checkpoint = FULL } }]
 | |
|     }] 0]
 | |
|     list [expr $us > 950000] $res
 | |
|   } {1 {1 4 3}}
 | |
| 
 | |
|   code1 {
 | |
|     db1.2 close
 | |
|   }
 | |
| }
 | |
| 
 | |
| #-------------------------------------------------------------------------
 | |
| # Check that even if the busy-handler fails (returns zero) within a
 | |
| # call to sqlite3_prepare() (or _v2(), or _v3()), it is still invoked
 | |
| # the next time an SQLITE_BUSY is encountered.
 | |
| #
 | |
| do_multiclient_test tn {
 | |
|   code1 {
 | |
|     set ::busy_called 0
 | |
|     proc busy {args} {
 | |
|       if {$::busy_called} { return 1 }
 | |
|       set ::busy_called 1
 | |
|       return 0
 | |
|     }
 | |
|     db busy busy
 | |
|   }
 | |
| 
 | |
|   do_test 3.$tn.1 {
 | |
|     sql2 {
 | |
|       CREATE TABLE t1(x);
 | |
|       BEGIN EXCLUSIVE;
 | |
|         INSERT INTO t1 VALUES('x');
 | |
|     }
 | |
|   } {}
 | |
| 
 | |
|   do_test 3.$tn.2 {
 | |
|     set ::busy_called 0
 | |
|     list [catch { sql1 { SELECT * FROM t1 } } msg] $::busy_called
 | |
|   } {1 1}
 | |
| 
 | |
|   do_test 3.$tn.3 {
 | |
|     set ::busy_called 0
 | |
|     list [catch { sql1 { SELECT * FROM t1 } } msg] $::busy_called
 | |
|   } {1 1}
 | |
| 
 | |
| }
 | |
| 
 | |
| finish_test
 |