exclusive2.test 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. # 2007 March 24
  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.
  12. #
  13. # $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $
  14. set testdir [file dirname $argv0]
  15. source $testdir/tester.tcl
  16. # Do not use a codec for tests in this file, as the database file is
  17. # manipulated directly using tcl scripts (using the [hexio_write] command).
  18. #
  19. do_not_use_codec
  20. ifcapable {!pager_pragmas} {
  21. finish_test
  22. return
  23. }
  24. # Tests in this file verify that locking_mode=exclusive causes SQLite to
  25. # use cached pages even if the database is changed on disk. This doesn't
  26. # work with mmap.
  27. if {[permutation]=="mmap"} {
  28. finish_test
  29. return
  30. }
  31. # This module does not work right if the cache spills at unexpected
  32. # moments. So disable the soft-heap-limit.
  33. #
  34. sqlite3_soft_heap_limit 0
  35. proc pagerChangeCounter {filename new {fd ""}} {
  36. if {$fd==""} {
  37. set fd [open $filename RDWR]
  38. fconfigure $fd -translation binary -encoding binary
  39. set needClose 1
  40. } else {
  41. set needClose 0
  42. }
  43. if {$new ne ""} {
  44. seek $fd 24
  45. set a [expr {($new&0xFF000000)>>24}]
  46. set b [expr {($new&0x00FF0000)>>16}]
  47. set c [expr {($new&0x0000FF00)>>8}]
  48. set d [expr {($new&0x000000FF)}]
  49. puts -nonewline $fd [binary format cccc $a $b $c $d]
  50. flush $fd
  51. }
  52. seek $fd 24
  53. foreach {a b c d} [list 0 0 0 0] {}
  54. binary scan [read $fd 4] cccc a b c d
  55. set ret [expr ($a&0x000000FF)<<24]
  56. incr ret [expr ($b&0x000000FF)<<16]
  57. incr ret [expr ($c&0x000000FF)<<8]
  58. incr ret [expr ($d&0x000000FF)<<0]
  59. if {$needClose} {close $fd}
  60. return $ret
  61. }
  62. proc readPagerChangeCounter {filename} {
  63. set fd [open $filename RDONLY]
  64. fconfigure $fd -translation binary -encoding binary
  65. seek $fd 24
  66. foreach {a b c d} [list 0 0 0 0] {}
  67. binary scan [read $fd 4] cccc a b c d
  68. set ret [expr ($a&0x000000FF)<<24]
  69. incr ret [expr ($b&0x000000FF)<<16]
  70. incr ret [expr ($c&0x000000FF)<<8]
  71. incr ret [expr ($d&0x000000FF)<<0]
  72. close $fd
  73. return $ret
  74. }
  75. proc t1sig {{db db}} {
  76. execsql {SELECT count(*), md5sum(a) FROM t1} $db
  77. }
  78. do_test exclusive2-1.0 {
  79. readPagerChangeCounter test.db
  80. } {0}
  81. #-----------------------------------------------------------------------
  82. # The following tests - exclusive2-1.X - check that:
  83. #
  84. # 1-3: Build a database with connection 1, calculate a signature.
  85. # 4-7: Modify the database using a second connection in a way that
  86. # does not modify the freelist, then reset the pager change-counter
  87. # to the value it had before the modifications.
  88. # 8: Check that using the first connection, the database signature
  89. # is still the same. This is because it uses the in-memory cache.
  90. # It can't tell the db has changed because we reset the change-counter.
  91. # 9: Increment the change-counter.
  92. # 10: Ensure that the first connection now sees the updated database. It
  93. # sees the change-counter has been incremented and discards the
  94. # invalid in-memory cache.
  95. #
  96. # This will only work if the database cache is large enough to hold
  97. # the entire database. In the case of 1024 byte pages, this means
  98. # the cache size must be at least 17. Otherwise, some pages will be
  99. # loaded from the database file in step 8.
  100. #
  101. # For similar reasons, this test does not work with the memsubsys1 permutation.
  102. # Permutation memsubsys1 configures the pcache subsystem to use a static
  103. # allocation of 24 pages (shared between all pagers). This is not enough for
  104. # this test.
  105. #
  106. do_test exclusive2-1.1 {
  107. execsql {
  108. BEGIN;
  109. CREATE TABLE t1(a, b);
  110. INSERT INTO t1(a) VALUES(randstr(10, 400));
  111. INSERT INTO t1(a) VALUES(randstr(10, 400));
  112. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  113. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  114. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  115. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  116. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  117. COMMIT;
  118. SELECT count(*) FROM t1;
  119. }
  120. } {64}
  121. do_test exclusive2-1.2.1 {
  122. # Make sure the pager cache is large enough to store the
  123. # entire database.
  124. set nPage [expr [file size test.db]/1024]
  125. if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
  126. execsql "PRAGMA cache_size = $nPage"
  127. }
  128. expr {[execsql {PRAGMA cache_size}] >= $nPage}
  129. } {1}
  130. do_test exclusive2-1.2 {
  131. set ::sig [t1sig]
  132. readPagerChangeCounter test.db
  133. } {1}
  134. do_test exclusive2-1.3 {
  135. t1sig
  136. } $::sig
  137. do_test exclusive2-1.4 {
  138. sqlite3 db2 test.db
  139. t1sig db2
  140. } $::sig
  141. do_test exclusive2-1.5 {
  142. execsql {
  143. UPDATE t1 SET b=a, a=NULL;
  144. } db2
  145. expr {[t1sig db2] eq $::sig}
  146. } 0
  147. do_test exclusive2-1.6 {
  148. readPagerChangeCounter test.db
  149. } {2}
  150. do_test exclusive2-1.7 {
  151. pagerChangeCounter test.db 1
  152. } {1}
  153. if {[permutation] != "memsubsys1"} {
  154. do_test exclusive2-1.9 {
  155. t1sig
  156. expr {[t1sig] eq $::sig}
  157. } {1}
  158. }
  159. do_test exclusive2-1.10 {
  160. pagerChangeCounter test.db 2
  161. } {2}
  162. do_test exclusive2-1.11 {
  163. expr {[t1sig] eq $::sig}
  164. } {0}
  165. db2 close
  166. #--------------------------------------------------------------------
  167. # These tests - exclusive2-2.X - are similar to exclusive2-1.X,
  168. # except that they are run with locking_mode=EXCLUSIVE.
  169. #
  170. # 1-3: Build a database with exclusive-access connection 1,
  171. # calculate a signature.
  172. # 4: Corrupt the database by writing 10000 bytes of garbage
  173. # starting at the beginning of page 2. Check that connection 1
  174. # still works. It should be accessing the in-memory cache.
  175. # 5-6: Modify the dataase change-counter. Connection 1 still works
  176. # entirely from in-memory cache, because it doesn't check the
  177. # change-counter.
  178. # 7-8 Set the locking-mode back to normal. After the db is unlocked,
  179. # SQLite detects the modified change-counter and discards the
  180. # in-memory cache. Then it finds the corruption caused in step 4....
  181. #
  182. # As above, this test is only applicable if the pager cache is
  183. # large enough to hold the entire database. With 1024 byte pages,
  184. # this means 19 pages. We also need to disable the soft-heap-limit
  185. # to prevent memory-induced cache spills.
  186. #
  187. do_test exclusive2-2.1 {
  188. execsql {PRAGMA cache_size=1000;}
  189. execsql {PRAGMA locking_mode = exclusive;}
  190. execsql {
  191. BEGIN;
  192. DELETE FROM t1;
  193. INSERT INTO t1(a) VALUES(randstr(10, 400));
  194. INSERT INTO t1(a) VALUES(randstr(10, 400));
  195. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  196. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  197. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  198. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  199. INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
  200. COMMIT;
  201. SELECT count(*) FROM t1;
  202. }
  203. } {64}
  204. do_test exclusive2-2.2.1 {
  205. # Make sure the pager cache is large enough to store the
  206. # entire database.
  207. set nPage [expr [file size test.db]/1024]
  208. if {$::SQLITE_DEFAULT_CACHE_SIZE < $nPage} {
  209. execsql "PRAGMA cache_size = $nPage"
  210. }
  211. expr {[execsql {PRAGMA cache_size}] >= $nPage}
  212. } {1}
  213. do_test exclusive2-2.2 {
  214. set ::sig [t1sig]
  215. readPagerChangeCounter test.db
  216. } {3}
  217. do_test exclusive2-2.3 {
  218. t1sig
  219. } $::sig
  220. do_test exclusive2-2.4 {
  221. set ::fd [open test.db RDWR]
  222. fconfigure $::fd -translation binary
  223. seek $::fd 1024
  224. puts -nonewline $::fd [string repeat [binary format c 0] 10000]
  225. flush $::fd
  226. t1sig
  227. } $::sig
  228. do_test exclusive2-2.5 {
  229. pagerChangeCounter test.db 5 $::fd
  230. } {5}
  231. do_test exclusive2-2.6 {
  232. t1sig
  233. } $::sig
  234. do_test exclusive2-2.7 {
  235. execsql {PRAGMA locking_mode = normal}
  236. t1sig
  237. } $::sig
  238. do_test exclusive2-2.8 {
  239. set rc [catch {t1sig} msg]
  240. list $rc $msg
  241. } {1 {database disk image is malformed}}
  242. #--------------------------------------------------------------------
  243. # These tests - exclusive2-3.X - verify that the pager change-counter
  244. # is only incremented by the first change when in exclusive access
  245. # mode. In normal mode, the change-counter is incremented once
  246. # per write-transaction.
  247. #
  248. db close
  249. catch {close $::fd}
  250. forcedelete test.db
  251. forcedelete test.db-journal
  252. do_test exclusive2-3.0 {
  253. sqlite3 db test.db
  254. execsql {
  255. BEGIN;
  256. CREATE TABLE t1(a UNIQUE);
  257. INSERT INTO t1 VALUES(randstr(200, 200));
  258. INSERT INTO t1 VALUES(randstr(200, 200));
  259. COMMIT;
  260. }
  261. readPagerChangeCounter test.db
  262. } {1}
  263. do_test exclusive2-3.1 {
  264. execsql {
  265. INSERT INTO t1 VALUES(randstr(200, 200));
  266. }
  267. readPagerChangeCounter test.db
  268. } {2}
  269. do_test exclusive2-3.2 {
  270. execsql {
  271. INSERT INTO t1 VALUES(randstr(200, 200));
  272. }
  273. readPagerChangeCounter test.db
  274. } {3}
  275. do_test exclusive2-3.3 {
  276. execsql {
  277. PRAGMA locking_mode = exclusive;
  278. INSERT INTO t1 VALUES(randstr(200, 200));
  279. }
  280. readPagerChangeCounter test.db
  281. } {4}
  282. do_test exclusive2-3.4 {
  283. breakpoint
  284. execsql {
  285. INSERT INTO t1 VALUES(randstr(200, 200));
  286. }
  287. readPagerChangeCounter test.db
  288. } {4}
  289. do_test exclusive2-3.5 {
  290. execsql {
  291. PRAGMA locking_mode = normal;
  292. INSERT INTO t1 VALUES(randstr(200, 200));
  293. }
  294. readPagerChangeCounter test.db
  295. } {4}
  296. do_test exclusive2-3.6 {
  297. execsql {
  298. INSERT INTO t1 VALUES(randstr(200, 200));
  299. }
  300. readPagerChangeCounter test.db
  301. } {5}
  302. sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
  303. finish_test