aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/sqlite/unix/sqlite-3.5.1/test/io.test
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/sqlite/unix/sqlite-3.5.1/test/io.test')
-rw-r--r--libraries/sqlite/unix/sqlite-3.5.1/test/io.test549
1 files changed, 549 insertions, 0 deletions
diff --git a/libraries/sqlite/unix/sqlite-3.5.1/test/io.test b/libraries/sqlite/unix/sqlite-3.5.1/test/io.test
new file mode 100644
index 0000000..74fb0e8
--- /dev/null
+++ b/libraries/sqlite/unix/sqlite-3.5.1/test/io.test
@@ -0,0 +1,549 @@
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