Annotation of embedaddon/sqlite3/test/io.test, revision 1.1.1.1

1.1       misho       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:   forcedelete 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: forcedelete 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:     forcedelete 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:   forcedelete 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:     forcedelete 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:   forcedelete 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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>