xref: /sqlite-3.40.0/test/mmap1.test (revision a32536b4)
1eecc3983Sdan# 2013 March 20
2eecc3983Sdan#
3eecc3983Sdan# The author disclaims copyright to this source code.  In place of
4eecc3983Sdan# a legal notice, here is a blessing:
5eecc3983Sdan#
6eecc3983Sdan#    May you do good and not evil.
7eecc3983Sdan#    May you find forgiveness for yourself and forgive others.
8eecc3983Sdan#    May you share freely, never taking more than you give.
9eecc3983Sdan#
10eecc3983Sdan#***********************************************************************
11eecc3983Sdan#
12eecc3983Sdan
13eecc3983Sdanset testdir [file dirname $argv0]
14eecc3983Sdansource $testdir/tester.tcl
15*a32536b4Sdanifcapable !mmap||!incrblob {
16188d4884Sdrh  finish_test
17188d4884Sdrh  return
18188d4884Sdrh}
19eecc3983Sdansource $testdir/lock_common.tcl
20eecc3983Sdanset testprefix mmap1
21eecc3983Sdan
22eecc3983Sdanproc nRead {db} {
23eecc3983Sdan  set bt [btree_from_db $db]
24eecc3983Sdan  db_enter $db
25eecc3983Sdan  array set stats [btree_pager_stats $bt]
26eecc3983Sdan  db_leave $db
27c3d53189Sdrh  # puts [array get stats]
28eecc3983Sdan  return $stats(read)
29eecc3983Sdan}
30eecc3983Sdan
31be7721d1Sdan# Return a Tcl script that registers a user-defined scalar function
32be7721d1Sdan# named rblob() with database handle $dbname. The function returns a
33be7721d1Sdan# sequence of pseudo-random blobs based on seed value $seed.
34be7721d1Sdan#
35ced9813bSdanproc register_rblob_code {dbname seed} {
36ced9813bSdan  return [subst -nocommands {
37ced9813bSdan    set ::rcnt $seed
38ced9813bSdan    proc rblob {n} {
39ced9813bSdan      set ::rcnt [expr (([set ::rcnt] << 3) + [set ::rcnt] + 456) & 0xFFFFFFFF]
40ced9813bSdan      set str [format %.8x [expr [set ::rcnt] ^ 0xbdf20da3]]
41ced9813bSdan      string range [string repeat [set str] [expr [set n]/4]] 1 [set n]
42ced9813bSdan    }
43ced9813bSdan    $dbname func rblob rblob
44ced9813bSdan  }]
45ced9813bSdan}
46ced9813bSdan
47be7721d1Sdan
48f054396bSdan# For cases 1.1 and 1.4, the number of pages read using xRead() is 4 on
49f054396bSdan# unix and 9 on windows. The difference is that windows only ever maps
50f054396bSdan# an integer number of OS pages (i.e. creates mappings that are a multiple
51f054396bSdan# of 4KB in size). Whereas on unix any sized mapping may be created.
52f054396bSdan#
539b4c59faSdrhforeach {t mmap_size nRead c2init} {
54f054396bSdan  1.1 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 0}
559b4c59faSdrh  1.2 { PRAGMA mmap_size =    53248 } 150    {PRAGMA mmap_size = 0}
569b4c59faSdrh  1.3 { PRAGMA mmap_size =        0 } 344    {PRAGMA mmap_size = 0}
57f054396bSdan  1.4 { PRAGMA mmap_size = 67108864 } /[49]/ {PRAGMA mmap_size = 67108864 }
589b4c59faSdrh  1.5 { PRAGMA mmap_size =    53248 } 150    {PRAGMA mmap_size = 67108864 }
599b4c59faSdrh  1.6 { PRAGMA mmap_size =        0 } 344    {PRAGMA mmap_size = 67108864 }
60eecc3983Sdan} {
61f054396bSdan
62eecc3983Sdan  do_multiclient_test tn {
635b04dc51Sdan    sql1 {PRAGMA cache_size=2000}
645b04dc51Sdan    sql2 {PRAGMA cache_size=2000}
655b04dc51Sdan
66c3d53189Sdrh    sql1 {PRAGMA page_size=1024}
679b4c59faSdrh    sql1 $mmap_size
689d56c6dfSdan    sql2 $c2init
69eecc3983Sdan
70ced9813bSdan    code2 [register_rblob_code db2 0]
71eecc3983Sdan
72eecc3983Sdan    sql2 {
73c3d53189Sdrh      PRAGMA page_size=1024;
74eecc3983Sdan      PRAGMA auto_vacuum = 1;
75eecc3983Sdan      CREATE TABLE t1(a, b, UNIQUE(a, b));
76eecc3983Sdan      INSERT INTO t1 VALUES(rblob(500), rblob(500));
77eecc3983Sdan      INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    2
78eecc3983Sdan      INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    4
79eecc3983Sdan      INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    8
80eecc3983Sdan      INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --   16
81eecc3983Sdan      INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --   32
82eecc3983Sdan    }
83eecc3983Sdan    do_test $t.$tn.1 {
84eecc3983Sdan      sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count"
85eecc3983Sdan    } {32 ok 77}
86eecc3983Sdan
87eecc3983Sdan    # Have connection 2 shrink the file. Check connection 1 can still read it.
88eecc3983Sdan    sql2 { DELETE FROM t1 WHERE rowid%2; }
89eecc3983Sdan    do_test $t.$tn.2 {
90eecc3983Sdan      sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count"
917da56b4fSdrh    } "16 ok [expr {42+[nonzero_reserved_bytes]}]"
92eecc3983Sdan
93eecc3983Sdan    # Have connection 2 grow the file. Check connection 1 can still read it.
94eecc3983Sdan    sql2 { INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1 }
95eecc3983Sdan    do_test $t.$tn.3 {
96eecc3983Sdan      sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count"
97eecc3983Sdan    } {32 ok 79}
98eecc3983Sdan
99eecc3983Sdan    # Have connection 2 grow the file again. Check connection 1 is still ok.
100eecc3983Sdan    sql2 { INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1 }
101eecc3983Sdan    do_test $t.$tn.4 {
102eecc3983Sdan      sql1 "SELECT count(*) FROM t1; PRAGMA integrity_check ; PRAGMA page_count"
103eecc3983Sdan    } {64 ok 149}
104eecc3983Sdan
105eecc3983Sdan    # Check that the number of pages read by connection 1 indicates that the
1069b4c59faSdrh    # "PRAGMA mmap_size" command worked.
1077da56b4fSdrh    if {[nonzero_reserved_bytes]==0} {
108eecc3983Sdan      do_test $t.$tn.5 { nRead db } $nRead
109eecc3983Sdan    }
110eecc3983Sdan  }
1117da56b4fSdrh}
112eecc3983Sdan
1139d56c6dfSdanset ::rcnt 0
1149d56c6dfSdanproc rblob {n} {
1159d56c6dfSdan  set ::rcnt [expr (($::rcnt << 3) + $::rcnt + 456) & 0xFFFFFFFF]
1169d56c6dfSdan  set str [format %.8x [expr $::rcnt ^ 0xbdf20da3]]
1179d56c6dfSdan  string range [string repeat $str [expr $n/4]] 1 $n
1189d56c6dfSdan}
1199d56c6dfSdan
1209d56c6dfSdanreset_db
1219d56c6dfSdandb func rblob rblob
1229d56c6dfSdan
123fdece7baSmistachkinifcapable wal {
1249d56c6dfSdan  do_execsql_test 2.1 {
1259d56c6dfSdan    PRAGMA auto_vacuum = 1;
1269b4c59faSdrh    PRAGMA mmap_size = 67108864;
1279d56c6dfSdan    PRAGMA journal_mode = wal;
1289d56c6dfSdan    CREATE TABLE t1(a, b, UNIQUE(a, b));
1299d56c6dfSdan    INSERT INTO t1 VALUES(rblob(500), rblob(500));
1309d56c6dfSdan    INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    2
1319d56c6dfSdan    INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    4
1329d56c6dfSdan    INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    8
1339d56c6dfSdan    INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --   16
1349d56c6dfSdan    INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --   32
1359d56c6dfSdan    PRAGMA wal_checkpoint;
13634f74903Sdrh  } {67108864 wal 0 103 103}
1379d56c6dfSdan
1389d56c6dfSdan  do_execsql_test 2.2 {
1399d56c6dfSdan    PRAGMA auto_vacuum;
1409d56c6dfSdan    SELECT count(*) FROM t1;
1419d56c6dfSdan  } {1 32}
1429d56c6dfSdan
1435b04dc51Sdan  if {[permutation] != "inmemory_journal"} {
1449d56c6dfSdan    do_test 2.3 {
1459d56c6dfSdan      sqlite3 db2 test.db
1469d56c6dfSdan      db2 func rblob rblob
1479d56c6dfSdan      db2 eval {
1489d56c6dfSdan        DELETE FROM t1 WHERE (rowid%4);
1499d56c6dfSdan          PRAGMA wal_checkpoint;
1509d56c6dfSdan      }
1519d56c6dfSdan      db2 eval {
1529d56c6dfSdan        INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    16
1539d56c6dfSdan        SELECT count(*) FROM t1;
1549d56c6dfSdan      }
1559d56c6dfSdan    } {16}
1569d56c6dfSdan
1579d56c6dfSdan    do_execsql_test 2.4 {
1589d56c6dfSdan      PRAGMA wal_checkpoint;
1599d56c6dfSdan    } {0 24 24}
160c3d53189Sdrh    db2 close
1615b04dc51Sdan  }
162fdece7baSmistachkin}
1635b04dc51Sdan
164db082408Sdanreset_db
165cf8489feSdanexecsql { PRAGMA mmap_size = 67108864; }
166db082408Sdandb func rblob rblob
167db082408Sdando_execsql_test 3.1 {
168db082408Sdan  PRAGMA auto_vacuum = 1;
169db082408Sdan
170db082408Sdan  CREATE TABLE t1(a, b, UNIQUE(a, b));
171db082408Sdan  INSERT INTO t1 VALUES(rblob(500), rblob(500));
172db082408Sdan  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    2
173db082408Sdan  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    4
174db082408Sdan  INSERT INTO t1 SELECT rblob(500), rblob(500) FROM t1; --    8
175db082408Sdan
176db082408Sdan  CREATE TABLE t2(a, b, UNIQUE(a, b));
177db082408Sdan  INSERT INTO t2 SELECT * FROM t1;
178db082408Sdan} {}
179db082408Sdan
180db082408Sdando_test 3.2 {
181db082408Sdan  set nRow 0
182db082408Sdan  db eval {SELECT * FROM t2 ORDER BY a, b} {
183db082408Sdan    if {$nRow==4} { db eval { DELETE FROM t1 } }
184db082408Sdan    incr nRow
185db082408Sdan  }
186db082408Sdan  set nRow
187db082408Sdan} {8}
188db082408Sdan
189227a1c48Sdan#-------------------------------------------------------------------------
190227a1c48Sdan# Ensure that existing cursors using xFetch() pages see changes made
191227a1c48Sdan# to rows using the incrblob API.
192227a1c48Sdan#
193227a1c48Sdanreset_db
194cf8489feSdanexecsql { PRAGMA mmap_size = 67108864; }
195227a1c48Sdanset aaa [string repeat a 400]
196227a1c48Sdanset bbb [string repeat b 400]
197227a1c48Sdanset ccc [string repeat c 400]
198227a1c48Sdanset ddd [string repeat d 400]
199227a1c48Sdanset eee [string repeat e 400]
200227a1c48Sdan
201227a1c48Sdando_execsql_test 4.1 {
202227a1c48Sdan  PRAGMA page_size = 1024;
203227a1c48Sdan  CREATE TABLE t1(x);
204227a1c48Sdan  INSERT INTO t1 VALUES($aaa);
205227a1c48Sdan  INSERT INTO t1 VALUES($bbb);
206227a1c48Sdan  INSERT INTO t1 VALUES($ccc);
207227a1c48Sdan  INSERT INTO t1 VALUES($ddd);
208227a1c48Sdan  SELECT * FROM t1;
209227a1c48Sdan  BEGIN;
210227a1c48Sdan} [list $aaa $bbb $ccc $ddd]
211227a1c48Sdan
212227a1c48Sdando_test 4.2 {
213227a1c48Sdan  set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY rowid" -1 dummy]
214227a1c48Sdan  sqlite3_step $::STMT
215227a1c48Sdan  sqlite3_column_text $::STMT 0
216227a1c48Sdan} $aaa
217227a1c48Sdan
218227a1c48Sdando_test 4.3 {
219227a1c48Sdan  foreach r {2 3 4} {
220227a1c48Sdan    set fd [db incrblob t1 x $r]
221227a1c48Sdan    puts -nonewline $fd $eee
222227a1c48Sdan    close $fd
223227a1c48Sdan  }
224227a1c48Sdan
225227a1c48Sdan  set res [list]
226227a1c48Sdan  while {"SQLITE_ROW" == [sqlite3_step $::STMT]} {
227227a1c48Sdan    lappend res [sqlite3_column_text $::STMT 0]
228227a1c48Sdan  }
229227a1c48Sdan  set res
230227a1c48Sdan} [list $eee $eee $eee]
231227a1c48Sdan
232227a1c48Sdando_test 4.4 {
233227a1c48Sdan  sqlite3_finalize $::STMT
234227a1c48Sdan} SQLITE_OK
235227a1c48Sdan
236227a1c48Sdando_execsql_test 4.5 { COMMIT }
237227a1c48Sdan
238f7679ad1Sdan#-------------------------------------------------------------------------
239f7679ad1Sdan# Ensure that existing cursors holding xFetch() references are not
240f7679ad1Sdan# confused if those pages are moved to make way for the root page of a
241f7679ad1Sdan# new table or index.
242f7679ad1Sdan#
243f7679ad1Sdanreset_db
244cf8489feSdanexecsql { PRAGMA mmap_size = 67108864; }
245f7679ad1Sdando_execsql_test 5.1 {
246f7679ad1Sdan  PRAGMA auto_vacuum = 2;
247f7679ad1Sdan  PRAGMA page_size = 1024;
248f7679ad1Sdan  CREATE TABLE t1(x);
249f7679ad1Sdan  INSERT INTO t1 VALUES($aaa);
250f7679ad1Sdan  INSERT INTO t1 VALUES($bbb);
251f7679ad1Sdan  INSERT INTO t1 VALUES($ccc);
252f7679ad1Sdan  INSERT INTO t1 VALUES($ddd);
253227a1c48Sdan
254f7679ad1Sdan  PRAGMA auto_vacuum;
255f7679ad1Sdan  SELECT * FROM t1;
256f7679ad1Sdan} [list 2 $aaa $bbb $ccc $ddd]
257f7679ad1Sdan
258f7679ad1Sdando_test 5.2 {
259f7679ad1Sdan  set ::STMT [sqlite3_prepare db "SELECT * FROM t1 ORDER BY rowid" -1 dummy]
260f7679ad1Sdan  sqlite3_step $::STMT
261f7679ad1Sdan  sqlite3_column_text $::STMT 0
262f7679ad1Sdan} $aaa
263f7679ad1Sdan
264f7679ad1Sdando_execsql_test 5.3 {
265f7679ad1Sdan  CREATE TABLE t2(x);
266f7679ad1Sdan  INSERT INTO t2 VALUES('tricked you!');
267f7679ad1Sdan  INSERT INTO t2 VALUES('tricked you!');
268f7679ad1Sdan}
269f7679ad1Sdan
270f7679ad1Sdando_test 5.4 {
271f7679ad1Sdan  sqlite3_step $::STMT
272f7679ad1Sdan  sqlite3_column_text $::STMT 0
273f7679ad1Sdan} $bbb
274f7679ad1Sdan
275f7679ad1Sdando_test 5.5 {
276f7679ad1Sdan  sqlite3_finalize $::STMT
277f7679ad1Sdan} SQLITE_OK
278f7679ad1Sdan
2791e8487dbSmistachkin#
2801e8487dbSmistachkin# The "6.*" tests are designed to test the interaction of mmap with file
2811e8487dbSmistachkin# truncation (e.g. on Win32) via the VACUUM command.
2821e8487dbSmistachkin#
2831e8487dbSmistachkinforcedelete test2.db
2841e8487dbSmistachkinsqlite3 db2 test2.db
2851e8487dbSmistachkindo_test 6.0 {
2861e8487dbSmistachkin  db2 eval {
287202a0274Sdan    PRAGMA auto_vacuum = 0;
2881e8487dbSmistachkin    PRAGMA page_size = 4096;
2891e8487dbSmistachkin  }
2901e8487dbSmistachkin} {}
2911e8487dbSmistachkindo_test 6.1 {
2921e8487dbSmistachkin  db2 eval {
2931e8487dbSmistachkin    CREATE TABLE t1(x);
2941e8487dbSmistachkin    INSERT INTO t1(x) VALUES(randomblob(1000000));
2951e8487dbSmistachkin  }
2961e8487dbSmistachkin} {}
2971e8487dbSmistachkindo_test 6.2 {
2981e8487dbSmistachkin  db2 eval {
2991e8487dbSmistachkin    PRAGMA mmap_size = 1048576;
3001e8487dbSmistachkin  }
3011e8487dbSmistachkin} {1048576}
3021e8487dbSmistachkindo_test 6.3 {
3031e8487dbSmistachkin  expr {[file size test2.db] > 1000000}
3041e8487dbSmistachkin} {1}
3051e8487dbSmistachkindo_test 6.4 {
3061e8487dbSmistachkin  db2 eval {
3071e8487dbSmistachkin    DELETE FROM t1;
3081e8487dbSmistachkin  }
3091e8487dbSmistachkin} {}
3101e8487dbSmistachkindo_test 6.5 {
3111e8487dbSmistachkin  expr {[file size test2.db] > 1000000}
3121e8487dbSmistachkin} {1}
3131e8487dbSmistachkindo_test 6.6 {
3141e8487dbSmistachkin  db2 eval {
3151e8487dbSmistachkin    VACUUM;
3161e8487dbSmistachkin  }
3171e8487dbSmistachkin} {}
3181e8487dbSmistachkindo_test 6.7 {
3191e8487dbSmistachkin  expr {[file size test2.db] < 1000000}
3201e8487dbSmistachkin} {1}
3211e8487dbSmistachkindb2 close
322ced9813bSdan
323f7679ad1Sdanfinish_test
324