ioerr.test 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. # 2001 October 12
  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 for correct handling of I/O errors
  13. # such as writes failing because the disk is full.
  14. #
  15. # The tests in this file use special facilities that are only
  16. # available in the SQLite test fixture.
  17. #
  18. # $Id: ioerr.test,v 1.43 2009/04/06 17:50:03 danielk1977 Exp $
  19. set testdir [file dirname $argv0]
  20. source $testdir/tester.tcl
  21. # If SQLITE_DEFAULT_AUTOVACUUM is set to true, then a simulated IO error
  22. # on the 8th IO operation in the SQL script below doesn't report an error.
  23. #
  24. # This is because the 8th IO call attempts to read page 2 of the database
  25. # file when the file on disk is only 1 page. The pager layer detects that
  26. # this has happened and suppresses the error returned by the OS layer.
  27. #
  28. do_ioerr_test ioerr-1 -erc 1 -ckrefcount 1 -sqlprep {
  29. SELECT * FROM sqlite_master;
  30. } -sqlbody {
  31. CREATE TABLE t1(a,b,c);
  32. SELECT * FROM sqlite_master;
  33. BEGIN TRANSACTION;
  34. INSERT INTO t1 VALUES(1,2,3);
  35. INSERT INTO t1 VALUES(4,5,6);
  36. ROLLBACK;
  37. SELECT * FROM t1;
  38. BEGIN TRANSACTION;
  39. INSERT INTO t1 VALUES(1,2,3);
  40. INSERT INTO t1 VALUES(4,5,6);
  41. COMMIT;
  42. SELECT * FROM t1;
  43. DELETE FROM t1 WHERE a<100;
  44. } -exclude [expr [string match [execsql {pragma auto_vacuum}] 1] ? 4 : 0]
  45. # Test for IO errors during a VACUUM.
  46. #
  47. # The first IO call is excluded from the test. This call attempts to read
  48. # the file-header of the temporary database used by VACUUM. Since the
  49. # database doesn't exist at that point, the IO error is not detected.
  50. #
  51. # Additionally, if auto-vacuum is enabled, the 12th IO error is not
  52. # detected. Same reason as the 8th in the test case above.
  53. #
  54. ifcapable vacuum {
  55. do_ioerr_test ioerr-2 -cksum true -ckrefcount true -sqlprep {
  56. BEGIN;
  57. CREATE TABLE t1(a, b, c);
  58. INSERT INTO t1 VALUES(1, randstr(50,50), randstr(50,50));
  59. INSERT INTO t1 SELECT a+2, b||'-'||rowid, c||'-'||rowid FROM t1;
  60. INSERT INTO t1 SELECT a+4, b||'-'||rowid, c||'-'||rowid FROM t1;
  61. INSERT INTO t1 SELECT a+8, b||'-'||rowid, c||'-'||rowid FROM t1;
  62. INSERT INTO t1 SELECT a+16, b||'-'||rowid, c||'-'||rowid FROM t1;
  63. INSERT INTO t1 SELECT a+32, b||'-'||rowid, c||'-'||rowid FROM t1;
  64. INSERT INTO t1 SELECT a+64, b||'-'||rowid, c||'-'||rowid FROM t1;
  65. INSERT INTO t1 SELECT a+128, b||'-'||rowid, c||'-'||rowid FROM t1;
  66. INSERT INTO t1 VALUES(1, randstr(600,600), randstr(600,600));
  67. CREATE TABLE t2 AS SELECT * FROM t1;
  68. CREATE TABLE t3 AS SELECT * FROM t1;
  69. COMMIT;
  70. DROP TABLE t2;
  71. } -sqlbody {
  72. VACUUM;
  73. } -exclude [list \
  74. 1 [expr [string match [execsql {pragma auto_vacuum}] 1]?9:-1]]
  75. }
  76. do_ioerr_test ioerr-3 -ckrefcount true -tclprep {
  77. execsql {
  78. PRAGMA cache_size = 10;
  79. BEGIN;
  80. CREATE TABLE abc(a);
  81. INSERT INTO abc VALUES(randstr(1500,1500)); -- Page 4 is overflow
  82. }
  83. for {set i 0} {$i<150} {incr i} {
  84. execsql {
  85. INSERT INTO abc VALUES(randstr(100,100));
  86. }
  87. }
  88. execsql COMMIT
  89. } -sqlbody {
  90. CREATE TABLE abc2(a);
  91. BEGIN;
  92. DELETE FROM abc WHERE length(a)>100;
  93. UPDATE abc SET a = randstr(90,90);
  94. COMMIT;
  95. CREATE TABLE abc3(a);
  96. }
  97. # Test IO errors that can occur retrieving a record header that flows over
  98. # onto an overflow page.
  99. do_ioerr_test ioerr-4 -ckrefcount true -tclprep {
  100. set sql "CREATE TABLE abc(a1"
  101. for {set i 2} {$i<1300} {incr i} {
  102. append sql ", a$i"
  103. }
  104. append sql ");"
  105. execsql $sql
  106. execsql {INSERT INTO abc (a1) VALUES(NULL)}
  107. } -sqlbody {
  108. SELECT * FROM abc;
  109. }
  110. # Test IO errors that may occur during a multi-file commit.
  111. #
  112. # Tests 8 and 17 are excluded when auto-vacuum is enabled for the same
  113. # reason as in test cases ioerr-1.XXX
  114. ifcapable attach {
  115. set ex ""
  116. if {[string match [execsql {pragma auto_vacuum}] 1]} {
  117. set ex [list 4 17]
  118. }
  119. do_ioerr_test ioerr-5 -restoreprng 0 -ckrefcount true -sqlprep {
  120. ATTACH 'test2.db' AS test2;
  121. } -sqlbody {
  122. BEGIN;
  123. CREATE TABLE t1(a,b,c);
  124. CREATE TABLE test2.t2(a,b,c);
  125. COMMIT;
  126. } -exclude $ex
  127. }
  128. # Test IO errors when replaying two hot journals from a 2-file
  129. # transaction. This test only runs on UNIX.
  130. #
  131. # It cannot be run under the "exclusive" permutation. In that case, the
  132. # locks held by the connection in the local (this) process prevent a
  133. # second connection from attempting the multi-file transaction.
  134. #
  135. ifcapable crashtest&&attach {
  136. if {![catch {sqlite3 -has-codec} r] && !$r && [permutation]!="exclusive"} {
  137. do_ioerr_test ioerr-6 -ckrefcount true -tclprep {
  138. execsql {
  139. ATTACH 'test2.db' as aux;
  140. CREATE TABLE tx(a, b);
  141. CREATE TABLE aux.ty(a, b);
  142. }
  143. set rc [crashsql -delay 2 -file test2.db-journal {
  144. ATTACH 'test2.db' as aux;
  145. PRAGMA cache_size = 10;
  146. BEGIN;
  147. CREATE TABLE aux.t2(a, b, c);
  148. CREATE TABLE t1(a, b, c);
  149. COMMIT;
  150. }]
  151. if {$rc!="1 {child process exited abnormally}"} {
  152. error "Wrong error message: $rc"
  153. }
  154. } -sqlbody {
  155. SELECT * FROM sqlite_master;
  156. SELECT * FROM aux.sqlite_master;
  157. }
  158. }
  159. }
  160. # Test handling of IO errors that occur while rolling back hot journal
  161. # files.
  162. #
  163. # These tests can't be run on windows because the windows version of
  164. # SQLite holds a mandatory exclusive lock on journal files it has open.
  165. #
  166. if {$tcl_platform(platform)!="windows"} {
  167. do_ioerr_test ioerr-7 -tclprep {
  168. db close
  169. sqlite3 db2 test2.db
  170. db2 eval {
  171. PRAGMA synchronous = 0;
  172. CREATE TABLE t1(a, b);
  173. INSERT INTO t1 VALUES(1, 2);
  174. BEGIN;
  175. INSERT INTO t1 VALUES(3, 4);
  176. }
  177. forcecopy test2.db test.db
  178. forcecopy test2.db-journal test.db-journal
  179. db2 close
  180. } -tclbody {
  181. sqlite3 db test.db
  182. db eval {
  183. SELECT * FROM t1;
  184. }
  185. } -exclude 1
  186. }
  187. # For test coverage: Cause an I/O failure while trying to read a
  188. # short field (one that fits into a Mem buffer without mallocing
  189. # for space).
  190. #
  191. do_ioerr_test ioerr-8 -ckrefcount true -tclprep {
  192. execsql {
  193. CREATE TABLE t1(a,b,c);
  194. INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
  195. }
  196. db close
  197. sqlite3 db test.db
  198. } -sqlbody {
  199. SELECT c FROM t1;
  200. }
  201. # For test coverage: Cause an IO error whilst reading the master-journal
  202. # name from a journal file.
  203. if {$tcl_platform(platform)=="unix"} {
  204. do_ioerr_test ioerr-9 -ckrefcount true -tclprep {
  205. execsql {
  206. CREATE TABLE t1(a,b,c);
  207. INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
  208. BEGIN;
  209. INSERT INTO t1 VALUES(randstr(200,200), randstr(1000,1000), 2);
  210. }
  211. forcecopy test.db-journal test2.db-journal
  212. execsql {
  213. COMMIT;
  214. }
  215. forcecopy test2.db-journal test.db-journal
  216. set f [open test.db-journal a]
  217. fconfigure $f -encoding binary
  218. puts -nonewline $f "hello"
  219. puts -nonewline $f "\x00\x00\x00\x05\x01\x02\x03\x04"
  220. puts -nonewline $f "\xd9\xd5\x05\xf9\x20\xa1\x63\xd7"
  221. close $f
  222. } -sqlbody {
  223. SELECT a FROM t1;
  224. }
  225. }
  226. # For test coverage: Cause an IO error during statement playback (i.e.
  227. # a constraint).
  228. do_ioerr_test ioerr-10 -ckrefcount true -tclprep {
  229. execsql {
  230. BEGIN;
  231. CREATE TABLE t1(a PRIMARY KEY, b);
  232. }
  233. for {set i 0} {$i < 500} {incr i} {
  234. execsql {INSERT INTO t1 VALUES(:i, 'hello world');}
  235. }
  236. execsql {
  237. COMMIT;
  238. }
  239. } -tclbody {
  240. catch {execsql {
  241. BEGIN;
  242. INSERT INTO t1 VALUES('abc', 123);
  243. INSERT INTO t1 VALUES('def', 123);
  244. INSERT INTO t1 VALUES('ghi', 123);
  245. INSERT INTO t1 SELECT (a+500)%900, 'good string' FROM t1;
  246. }} msg
  247. if {$msg != "column a is not unique"} {
  248. error $msg
  249. }
  250. }
  251. # Assertion fault bug reported by alex dimitrov.
  252. #
  253. do_ioerr_test ioerr-11 -ckrefcount true -erc 1 -sqlprep {
  254. CREATE TABLE A(Id INTEGER, Name TEXT);
  255. INSERT INTO A(Id, Name) VALUES(1, 'Name');
  256. } -sqlbody {
  257. UPDATE A SET Id = 2, Name = 'Name2' WHERE Id = 1;
  258. }
  259. # Test that an io error encountered in a sync() caused by a call to
  260. # sqlite3_release_memory() is handled Ok. Only try this if
  261. # memory-management is enabled.
  262. #
  263. ifcapable memorymanage {
  264. do_ioerr_test memmanage-ioerr1 -ckrefcount true -sqlprep {
  265. BEGIN;
  266. CREATE TABLE t1(a, b, c);
  267. INSERT INTO t1 VALUES(randstr(50,50), randstr(100,100), randstr(10,10));
  268. INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
  269. INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
  270. INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
  271. INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
  272. INSERT INTO t1 SELECT randstr(50,50), randstr(9,9), randstr(90,90) FROM t1;
  273. } -tclbody {
  274. sqlite3_release_memory
  275. } -sqlbody {
  276. COMMIT;
  277. }
  278. }
  279. ifcapable pager_pragmas&&autovacuum {
  280. do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -sqlprep {
  281. PRAGMA page_size = 512;
  282. PRAGMA auto_vacuum = incremental;
  283. CREATE TABLE t1(x);
  284. INSERT INTO t1 VALUES( randomblob(1 * (512-4)) );
  285. INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
  286. INSERT INTO t1 VALUES( randomblob(2 * (512-4)) );
  287. INSERT INTO t1 VALUES( randomblob(110 * (512-4)) );
  288. INSERT INTO t1 VALUES( randomblob(3 * (512-4)) );
  289. DELETE FROM t1 WHERE rowid = 3;
  290. PRAGMA incremental_vacuum = 2;
  291. DELETE FROM t1 WHERE rowid = 1;
  292. } -sqlbody {
  293. PRAGMA incremental_vacuum = 1;
  294. }
  295. }
  296. # Usually, after a new page is allocated from the end of the file, it does
  297. # not need to be written to the journal. The exception is when the new page
  298. # shares its sector with an existing page that does need to be journalled.
  299. # This test case provokes this condition to test for the sake of coverage
  300. # that an IO error while journalling the coresident page is handled correctly.
  301. #
  302. sqlite3_simulate_device -char {} -sectorsize 2048
  303. do_ioerr_test ioerr-12 -ckrefcount true -erc 1 -tclprep {
  304. db close
  305. sqlite3 db test.db -vfs devsym
  306. # Create a test database. Page 2 is the root page of table t1. The only
  307. # row inserted into t1 has an overflow page - page 3. Page 3 will be
  308. # coresident on the 2048 byte sector with the next page to be allocated.
  309. #
  310. db eval { PRAGMA page_size = 1024 }
  311. db eval { CREATE TABLE t1(x) }
  312. db eval { INSERT INTO t1 VALUES(randomblob(1100)); }
  313. } -tclbody {
  314. db eval { INSERT INTO t1 VALUES(randomblob(2000)); }
  315. }
  316. sqlite3_simulate_device -char {} -sectorsize 0
  317. catch {db close}
  318. do_ioerr_test ioerr-13 -ckrefcount true -erc 1 -sqlprep {
  319. PRAGMA auto_vacuum = incremental;
  320. CREATE TABLE t1(x);
  321. CREATE TABLE t2(x);
  322. INSERT INTO t2 VALUES(randomblob(1500));
  323. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  324. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  325. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  326. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  327. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  328. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  329. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  330. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  331. INSERT INTO t1 VALUES(randomblob(20));
  332. INSERT INTO t1 SELECT x FROM t1;
  333. INSERT INTO t1 SELECT x FROM t1;
  334. INSERT INTO t1 SELECT x FROM t1;
  335. INSERT INTO t1 SELECT x FROM t1;
  336. INSERT INTO t1 SELECT x FROM t1;
  337. INSERT INTO t1 SELECT x FROM t1; /* 64 entries in t1 */
  338. INSERT INTO t1 SELECT x FROM t1 LIMIT 14; /* 78 entries in t1 */
  339. DELETE FROM t2 WHERE rowid = 3;
  340. } -sqlbody {
  341. -- This statement uses the balance_quick() optimization. The new page
  342. -- is appended to the database file. But the overflow page used by
  343. -- the new record will be positioned near the start of the database
  344. -- file, in the gap left by the "DELETE FROM t2 WHERE rowid=3" statement
  345. -- above.
  346. --
  347. -- The point of this is that the statement wil need to update two pointer
  348. -- map pages. Which introduces another opportunity for an IO error.
  349. --
  350. INSERT INTO t1 VALUES(randomblob(2000));
  351. }
  352. do_ioerr_test ioerr-14 -ckrefcount true -erc 1 -sqlprep {
  353. PRAGMA auto_vacuum = incremental;
  354. CREATE TABLE t1(x);
  355. CREATE TABLE t2(x);
  356. INSERT INTO t2 VALUES(randomblob(1500));
  357. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  358. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  359. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  360. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  361. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  362. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  363. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  364. INSERT INTO t2 SELECT randomblob(1500) FROM t2;
  365. -- This statement inserts a row into t1 with an overflow page at the
  366. -- end of the file. A long way from its parent (the root of t1).
  367. INSERT INTO t1 VALUES(randomblob(1500));
  368. DELETE FROM t2 WHERE rowid<10;
  369. } -sqlbody {
  370. -- This transaction will cause the root-page of table t1 to divide
  371. -- (by calling balance_deeper()). When it does, the "parent" page of the
  372. -- overflow page inserted in the -sqlprep block above will change and
  373. -- the corresponding pointer map page be updated. This test case attempts
  374. -- to cause an IO error during the pointer map page update.
  375. --
  376. BEGIN;
  377. INSERT INTO t1 VALUES(randomblob(100));
  378. INSERT INTO t1 VALUES(randomblob(100));
  379. INSERT INTO t1 VALUES(randomblob(100));
  380. INSERT INTO t1 VALUES(randomblob(100));
  381. INSERT INTO t1 VALUES(randomblob(100));
  382. INSERT INTO t1 VALUES(randomblob(100));
  383. INSERT INTO t1 VALUES(randomblob(100));
  384. INSERT INTO t1 VALUES(randomblob(100));
  385. INSERT INTO t1 VALUES(randomblob(100));
  386. INSERT INTO t1 VALUES(randomblob(100));
  387. COMMIT;
  388. }
  389. do_ioerr_test ioerr-15 -tclprep {
  390. db eval {
  391. BEGIN;
  392. PRAGMA cache_size = 10;
  393. CREATE TABLE t1(a);
  394. CREATE INDEX i1 ON t1(a);
  395. CREATE TABLE t2(a);
  396. }
  397. for {set ii 1} {$ii < 100} {incr ii} {
  398. set v [string range [string repeat [format %.3d $ii] 200] 0 220]
  399. db eval {INSERT INTO t1 VALUES($v)}
  400. }
  401. db eval {
  402. DELETE FROM t1 WHERE oid > 85;
  403. COMMIT;
  404. }
  405. } -sqlbody {
  406. BEGIN;
  407. INSERT INTO t2 VALUES(randstr(22000,22000));
  408. DELETE FROM t1 WHERE oid = 83;
  409. COMMIT;
  410. }
  411. # This test verifies that IO errors that occur within the obscure branch
  412. # of code executed by tkt3762.test are correctly reported.
  413. #
  414. ifcapable vacuum&&autovacuum&&pragma {
  415. do_ioerr_test ioerr-16 -erc 1 -ckrefcount 1 -sqlprep {
  416. PRAGMA auto_vacuum=INCREMENTAL;
  417. PRAGMA page_size=1024;
  418. BEGIN;
  419. CREATE TABLE t1(x);
  420. INSERT INTO t1 VALUES(zeroblob(900));
  421. INSERT INTO t1 VALUES(zeroblob(900));
  422. INSERT INTO t1 SELECT x FROM t1;
  423. INSERT INTO t1 SELECT x FROM t1;
  424. INSERT INTO t1 SELECT x FROM t1;
  425. INSERT INTO t1 SELECT x FROM t1;
  426. INSERT INTO t1 SELECT x FROM t1;
  427. INSERT INTO t1 SELECT x FROM t1;
  428. INSERT INTO t1 SELECT x FROM t1;
  429. DELETE FROM t1 WHERE rowid>202;
  430. COMMIT;
  431. VACUUM;
  432. PRAGMA cache_size = 10;
  433. BEGIN;
  434. DELETE FROM t1 WHERE rowid IN (10,11,12) ;
  435. } -sqlbody {
  436. PRAGMA incremental_vacuum(10);
  437. COMMIT;
  438. }
  439. }
  440. finish_test