wal5.test 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. # 2010 April 13
  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. # This file implements regression tests for SQLite library. The
  12. # focus of this file is testing the operation of "blocking-checkpoint"
  13. # operations.
  14. #
  15. set testdir [file dirname $argv0]
  16. source $testdir/tester.tcl
  17. source $testdir/lock_common.tcl
  18. source $testdir/wal_common.tcl
  19. ifcapable !wal {finish_test ; return }
  20. set testprefix wal5
  21. proc db_page_count {{file test.db}} { expr [file size $file] / 1024 }
  22. proc wal_page_count {{file test.db}} { wal_frame_count ${file}-wal 1024 }
  23. # A checkpoint may be requested either using the C API or by executing
  24. # an SQL PRAGMA command. To test both methods, all tests in this file are
  25. # run twice - once using each method to request checkpoints.
  26. #
  27. foreach {testprefix do_wal_checkpoint} {
  28. wal5-pragma {
  29. proc do_wal_checkpoint { dbhandle args } {
  30. array set a $args
  31. foreach key [array names a] {
  32. if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" }
  33. }
  34. set sql "PRAGMA "
  35. if {[info exists a(-db)]} { append sql "$a(-db)." }
  36. append sql "wal_checkpoint"
  37. if {[info exists a(-mode)]} { append sql " = $a(-mode)" }
  38. uplevel [list $dbhandle eval $sql]
  39. }
  40. }
  41. wal5-capi {
  42. proc do_wal_checkpoint { dbhandle args } {
  43. set a(-mode) passive
  44. array set a $args
  45. foreach key [array names a] {
  46. if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" }
  47. }
  48. if {$a(-mode)!="restart" && $a(-mode)!="full"} { set a(-mode) passive }
  49. set cmd [list sqlite3_wal_checkpoint_v2 $dbhandle $a(-mode)]
  50. if {[info exists a(-db)]} { lappend sql $a(-db) }
  51. uplevel $cmd
  52. }
  53. }
  54. } {
  55. eval $do_wal_checkpoint
  56. do_multiclient_test tn {
  57. set ::nBusyHandler 0
  58. set ::busy_handler_script ""
  59. proc busyhandler {n} {
  60. incr ::nBusyHandler
  61. eval $::busy_handler_script
  62. return 0
  63. }
  64. proc reopen_all {} {
  65. code1 {db close}
  66. code2 {db2 close}
  67. code3 {db3 close}
  68. code1 {sqlite3 db test.db}
  69. code2 {sqlite3 db2 test.db}
  70. code3 {sqlite3 db3 test.db}
  71. sql1 { PRAGMA synchronous = NORMAL }
  72. code1 { db busy busyhandler }
  73. }
  74. do_test 1.$tn.1 {
  75. reopen_all
  76. sql1 {
  77. PRAGMA page_size = 1024;
  78. PRAGMA auto_vacuum = 0;
  79. CREATE TABLE t1(x, y);
  80. PRAGMA journal_mode = WAL;
  81. INSERT INTO t1 VALUES(1, zeroblob(1200));
  82. INSERT INTO t1 VALUES(2, zeroblob(1200));
  83. INSERT INTO t1 VALUES(3, zeroblob(1200));
  84. }
  85. expr [file size test.db] / 1024
  86. } {2}
  87. # Have connection 2 grab a read-lock on the current snapshot.
  88. do_test 1.$tn.2 { sql2 { BEGIN; SELECT x FROM t1 } } {1 2 3}
  89. # Attempt a checkpoint.
  90. do_test 1.$tn.3 {
  91. code1 { do_wal_checkpoint db }
  92. list [db_page_count] [wal_page_count]
  93. } {5 9}
  94. # Write to the db again. The log cannot wrap because of the lock still
  95. # held by connection 2. The busy-handler has not yet been invoked.
  96. do_test 1.$tn.4 {
  97. sql1 { INSERT INTO t1 VALUES(4, zeroblob(1200)) }
  98. list [db_page_count] [wal_page_count] $::nBusyHandler
  99. } {5 12 0}
  100. # Now do a blocking-checkpoint. Set the busy-handler up so that connection
  101. # 2 releases its lock on the 6th invocation. The checkpointer should then
  102. # proceed to checkpoint the entire log file. Next write should go to the
  103. # start of the log file.
  104. #
  105. set ::busy_handler_script { if {$n==5} { sql2 COMMIT } }
  106. do_test 1.$tn.5 {
  107. code1 { do_wal_checkpoint db -mode restart }
  108. list [db_page_count] [wal_page_count] $::nBusyHandler
  109. } {6 12 6}
  110. do_test 1.$tn.6 {
  111. set ::nBusyHandler 0
  112. sql1 { INSERT INTO t1 VALUES(5, zeroblob(1200)) }
  113. list [db_page_count] [wal_page_count] $::nBusyHandler
  114. } {6 12 0}
  115. do_test 1.$tn.7 {
  116. reopen_all
  117. list [db_page_count] [wal_page_count] $::nBusyHandler
  118. } {7 0 0}
  119. do_test 1.$tn.8 { sql2 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5}
  120. do_test 1.$tn.9 {
  121. sql1 { INSERT INTO t1 VALUES(6, zeroblob(1200)) }
  122. list [db_page_count] [wal_page_count] $::nBusyHandler
  123. } {7 5 0}
  124. do_test 1.$tn.10 { sql3 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5 6}
  125. set ::busy_handler_script {
  126. if {$n==5} { sql2 COMMIT }
  127. if {$n==6} { set ::db_file_size [db_page_count] }
  128. if {$n==7} { sql3 COMMIT }
  129. }
  130. do_test 1.$tn.11 {
  131. code1 { do_wal_checkpoint db -mode restart }
  132. list [db_page_count] [wal_page_count] $::nBusyHandler
  133. } {10 5 8}
  134. do_test 1.$tn.12 { set ::db_file_size } 10
  135. }
  136. #-------------------------------------------------------------------------
  137. # This block of tests explores checkpoint operations on more than one
  138. # database file.
  139. #
  140. proc setup_and_attach_aux {} {
  141. sql1 { ATTACH 'test.db2' AS aux }
  142. sql2 { ATTACH 'test.db2' AS aux }
  143. sql3 { ATTACH 'test.db2' AS aux }
  144. sql1 {
  145. PRAGMA aux.auto_vacuum = 0;
  146. PRAGMA main.auto_vacuum = 0;
  147. PRAGMA main.page_size=1024; PRAGMA main.journal_mode=WAL;
  148. PRAGMA aux.page_size=1024; PRAGMA aux.journal_mode=WAL;
  149. }
  150. }
  151. proc file_page_counts {} {
  152. list [db_page_count test.db ] \
  153. [wal_page_count test.db ] \
  154. [db_page_count test.db2] \
  155. [wal_page_count test.db2]
  156. }
  157. # Test that executing "PRAGMA wal_checkpoint" checkpoints all attached
  158. # databases, not just the main db. In capi mode, check that this is
  159. # true if a NULL pointer is passed to wal_checkpoint_v2() in place of a
  160. # database name.
  161. do_multiclient_test tn {
  162. setup_and_attach_aux
  163. do_test 2.1.$tn.1 {
  164. sql1 {
  165. CREATE TABLE t1(a, b);
  166. INSERT INTO t1 VALUES(1, 2);
  167. CREATE TABLE aux.t2(a, b);
  168. INSERT INTO t2 VALUES(1, 2);
  169. }
  170. } {}
  171. do_test 2.2.$tn.2 { file_page_counts } {1 3 1 3}
  172. do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 3 3}
  173. do_test 2.1.$tn.4 { file_page_counts } {2 3 2 3}
  174. }
  175. do_multiclient_test tn {
  176. setup_and_attach_aux
  177. do_test 2.2.$tn.1 {
  178. execsql {
  179. CREATE TABLE t1(a, b);
  180. INSERT INTO t1 VALUES(1, 2);
  181. CREATE TABLE aux.t2(a, b);
  182. INSERT INTO t2 VALUES(1, 2);
  183. INSERT INTO t2 VALUES(3, 4);
  184. }
  185. } {}
  186. do_test 2.2.$tn.2 { file_page_counts } {1 3 1 4}
  187. do_test 2.2.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
  188. do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 3 3}
  189. do_test 2.2.$tn.5 { file_page_counts } {2 3 2 4}
  190. }
  191. do_multiclient_test tn {
  192. setup_and_attach_aux
  193. do_test 2.3.$tn.1 {
  194. execsql {
  195. CREATE TABLE t1(a, b);
  196. INSERT INTO t1 VALUES(1, 2);
  197. CREATE TABLE aux.t2(a, b);
  198. INSERT INTO t2 VALUES(1, 2);
  199. }
  200. } {}
  201. do_test 2.3.$tn.2 { file_page_counts } {1 3 1 3}
  202. do_test 2.3.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
  203. do_test 2.3.$tn.4 { sql1 { INSERT INTO t1 VALUES(3, 4) } } {}
  204. do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {}
  205. do_test 2.3.$tn.6 { file_page_counts } {1 4 1 4}
  206. do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 4 3}
  207. # The checkpoint above only writes page 1 of the db file. The other
  208. # page (page 2) is locked by the read-transaction opened by the
  209. # [sql2] commmand above. So normally, the db is 1 page in size here.
  210. # However, in mmap() mode, the db is pre-allocated to 2 pages at the
  211. # start of the checkpoint, even though page 2 cannot be written.
  212. set nDb 2
  213. if {[permutation]!="mmap"} {set nDb 1}
  214. ifcapable !mmap {set nDb 1}
  215. do_test 2.3.$tn.8 { file_page_counts } [list $nDb 4 2 4]
  216. }
  217. # Check that checkpoints block on the correct locks. And respond correctly
  218. # if they cannot obtain those locks. There are three locks that a checkpoint
  219. # may block on (in the following order):
  220. #
  221. # 1. The writer lock: FULL and RESTART checkpoints block until any writer
  222. # process releases its lock.
  223. #
  224. # 2. Readers using part of the log file. FULL and RESTART checkpoints block
  225. # until readers using part (but not all) of the log file have finished.
  226. #
  227. # 3. Readers using any of the log file. After copying data into the
  228. # database file, RESTART checkpoints block until readers using any part
  229. # of the log file have finished.
  230. #
  231. # This test case involves running a checkpoint while there exist other
  232. # processes holding all three types of locks.
  233. #
  234. foreach {tn1 checkpoint busy_on ckpt_expected expected} {
  235. 1 PASSIVE - {0 3 3} -
  236. 2 TYPO - {0 3 3} -
  237. 3 FULL - {0 4 4} 2
  238. 4 FULL 1 {1 3 3} 1
  239. 5 FULL 2 {1 4 3} 2
  240. 6 FULL 3 {0 4 4} 2
  241. 7 RESTART - {0 4 4} 3
  242. 8 RESTART 1 {1 3 3} 1
  243. 9 RESTART 2 {1 4 3} 2
  244. 10 RESTART 3 {1 4 4} 3
  245. } {
  246. do_multiclient_test tn {
  247. setup_and_attach_aux
  248. proc busyhandler {x} {
  249. set ::max_busyhandler $x
  250. if {$::busy_on!="-" && $x==$::busy_on} { return 1 }
  251. switch -- $x {
  252. 1 { sql2 "COMMIT ; BEGIN ; SELECT * FROM t1" }
  253. 2 { sql3 "COMMIT" }
  254. 3 { sql2 "COMMIT" }
  255. }
  256. return 0
  257. }
  258. set ::max_busyhandler -
  259. do_test 2.4.$tn1.$tn.1 {
  260. sql1 {
  261. CREATE TABLE t1(a, b);
  262. INSERT INTO t1 VALUES(1, 2);
  263. }
  264. sql2 { BEGIN; INSERT INTO t1 VALUES(3, 4) }
  265. sql3 { BEGIN; SELECT * FROM t1 }
  266. } {1 2}
  267. do_test 2.4.$tn1.$tn.2 {
  268. code1 { db busy busyhandler }
  269. code1 { do_wal_checkpoint db -mode [string tolower $checkpoint] }
  270. } $ckpt_expected
  271. do_test 2.4.$tn1.$tn.3 { set ::max_busyhandler } $expected
  272. }
  273. }
  274. do_multiclient_test tn {
  275. code1 $do_wal_checkpoint
  276. code2 $do_wal_checkpoint
  277. code3 $do_wal_checkpoint
  278. do_test 3.$tn.1 {
  279. sql1 {
  280. PRAGMA auto_vacuum = 0;
  281. PRAGMA journal_mode = WAL;
  282. PRAGMA synchronous = normal;
  283. CREATE TABLE t1(x, y);
  284. }
  285. sql2 { PRAGMA journal_mode }
  286. sql3 { PRAGMA journal_mode }
  287. } {wal}
  288. do_test 3.$tn.2 { code2 { do_wal_checkpoint db2 } } {0 2 2}
  289. do_test 3.$tn.3 { code2 { do_wal_checkpoint db2 } } {0 2 2}
  290. do_test 3.$tn.4 { code3 { do_wal_checkpoint db3 } } {0 2 2}
  291. code1 {db close}
  292. code2 {db2 close}
  293. code3 {db3 close}
  294. code1 {sqlite3 db test.db}
  295. code2 {sqlite3 db2 test.db}
  296. code3 {sqlite3 db3 test.db}
  297. do_test 3.$tn.5 { sql3 { PRAGMA journal_mode } } {wal}
  298. do_test 3.$tn.6 { code3 { do_wal_checkpoint db3 } } {0 0 0}
  299. }
  300. }
  301. finish_test