PageRenderTime 49ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/src/sqlite/test/crash8.test

#
Unknown | 402 lines | 366 code | 36 blank | 0 comment | 0 complexity | 665d0386094a4b5d8957dd74d85d5f8a MD5 | raw file
Possible License(s): BSD-3-Clause
  1. # 2009 January 8
  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. # This test verifies a couple of specific potential data corruption
  13. # scenarios involving crashes or power failures.
  14. #
  15. # Later: Also, some other specific scenarios required for coverage
  16. # testing that do not lead to corruption.
  17. #
  18. # $Id: crash8.test,v 1.4 2009/01/11 00:44:48 drh Exp $
  19. set testdir [file dirname $argv0]
  20. source $testdir/tester.tcl
  21. ifcapable !crashtest {
  22. finish_test
  23. return
  24. }
  25. do_test crash8-1.1 {
  26. execsql {
  27. PRAGMA auto_vacuum=OFF;
  28. CREATE TABLE t1(a, b);
  29. CREATE INDEX i1 ON t1(a, b);
  30. INSERT INTO t1 VALUES(1, randstr(1000,1000));
  31. INSERT INTO t1 VALUES(2, randstr(1000,1000));
  32. INSERT INTO t1 VALUES(3, randstr(1000,1000));
  33. INSERT INTO t1 VALUES(4, randstr(1000,1000));
  34. INSERT INTO t1 VALUES(5, randstr(1000,1000));
  35. INSERT INTO t1 VALUES(6, randstr(1000,1000));
  36. CREATE TABLE t2(a, b);
  37. CREATE TABLE t3(a, b);
  38. CREATE TABLE t4(a, b);
  39. CREATE TABLE t5(a, b);
  40. CREATE TABLE t6(a, b);
  41. CREATE TABLE t7(a, b);
  42. CREATE TABLE t8(a, b);
  43. CREATE TABLE t9(a, b);
  44. CREATE TABLE t10(a, b);
  45. PRAGMA integrity_check
  46. }
  47. } {ok}
  48. # Potential corruption scenario 1. A second process opens the database
  49. # and modifies a large portion of it. It then opens a second transaction
  50. # and modifies a small part of the database, but crashes before it commits
  51. # the transaction.
  52. #
  53. # When the first process accessed the database again, it was rolling back
  54. # the aborted transaction, but was not purging its in-memory cache (which
  55. # was loaded before the second process made its first, successful,
  56. # modification). Producing an inconsistent cache.
  57. #
  58. do_test crash8-1.2 {
  59. crashsql -delay 2 -file test.db {
  60. PRAGMA cache_size = 10;
  61. UPDATE t1 SET b = randstr(1000,1000);
  62. INSERT INTO t9 VALUES(1, 2);
  63. }
  64. } {1 {child process exited abnormally}}
  65. do_test crash8-1.3 {
  66. execsql {PRAGMA integrity_check}
  67. } {ok}
  68. # Potential corruption scenario 2. The second process, operating in
  69. # persistent-journal mode, makes a large change to the database file
  70. # with a small in-memory cache. Such that more than one journal-header
  71. # was written to the file. It then opens a second transaction and makes
  72. # a smaller change that requires only a single journal-header to be
  73. # written to the journal file. The second change is such that the
  74. # journal content written to the persistent journal file exactly overwrites
  75. # the first journal-header and set of subsequent records written by the
  76. # first, successful, change. The second process crashes before it can
  77. # commit its second change.
  78. #
  79. # When the first process accessed the database again, it was rolling back
  80. # the second aborted transaction, then continuing to rollback the second
  81. # and subsequent journal-headers written by the first, successful, change.
  82. # Database corruption.
  83. #
  84. do_test crash8.2.1 {
  85. crashsql -delay 2 -file test.db {
  86. PRAGMA journal_mode = persist;
  87. PRAGMA cache_size = 10;
  88. UPDATE t1 SET b = randstr(1000,1000);
  89. PRAGMA cache_size = 100;
  90. BEGIN;
  91. INSERT INTO t2 VALUES('a', 'b');
  92. INSERT INTO t3 VALUES('a', 'b');
  93. INSERT INTO t4 VALUES('a', 'b');
  94. INSERT INTO t5 VALUES('a', 'b');
  95. INSERT INTO t6 VALUES('a', 'b');
  96. INSERT INTO t7 VALUES('a', 'b');
  97. INSERT INTO t8 VALUES('a', 'b');
  98. INSERT INTO t9 VALUES('a', 'b');
  99. INSERT INTO t10 VALUES('a', 'b');
  100. COMMIT;
  101. }
  102. } {1 {child process exited abnormally}}
  103. do_test crash8-2.3 {
  104. execsql {PRAGMA integrity_check}
  105. } {ok}
  106. proc read_file {zFile} {
  107. set fd [open $zFile]
  108. fconfigure $fd -translation binary
  109. set zData [read $fd]
  110. close $fd
  111. return $zData
  112. }
  113. proc write_file {zFile zData} {
  114. set fd [open $zFile w]
  115. fconfigure $fd -translation binary
  116. puts -nonewline $fd $zData
  117. close $fd
  118. }
  119. # The following tests check that SQLite will not roll back a hot-journal
  120. # file if the sector-size field in the first journal file header is
  121. # suspect. Definition of suspect:
  122. #
  123. # a) Not a power of 2, or (crash8-3.5)
  124. # b) Greater than 0x01000000 (16MB), or (crash8-3.6)
  125. # c) Less than 512. (crash8-3.7)
  126. #
  127. # Also test that SQLite will not rollback a hot-journal file with a
  128. # suspect page-size. In this case "suspect" means:
  129. #
  130. # a) Not a power of 2, or
  131. # b) Less than 512, or
  132. # c) Greater than SQLITE_MAX_PAGE_SIZE
  133. #
  134. do_test crash8-3.1 {
  135. list [file exists test.db-joural] [file exists test.db]
  136. } {0 1}
  137. do_test crash8-3.2 {
  138. execsql {
  139. PRAGMA synchronous = off;
  140. BEGIN;
  141. DELETE FROM t1;
  142. SELECT count(*) FROM t1;
  143. }
  144. } {0}
  145. do_test crash8-3.3 {
  146. set zJournal [read_file test.db-journal]
  147. execsql {
  148. COMMIT;
  149. SELECT count(*) FROM t1;
  150. }
  151. } {0}
  152. do_test crash8-3.4 {
  153. binary scan [string range $zJournal 20 23] I nSector
  154. set nSector
  155. } {512}
  156. do_test crash8-3.5 {
  157. set zJournal2 [string replace $zJournal 20 23 [binary format I 513]]
  158. write_file test.db-journal $zJournal2
  159. execsql {
  160. SELECT count(*) FROM t1;
  161. PRAGMA integrity_check
  162. }
  163. } {0 ok}
  164. do_test crash8-3.6 {
  165. set zJournal2 [string replace $zJournal 20 23 [binary format I 0x2000000]]
  166. write_file test.db-journal $zJournal2
  167. execsql {
  168. SELECT count(*) FROM t1;
  169. PRAGMA integrity_check
  170. }
  171. } {0 ok}
  172. do_test crash8-3.7 {
  173. set zJournal2 [string replace $zJournal 20 23 [binary format I 256]]
  174. write_file test.db-journal $zJournal2
  175. execsql {
  176. SELECT count(*) FROM t1;
  177. PRAGMA integrity_check
  178. }
  179. } {0 ok}
  180. do_test crash8-3.8 {
  181. set zJournal2 [string replace $zJournal 24 27 [binary format I 513]]
  182. write_file test.db-journal $zJournal2
  183. execsql {
  184. SELECT count(*) FROM t1;
  185. PRAGMA integrity_check
  186. }
  187. } {0 ok}
  188. do_test crash8-3.9 {
  189. set big [expr $SQLITE_MAX_PAGE_SIZE * 2]
  190. set zJournal2 [string replace $zJournal 24 27 [binary format I $big]]
  191. write_file test.db-journal $zJournal2
  192. execsql {
  193. SELECT count(*) FROM t1;
  194. PRAGMA integrity_check
  195. }
  196. } {0 ok}
  197. do_test crash8-3.10 {
  198. set zJournal2 [string replace $zJournal 24 27 [binary format I 256]]
  199. write_file test.db-journal $zJournal2
  200. execsql {
  201. SELECT count(*) FROM t1;
  202. PRAGMA integrity_check
  203. }
  204. } {0 ok}
  205. do_test crash8-3.11 {
  206. set fd [open test.db-journal w]
  207. fconfigure $fd -translation binary
  208. puts -nonewline $fd $zJournal
  209. close $fd
  210. execsql {
  211. SELECT count(*) FROM t1;
  212. PRAGMA integrity_check
  213. }
  214. } {6 ok}
  215. # If a connection running in persistent-journal mode is part of a
  216. # multi-file transaction, it must ensure that the master-journal name
  217. # appended to the journal file contents during the commit is located
  218. # at the end of the physical journal file. If there was already a
  219. # large journal file allocated at the start of the transaction, this
  220. # may mean truncating the file so that the master journal name really
  221. # is at the physical end of the file.
  222. #
  223. # This block of tests test that SQLite correctly truncates such
  224. # journal files, and that the results behave correctly if a hot-journal
  225. # rollback occurs.
  226. #
  227. ifcapable pragma {
  228. reset_db
  229. file delete -force test2.db
  230. do_test crash8-4.1 {
  231. execsql {
  232. PRAGMA journal_mode = persist;
  233. CREATE TABLE ab(a, b);
  234. INSERT INTO ab VALUES(0, 'abc');
  235. INSERT INTO ab VALUES(1, NULL);
  236. INSERT INTO ab VALUES(2, NULL);
  237. INSERT INTO ab VALUES(3, NULL);
  238. INSERT INTO ab VALUES(4, NULL);
  239. INSERT INTO ab VALUES(5, NULL);
  240. INSERT INTO ab VALUES(6, NULL);
  241. UPDATE ab SET b = randstr(1000,1000);
  242. ATTACH 'test2.db' AS aux;
  243. PRAGMA aux.journal_mode = persist;
  244. CREATE TABLE aux.ab(a, b);
  245. INSERT INTO aux.ab SELECT * FROM main.ab;
  246. UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1;
  247. UPDATE ab SET b = randstr(1000,1000) WHERE a>=1;
  248. }
  249. list [file exists test.db-journal] [file exists test2.db-journal]
  250. } {1 1}
  251. do_test crash8-4.2 {
  252. execsql {
  253. BEGIN;
  254. UPDATE aux.ab SET b = 'def' WHERE a = 0;
  255. UPDATE main.ab SET b = 'def' WHERE a = 0;
  256. COMMIT;
  257. }
  258. } {}
  259. do_test crash8-4.3 {
  260. execsql {
  261. UPDATE aux.ab SET b = randstr(1000,1000) WHERE a>=1;
  262. UPDATE ab SET b = randstr(1000,1000) WHERE a>=1;
  263. }
  264. } {}
  265. set contents_main [db eval {SELECT b FROM main.ab WHERE a = 1}]
  266. set contents_aux [db eval {SELECT b FROM aux.ab WHERE a = 1}]
  267. do_test crash8-4.4 {
  268. crashsql -file test2.db -delay 1 {
  269. ATTACH 'test2.db' AS aux;
  270. BEGIN;
  271. UPDATE aux.ab SET b = 'ghi' WHERE a = 0;
  272. UPDATE main.ab SET b = 'ghi' WHERE a = 0;
  273. COMMIT;
  274. }
  275. } {1 {child process exited abnormally}}
  276. do_test crash8-4.5 {
  277. list [file exists test.db-journal] [file exists test2.db-journal]
  278. } {1 1}
  279. do_test crash8-4.6 {
  280. execsql {
  281. SELECT b FROM main.ab WHERE a = 0;
  282. SELECT b FROM aux.ab WHERE a = 0;
  283. }
  284. } {def def}
  285. do_test crash8-4.7 {
  286. crashsql -file test2.db -delay 1 {
  287. ATTACH 'test2.db' AS aux;
  288. BEGIN;
  289. UPDATE aux.ab SET b = 'jkl' WHERE a = 0;
  290. UPDATE main.ab SET b = 'jkl' WHERE a = 0;
  291. COMMIT;
  292. }
  293. } {1 {child process exited abnormally}}
  294. do_test crash8-4.8 {
  295. set fd [open test.db-journal]
  296. fconfigure $fd -translation binary
  297. seek $fd -16 end
  298. binary scan [read $fd 4] I len
  299. seek $fd [expr {-1 * ($len + 16)}] end
  300. set zMasterJournal [read $fd $len]
  301. close $fd
  302. file exists $zMasterJournal
  303. } {1}
  304. do_test crash8-4.9 {
  305. execsql { SELECT b FROM aux.ab WHERE a = 0 }
  306. } {def}
  307. do_test crash8-4.10 {
  308. file delete $zMasterJournal
  309. execsql { SELECT b FROM main.ab WHERE a = 0 }
  310. } {jkl}
  311. }
  312. for {set i 1} {$i < 10} {incr i} {
  313. catch { db close }
  314. file delete -force test.db test.db-journal
  315. sqlite3 db test.db
  316. do_test crash8-5.$i.1 {
  317. execsql {
  318. CREATE TABLE t1(x PRIMARY KEY);
  319. INSERT INTO t1 VALUES(randomblob(900));
  320. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  321. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  322. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  323. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  324. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  325. INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
  326. }
  327. crashsql -file test.db -delay [expr ($::i%2) + 1] {
  328. PRAGMA cache_size = 10;
  329. BEGIN;
  330. UPDATE t1 SET x = randomblob(900);
  331. ROLLBACK;
  332. INSERT INTO t1 VALUES(randomblob(900));
  333. }
  334. execsql { PRAGMA integrity_check }
  335. } {ok}
  336. catch { db close }
  337. file delete -force test.db test.db-journal
  338. sqlite3 db test.db
  339. do_test crash8-5.$i.2 {
  340. execsql {
  341. PRAGMA cache_size = 10;
  342. CREATE TABLE t1(x PRIMARY KEY);
  343. INSERT INTO t1 VALUES(randomblob(900));
  344. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  345. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  346. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  347. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  348. INSERT INTO t1 SELECT randomblob(900) FROM t1;
  349. INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
  350. BEGIN;
  351. UPDATE t1 SET x = randomblob(900);
  352. }
  353. file delete -force testX.db testX.db-journal testX.db-wal
  354. copy_file test.db testX.db
  355. copy_file test.db-journal testX.db-journal
  356. db close
  357. crashsql -file test.db -delay [expr ($::i%2) + 1] {
  358. SELECT * FROM sqlite_master;
  359. INSERT INTO t1 VALUES(randomblob(900));
  360. }
  361. sqlite3 db2 testX.db
  362. execsql { PRAGMA integrity_check } db2
  363. } {ok}
  364. }
  365. catch {db2 close}
  366. finish_test