fts5integrity.test 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. # 2015 Jan 13
  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 contains tests focused on the integrity-check procedure.
  13. #
  14. source [file join [file dirname [info script]] fts5_common.tcl]
  15. set testprefix fts5integrity
  16. # If SQLITE_ENABLE_FTS5 is not defined, omit this file.
  17. ifcapable !fts5 {
  18. finish_test
  19. return
  20. }
  21. do_execsql_test 1.0 {
  22. CREATE VIRTUAL TABLE xx USING fts5(x);
  23. INSERT INTO xx VALUES('term');
  24. }
  25. do_execsql_test 1.1 {
  26. INSERT INTO xx(xx) VALUES('integrity-check');
  27. }
  28. do_execsql_test 2.0 {
  29. CREATE VIRTUAL TABLE yy USING fts5(x, prefix=1);
  30. INSERT INTO yy VALUES('term');
  31. }
  32. do_execsql_test 2.1 {
  33. INSERT INTO yy(yy) VALUES('integrity-check');
  34. }
  35. #--------------------------------------------------------------------
  36. #
  37. do_execsql_test 3.0 {
  38. CREATE VIRTUAL TABLE zz USING fts5(z);
  39. INSERT INTO zz(zz, rank) VALUES('pgsz', 32);
  40. INSERT INTO zz VALUES('b b b b b b b b b b b b b b');
  41. INSERT INTO zz SELECT z FROM zz;
  42. INSERT INTO zz SELECT z FROM zz;
  43. INSERT INTO zz SELECT z FROM zz;
  44. INSERT INTO zz SELECT z FROM zz;
  45. INSERT INTO zz SELECT z FROM zz;
  46. INSERT INTO zz SELECT z FROM zz;
  47. INSERT INTO zz(zz) VALUES('optimize');
  48. }
  49. do_execsql_test 3.1 { INSERT INTO zz(zz) VALUES('integrity-check'); }
  50. #--------------------------------------------------------------------
  51. # Mess around with a docsize record. And the averages record. Then
  52. # check that integrity-check picks it up.
  53. #
  54. do_execsql_test 4.0 {
  55. CREATE VIRTUAL TABLE aa USING fts5(zz);
  56. INSERT INTO aa(zz) VALUES('a b c d e');
  57. INSERT INTO aa(zz) VALUES('a b c d');
  58. INSERT INTO aa(zz) VALUES('a b c');
  59. INSERT INTO aa(zz) VALUES('a b');
  60. INSERT INTO aa(zz) VALUES('a');
  61. SELECT length(sz) FROM aa_docsize;
  62. } {1 1 1 1 1}
  63. do_execsql_test 4.1 {
  64. INSERT INTO aa(aa) VALUES('integrity-check');
  65. }
  66. sqlite3_db_config db DEFENSIVE 0
  67. do_catchsql_test 4.2 {
  68. BEGIN;
  69. UPDATE aa_docsize SET sz = X'44' WHERE rowid = 3;
  70. INSERT INTO aa(aa) VALUES('integrity-check');
  71. } {1 {database disk image is malformed}}
  72. do_execsql_test 4.2.1 {
  73. PRAGMA integrity_check(aa);
  74. } {{malformed inverted index for FTS5 table main.aa}}
  75. do_catchsql_test 4.3 {
  76. ROLLBACK;
  77. BEGIN;
  78. UPDATE aa_data SET block = X'44' WHERE rowid = 1;
  79. INSERT INTO aa(aa) VALUES('integrity-check');
  80. } {1 {database disk image is malformed}}
  81. do_catchsql_test 4.4 {
  82. ROLLBACK;
  83. BEGIN;
  84. INSERT INTO aa_docsize VALUES(23, X'04');
  85. INSERT INTO aa(aa) VALUES('integrity-check');
  86. } {1 {database disk image is malformed}}
  87. do_catchsql_test 4.5 {
  88. ROLLBACK;
  89. BEGIN;
  90. INSERT INTO aa_docsize VALUES(23, X'00');
  91. INSERT INTO aa_content VALUES(23, '');
  92. INSERT INTO aa(aa) VALUES('integrity-check');
  93. } {1 {database disk image is malformed}}
  94. #db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM zz_data} {puts $r}
  95. #exit
  96. execsql { ROLLBACK }
  97. #-------------------------------------------------------------------------
  98. # Test that integrity-check works on a reasonably large db with many
  99. # different terms.
  100. # Document generator command.
  101. proc rnddoc {n} {
  102. set doc [list]
  103. for {set i 0} {$i<$n} {incr i} {
  104. lappend doc [format %.5d [expr int(rand()*10000)]]
  105. }
  106. return $doc
  107. }
  108. db func rnddoc rnddoc
  109. expr srand(0)
  110. do_execsql_test 5.0 {
  111. CREATE VIRTUAL TABLE gg USING fts5(a, prefix="1,2,3");
  112. INSERT INTO gg(gg, rank) VALUES('pgsz', 256);
  113. INSERT INTO gg VALUES(rnddoc(20));
  114. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  115. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  116. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  117. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  118. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  119. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  120. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  121. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  122. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  123. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  124. INSERT INTO gg SELECT rnddoc(20) FROM gg;
  125. }
  126. do_execsql_test 5.1 {
  127. INSERT INTO gg(gg) VALUES('integrity-check');
  128. }
  129. do_execsql_test 5.2 {
  130. INSERT INTO gg(gg) VALUES('optimize');
  131. }
  132. do_execsql_test 5.3 {
  133. INSERT INTO gg(gg) VALUES('integrity-check');
  134. }
  135. unset -nocomplain res
  136. do_test 5.4.1 {
  137. set ok 0
  138. for {set i 0} {$i < 10000} {incr i} {
  139. set T [format %.5d $i]
  140. set res [db eval { SELECT rowid FROM gg($T) ORDER BY rowid ASC }]
  141. set res2 [db eval { SELECT rowid FROM gg($T) ORDER BY rowid DESC }]
  142. if {$res == [lsort -integer $res2]} { incr ok }
  143. }
  144. set ok
  145. } {10000}
  146. do_test 5.4.2 {
  147. set ok 0
  148. for {set i 0} {$i < 100} {incr i} {
  149. set T "[format %.3d $i]*"
  150. set res [db eval { SELECT rowid FROM gg($T) ORDER BY rowid ASC }]
  151. set res2 [db eval { SELECT rowid FROM gg($T) ORDER BY rowid DESC }]
  152. if {$res == [lsort -integer $res2]} { incr ok }
  153. }
  154. set ok
  155. } {100}
  156. #-------------------------------------------------------------------------
  157. # Similar to 5.*.
  158. #
  159. foreach {tn pgsz} {
  160. 1 32
  161. 2 36
  162. 3 40
  163. 4 44
  164. 5 48
  165. } {
  166. do_execsql_test 6.$tn.1 {
  167. DROP TABLE IF EXISTS hh;
  168. CREATE VIRTUAL TABLE hh USING fts5(y);
  169. INSERT INTO hh(hh, rank) VALUES('pgsz', $pgsz);
  170. WITH s(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<999)
  171. INSERT INTO hh SELECT printf('%.3d%.3d%.3d %.3d%.3d%.3d',i,i,i,i+1,i+1,i+1)
  172. FROM s;
  173. WITH s(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<999)
  174. INSERT INTO hh SELECT printf('%.3d%.3d%.3d %.3d%.3d%.3d',i,i,i,i+1,i+1,i+1)
  175. FROM s;
  176. INSERT INTO hh(hh) VALUES('optimize');
  177. }
  178. do_test 6.$tn.2 {
  179. set ok 0
  180. for {set i 0} {$i < 1000} {incr i} {
  181. set T [format %.3d%.3d%.3d $i $i $i]
  182. set res [db eval { SELECT rowid FROM hh($T) ORDER BY rowid ASC }]
  183. set res2 [db eval { SELECT rowid FROM hh($T) ORDER BY rowid DESC }]
  184. if {$res == [lsort -integer $res2]} { incr ok }
  185. }
  186. set ok
  187. } {1000}
  188. }
  189. #-------------------------------------------------------------------------
  190. #
  191. reset_db
  192. do_execsql_test 7.0 {
  193. PRAGMA encoding = 'UTF-16';
  194. CREATE VIRTUAL TABLE vt0 USING fts5(c0);
  195. INSERT INTO vt0 VALUES (x'46f0');
  196. SELECT quote(c0) FROM vt0;
  197. } {X'46F0'}
  198. do_execsql_test 7.1 {
  199. INSERT INTO vt0(vt0) VALUES('integrity-check');
  200. }
  201. do_execsql_test 7.2 {
  202. INSERT INTO vt0(vt0) VALUES('rebuild');
  203. }
  204. do_execsql_test 7.3 {
  205. INSERT INTO vt0(vt0) VALUES('integrity-check');
  206. }
  207. do_execsql_test 7.4 {
  208. UPDATE vt0 SET c0='';
  209. }
  210. do_execsql_test 7.5 {
  211. INSERT INTO vt0(vt0) VALUES('integrity-check');
  212. }
  213. #-------------------------------------------------------------------------
  214. # Ticket 7a458c2a5f4
  215. #
  216. reset_db
  217. do_execsql_test 8.0 {
  218. PRAGMA locking_mode = EXCLUSIVE;
  219. PRAGMA journal_mode = PERSIST;
  220. CREATE VIRTUAL TABLE vt0 USING fts5(c0);
  221. } {exclusive persist}
  222. do_execsql_test 8.1 {
  223. PRAGMA data_version
  224. } {1}
  225. do_execsql_test 8.2 {
  226. INSERT INTO vt0(vt0) VALUES('integrity-check');
  227. PRAGMA data_version;
  228. } {1}
  229. do_execsql_test 8.1 {
  230. INSERT INTO vt0(vt0, rank) VALUES('usermerge', 2);
  231. }
  232. #-------------------------------------------------------------------------
  233. # Ticket [771fe617]
  234. #
  235. reset_db
  236. do_execsql_test 9.0 {
  237. PRAGMA encoding = 'UTF16';
  238. CREATE VIRTUAL TABLE vt0 USING fts5(c0);
  239. }
  240. #explain_i { SELECT quote(SUBSTR(x'37', 0)); }
  241. #execsql { PRAGMA vdbe_trace = 1 }
  242. do_execsql_test 9.1.1 {
  243. SELECT quote(SUBSTR(x'37', 0));
  244. } {X'37'}
  245. do_execsql_test 9.1.2 {
  246. SELECT quote(x'37');
  247. } {X'37'}
  248. do_execsql_test 9.2 {
  249. INSERT INTO vt0 VALUES (SUBSTR(x'37', 0));
  250. -- INSERT INTO vt0 VALUES (x'37');
  251. }
  252. do_execsql_test 9.3 {
  253. INSERT INTO vt0(vt0) VALUES('integrity-check');
  254. }
  255. #-------------------------------------------------------------------------
  256. reset_db
  257. do_execsql_test 10.0 {
  258. CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
  259. CREATE VIRTUAL TABLE vt0 USING fts5(a, b, content=t1);
  260. INSERT INTO vt0(rowid, a, b) VALUES(1, 'abc', 'def');
  261. }
  262. do_catchsql_test 10.1 {
  263. INSERT INTO vt0(vt0) VALUES('integrity-check');
  264. } {0 {}}
  265. do_catchsql_test 10.2 {
  266. INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
  267. } {0 {}}
  268. do_catchsql_test 10.3 {
  269. INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
  270. } {1 {database disk image is malformed}}
  271. do_catchsql_test 10.3 {
  272. INSERT INTO t1 VALUES(1, 'abc', 'def');
  273. INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
  274. } {0 {}}
  275. do_execsql_test 10.4 {
  276. CREATE VIRTUAL TABLE vt1 USING fts5(a, b, content=);
  277. INSERT INTO vt1(rowid, a, b) VALUES(1, 'abc', 'def');
  278. }
  279. do_catchsql_test 10.5.1 {
  280. INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
  281. } {0 {}}
  282. do_catchsql_test 10.5.2 {
  283. INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
  284. } {0 {}}
  285. do_catchsql_test 10.5.3 {
  286. INSERT INTO vt0(vt0) VALUES('integrity-check');
  287. } {0 {}}
  288. reset_db
  289. proc slang {in} {return [string map {th d e eh} $in]}
  290. db function slang -deterministic -innocuous slang
  291. do_execsql_test 11.0 {
  292. CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT AS (slang(b)));
  293. INSERT INTO t1(b) VALUES('the quick fox jumps over the lazy brown dog');
  294. SELECT c FROM t1;
  295. } {{deh quick fox jumps ovehr deh lazy brown dog}}
  296. do_execsql_test 11.1 {
  297. CREATE VIRTUAL TABLE t2 USING fts5(content="t1", c);
  298. INSERT INTO t2(t2) VALUES('rebuild');
  299. SELECT rowid FROM t2 WHERE t2 MATCH 'deh';
  300. } {1}
  301. do_execsql_test 11.2 {
  302. PRAGMA integrity_check(t2);
  303. } {ok}
  304. db close
  305. sqlite3 db test.db
  306. # FIX ME?
  307. #
  308. # FTS5 integrity-check does not care if the content table is unreadable or
  309. # does not exist. It only looks for internal inconsistencies in the
  310. # inverted index.
  311. #
  312. do_execsql_test 11.3 {
  313. PRAGMA integrity_check(t2);
  314. } {ok}
  315. do_execsql_test 11.4 {
  316. DROP TABLE t1;
  317. PRAGMA integrity_check(t2);
  318. } {ok}
  319. #-------------------------------------------------------------------
  320. reset_db
  321. do_execsql_test 12.1 {
  322. CREATE VIRTUAL TABLE x1 USING fts5(a, b);
  323. INSERT INTO x1 VALUES('one', 'two');
  324. INSERT INTO x1 VALUES('three', 'four');
  325. INSERT INTO x1 VALUES('five', 'six');
  326. }
  327. do_execsql_test 12.2 {
  328. PRAGMA integrity_check
  329. } {ok}
  330. db close
  331. sqlite3 db test.db -readonly 1
  332. explain_i {
  333. PRAGMA integrity_check
  334. }
  335. do_execsql_test 12.3 {
  336. PRAGMA integrity_check
  337. } {ok}
  338. #-------------------------------------------------------------------
  339. reset_db
  340. do_execsql_test 13.1 {
  341. CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=ascii);
  342. INSERT INTO t1 VALUES('a b c'), ('d e f');
  343. PRAGMA integrity_check;
  344. } {ok}
  345. db close
  346. sqlite3 db test.db
  347. do_catchsql_test 13.2 {
  348. PRAGMA integrity_check;
  349. } {0 ok}
  350. do_execsql_test 13.3 {
  351. PRAGMA writable_schema = 1;
  352. UPDATE sqlite_schema SET sql = 'CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=blah)'
  353. WHERE name = 't1';
  354. }
  355. db close
  356. sqlite3 db test.db
  357. breakpoint
  358. do_catchsql_test 13.4 {
  359. PRAGMA integrity_check;
  360. } {1 {SQL logic error}}
  361. finish_test