fts5fault4.test 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. # 2014 June 17
  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 file is focused on OOM errors.
  13. #
  14. source [file join [file dirname [info script]] fts5_common.tcl]
  15. source $testdir/malloc_common.tcl
  16. set testprefix fts5fault4
  17. # If SQLITE_ENABLE_FTS5 is not defined, omit this file.
  18. ifcapable !fts5 {
  19. finish_test
  20. return
  21. }
  22. set ::TMPDBERROR [list 1 \
  23. {unable to open a temporary database file for storing temporary tables}
  24. ]
  25. #-------------------------------------------------------------------------
  26. # An OOM while dropping an fts5 table.
  27. #
  28. db func rnddoc fts5_rnddoc
  29. do_test 1.0 {
  30. execsql { CREATE VIRTUAL TABLE xx USING fts5(x) }
  31. } {}
  32. faultsim_save_and_close
  33. do_faultsim_test 1 -faults oom-* -prep {
  34. faultsim_restore_and_reopen
  35. execsql { SELECT * FROM xx }
  36. } -body {
  37. execsql { DROP TABLE xx }
  38. } -test {
  39. faultsim_test_result [list 0 {}]
  40. }
  41. #-------------------------------------------------------------------------
  42. # An OOM while "reseeking" an FTS cursor.
  43. #
  44. do_execsql_test 3.0 {
  45. CREATE VIRTUAL TABLE jj USING fts5(j);
  46. INSERT INTO jj(rowid, j) VALUES(101, 'm t w t f s s');
  47. INSERT INTO jj(rowid, j) VALUES(202, 't w t f s');
  48. INSERT INTO jj(rowid, j) VALUES(303, 'w t f');
  49. INSERT INTO jj(rowid, j) VALUES(404, 't');
  50. }
  51. faultsim_save_and_close
  52. do_faultsim_test 3 -faults oom-* -prep {
  53. faultsim_restore_and_reopen
  54. execsql { SELECT * FROM jj }
  55. } -body {
  56. set res [list]
  57. db eval { SELECT rowid FROM jj WHERE jj MATCH 't' } {
  58. lappend res $rowid
  59. if {$rowid==303} {
  60. execsql { DELETE FROM jj WHERE rowid=404 }
  61. }
  62. }
  63. set res
  64. } -test {
  65. faultsim_test_result [list 0 {101 202 303}]
  66. }
  67. #-------------------------------------------------------------------------
  68. # An OOM within a special "*reads" query.
  69. #
  70. reset_db
  71. db func rnddoc fts5_rnddoc
  72. do_execsql_test 4.0 {
  73. CREATE VIRTUAL TABLE x1 USING fts5(x);
  74. INSERT INTO x1(x1, rank) VALUES('pgsz', 32);
  75. WITH ii(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<10 )
  76. INSERT INTO x1 SELECT rnddoc(5) FROM ii;
  77. }
  78. set ::res [db eval {SELECT rowid, x1 FROM x1 WHERE x1 MATCH '*reads'}]
  79. do_faultsim_test 4 -faults oom-* -body {
  80. db eval {SELECT rowid, x, x1 FROM x1 WHERE x1 MATCH '*reads'}
  81. } -test {
  82. faultsim_test_result {0 {0 {} 2}}
  83. }
  84. #-------------------------------------------------------------------------
  85. # An OOM within a query that uses a custom rank function.
  86. #
  87. reset_db
  88. do_execsql_test 5.0 {
  89. PRAGMA encoding='utf16';
  90. CREATE VIRTUAL TABLE x2 USING fts5(x);
  91. INSERT INTO x2(rowid, x) VALUES(10, 'a b c'); -- 3
  92. INSERT INTO x2(rowid, x) VALUES(20, 'a b c'); -- 6
  93. INSERT INTO x2(rowid, x) VALUES(30, 'a b c'); -- 2
  94. INSERT INTO x2(rowid, x) VALUES(40, 'a b c'); -- 5
  95. INSERT INTO x2(rowid, x) VALUES(50, 'a b c'); -- 1
  96. }
  97. proc rowidmod {cmd mod} {
  98. set row [$cmd xRowid]
  99. expr {$row % $mod}
  100. }
  101. sqlite3_fts5_create_function db rowidmod rowidmod
  102. do_faultsim_test 5.1 -faults oom-* -body {
  103. db eval {
  104. SELECT rowid || '-' || rank FROM x2 WHERE x2 MATCH 'b' AND
  105. rank MATCH "rowidmod('7')" ORDER BY rank
  106. }
  107. } -test {
  108. faultsim_test_result {0 {50-1 30-2 10-3 40-5 20-6}}
  109. }
  110. proc rowidprefix {cmd prefix} {
  111. set row [$cmd xRowid]
  112. set {} "${row}-${prefix}"
  113. }
  114. sqlite3_fts5_create_function db rowidprefix rowidprefix
  115. set str [string repeat abcdefghijklmnopqrstuvwxyz 10]
  116. do_faultsim_test 5.2 -faults oom-* -body {
  117. db eval "
  118. SELECT rank, x FROM x2 WHERE x2 MATCH 'b' AND
  119. rank MATCH 'rowidprefix(''$::str'')'
  120. LIMIT 1
  121. "
  122. } -test {
  123. faultsim_test_result "0 {10-$::str {a b c}}"
  124. }
  125. #-------------------------------------------------------------------------
  126. # OOM errors within auxiliary functions.
  127. #
  128. reset_db
  129. do_execsql_test 6.0 {
  130. CREATE VIRTUAL TABLE x3 USING fts5(xxx);
  131. INSERT INTO x3 VALUES('a b c d c b a');
  132. INSERT INTO x3 VALUES('a a a a a a a');
  133. INSERT INTO x3 VALUES('a a a a a a a');
  134. }
  135. do_faultsim_test 6.1 -faults oom-t* -body {
  136. db eval { SELECT highlight(x3, 0, '*', '*') FROM x3 WHERE x3 MATCH 'c' }
  137. } -test {
  138. faultsim_test_result {0 {{a b *c* d *c* b a}}}
  139. }
  140. proc firstinst {cmd} {
  141. foreach {p c o} [$cmd xInst 0] {}
  142. expr $c*100 + $o
  143. }
  144. sqlite3_fts5_create_function db firstinst firstinst
  145. do_faultsim_test 6.2 -faults oom-t* -body {
  146. db eval { SELECT firstinst(x3) FROM x3 WHERE x3 MATCH 'c' }
  147. } -test {
  148. faultsim_test_result {0 2} {1 SQLITE_NOMEM}
  149. }
  150. proc previc {cmd} {
  151. set res [$cmd xGetAuxdataInt 0]
  152. $cmd xSetAuxdataInt [$cmd xInstCount]
  153. return $res
  154. }
  155. sqlite3_fts5_create_function db previc previc
  156. do_faultsim_test 6.2 -faults oom-t* -body {
  157. db eval { SELECT previc(x3) FROM x3 WHERE x3 MATCH 'a' }
  158. } -test {
  159. faultsim_test_result {0 {0 2 7}} {1 SQLITE_NOMEM}
  160. }
  161. #-------------------------------------------------------------------------
  162. # OOM error when querying for a phrase with many tokens.
  163. #
  164. reset_db
  165. do_execsql_test 7.0 {
  166. CREATE VIRTUAL TABLE tt USING fts5(x, y);
  167. INSERT INTO tt VALUES('f b g b c b', 'f a d c c b'); -- 1
  168. INSERT INTO tt VALUES('d a e f e d', 'f b b d e e'); -- 2
  169. INSERT INTO tt VALUES('f b g a d c', 'e f c f a d'); -- 3
  170. INSERT INTO tt VALUES('f f c d g f', 'f a e b g b'); -- 4
  171. INSERT INTO tt VALUES('a g b d a g', 'e g a e a c'); -- 5
  172. INSERT INTO tt VALUES('c d b d e f', 'f g e g e e'); -- 6
  173. INSERT INTO tt VALUES('e g f f b c', 'f c e f g f'); -- 7
  174. INSERT INTO tt VALUES('e g c f c e', 'f e e a f g'); -- 8
  175. INSERT INTO tt VALUES('e a e b e e', 'd c c f f f'); -- 9
  176. INSERT INTO tt VALUES('f a g g c c', 'e g d g c e'); -- 10
  177. INSERT INTO tt VALUES('c d b a e f', 'f g e h e e'); -- 11
  178. CREATE VIRTUAL TABLE tt2 USING fts5(o);
  179. INSERT INTO tt2(rowid, o) SELECT rowid, x||' '||y FROM tt;
  180. INSERT INTO tt2(rowid, o) VALUES(12, 'a b c d e f g h i j k l');
  181. }
  182. do_faultsim_test 7.2 -faults oom-* -body {
  183. db eval { SELECT rowid FROM tt WHERE tt MATCH 'f+g+e+g+e+e' }
  184. } -test {
  185. faultsim_test_result {0 6} {1 SQLITE_NOMEM}
  186. }
  187. do_faultsim_test 7.3 -faults oom-* -body {
  188. db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d e f)' }
  189. } -test {
  190. faultsim_test_result {0 11} {1 SQLITE_NOMEM}
  191. }
  192. do_faultsim_test 7.4 -faults oom-t* -body {
  193. db eval { SELECT rowid FROM tt2 WHERE tt2 MATCH '"g c f c e f e e a f"' }
  194. } -test {
  195. faultsim_test_result {0 8} {1 SQLITE_NOMEM}
  196. }
  197. do_faultsim_test 7.5 -faults oom-* -body {
  198. db eval {SELECT rowid FROM tt2 WHERE tt2 MATCH 'NEAR(a b c d e f g h i j k)'}
  199. } -test {
  200. faultsim_test_result {0 12} {1 SQLITE_NOMEM}
  201. }
  202. do_faultsim_test 7.6 -faults oom-* -body {
  203. db eval {SELECT rowid FROM tt WHERE tt MATCH 'y: "c c"'}
  204. } -test {
  205. faultsim_test_result {0 {1 9}} {1 SQLITE_NOMEM}
  206. }
  207. #-------------------------------------------------------------------------
  208. #
  209. reset_db
  210. do_execsql_test 8.0 {
  211. CREATE VIRTUAL TABLE tt USING fts5(x);
  212. INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
  213. BEGIN;
  214. INSERT INTO tt(rowid, x) VALUES(1, 'a b c d x x');
  215. WITH ii(i) AS (SELECT 2 UNION ALL SELECT i+1 FROM ii WHERE i<99)
  216. INSERT INTO tt(rowid, x) SELECT i, 'a b c x x d' FROM ii;
  217. INSERT INTO tt(rowid, x) VALUES(100, 'a b c d x x');
  218. COMMIT;
  219. }
  220. do_faultsim_test 8.1 -faults oom-t* -body {
  221. db eval { SELECT rowid FROM tt WHERE tt MATCH 'NEAR(a b c d, 2)' }
  222. } -test {
  223. faultsim_test_result {0 {1 100}} {1 SQLITE_NOMEM}
  224. }
  225. do_faultsim_test 8.2 -faults oom-t* -body {
  226. db eval { SELECT count(*) FROM tt WHERE tt MATCH 'a OR d' }
  227. } -test {
  228. faultsim_test_result {0 100} {1 SQLITE_NOMEM}
  229. }
  230. #-------------------------------------------------------------------------
  231. # Fault in NOT query.
  232. #
  233. reset_db
  234. do_execsql_test 9.0 {
  235. CREATE VIRTUAL TABLE tt USING fts5(x);
  236. INSERT INTO tt(tt, rank) VALUES('pgsz', 32);
  237. BEGIN;
  238. WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<200)
  239. INSERT INTO tt(rowid, x)
  240. SELECT i, CASE WHEN (i%50)==0 THEN 'a a a a a a' ELSE 'a x a x a x' END
  241. FROM ii;
  242. COMMIT;
  243. }
  244. do_faultsim_test 9.1 -faults oom-* -body {
  245. db eval { SELECT rowid FROM tt WHERE tt MATCH 'a NOT x' }
  246. } -test {
  247. faultsim_test_result {0 {50 100 150 200}} {1 SQLITE_NOMEM}
  248. }
  249. #-------------------------------------------------------------------------
  250. # OOM in fts5_expr() SQL function.
  251. #
  252. do_faultsim_test 10.1 -faults oom-t* -body {
  253. db one { SELECT fts5_expr('a AND b NEAR(a b)') }
  254. } -test {
  255. faultsim_test_result {0 {"a" AND "b" AND NEAR("a" "b", 10)}}
  256. }
  257. do_faultsim_test 10.2 -faults oom-t* -body {
  258. db one { SELECT fts5_expr_tcl('x:"a b c" AND b NEAR(a b)', 'ns', 'x') }
  259. } -test {
  260. set res {AND [ns -col 0 -- {a b c}] [ns -- {b}] [ns -near 10 -- {a} {b}]}
  261. faultsim_test_result [list 0 $res]
  262. }
  263. do_faultsim_test 10.3 -faults oom-t* -body {
  264. db one { SELECT fts5_expr('x:a', 'x') }
  265. } -test {
  266. faultsim_test_result {0 {x : "a"}}
  267. }
  268. #-------------------------------------------------------------------------
  269. # OOM while configuring 'rank' option.
  270. #
  271. reset_db
  272. do_execsql_test 11.0 {
  273. CREATE VIRTUAL TABLE ft USING fts5(x);
  274. }
  275. do_faultsim_test 11.1 -faults oom-t* -body {
  276. db eval { INSERT INTO ft(ft, rank) VALUES('rank', 'bm25(10.0, 5.0)') }
  277. } -test {
  278. faultsim_test_result {0 {}} {1 {disk I/O error}}
  279. }
  280. #-------------------------------------------------------------------------
  281. # OOM while creating an fts5vocab table.
  282. #
  283. reset_db
  284. do_execsql_test 12.0 {
  285. CREATE VIRTUAL TABLE ft USING fts5(x);
  286. }
  287. faultsim_save_and_close
  288. do_faultsim_test 12.1 -faults oom-t* -prep {
  289. faultsim_restore_and_reopen
  290. db eval { SELECT * FROM sqlite_master }
  291. } -body {
  292. db eval { CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row') }
  293. } -test {
  294. faultsim_test_result {0 {}}
  295. }
  296. #-------------------------------------------------------------------------
  297. # OOM while querying an fts5vocab table.
  298. #
  299. reset_db
  300. do_execsql_test 13.0 {
  301. CREATE VIRTUAL TABLE ft USING fts5(x);
  302. INSERT INTO ft VALUES('a b');
  303. CREATE VIRTUAL TABLE vv USING fts5vocab(ft, 'row');
  304. }
  305. faultsim_save_and_close
  306. do_faultsim_test 13.1 -faults oom-t* -prep {
  307. faultsim_restore_and_reopen
  308. db eval { SELECT * FROM vv }
  309. } -body {
  310. db eval { SELECT * FROM vv }
  311. } -test {
  312. faultsim_test_result {0 {a 1 1 b 1 1}}
  313. }
  314. #-------------------------------------------------------------------------
  315. # OOM in multi-column token query.
  316. #
  317. reset_db
  318. do_execsql_test 13.0 {
  319. CREATE VIRTUAL TABLE ft USING fts5(x, y, z);
  320. INSERT INTO ft(ft, rank) VALUES('pgsz', 32);
  321. INSERT INTO ft VALUES(
  322. 'x x x x x x x x x x x x x x x x',
  323. 'y y y y y y y y y y y y y y y y',
  324. 'z z z z z z z z x x x x x x x x'
  325. );
  326. INSERT INTO ft SELECT * FROM ft;
  327. INSERT INTO ft SELECT * FROM ft;
  328. INSERT INTO ft SELECT * FROM ft;
  329. INSERT INTO ft SELECT * FROM ft;
  330. }
  331. faultsim_save_and_close
  332. do_faultsim_test 13.1 -faults oom-t* -prep {
  333. faultsim_restore_and_reopen
  334. db eval { SELECT * FROM ft }
  335. } -body {
  336. db eval { SELECT rowid FROM ft WHERE ft MATCH '{x z}: x' }
  337. } -test {
  338. faultsim_test_result {0 {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}}
  339. }
  340. #-------------------------------------------------------------------------
  341. # OOM in an "ALTER TABLE RENAME TO"
  342. #
  343. reset_db
  344. do_execsql_test 14.0 {
  345. CREATE VIRTUAL TABLE "tbl one" USING fts5(x, y, z);
  346. }
  347. faultsim_save_and_close
  348. do_faultsim_test 14.1 -faults oom-t* -prep {
  349. faultsim_restore_and_reopen
  350. db eval { SELECT * FROM "tbl one" }
  351. } -body {
  352. db eval { ALTER TABLE "tbl one" RENAME TO "tbl two" }
  353. } -test {
  354. faultsim_test_result {0 {}} $::TMPDBERROR
  355. }
  356. finish_test