1 # 2010 March 10 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 # Tests for the sqlite3_db_status() function 13 # 14 15 set testdir [file dirname $argv0] 16 source $testdir/tester.tcl 17 18 # Memory statistics must be enabled for this test. 19 db close 20 sqlite3_shutdown 21 sqlite3_config_memstatus 1 22 sqlite3_initialize 23 sqlite3 db test.db 24 25 26 # Make sure sqlite3_db_config() and sqlite3_db_status are working. 27 # 28 unset -nocomplain PAGESZ 29 unset -nocomplain BASESZ 30 do_test dbstatus-1.1 { 31 db close 32 sqlite3 db :memory: 33 db eval { 34 CREATE TABLE t1(x); 35 } 36 set sz1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1] 37 db eval { 38 CREATE TABLE t2(y); 39 } 40 set sz2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1] 41 set ::PAGESZ [expr {$sz2-$sz1}] 42 set ::BASESZ [expr {$sz1-$::PAGESZ}] 43 expr {$::PAGESZ>1024 && $::PAGESZ<1300} 44 } {1} 45 do_test dbstatus-1.2 { 46 db eval { 47 INSERT INTO t1 VALUES(zeroblob(9000)); 48 } 49 lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1 50 } [expr {$BASESZ + 10*$PAGESZ}] 51 52 53 proc lookaside {db} { 54 expr { $::lookaside_buffer_size * 55 [lindex [sqlite3_db_status $db SQLITE_DBSTATUS_LOOKASIDE_USED 0] 1] 56 } 57 } 58 59 #--------------------------------------------------------------------------- 60 # Run the dbstatus-2 and dbstatus-3 tests with several of different 61 # lookaside buffer sizes. 62 # 63 foreach ::lookaside_buffer_size {0 64 120} { 64 65 # Do not run any of these tests if there is SQL configured to run 66 # as part of the [sqlite3] command. This prevents the script from 67 # configuring the size of the lookaside buffer after [sqlite3] has 68 # returned. 69 if {[presql] != ""} break 70 71 #------------------------------------------------------------------------- 72 # Tests for SQLITE_DBSTATUS_SCHEMA_USED. 73 # 74 # Each test in the following block works as follows. Each test uses a 75 # different database schema. 76 # 77 # 1. Open a connection to an empty database. Disable statement caching. 78 # 79 # 2. Execute the SQL to create the database schema. Measure the total 80 # heap and lookaside memory allocated by SQLite, and the memory 81 # allocated for the database schema according to sqlite3_db_status(). 82 # 83 # 3. Drop all tables in the database schema. Measure the total memory 84 # and the schema memory again. 85 # 86 # 4. Repeat step 2. 87 # 88 # 5. Repeat step 3. 89 # 90 # Then test that: 91 # 92 # a) The difference in schema memory quantities in steps 2 and 3 is the 93 # same as the difference in total memory in steps 2 and 3. 94 # 95 # b) Step 4 reports the same amount of schema and total memory used as 96 # in step 2. 97 # 98 # c) Step 5 reports the same amount of schema and total memory used as 99 # in step 3. 100 # 101 foreach {tn schema} { 102 1 { CREATE TABLE t1(a, b) } 103 2 { CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1, c UNIQUE) } 104 3 { 105 CREATE TABLE t1(a, b); 106 CREATE INDEX i1 ON t1(a, b); 107 } 108 4 { 109 CREATE TABLE t1(a, b); 110 CREATE TABLE t2(c, d); 111 CREATE TRIGGER AFTER INSERT ON t1 BEGIN 112 INSERT INTO t2 VALUES(new.a, new.b); 113 SELECT * FROM t1, t2 WHERE a=c AND b=d GROUP BY b HAVING a>5 ORDER BY a; 114 END; 115 } 116 5 { 117 CREATE TABLE t1(a, b); 118 CREATE TABLE t2(c, d); 119 CREATE VIEW v1 AS SELECT * FROM t1 UNION SELECT * FROM t2; 120 } 121 6 { 122 CREATE TABLE t1(a, b); 123 CREATE INDEX i1 ON t1(a); 124 CREATE INDEX i2 ON t1(a,b); 125 CREATE INDEX i3 ON t1(b,b); 126 INSERT INTO t1 VALUES(randomblob(20), randomblob(25)); 127 INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; 128 INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; 129 INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; 130 ANALYZE; 131 } 132 7 { 133 CREATE TABLE t1(a, b); 134 CREATE TABLE t2(c, d); 135 CREATE VIEW v1 AS 136 SELECT * FROM t1 137 UNION 138 SELECT * FROM t2 139 UNION ALL 140 SELECT c||b, d||a FROM t2 LEFT OUTER JOIN t1 GROUP BY c, d 141 ORDER BY 1, 2 142 ; 143 CREATE TRIGGER tr1 INSTEAD OF INSERT ON v1 BEGIN 144 SELECT * FROM v1; 145 UPDATE t1 SET a=5, b=(SELECT c FROM t2); 146 END; 147 SELECT * FROM v1; 148 } 149 8x { 150 CREATE TABLE t1(a, b, UNIQUE(a, b)); 151 CREATE VIRTUAL TABLE t2 USING echo(t1); 152 } 153 } { 154 set tn "$::lookaside_buffer_size-$tn" 155 156 # Step 1. 157 db close 158 file delete -force test.db 159 sqlite3 db test.db 160 sqlite3_db_config_lookaside db 0 $::lookaside_buffer_size 500 161 db cache size 0 162 163 catch { register_echo_module db } 164 ifcapable !vtab { if {[string match *x $tn]} continue } 165 166 # Step 2. 167 execsql $schema 168 set nAlloc1 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 169 incr nAlloc1 [lookaside db] 170 set nSchema1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] 171 172 # Step 3. 173 drop_all_tables 174 set nAlloc2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 175 incr nAlloc2 [lookaside db] 176 set nSchema2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] 177 178 # Step 4. 179 execsql $schema 180 set nAlloc3 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 181 incr nAlloc3 [lookaside db] 182 set nSchema3 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] 183 184 # Step 5. 185 drop_all_tables 186 set nAlloc4 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 187 incr nAlloc4 [lookaside db] 188 set nSchema4 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] 189 set nFree [expr {$nAlloc1-$nAlloc2}] 190 191 # Tests for which the test name ends in an "x" report slightly less 192 # memory than is actually freed when all schema items are finalized. 193 # This is because memory allocated by virtual table implementations 194 # for any reason is not counted as "schema memory". 195 # 196 # Additionally, in auto-vacuum mode, dropping tables and indexes causes 197 # the page-cache to shrink. So the amount of memory freed is always 198 # much greater than just that reported by DBSTATUS_SCHEMA_USED in this 199 # case. 200 # 201 if {[string match *x $tn] || $AUTOVACUUM} { 202 do_test dbstatus-2.$tn.ax { expr {($nSchema1-$nSchema2)<=$nFree} } 1 203 } else { 204 do_test dbstatus-2.$tn.a { expr {$nSchema1-$nSchema2} } $nFree 205 } 206 207 do_test dbstatus-2.$tn.b { list $nAlloc1 $nSchema1 } "$nAlloc3 $nSchema3" 208 do_test dbstatus-2.$tn.c { list $nAlloc2 $nSchema2 } "$nAlloc4 $nSchema4" 209 } 210 211 #------------------------------------------------------------------------- 212 # Tests for SQLITE_DBSTATUS_STMT_USED. 213 # 214 # Each test in the following block works as follows. Each test uses a 215 # different database schema. 216 # 217 # 1. Open a connection to an empty database. Initialized the database 218 # schema. 219 # 220 # 2. Prepare a bunch of SQL statements. Measure the total heap and 221 # lookaside memory allocated by SQLite, and the memory allocated 222 # for the prepared statements according to sqlite3_db_status(). 223 # 224 # 3. Finalize all prepared statements Measure the total memory 225 # and the prepared statement memory again. 226 # 227 # 4. Repeat step 2. 228 # 229 # 5. Repeat step 3. 230 # 231 # Then test that: 232 # 233 # a) The difference in schema memory quantities in steps 2 and 3 is the 234 # same as the difference in total memory in steps 2 and 3. 235 # 236 # b) Step 4 reports the same amount of schema and total memory used as 237 # in step 2. 238 # 239 # c) Step 5 reports the same amount of schema and total memory used as 240 # in step 3. 241 # 242 foreach {tn schema statements} { 243 1 { CREATE TABLE t1(a, b) } { 244 SELECT * FROM t1; 245 INSERT INTO t1 VALUES(1, 2); 246 INSERT INTO t1 SELECT * FROM t1; 247 UPDATE t1 SET a=5; 248 DELETE FROM t1; 249 } 250 2 { 251 PRAGMA recursive_triggers = 1; 252 CREATE TABLE t1(a, b); 253 CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN 254 INSERT INTO t1 VALUES(new.a-1, new.b); 255 END; 256 } { 257 INSERT INTO t1 VALUES(5, 'x'); 258 } 259 3 { 260 PRAGMA recursive_triggers = 1; 261 CREATE TABLE t1(a, b); 262 CREATE TABLE t2(a, b); 263 CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN 264 INSERT INTO t2 VALUES(new.a-1, new.b); 265 END; 266 CREATE TRIGGER tr2 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN 267 INSERT INTO t1 VALUES(new.a-1, new.b); 268 END; 269 } { 270 INSERT INTO t1 VALUES(10, 'x'); 271 } 272 4 { 273 CREATE TABLE t1(a, b); 274 } { 275 SELECT count(*) FROM t1 WHERE upper(a)='ABC'; 276 } 277 5x { 278 CREATE TABLE t1(a, b UNIQUE); 279 CREATE VIRTUAL TABLE t2 USING echo(t1); 280 } { 281 SELECT count(*) FROM t2; 282 SELECT * FROM t2 WHERE b>5; 283 SELECT * FROM t2 WHERE b='abcdefg'; 284 } 285 } { 286 set tn "$::lookaside_buffer_size-$tn" 287 288 # Step 1. 289 db close 290 file delete -force test.db 291 sqlite3 db test.db 292 sqlite3_db_config_lookaside db 0 $::lookaside_buffer_size 500 293 db cache size 1000 294 295 catch { register_echo_module db } 296 ifcapable !vtab { if {[string match *x $tn]} continue } 297 298 execsql $schema 299 db cache flush 300 301 # Step 2. 302 execsql $statements 303 set nAlloc1 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 304 incr nAlloc1 [lookaside db] 305 set nStmt1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] 306 execsql $statements 307 308 # Step 3. 309 db cache flush 310 set nAlloc2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 311 incr nAlloc2 [lookaside db] 312 set nStmt2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] 313 314 # Step 3. 315 execsql $statements 316 set nAlloc3 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 317 incr nAlloc3 [lookaside db] 318 set nStmt3 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] 319 execsql $statements 320 321 # Step 4. 322 db cache flush 323 set nAlloc4 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] 324 incr nAlloc4 [lookaside db] 325 set nStmt4 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] 326 327 set nFree [expr {$nAlloc1-$nAlloc2}] 328 329 do_test dbstatus-3.$tn.a { expr $nStmt2 } {0} 330 331 # Tests for which the test name ends in an "x" report slightly less 332 # memory than is actually freed when all statements are finalized. 333 # This is because a small amount of memory allocated by a virtual table 334 # implementation using sqlite3_mprintf() is technically considered 335 # external and so is not counted as "statement memory". 336 # 337 #puts "$nStmt1 $nFree" 338 if {[string match *x $tn]} { 339 do_test dbstatus-3.$tn.bx { expr $nStmt1<=$nFree } {1} 340 } else { 341 do_test dbstatus-3.$tn.b { expr $nStmt1==$nFree } {1} 342 } 343 344 do_test dbstatus-3.$tn.c { list $nAlloc1 $nStmt1 } [list $nAlloc3 $nStmt3] 345 do_test dbstatus-3.$tn.d { list $nAlloc2 $nStmt2 } [list $nAlloc4 $nStmt4] 346 } 347 } 348 349 finish_test 350