diff options
Diffstat (limited to '')
-rw-r--r-- | test/dbstatus.test | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/test/dbstatus.test b/test/dbstatus.test new file mode 100644 index 0000000..8d5d834 --- /dev/null +++ b/test/dbstatus.test @@ -0,0 +1,457 @@ +# 2010 March 10 +# +# 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 the sqlite3_db_status() function +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix dbstatus + +ifcapable !compound { + finish_test + return +} + +# Memory statistics must be enabled for this test. +db close +sqlite3_shutdown +sqlite3_config_memstatus 1 +sqlite3_config_uri 1 +sqlite3_initialize +sqlite3 db test.db + + +# Make sure sqlite3_db_config() and sqlite3_db_status are working. +# +unset -nocomplain PAGESZ +unset -nocomplain BASESZ +do_test dbstatus-1.1 { + db close + sqlite3 db :memory: + db eval { + CREATE TABLE t1(x); + } + set sz1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1] + db eval { + CREATE TABLE t2(y); + } + set sz2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1] + set ::PAGESZ [expr {$sz2-$sz1}] + set ::BASESZ [expr {$sz1-$::PAGESZ}] + expr {$::PAGESZ>1024 && $::PAGESZ<1300} +} {1} +do_test dbstatus-1.2 { + db eval { + INSERT INTO t1 VALUES(zeroblob(9000)); + } + lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1 +} [expr {$BASESZ + 10*$PAGESZ}] + + +proc lookaside {db} { + expr { $::lookaside_buffer_size * + [lindex [sqlite3_db_status $db SQLITE_DBSTATUS_LOOKASIDE_USED 0] 1] + } +} + +ifcapable stat4 { + set STAT3 1 +} else { + set STAT3 0 +} + +#--------------------------------------------------------------------------- +# Run the dbstatus-2 and dbstatus-3 tests with several of different +# lookaside buffer sizes. +# +foreach ::lookaside_buffer_size {0 64 120} { + ifcapable malloc_usable_size break + + # Do not run any of these tests if there is SQL configured to run + # as part of the [sqlite3] command. This prevents the script from + # configuring the size of the lookaside buffer after [sqlite3] has + # returned. + if {[presql] != ""} break + + #------------------------------------------------------------------------- + # Tests for SQLITE_DBSTATUS_SCHEMA_USED. + # + # Each test in the following block works as follows. Each test uses a + # different database schema. + # + # 1. Open a connection to an empty database. Disable statement caching. + # + # 2. Execute the SQL to create the database schema. Measure the total + # heap and lookaside memory allocated by SQLite, and the memory + # allocated for the database schema according to sqlite3_db_status(). + # + # 3. Drop all tables in the database schema. Measure the total memory + # and the schema memory again. + # + # 4. Repeat step 2. + # + # 5. Repeat step 3. + # + # Then test that: + # + # a) The difference in schema memory quantities in steps 2 and 3 is the + # same as the difference in total memory in steps 2 and 3. + # + # b) Step 4 reports the same amount of schema and total memory used as + # in step 2. + # + # c) Step 5 reports the same amount of schema and total memory used as + # in step 3. + # + foreach {tn schema} { + 1 { CREATE TABLE t1(a, b) } + 2 { CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1, c UNIQUE) } + 3 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + } + 4 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE TRIGGER AFTER INSERT ON t1 BEGIN + INSERT INTO t2 VALUES(new.a, new.b); + SELECT * FROM t1, t2 WHERE a=c AND b=d GROUP BY b HAVING a>5 ORDER BY a; + END; + } + 5 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE VIEW v1 AS SELECT * FROM t1 UNION SELECT * FROM t2; + } + 6k { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a); + CREATE INDEX i2 ON t1(a,b); + CREATE INDEX i3 ON t1(b,b); + INSERT INTO t1 VALUES(randomblob(20), randomblob(25)); + INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; + INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; + INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; + ANALYZE; + } + 7 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE VIEW v1 AS + SELECT * FROM t1 + UNION + SELECT * FROM t2 + UNION ALL + SELECT c||b, d||a FROM t2 LEFT OUTER JOIN t1 GROUP BY c, d + ORDER BY 1, 2 + ; + CREATE TRIGGER tr1 INSTEAD OF INSERT ON v1 BEGIN + SELECT * FROM v1; + UPDATE t1 SET a=5, b=(SELECT c FROM t2); + END; + SELECT * FROM v1; + } + 8x { + CREATE TABLE t1(a, b, UNIQUE(a, b)); + CREATE VIRTUAL TABLE t2 USING echo(t1); + } + } { + set tn "$::lookaside_buffer_size-$tn" + + # Step 1. + db close + forcedelete test.db + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 $::lookaside_buffer_size 500 + db cache size 0 + + catch { register_echo_module db } + ifcapable !vtab { if {[string match *x $tn]} continue } + + # Step 2. + execsql $schema + set nAlloc1 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc1 [lookaside db] + set nSchema1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + + # Step 3. + drop_all_tables + set nAlloc2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc2 [lookaside db] + set nSchema2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + + # Step 4. + execsql $schema + set nAlloc3 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc3 [lookaside db] + set nSchema3 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + + # Step 5. + drop_all_tables + set nAlloc4 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc4 [lookaside db] + set nSchema4 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + set nFree [expr {$nAlloc1-$nAlloc2}] + + # Tests for which the test name ends in an "k" report slightly less + # memory than is actually freed when all schema items are finalized. + # This is because memory allocated by KeyInfo objects is no longer + # counted as "schema memory". + # + # Tests for which the test name ends in an "x" report slightly less + # memory than is actually freed when all schema items are finalized. + # This is because memory allocated by virtual table implementations + # for any reason is not counted as "schema memory". + # + # Additionally, in auto-vacuum mode, dropping tables and indexes causes + # the page-cache to shrink. So the amount of memory freed is always + # much greater than just that reported by DBSTATUS_SCHEMA_USED in this + # case. + # + # Some of the memory used for sqlite_stat4 is unaccounted for by + # dbstatus. + # + # Finally, on osx the estimate of memory used by the schema may be + # slightly low. + # + if {[string match *k $tn] + || [string match *x $tn] || $AUTOVACUUM + || ([string match *y $tn] && $STAT3) + || ($::tcl_platform(os) == "Darwin") + } { + do_test dbstatus-2.$tn.ax { expr {($nSchema1-$nSchema2)<=$nFree} } 1 + } else { + do_test dbstatus-2.$tn.a { expr {$nSchema1-$nSchema2} } $nFree + } + + do_test dbstatus-2.$tn.b { list $nAlloc1 $nSchema1 } "$nAlloc3 $nSchema3" + do_test dbstatus-2.$tn.c { list $nAlloc2 $nSchema2 } "$nAlloc4 $nSchema4" + } + + #------------------------------------------------------------------------- + # Tests for SQLITE_DBSTATUS_STMT_USED. + # + # Each test in the following block works as follows. Each test uses a + # different database schema. + # + # 1. Open a connection to an empty database. Initialized the database + # schema. + # + # 2. Prepare a bunch of SQL statements. Measure the total heap and + # lookaside memory allocated by SQLite, and the memory allocated + # for the prepared statements according to sqlite3_db_status(). + # + # 3. Finalize all prepared statements. Measure the total memory + # and the prepared statement memory again. + # + # 4. Repeat step 2. + # + # 5. Repeat step 3. + # + # Then test that: + # + # a) The difference in schema memory quantities in steps 2 and 3 is the + # same as the difference in total memory in steps 2 and 3. + # + # b) Step 4 reports the same amount of schema and total memory used as + # in step 2. + # + # c) Step 5 reports the same amount of schema and total memory used as + # in step 3. + # + foreach {tn schema statements} { + 1 { CREATE TABLE t1(a, b) } { + SELECT * FROM t1; + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 SELECT * FROM t1; + UPDATE t1 SET a=5; + DELETE FROM t1; + } + 2 { + PRAGMA recursive_triggers = 1; + CREATE TABLE t1(a, b); + CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN + INSERT INTO t1 VALUES(new.a-1, new.b); + END; + } { + INSERT INTO t1 VALUES(5, 'x'); + } + 3 { + PRAGMA recursive_triggers = 1; + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN + INSERT INTO t2 VALUES(new.a-1, new.b); + END; + CREATE TRIGGER tr2 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN + INSERT INTO t1 VALUES(new.a-1, new.b); + END; + } { + INSERT INTO t1 VALUES(10, 'x'); + } + 4 { + CREATE TABLE t1(a, b); + } { + SELECT count(*) FROM t1 WHERE upper(a)='ABC'; + } + 5x { + CREATE TABLE t1(a, b UNIQUE); + CREATE VIRTUAL TABLE t2 USING echo(t1); + } { + SELECT count(*) FROM t2; + SELECT * FROM t2 WHERE b>5; + SELECT * FROM t2 WHERE b='abcdefg'; + } + } { + set tn "$::lookaside_buffer_size-$tn" + + # Step 1. + db close + forcedelete test.db + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 $::lookaside_buffer_size 500 + db cache size 1000 + + catch { register_echo_module db } + ifcapable !vtab { if {[string match *x $tn]} continue } + + execsql $schema + db cache flush + + # Step 2. + execsql $statements + set nAlloc1 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc1 [lookaside db] + set nStmt1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + execsql $statements + + # Step 3. + db cache flush + set nAlloc2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc2 [lookaside db] + set nStmt2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + + # Step 3. + execsql $statements + set nAlloc3 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc3 [lookaside db] + set nStmt3 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + execsql $statements + + # Step 4. + db cache flush + set nAlloc4 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc4 [lookaside db] + set nStmt4 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + + set nFree [expr {$nAlloc1-$nAlloc2}] + + do_test dbstatus-3.$tn.a { expr $nStmt2 } {0} + + # Tests for which the test name ends in an "x" report slightly less + # memory than is actually freed when all statements are finalized. + # This is because a small amount of memory allocated by a virtual table + # implementation using sqlite3_mprintf() is technically considered + # external and so is not counted as "statement memory". + # +#puts "$nStmt1 $nFree" + if {[string match *x $tn]} { + do_test dbstatus-3.$tn.bx { expr $nStmt1<=$nFree } {1} + } else { + do_test dbstatus-3.$tn.b { expr $nStmt1==$nFree } {1} + } + + do_test dbstatus-3.$tn.c { list $nAlloc1 $nStmt1 } [list $nAlloc3 $nStmt3] + do_test dbstatus-3.$tn.d { list $nAlloc2 $nStmt2 } [list $nAlloc4 $nStmt4] + } +} + +#------------------------------------------------------------------------- +# The following tests focus on DBSTATUS_CACHE_USED_SHARED +# +ifcapable shared_cache { + if {([permutation]=="memsys3" + || [permutation]=="memsys5" + || $::tcl_platform(os)=="Linux") && ![sqlite3 -has-codec]} { + proc do_cacheused_test {tn db res} { + set cu [sqlite3_db_status $db SQLITE_DBSTATUS_CACHE_USED 0] + set pcu [sqlite3_db_status $db SQLITE_DBSTATUS_CACHE_USED_SHARED 0] + set cu [lindex $cu 1] + set pcu [lindex $pcu 1] + uplevel [list do_test $tn [list list $cu $pcu] "#/$res/"] + } + reset_db + sqlite3 db file:test.db?cache=shared + + do_execsql_test 4.0 { + PRAGMA auto_vacuum=NONE; + CREATE TABLE t1(a, b, c); + INSERT INTO t1 VALUES(1, 2, 3); + } + do_cacheused_test 4.0.1 db { 4568 4568 } + do_execsql_test 4.1 { + CREATE TEMP TABLE tt(a, b, c); + INSERT INTO tt VALUES(1, 2, 3); + } + do_cacheused_test 4.1.1 db { 9000 9000 } + + sqlite3 db2 file:test.db?cache=shared + do_cacheused_test 4.2.1 db2 { 4568 2284 } + do_cacheused_test 4.2.2 db { 9000 6716 } + db close + do_cacheused_test 4.2.3 db2 { 4568 4568 } + sqlite3 db file:test.db?cache=shared + do_cacheused_test 4.2.4 db2 { 4568 2284 } + db2 close + } +} + +#------------------------------------------------------------------------- +# Test that passing an out-of-range value to sqlite3_stmt_status does +# not cause a crash. +reset_db +do_execsql_test 5.0 { + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); +} + +do_test 5.1 { + set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy] + sqlite3_step $::stmt + sqlite3_step $::stmt + sqlite3_step $::stmt + sqlite3_reset $::stmt +} {SQLITE_OK} + +ifcapable api_armor { + do_test 5.2 { sqlite3_stmt_status $::stmt -1 0 } 0 +} +do_test 5.3 { sqlite3_stmt_status $::stmt 0 0 } 0 +do_test 5.4 { + expr [sqlite3_stmt_status $::stmt 99 0]>0 +} 1 +foreach {tn id res} { + 1 SQLITE_STMTSTATUS_MEMUSED 1 + 2 SQLITE_STMTSTATUS_FULLSCAN_STEP 1 + 3 SQLITE_STMTSTATUS_SORT 0 + 4 SQLITE_STMTSTATUS_AUTOINDEX 0 + 5 SQLITE_STMTSTATUS_VM_STEP 1 + 6 SQLITE_STMTSTATUS_REPREPARE 0 + 7 SQLITE_STMTSTATUS_RUN 1 +} { +if {$tn==2} breakpoint + do_test 5.5.$tn { expr [sqlite3_stmt_status $::stmt $id 0]>0 } $res +} + +sqlite3_finalize $::stmt +finish_test |