1 # 2007 August 21 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 # The focus of this file is testing some specific characteristics of the 13 # IO traffic generated by SQLite (making sure SQLite is not writing out 14 # more database pages than it has to, stuff like that). 15 # 16 17 set testdir [file dirname $argv0] 18 source $testdir/tester.tcl 19 20 db close 21 sqlite3_simulate_device 22 sqlite3 db test.db -vfs devsym 23 24 # Test summary: 25 # 26 # io-1.* - Test that quick-balance does not journal pages unnecessarily. 27 # 28 # io-2.* - Test the "atomic-write optimization". 29 # 30 # io-3.* - Test the IO traffic enhancements triggered when the 31 # IOCAP_SEQUENTIAL device capability flag is set (no 32 # fsync() calls on the journal file). 33 # 34 # io-4.* - Test the IO traffic enhancements triggered when the 35 # IOCAP_SAFE_APPEND device capability flag is set (fewer 36 # fsync() calls on the journal file, no need to set nRec 37 # field in the single journal header). 38 # 39 # io-5.* - Test that the default page size is selected and used 40 # correctly. 41 # 42 43 set ::nWrite 0 44 proc nWrite {db} { 45 set bt [btree_from_db $db] 46 db_enter $db 47 array set stats [btree_pager_stats $bt] 48 db_leave $db 49 set res [expr $stats(write) - $::nWrite] 50 set ::nWrite $stats(write) 51 set res 52 } 53 54 set ::nSync 0 55 proc nSync {} { 56 set res [expr {$::sqlite_sync_count - $::nSync}] 57 set ::nSync $::sqlite_sync_count 58 set res 59 } 60 61 do_test io-1.1 { 62 execsql { 63 PRAGMA auto_vacuum = OFF; 64 PRAGMA page_size = 1024; 65 CREATE TABLE abc(a,b); 66 } 67 nWrite db 68 } {2} 69 70 # Insert into the table 4 records of aproximately 240 bytes each. 71 # This should completely fill the root-page of the table. Each 72 # INSERT causes 2 db pages to be written - the root-page of "abc" 73 # and page 1 (db change-counter page). 74 do_test io-1.2 { 75 set ret [list] 76 execsql { INSERT INTO abc VALUES(1,randstr(230,230)); } 77 lappend ret [nWrite db] 78 execsql { INSERT INTO abc VALUES(2,randstr(230,230)); } 79 lappend ret [nWrite db] 80 execsql { INSERT INTO abc VALUES(3,randstr(230,230)); } 81 lappend ret [nWrite db] 82 execsql { INSERT INTO abc VALUES(4,randstr(230,230)); } 83 lappend ret [nWrite db] 84 } {2 2 2 2} 85 86 # Insert another 240 byte record. This causes two leaf pages 87 # to be added to the root page of abc. 4 pages in total 88 # are written to the db file - the two leaf pages, the root 89 # of abc and the change-counter page. 90 do_test io-1.3 { 91 execsql { INSERT INTO abc VALUES(5,randstr(230,230)); } 92 nWrite db 93 } {4} 94 95 # Insert another 3 240 byte records. After this, the tree consists of 96 # the root-node, which is close to empty, and two leaf pages, both of 97 # which are full. 98 do_test io-1.4 { 99 set ret [list] 100 execsql { INSERT INTO abc VALUES(6,randstr(230,230)); } 101 lappend ret [nWrite db] 102 execsql { INSERT INTO abc VALUES(7,randstr(230,230)); } 103 lappend ret [nWrite db] 104 execsql { INSERT INTO abc VALUES(8,randstr(230,230)); } 105 lappend ret [nWrite db] 106 } {2 2 2} 107 108 # This insert should use the quick-balance trick to add a third leaf 109 # to the b-tree used to store table abc. It should only be necessary to 110 # write to 3 pages to do this: the change-counter, the root-page and 111 # the new leaf page. 112 do_test io-1.5 { 113 execsql { INSERT INTO abc VALUES(9,randstr(230,230)); } 114 nWrite db 115 } {3} 116 117 ifcapable atomicwrite { 118 119 #---------------------------------------------------------------------- 120 # Test cases io-2.* test the atomic-write optimization. 121 # 122 do_test io-2.1 { 123 execsql { DELETE FROM abc; VACUUM; } 124 } {} 125 126 # Clear the write and sync counts. 127 nWrite db ; nSync 128 129 # The following INSERT updates 2 pages and requires 4 calls to fsync(): 130 # 131 # 1) The directory in which the journal file is created, 132 # 2) The journal file (to sync the page data), 133 # 3) The journal file (to sync the journal file header), 134 # 4) The database file. 135 # 136 do_test io-2.2 { 137 execsql { INSERT INTO abc VALUES(1, 2) } 138 list [nWrite db] [nSync] 139 } {2 4} 140 141 # Set the device-characteristic mask to include the SQLITE_IOCAP_ATOMIC, 142 # then do another INSERT similar to the one in io-2.2. This should 143 # only write 1 page and require a single fsync(). 144 # 145 # The single fsync() is the database file. Only one page is reported as 146 # written because page 1 - the change-counter page - is written using 147 # an out-of-band method that bypasses the write counter. 148 # 149 sqlite3_simulate_device -char atomic 150 do_test io-2.3 { 151 execsql { INSERT INTO abc VALUES(3, 4) } 152 list [nWrite db] [nSync] 153 } {1 1} 154 155 # Test that the journal file is not created and the change-counter is 156 # updated when the atomic-write optimization is used. 157 # 158 do_test io-2.4.1 { 159 execsql { 160 BEGIN; 161 INSERT INTO abc VALUES(5, 6); 162 } 163 sqlite3 db2 test.db -vfs devsym 164 execsql { SELECT * FROM abc } db2 165 } {1 2 3 4} 166 do_test io-2.4.2 { 167 file exists test.db-journal 168 } {0} 169 do_test io-2.4.3 { 170 execsql { COMMIT } 171 execsql { SELECT * FROM abc } db2 172 } {1 2 3 4 5 6} 173 db2 close 174 175 # Test that the journal file is created and sync()d if the transaction 176 # modifies more than one database page, even if the IOCAP_ATOMIC flag 177 # is set. 178 # 179 do_test io-2.5.1 { 180 execsql { CREATE TABLE def(d, e) } 181 nWrite db ; nSync 182 execsql { 183 BEGIN; 184 INSERT INTO abc VALUES(7, 8); 185 } 186 file exists test.db-journal 187 } {0} 188 do_test io-2.5.2 { 189 execsql { INSERT INTO def VALUES('a', 'b'); } 190 file exists test.db-journal 191 } {1} 192 do_test io-2.5.3 { 193 execsql { COMMIT } 194 list [nWrite db] [nSync] 195 } {3 4} 196 197 # Test that the journal file is created and sync()d if the transaction 198 # modifies a single database page and also appends a page to the file. 199 # Internally, this case is handled differently to the one above. The 200 # journal file is not actually created until the 'COMMIT' statement 201 # is executed. 202 # 203 # Changed 2010-03-27: The size of the database is now stored in 204 # bytes 28..31 and so when a page is added to the database, page 1 205 # is immediately modified and the journal file immediately comes into 206 # existance. To fix this test, the BEGIN is changed into a a 207 # BEGIN IMMEDIATE and the INSERT is omitted. 208 # 209 do_test io-2.6.1 { 210 execsql { 211 BEGIN IMMEDIATE; 212 -- INSERT INTO abc VALUES(9, randstr(1000,1000)); 213 } 214 file exists test.db-journal 215 } {0} 216 do_test io-2.6.2 { 217 # Create a file at "test.db-journal". This will prevent SQLite from 218 # opening the journal for exclusive access. As a result, the COMMIT 219 # should fail with SQLITE_CANTOPEN and the transaction rolled back. 220 # 221 file mkdir test.db-journal 222 catchsql { 223 INSERT INTO abc VALUES(9, randstr(1000,1000)); 224 COMMIT 225 } 226 } {1 {unable to open database file}} 227 do_test io-2.6.3 { 228 file delete -force test.db-journal 229 catchsql { COMMIT } 230 } {0 {}} 231 do_test io-2.6.4 { 232 execsql { SELECT * FROM abc } 233 } {1 2 3 4 5 6 7 8} 234 235 # Test that if the database modification is part of multi-file commit, 236 # the journal file is always created. In this case, the journal file 237 # is created during execution of the COMMIT statement, so we have to 238 # use the same technique to check that it is created as in the above 239 # block. 240 file delete -force test2.db test2.db-journal 241 ifcapable attach { 242 do_test io-2.7.1 { 243 execsql { 244 ATTACH 'test2.db' AS aux; 245 PRAGMA aux.page_size = 1024; 246 CREATE TABLE aux.abc2(a, b); 247 BEGIN; 248 INSERT INTO abc VALUES(9, 10); 249 } 250 file exists test.db-journal 251 } {0} 252 do_test io-2.7.2 { 253 execsql { INSERT INTO abc2 SELECT * FROM abc } 254 file exists test2.db-journal 255 } {0} 256 do_test io-2.7.3 { 257 execsql { SELECT * FROM abc UNION ALL SELECT * FROM abc2 } 258 } {1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10} 259 do_test io-2.7.4 { 260 file mkdir test2.db-journal 261 catchsql { COMMIT } 262 } {1 {unable to open database file}} 263 do_test io-2.7.5 { 264 file delete -force test2.db-journal 265 catchsql { COMMIT } 266 } {1 {cannot commit - no transaction is active}} 267 do_test io-2.7.6 { 268 execsql { SELECT * FROM abc UNION ALL SELECT * FROM abc2 } 269 } {1 2 3 4 5 6 7 8} 270 } 271 272 # Try an explicit ROLLBACK before the journal file is created. 273 # 274 do_test io-2.8.1 { 275 execsql { 276 BEGIN; 277 DELETE FROM abc; 278 } 279 file exists test.db-journal 280 } {0} 281 do_test io-2.8.2 { 282 execsql { SELECT * FROM abc } 283 } {} 284 do_test io-2.8.3 { 285 execsql { 286 ROLLBACK; 287 SELECT * FROM abc; 288 } 289 } {1 2 3 4 5 6 7 8} 290 291 # Test that the atomic write optimisation is not enabled if the sector 292 # size is larger than the page-size. 293 # 294 do_test io-2.9.1 { 295 db close 296 sqlite3 db test.db 297 sqlite3_simulate_device -char atomic -sectorsize 2048 298 execsql { 299 BEGIN; 300 INSERT INTO abc VALUES(9, 10); 301 } 302 file exists test.db-journal 303 } {1} 304 do_test io-2.9.2 { 305 execsql { ROLLBACK; } 306 db close 307 file delete -force test.db test.db-journal 308 sqlite3 db test.db -vfs devsym 309 execsql { 310 PRAGMA auto_vacuum = OFF; 311 PRAGMA page_size = 2048; 312 CREATE TABLE abc(a, b); 313 } 314 execsql { 315 BEGIN; 316 INSERT INTO abc VALUES(9, 10); 317 } 318 file exists test.db-journal 319 } {0} 320 do_test io-2.9.3 { 321 execsql { COMMIT } 322 } {} 323 324 # Test a couple of the more specific IOCAP_ATOMIC flags 325 # (i.e IOCAP_ATOMIC2K etc.). 326 # 327 do_test io-2.10.1 { 328 sqlite3_simulate_device -char atomic1k 329 execsql { 330 BEGIN; 331 INSERT INTO abc VALUES(11, 12); 332 } 333 file exists test.db-journal 334 } {1} 335 do_test io-2.10.2 { 336 execsql { ROLLBACK } 337 sqlite3_simulate_device -char atomic2k 338 execsql { 339 BEGIN; 340 INSERT INTO abc VALUES(11, 12); 341 } 342 file exists test.db-journal 343 } {0} 344 do_test io-2.10.3 { 345 execsql { ROLLBACK } 346 } {} 347 348 do_test io-2.11.0 { 349 execsql { 350 PRAGMA locking_mode = exclusive; 351 PRAGMA locking_mode; 352 } 353 } {exclusive exclusive} 354 do_test io-2.11.1 { 355 execsql { 356 INSERT INTO abc VALUES(11, 12); 357 } 358 file exists test.db-journal 359 } {0} 360 361 do_test io-2.11.2 { 362 execsql { 363 PRAGMA locking_mode = normal; 364 INSERT INTO abc VALUES(13, 14); 365 } 366 file exists test.db-journal 367 } {0} 368 369 } ;# /* ifcapable atomicwrite */ 370 371 #---------------------------------------------------------------------- 372 # Test cases io-3.* test the IOCAP_SEQUENTIAL optimization. 373 # 374 sqlite3_simulate_device -char sequential -sectorsize 0 375 ifcapable pager_pragmas { 376 do_test io-3.1 { 377 db close 378 file delete -force test.db test.db-journal 379 sqlite3 db test.db -vfs devsym 380 db eval { 381 PRAGMA auto_vacuum=OFF; 382 } 383 # File size might be 1 due to the hack to work around ticket #3260. 384 # Search for #3260 in os_unix.c for additional information. 385 expr {[file size test.db]>1} 386 } {0} 387 do_test io-3.2 { 388 execsql { CREATE TABLE abc(a, b) } 389 nSync 390 execsql { 391 PRAGMA temp_store = memory; 392 PRAGMA cache_size = 10; 393 BEGIN; 394 INSERT INTO abc VALUES('hello', 'world'); 395 INSERT INTO abc SELECT * FROM abc; 396 INSERT INTO abc SELECT * FROM abc; 397 INSERT INTO abc SELECT * FROM abc; 398 INSERT INTO abc SELECT * FROM abc; 399 INSERT INTO abc SELECT * FROM abc; 400 INSERT INTO abc SELECT * FROM abc; 401 INSERT INTO abc SELECT * FROM abc; 402 INSERT INTO abc SELECT * FROM abc; 403 INSERT INTO abc SELECT * FROM abc; 404 INSERT INTO abc SELECT * FROM abc; 405 INSERT INTO abc SELECT * FROM abc; 406 } 407 # File has grown - showing there was a cache-spill - but there 408 # have been no calls to fsync(). The file is probably about 30KB. 409 # But some VFS implementations (symbian) buffer writes so the actual 410 # size may be a little less than that. So this test case just tests 411 # that the file is now greater than 20000 bytes in size. 412 list [expr [file size test.db]>20000] [nSync] 413 } {1 0} 414 do_test io-3.3 { 415 # The COMMIT requires a single fsync() - to the database file. 416 execsql { COMMIT } 417 list [file size test.db] [nSync] 418 } {39936 1} 419 } 420 421 #---------------------------------------------------------------------- 422 # Test cases io-4.* test the IOCAP_SAFE_APPEND optimization. 423 # 424 sqlite3_simulate_device -char safe_append 425 426 # With the SAFE_APPEND flag set, simple transactions require 3, rather 427 # than 4, calls to fsync(). The fsync() calls are on: 428 # 429 # 1) The directory in which the journal file is created, (unix only) 430 # 2) The journal file (to sync the page data), 431 # 3) The database file. 432 # 433 # Normally, when the SAFE_APPEND flag is not set, there is another fsync() 434 # on the journal file between steps (2) and (3) above. 435 # 436 set expected_sync_count 2 437 if {$::tcl_platform(platform)=="unix"} { 438 ifcapable dirsync { 439 incr expected_sync_count 440 } 441 } 442 443 do_test io-4.1 { 444 execsql { DELETE FROM abc } 445 nSync 446 execsql { INSERT INTO abc VALUES('a', 'b') } 447 nSync 448 } $expected_sync_count 449 450 # With SAFE_APPEND set, the nRec field of the journal file header should 451 # be set to 0xFFFFFFFF before the first journal sync. The nRec field 452 # occupies bytes 8-11 of the journal file. 453 # 454 do_test io-4.2.1 { 455 execsql { BEGIN } 456 execsql { INSERT INTO abc VALUES('c', 'd') } 457 file exists test.db-journal 458 } {1} 459 if {$::tcl_platform(platform)=="unix"} { 460 do_test io-4.2.2 { 461 hexio_read test.db-journal 8 4 462 } {FFFFFFFF} 463 } 464 do_test io-4.2.3 { 465 execsql { COMMIT } 466 nSync 467 } $expected_sync_count 468 sqlite3_simulate_device -char safe_append 469 470 # With SAFE_APPEND set, there should only ever be one journal-header 471 # written to the database, even though the sync-mode is "full". 472 # 473 do_test io-4.3.1 { 474 execsql { 475 INSERT INTO abc SELECT * FROM abc; 476 INSERT INTO abc SELECT * FROM abc; 477 INSERT INTO abc SELECT * FROM abc; 478 INSERT INTO abc SELECT * FROM abc; 479 INSERT INTO abc SELECT * FROM abc; 480 INSERT INTO abc SELECT * FROM abc; 481 INSERT INTO abc SELECT * FROM abc; 482 INSERT INTO abc SELECT * FROM abc; 483 INSERT INTO abc SELECT * FROM abc; 484 INSERT INTO abc SELECT * FROM abc; 485 INSERT INTO abc SELECT * FROM abc; 486 } 487 expr {[file size test.db]/1024} 488 } {43} 489 ifcapable pager_pragmas { 490 do_test io-4.3.2 { 491 execsql { 492 PRAGMA synchronous = full; 493 PRAGMA cache_size = 10; 494 PRAGMA synchronous; 495 } 496 } {2} 497 } 498 do_test io-4.3.3 { 499 execsql { 500 BEGIN; 501 UPDATE abc SET a = 'x'; 502 } 503 file exists test.db-journal 504 } {1} 505 if {$tcl_platform(platform) != "symbian"} { 506 # This test is not run on symbian because the file-buffer makes it 507 # difficult to predict the exact size of the file as reported by 508 # [file size]. 509 do_test io-4.3.4 { 510 # The UPDATE statement in the statement above modifies 41 pages 511 # (all pages in the database except page 1 and the root page of 512 # abc). Because the cache_size is set to 10, this must have required 513 # at least 4 cache-spills. If there were no journal headers written 514 # to the journal file after the cache-spill, then the size of the 515 # journal file is give by: 516 # 517 # <jrnl file size> = <jrnl header size> + nPage * (<page-size> + 8) 518 # 519 # If the journal file contains additional headers, this formula 520 # will not predict the size of the journal file. 521 # 522 file size test.db-journal 523 } [expr 512 + (1024+8)*41] 524 } 525 526 #---------------------------------------------------------------------- 527 # Test cases io-5.* test that the default page size is selected and 528 # used correctly. 529 # 530 set tn 0 531 foreach {char sectorsize pgsize} { 532 {} 512 1024 533 {} 1024 1024 534 {} 2048 2048 535 {} 8192 8192 536 {} 16384 8192 537 {atomic} 512 8192 538 {atomic512} 512 1024 539 {atomic2K} 512 2048 540 {atomic2K} 4096 4096 541 {atomic2K atomic} 512 8192 542 {atomic64K} 512 1024 543 } { 544 incr tn 545 if {$pgsize>$::SQLITE_MAX_PAGE_SIZE} continue 546 db close 547 file delete -force test.db test.db-journal 548 sqlite3_simulate_device -char $char -sectorsize $sectorsize 549 sqlite3 db test.db -vfs devsym 550 db eval { 551 PRAGMA auto_vacuum=OFF; 552 } 553 ifcapable !atomicwrite { 554 if {[regexp {^atomic} $char]} continue 555 } 556 do_test io-5.$tn { 557 execsql { 558 CREATE TABLE abc(a, b, c); 559 } 560 expr {[file size test.db]/2} 561 } $pgsize 562 } 563 564 sqlite3_simulate_device -char {} -sectorsize 0 565 finish_test 566