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