sessionfault.test 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. # 2011 Mar 21
  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. # The focus of this file is testing the session module.
  13. #
  14. if {![info exists testdir]} {
  15. set testdir [file join [file dirname [info script]] .. .. test]
  16. }
  17. source [file join [file dirname [info script]] session_common.tcl]
  18. source $testdir/tester.tcl
  19. ifcapable !session {finish_test; return}
  20. set testprefix sessionfault
  21. forcedelete test.db2
  22. sqlite3 db2 test.db2
  23. do_common_sql {
  24. CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
  25. INSERT INTO t1 VALUES(1, 2, 3);
  26. INSERT INTO t1 VALUES(4, 5, 6);
  27. }
  28. faultsim_save_and_close
  29. db2 close
  30. #-------------------------------------------------------------------------
  31. # Test OOM error handling when collecting and applying a simple changeset.
  32. #
  33. # Test 1.1 attaches tables individually by name to the session object.
  34. # Whereas test 1.2 passes NULL to sqlite3session_attach() to attach all
  35. # tables.
  36. #
  37. do_faultsim_test 1.1 -faults oom-* -prep {
  38. catch {db2 close}
  39. catch {db close}
  40. faultsim_restore_and_reopen
  41. sqlite3 db2 test.db2
  42. } -body {
  43. do_then_apply_sql -ignorenoop {
  44. INSERT INTO t1 VALUES('a string value', 8, 9);
  45. UPDATE t1 SET c = 10 WHERE a = 1;
  46. DELETE FROM t1 WHERE a = 4;
  47. }
  48. } -test {
  49. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  50. faultsim_integrity_check
  51. if {$testrc==0} { compare_db db db2 }
  52. }
  53. do_faultsim_test 1.2 -faults oom-* -prep {
  54. catch {db2 close}
  55. catch {db close}
  56. faultsim_restore_and_reopen
  57. } -body {
  58. sqlite3session S db main
  59. S attach *
  60. execsql {
  61. INSERT INTO t1 VALUES('a string value', 8, 9);
  62. UPDATE t1 SET c = 10 WHERE a = 1;
  63. DELETE FROM t1 WHERE a = 4;
  64. }
  65. set ::changeset [S changeset]
  66. set {} {}
  67. } -test {
  68. catch { S delete }
  69. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  70. faultsim_integrity_check
  71. if {$testrc==0} {
  72. proc xConflict {args} { return "OMIT" }
  73. sqlite3 db2 test.db2
  74. sqlite3changeset_apply db2 $::changeset xConflict
  75. compare_db db db2
  76. }
  77. }
  78. #-------------------------------------------------------------------------
  79. # The following block of tests - 2.* - are designed to check
  80. # the handling of faults in the sqlite3changeset_apply() function.
  81. #
  82. catch {db close}
  83. catch {db2 close}
  84. forcedelete test.db2 test.db
  85. sqlite3 db2 test.db2
  86. sqlite3 db test.db
  87. do_common_sql {
  88. CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b));
  89. INSERT INTO t1 VALUES('apple', 'orange', 'pear');
  90. CREATE TABLE t2(x PRIMARY KEY, y);
  91. }
  92. db2 close
  93. faultsim_save_and_close
  94. foreach {tn conflict_policy sql sql2} {
  95. 1 OMIT { INSERT INTO t1 VALUES('one text', 'two text', X'00ff00') } {}
  96. 2 OMIT { DELETE FROM t1 WHERE a = 'apple' } {}
  97. 3 OMIT { UPDATE t1 SET c = 'banana' WHERE b = 'orange' } {}
  98. 4 REPLACE { INSERT INTO t2 VALUES('keyvalue', 'value 1') } {
  99. INSERT INTO t2 VALUES('keyvalue', 'value 2');
  100. }
  101. } {
  102. proc xConflict args [list return $conflict_policy]
  103. do_faultsim_test 2.$tn -faults oom-transient -prep {
  104. catch {db2 close}
  105. catch {db close}
  106. faultsim_restore_and_reopen
  107. set ::changeset [changeset_from_sql $::sql]
  108. sqlite3 db2 test.db2
  109. sqlite3_db_config_lookaside db2 0 0 0
  110. execsql $::sql2 db2
  111. } -body {
  112. sqlite3changeset_apply db2 $::changeset xConflict
  113. } -test {
  114. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  115. faultsim_integrity_check
  116. if {$testrc==0} { compare_db db db2 }
  117. }
  118. }
  119. #-------------------------------------------------------------------------
  120. # This test case is designed so that a malloc() failure occurs while
  121. # resizing the session object hash-table from 256 to 512 buckets. This
  122. # is not an error, just a sub-optimal condition.
  123. #
  124. do_faultsim_test 3 -faults oom-* -prep {
  125. catch {db2 close}
  126. catch {db close}
  127. faultsim_restore_and_reopen
  128. sqlite3 db2 test.db2
  129. sqlite3session S db main
  130. S attach t1
  131. execsql { BEGIN }
  132. for {set i 0} {$i < 125} {incr i} {
  133. execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 10+$i)}
  134. }
  135. } -body {
  136. for {set i 125} {$i < 133} {incr i} {
  137. execsql {INSERT INTO t1 VALUES(10+$i, 10+$i, 1-+$i)}
  138. }
  139. S changeset
  140. set {} {}
  141. } -test {
  142. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  143. if {$testrc==0} {
  144. sqlite3changeset_apply db2 [S changeset] xConflict
  145. compare_db db db2
  146. }
  147. catch { S delete }
  148. faultsim_integrity_check
  149. }
  150. catch { db close }
  151. catch { db2 close }
  152. forcedelete test.db2 test.db
  153. sqlite3 db2 test.db2
  154. sqlite3 db test.db
  155. proc xConflict {op tbl type args} {
  156. if { $type=="CONFLICT" || $type=="DATA" } {
  157. return "REPLACE"
  158. }
  159. return "OMIT"
  160. }
  161. do_test 4.0 {
  162. execsql {
  163. PRAGMA encoding = 'utf16';
  164. CREATE TABLE t1(a PRIMARY KEY, b);
  165. INSERT INTO t1 VALUES(5, 32);
  166. }
  167. execsql {
  168. PRAGMA encoding = 'utf16';
  169. CREATE TABLE t1(a PRIMARY KEY, b NOT NULL);
  170. INSERT INTO t1 VALUES(1, 2);
  171. INSERT INTO t1 VALUES(2, 4);
  172. INSERT INTO t1 VALUES(4, 16);
  173. } db2
  174. } {}
  175. faultsim_save_and_close
  176. db2 close
  177. do_faultsim_test 4 -faults oom-* -prep {
  178. catch {db2 close}
  179. catch {db close}
  180. faultsim_restore_and_reopen
  181. sqlite3 db2 test.db2
  182. sqlite3session S db main
  183. S attach t1
  184. execsql {
  185. INSERT INTO t1 VALUES(1, 45);
  186. INSERT INTO t1 VALUES(2, 55);
  187. INSERT INTO t1 VALUES(3, 55);
  188. UPDATE t1 SET a = 4 WHERE a = 5;
  189. }
  190. } -body {
  191. sqlite3changeset_apply db2 [S changeset] xConflict
  192. } -test {
  193. catch { S delete }
  194. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  195. if {$testrc==0} { compare_db db db2 }
  196. }
  197. #-------------------------------------------------------------------------
  198. # This block of tests verifies that OOM faults in the
  199. # sqlite3changeset_invert() function are handled correctly.
  200. #
  201. catch {db close}
  202. catch {db2 close}
  203. forcedelete test.db
  204. sqlite3 db test.db
  205. execsql {
  206. CREATE TABLE t1(a, b, PRIMARY KEY(b));
  207. CREATE TABLE t2(a PRIMARY KEY, b);
  208. INSERT INTO t1 VALUES('string', 1);
  209. INSERT INTO t1 VALUES(4, 2);
  210. INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
  211. }
  212. set changeset [changeset_from_sql {
  213. INSERT INTO t1 VALUES('xxx', 'yyy');
  214. DELETE FROM t1 WHERE a = 'string';
  215. UPDATE t1 SET a = 20 WHERE b = 2;
  216. }]
  217. db close
  218. do_faultsim_test 5.1 -faults oom* -body {
  219. set ::inverse [sqlite3changeset_invert $::changeset]
  220. set {} {}
  221. } -test {
  222. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  223. if {$testrc==0} {
  224. set x [list]
  225. sqlite3session_foreach c $::inverse { lappend x $c }
  226. foreach c {
  227. {DELETE t1 0 .X {t xxx t yyy} {}}
  228. {INSERT t1 0 .X {} {t string i 1}}
  229. {UPDATE t1 0 .X {i 20 i 2} {i 4 {} {}}}
  230. } { lappend y $c }
  231. if {$x != $y} { error "changeset no good" }
  232. }
  233. }
  234. catch {db close}
  235. catch {db2 close}
  236. forcedelete test.db
  237. sqlite3 db test.db
  238. execsql {
  239. CREATE TABLE t2(a PRIMARY KEY, b);
  240. INSERT INTO t2 VALUES(1, 'abc');
  241. INSERT INTO t2 VALUES(2, 'def');
  242. }
  243. set changeset [changeset_from_sql {
  244. UPDATE t2 SET b = (b || b || b || b);
  245. UPDATE t2 SET b = (b || b || b || b);
  246. UPDATE t2 SET b = (b || b || b || b);
  247. UPDATE t2 SET b = (b || b || b || b);
  248. }]
  249. db close
  250. set abc [string repeat abc 256]
  251. set def [string repeat def 256]
  252. do_faultsim_test 5.2 -faults oom-tra* -body {
  253. set ::inverse [sqlite3changeset_invert $::changeset]
  254. set {} {}
  255. } -test {
  256. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  257. if {$testrc==0} {
  258. set x [list]
  259. sqlite3session_foreach c $::inverse { lappend x $c }
  260. foreach c "
  261. {UPDATE t2 0 X. {i 1 t $::abc} {{} {} t abc}}
  262. {UPDATE t2 0 X. {i 2 t $::def} {{} {} t def}}
  263. " { lappend y $c }
  264. if {$x != $y} { error "changeset no good" }
  265. }
  266. }
  267. catch {db close}
  268. catch {db2 close}
  269. forcedelete test.db
  270. sqlite3 db test.db
  271. set abc [string repeat abc 256]
  272. set def [string repeat def 256]
  273. execsql "
  274. CREATE TABLE t2(a PRIMARY KEY, b);
  275. INSERT INTO t2 VALUES(1, '$abc');
  276. "
  277. set changeset [changeset_from_sql "
  278. INSERT INTO t2 VALUES(2, '$def');
  279. DELETE FROM t2 WHERE a = 1;
  280. "]
  281. db close
  282. do_faultsim_test 5.3 -faults oom-tra* -body {
  283. set ::inverse [sqlite3changeset_invert $::changeset]
  284. set {} {}
  285. } -test {
  286. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  287. if {$testrc==0} {
  288. set x [list]
  289. sqlite3session_foreach c $::inverse { lappend x $c }
  290. foreach c "
  291. {INSERT t2 0 X. {} {i 1 t $::abc}}
  292. {DELETE t2 0 X. {i 2 t $::def} {}}
  293. " { lappend y $c }
  294. if {$x != $y} { error "changeset no good" }
  295. }
  296. }
  297. #-------------------------------------------------------------------------
  298. # Test that OOM errors in sqlite3changeset_concat() are handled correctly.
  299. #
  300. catch {db close}
  301. forcedelete test.db
  302. sqlite3 db test.db
  303. do_execsql_test 5.prep1 {
  304. CREATE TABLE t1(a, b, PRIMARY KEY(b));
  305. CREATE TABLE t2(a PRIMARY KEY, b);
  306. INSERT INTO t1 VALUES('string', 1);
  307. INSERT INTO t1 VALUES(4, 2);
  308. INSERT INTO t1 VALUES(X'FFAAFFAAFFAA', 3);
  309. }
  310. do_test 6.prep2 {
  311. sqlite3session M db main
  312. M attach *
  313. set ::c2 [changeset_from_sql {
  314. INSERT INTO t2 VALUES(randomblob(1000), randomblob(1000));
  315. INSERT INTO t2 VALUES('one', 'two');
  316. INSERT INTO t2 VALUES(1, NULL);
  317. UPDATE t1 SET a = 5 WHERE a = 2;
  318. }]
  319. set ::c1 [changeset_from_sql {
  320. DELETE FROM t2 WHERE a = 1;
  321. UPDATE t1 SET a = 4 WHERE a = 2;
  322. INSERT INTO t2 VALUES('x', 'y');
  323. }]
  324. set ::total [changeset_to_list [M changeset]]
  325. M delete
  326. } {}
  327. do_faultsim_test 6 -faults oom-* -body {
  328. set ::result [sqlite3changeset_concat $::c1 $::c2]
  329. set {} {}
  330. } -test {
  331. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  332. if {$testrc==0} {
  333. set v [changeset_to_list $::result]
  334. if {$v != $::total} { error "result no good" }
  335. }
  336. }
  337. faultsim_delete_and_reopen
  338. do_execsql_test 7.prep1 {
  339. CREATE TABLE t1(a, b, PRIMARY KEY(a));
  340. }
  341. faultsim_save_and_close
  342. unset -nocomplain res
  343. set res [list]
  344. for {set ::i 0} {$::i < 480} {incr ::i 4} {
  345. lappend res "INSERT t1 0 X. {} {i $::i i $::i}"
  346. }
  347. set res [lsort $res]
  348. do_faultsim_test 7 -faults oom-transient -prep {
  349. catch { S delete }
  350. faultsim_restore_and_reopen
  351. sqlite3session S db main
  352. S attach *
  353. } -body {
  354. for {set ::i 0} {$::i < 480} {incr ::i 4} {
  355. execsql {INSERT INTO t1 VALUES($::i, $::i)}
  356. }
  357. } -test {
  358. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  359. if {$testrc==0} {
  360. set cres [list [catch {changeset_to_list [S changeset]} msg] $msg]
  361. S delete
  362. if {$cres != "1 SQLITE_NOMEM" && $cres != "0 {$::res}"} {
  363. error "Expected {0 $::res} Got {$cres}"
  364. }
  365. } else {
  366. catch { S changeset }
  367. catch { S delete }
  368. }
  369. }
  370. faultsim_delete_and_reopen
  371. do_test 8.prep {
  372. sqlite3session S db main
  373. S attach *
  374. execsql {
  375. CREATE TABLE t1(a, b, PRIMARY KEY(a));
  376. INSERT INTO t1 VALUES(1, 2);
  377. INSERT INTO t1 VALUES(3, 4);
  378. INSERT INTO t1 VALUES(5, 6);
  379. }
  380. set ::changeset [S changeset]
  381. S delete
  382. } {}
  383. set expected [normalize_list {
  384. {INSERT t1 0 X. {} {i 1 i 2}}
  385. {INSERT t1 0 X. {} {i 3 i 4}}
  386. {INSERT t1 0 X. {} {i 5 i 6}}
  387. }]
  388. do_faultsim_test 8.1 -faults oom* -body {
  389. set ::res [list]
  390. sqlite3session_foreach -next v $::changeset { lappend ::res $v }
  391. normalize_list $::res
  392. } -test {
  393. faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
  394. }
  395. do_faultsim_test 8.2 -faults oom* -body {
  396. set ::res [list]
  397. sqlite3session_foreach v $::changeset { lappend ::res $v }
  398. normalize_list $::res
  399. } -test {
  400. faultsim_test_result [list 0 $::expected] {1 SQLITE_NOMEM}
  401. }
  402. faultsim_delete_and_reopen
  403. do_test 9.1.prep {
  404. execsql {
  405. PRAGMA encoding = 'utf16';
  406. CREATE TABLE t1(a PRIMARY KEY, b);
  407. }
  408. } {}
  409. faultsim_save_and_close
  410. set answers [list {0 {}} {1 SQLITE_NOMEM} \
  411. {1 {callback requested query abort}} \
  412. {1 {abort due to ROLLBACK}}]
  413. do_faultsim_test 9.1 -faults oom-transient -prep {
  414. catch { unset ::c }
  415. faultsim_restore_and_reopen
  416. sqlite3session S db main
  417. S attach *
  418. } -body {
  419. execsql {
  420. INSERT INTO t1 VALUES('abcdefghijklmnopqrstuv', 'ABCDEFGHIJKLMNOPQRSTUV');
  421. }
  422. set ::c [S changeset]
  423. set {} {}
  424. } -test {
  425. S delete
  426. eval faultsim_test_result $::answers
  427. if {[info exists ::c]} {
  428. set expected [normalize_list {
  429. {INSERT t1 0 X. {} {t abcdefghijklmnopqrstuv t ABCDEFGHIJKLMNOPQRSTUV}}
  430. }]
  431. if { [changeset_to_list $::c] != $expected } {
  432. error "changeset mismatch"
  433. }
  434. }
  435. }
  436. faultsim_delete_and_reopen
  437. do_test 9.2.prep {
  438. execsql {
  439. PRAGMA encoding = 'utf16';
  440. CREATE TABLE t1(a PRIMARY KEY, b);
  441. INSERT INTO t1 VALUES('abcdefghij', 'ABCDEFGHIJKLMNOPQRSTUV');
  442. }
  443. } {}
  444. faultsim_save_and_close
  445. set answers [list {0 {}} {1 SQLITE_NOMEM} \
  446. {1 {callback requested query abort}} \
  447. {1 {abort due to ROLLBACK}}]
  448. do_faultsim_test 9.2 -faults oom-transient -prep {
  449. catch { unset ::c }
  450. faultsim_restore_and_reopen
  451. sqlite3session S db main
  452. S attach *
  453. } -body {
  454. execsql {
  455. UPDATE t1 SET b = 'xyz';
  456. }
  457. set ::c [S changeset]
  458. set {} {}
  459. } -test {
  460. S delete
  461. eval faultsim_test_result $::answers
  462. if {[info exists ::c]} {
  463. set expected [normalize_list {
  464. {UPDATE t1 0 X. {t abcdefghij t ABCDEFGHIJKLMNOPQRSTUV} {{} {} t xyz}}
  465. }]
  466. if { [changeset_to_list $::c] != $expected } {
  467. error "changeset mismatch"
  468. }
  469. }
  470. }
  471. #-------------------------------------------------------------------------
  472. # Test that if a conflict-handler encounters an OOM in
  473. # sqlite3_value_text() but goes on to return SQLITE_CHANGESET_REPLACE
  474. # anyway, the OOM is picked up by the sessions module.
  475. set bigstr [string repeat abcdefghij 100]
  476. faultsim_delete_and_reopen
  477. do_test 10.prep.1 {
  478. execsql {
  479. CREATE TABLE t1(a PRIMARY KEY, b);
  480. INSERT INTO t1 VALUES($bigstr, $bigstr);
  481. }
  482. sqlite3session S db main
  483. S attach *
  484. execsql { UPDATE t1 SET b = b||'x' }
  485. set C [S changeset]
  486. S delete
  487. execsql { UPDATE t1 SET b = b||'xyz' }
  488. } {}
  489. faultsim_save_and_close
  490. faultsim_restore_and_reopen
  491. do_test 10.prep.2 {
  492. proc xConflict {args} { return "ABORT" }
  493. list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
  494. } {1 SQLITE_ABORT}
  495. do_execsql_test 10.prep.3 { SELECT b=$bigstr||'x' FROM t1 } 0
  496. do_test 10.prep.4 {
  497. proc xConflict {args} { return "REPLACE" }
  498. list [catch { sqlite3changeset_apply db $C xConflict } msg] $msg
  499. } {0 {}}
  500. do_execsql_test 10.prep.5 { SELECT b=$bigstr||'x' FROM t1 } 1
  501. db close
  502. do_faultsim_test 10 -faults oom-tra* -prep {
  503. faultsim_restore_and_reopen
  504. } -body {
  505. sqlite3changeset_apply_replace_all db $::C
  506. } -test {
  507. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  508. if {$testrc==0} {
  509. if {[db one {SELECT b=$bigstr||'x' FROM t1}]==0} {
  510. error "data does not look right"
  511. }
  512. }
  513. }
  514. #-------------------------------------------------------------------------
  515. # Test an OOM with an sqlite3changeset_apply() filter callback.
  516. #
  517. reset_db
  518. do_test 11.prep {
  519. execsql {
  520. CREATE TABLE t1(a PRIMARY KEY, b);
  521. CREATE TABLE t2(x PRIMARY KEY, y);
  522. BEGIN;
  523. }
  524. set ::cs [changeset_from_sql {
  525. INSERT INTO t1 VALUES(1, 2);
  526. INSERT INTO t2 VALUES('x', 'y');
  527. }]
  528. execsql ROLLBACK
  529. set {} {}
  530. } {}
  531. proc filter {x} { return [string equal t1 $x] }
  532. faultsim_save_and_close
  533. do_faultsim_test 11 -faults oom-tra* -prep {
  534. faultsim_restore_and_reopen
  535. } -body {
  536. sqlite3changeset_apply db $::cs {} filter
  537. } -test {
  538. faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
  539. if {$testrc==0} {
  540. if {[db eval {SELECT * FROM t1 UNION ALL SELECT * FROM t2}] != "1 2"} {
  541. error "data does not look right"
  542. }
  543. }
  544. }
  545. finish_test