fts3d.test 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. # 2008 June 26
  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 focus
  12. # of this script is testing the FTS3 module's optimize() function.
  13. #
  14. set testdir [file dirname $argv0]
  15. source $testdir/tester.tcl
  16. source $testdir/fts3_common.tcl
  17. # If SQLITE_ENABLE_FTS3 is not defined, omit this file.
  18. ifcapable !fts3 {
  19. finish_test
  20. return
  21. }
  22. #*************************************************************************
  23. # Utility function to check for the expected terms in the segment
  24. # level/index. _all version does same but for entire index.
  25. proc check_terms {test level index terms} {
  26. set where "level = $level AND idx = $index"
  27. do_test $test.terms [list fts3_terms t1 $where] $terms
  28. }
  29. proc check_terms_all {test terms} {
  30. do_test $test.terms [list fts3_terms t1 1] $terms
  31. }
  32. # Utility function to check for the expected doclist for the term in
  33. # segment level/index. _all version does same for entire index.
  34. proc check_doclist {test level index term doclist} {
  35. set where "level = $level AND idx = $index"
  36. do_test $test.doclist [list fts3_doclist t1 $term $where] $doclist
  37. }
  38. proc check_doclist_all {test term doclist} {
  39. do_test $test.doclist [list fts3_doclist t1 $term 1] $doclist
  40. }
  41. #*************************************************************************
  42. # Test results when all rows are deleted and one is added back.
  43. # Previously older segments would continue to exist, but now the index
  44. # should be dropped when the table is empty. The results should look
  45. # exactly like we never added the earlier rows in the first place.
  46. db eval {
  47. DROP TABLE IF EXISTS t1;
  48. CREATE VIRTUAL TABLE t1 USING fts3(c);
  49. INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
  50. INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
  51. INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
  52. DELETE FROM t1 WHERE 1=1; -- Delete each row rather than dropping table.
  53. INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
  54. }
  55. # Should be a single initial segment.
  56. do_test fts3d-1.segments {
  57. execsql {
  58. SELECT level, idx FROM t1_segdir ORDER BY level, idx;
  59. }
  60. } {0 0}
  61. do_test fts3d-1.matches {
  62. execsql {
  63. SELECT OFFSETS(t1) FROM t1
  64. WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
  65. }
  66. } {{0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}}
  67. check_terms_all fts3d-1.1 {a is test this}
  68. check_doclist_all fts3d-1.1.1 a {[1 0[2]]}
  69. check_doclist_all fts3d-1.1.2 is {[1 0[1]]}
  70. check_doclist_all fts3d-1.1.3 test {[1 0[3]]}
  71. check_doclist_all fts3d-1.1.4 this {[1 0[0]]}
  72. check_terms fts3d-1.2 0 0 {a is test this}
  73. check_doclist fts3d-1.2.1 0 0 a {[1 0[2]]}
  74. check_doclist fts3d-1.2.2 0 0 is {[1 0[1]]}
  75. check_doclist fts3d-1.2.3 0 0 test {[1 0[3]]}
  76. check_doclist fts3d-1.2.4 0 0 this {[1 0[0]]}
  77. #*************************************************************************
  78. # Test results when everything is optimized manually.
  79. # NOTE(shess): This is a copy of fts3c-1.3. I've pulled a copy here
  80. # because fts3d-2 and fts3d-3 should have identical results.
  81. db eval {
  82. DROP TABLE IF EXISTS t1;
  83. CREATE VIRTUAL TABLE t1 USING fts3(c);
  84. INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
  85. INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
  86. INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
  87. DELETE FROM t1 WHERE docid IN (1,3);
  88. DROP TABLE IF EXISTS t1old;
  89. ALTER TABLE t1 RENAME TO t1old;
  90. CREATE VIRTUAL TABLE t1 USING fts3(c);
  91. INSERT INTO t1 (docid, c) SELECT docid, c FROM t1old;
  92. DROP TABLE t1old;
  93. }
  94. # Should be a single optimal segment with the same logical results.
  95. do_test fts3d-2.segments {
  96. execsql {
  97. SELECT level, idx FROM t1_segdir ORDER BY level, idx;
  98. }
  99. } {0 0}
  100. do_test fts3d-2.matches {
  101. execsql {
  102. SELECT OFFSETS(t1) FROM t1
  103. WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
  104. }
  105. } {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
  106. check_terms_all fts3d-2.1 {a test that was}
  107. check_doclist_all fts3d-2.1.1 a {[2 0[2]]}
  108. check_doclist_all fts3d-2.1.2 test {[2 0[3]]}
  109. check_doclist_all fts3d-2.1.3 that {[2 0[0]]}
  110. check_doclist_all fts3d-2.1.4 was {[2 0[1]]}
  111. check_terms fts3d-2.2 0 0 {a test that was}
  112. check_doclist fts3d-2.2.1 0 0 a {[2 0[2]]}
  113. check_doclist fts3d-2.2.2 0 0 test {[2 0[3]]}
  114. check_doclist fts3d-2.2.3 0 0 that {[2 0[0]]}
  115. check_doclist fts3d-2.2.4 0 0 was {[2 0[1]]}
  116. #*************************************************************************
  117. # Test results when everything is optimized via optimize().
  118. db eval {
  119. DROP TABLE IF EXISTS t1;
  120. CREATE VIRTUAL TABLE t1 USING fts3(c);
  121. INSERT INTO t1 (docid, c) VALUES (1, 'This is a test');
  122. INSERT INTO t1 (docid, c) VALUES (2, 'That was a test');
  123. INSERT INTO t1 (docid, c) VALUES (3, 'This is a test');
  124. DELETE FROM t1 WHERE docid IN (1,3);
  125. SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
  126. }
  127. # Should be a single optimal segment with the same logical results.
  128. do_test fts3d-3.segments {
  129. execsql {
  130. SELECT level, idx FROM t1_segdir ORDER BY level, idx;
  131. }
  132. } {0 0}
  133. do_test fts3d-3.matches {
  134. execsql {
  135. SELECT OFFSETS(t1) FROM t1
  136. WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
  137. }
  138. } {{0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4}}
  139. check_terms_all fts3d-3.1 {a test that was}
  140. check_doclist_all fts3d-3.1.1 a {[2 0[2]]}
  141. check_doclist_all fts3d-3.1.2 test {[2 0[3]]}
  142. check_doclist_all fts3d-3.1.3 that {[2 0[0]]}
  143. check_doclist_all fts3d-3.1.4 was {[2 0[1]]}
  144. check_terms fts3d-3.2 0 0 {a test that was}
  145. check_doclist fts3d-3.2.1 0 0 a {[2 0[2]]}
  146. check_doclist fts3d-3.2.2 0 0 test {[2 0[3]]}
  147. check_doclist fts3d-3.2.3 0 0 that {[2 0[0]]}
  148. check_doclist fts3d-3.2.4 0 0 was {[2 0[1]]}
  149. #*************************************************************************
  150. # Test optimize() against a table involving segment merges.
  151. # NOTE(shess): Since there's no transaction, each of the INSERT/UPDATE
  152. # statements generates a segment.
  153. db eval {
  154. DROP TABLE IF EXISTS t1;
  155. CREATE VIRTUAL TABLE t1 USING fts3(c);
  156. INSERT INTO t1 (rowid, c) VALUES (1, 'This is a test');
  157. INSERT INTO t1 (rowid, c) VALUES (2, 'That was a test');
  158. INSERT INTO t1 (rowid, c) VALUES (3, 'This is a test');
  159. UPDATE t1 SET c = 'This is a test one' WHERE rowid = 1;
  160. UPDATE t1 SET c = 'That was a test one' WHERE rowid = 2;
  161. UPDATE t1 SET c = 'This is a test one' WHERE rowid = 3;
  162. UPDATE t1 SET c = 'This is a test two' WHERE rowid = 1;
  163. UPDATE t1 SET c = 'That was a test two' WHERE rowid = 2;
  164. UPDATE t1 SET c = 'This is a test two' WHERE rowid = 3;
  165. UPDATE t1 SET c = 'This is a test three' WHERE rowid = 1;
  166. UPDATE t1 SET c = 'That was a test three' WHERE rowid = 2;
  167. UPDATE t1 SET c = 'This is a test three' WHERE rowid = 3;
  168. UPDATE t1 SET c = 'This is a test four' WHERE rowid = 1;
  169. UPDATE t1 SET c = 'That was a test four' WHERE rowid = 2;
  170. UPDATE t1 SET c = 'This is a test four' WHERE rowid = 3;
  171. UPDATE t1 SET c = 'This is a test' WHERE rowid = 1;
  172. UPDATE t1 SET c = 'That was a test' WHERE rowid = 2;
  173. UPDATE t1 SET c = 'This is a test' WHERE rowid = 3;
  174. }
  175. # 2 segments in level 0, 1 in level 1 (18 segments created, 16
  176. # merged).
  177. do_test fts3d-4.segments {
  178. execsql {
  179. SELECT level, idx FROM t1_segdir ORDER BY level, idx;
  180. }
  181. } {0 0 0 1 1 0}
  182. do_test fts3d-4.matches {
  183. execsql {
  184. SELECT OFFSETS(t1) FROM t1
  185. WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
  186. }
  187. } [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
  188. {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
  189. {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
  190. check_terms_all fts3d-4.1 {a four is one test that this three two was}
  191. check_doclist_all fts3d-4.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
  192. check_doclist_all fts3d-4.1.2 four {}
  193. check_doclist_all fts3d-4.1.3 is {[1 0[1]] [3 0[1]]}
  194. check_doclist_all fts3d-4.1.4 one {}
  195. check_doclist_all fts3d-4.1.5 test {[1 0[3]] [2 0[3]] [3 0[3]]}
  196. check_doclist_all fts3d-4.1.6 that {[2 0[0]]}
  197. check_doclist_all fts3d-4.1.7 this {[1 0[0]] [3 0[0]]}
  198. check_doclist_all fts3d-4.1.8 three {}
  199. check_doclist_all fts3d-4.1.9 two {}
  200. check_doclist_all fts3d-4.1.10 was {[2 0[1]]}
  201. check_terms fts3d-4.2 0 0 {a four test that was}
  202. check_doclist fts3d-4.2.1 0 0 a {[2 0[2]]}
  203. check_doclist fts3d-4.2.2 0 0 four {[2]}
  204. check_doclist fts3d-4.2.3 0 0 test {[2 0[3]]}
  205. check_doclist fts3d-4.2.4 0 0 that {[2 0[0]]}
  206. check_doclist fts3d-4.2.5 0 0 was {[2 0[1]]}
  207. check_terms fts3d-4.3 0 1 {a four is test this}
  208. check_doclist fts3d-4.3.1 0 1 a {[3 0[2]]}
  209. check_doclist fts3d-4.3.2 0 1 four {[3]}
  210. check_doclist fts3d-4.3.3 0 1 is {[3 0[1]]}
  211. check_doclist fts3d-4.3.4 0 1 test {[3 0[3]]}
  212. check_doclist fts3d-4.3.5 0 1 this {[3 0[0]]}
  213. check_terms fts3d-4.4 1 0 {a four is one test that this three two was}
  214. check_doclist fts3d-4.4.1 1 0 a {[1 0[2]] [2 0[2]] [3 0[2]]}
  215. check_doclist fts3d-4.4.2 1 0 four {[1] [2 0[4]] [3 0[4]]}
  216. check_doclist fts3d-4.4.3 1 0 is {[1 0[1]] [3 0[1]]}
  217. check_doclist fts3d-4.4.4 1 0 one {[1] [2] [3]}
  218. check_doclist fts3d-4.4.5 1 0 test {[1 0[3]] [2 0[3]] [3 0[3]]}
  219. check_doclist fts3d-4.4.6 1 0 that {[2 0[0]]}
  220. check_doclist fts3d-4.4.7 1 0 this {[1 0[0]] [3 0[0]]}
  221. check_doclist fts3d-4.4.8 1 0 three {[1] [2] [3]}
  222. check_doclist fts3d-4.4.9 1 0 two {[1] [2] [3]}
  223. check_doclist fts3d-4.4.10 1 0 was {[2 0[1]]}
  224. # Optimize should leave the result in the level of the highest-level
  225. # prior segment.
  226. breakpoint
  227. do_test fts3d-4.5 {
  228. execsql {
  229. SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
  230. SELECT level, idx FROM t1_segdir ORDER BY level, idx;
  231. }
  232. } {{Index optimized} 1 0}
  233. # Identical to fts3d-4.matches.
  234. do_test fts3d-4.5.matches {
  235. execsql {
  236. SELECT OFFSETS(t1) FROM t1
  237. WHERE t1 MATCH 'this OR that OR was OR a OR is OR test' ORDER BY docid;
  238. }
  239. } [list {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4} \
  240. {0 1 0 4 0 2 5 3 0 3 9 1 0 5 11 4} \
  241. {0 0 0 4 0 4 5 2 0 3 8 1 0 5 10 4}]
  242. check_terms_all fts3d-4.5.1 {a is test that this was}
  243. check_doclist_all fts3d-4.5.1.1 a {[1 0[2]] [2 0[2]] [3 0[2]]}
  244. check_doclist_all fts3d-4.5.1.2 is {[1 0[1]] [3 0[1]]}
  245. check_doclist_all fts3d-4.5.1.3 test {[1 0[3]] [2 0[3]] [3 0[3]]}
  246. check_doclist_all fts3d-4.5.1.4 that {[2 0[0]]}
  247. check_doclist_all fts3d-4.5.1.5 this {[1 0[0]] [3 0[0]]}
  248. check_doclist_all fts3d-4.5.1.6 was {[2 0[1]]}
  249. check_terms fts3d-4.5.2 1 0 {a is test that this was}
  250. check_doclist fts3d-4.5.2.1 1 0 a {[1 0[2]] [2 0[2]] [3 0[2]]}
  251. check_doclist fts3d-4.5.2.2 1 0 is {[1 0[1]] [3 0[1]]}
  252. check_doclist fts3d-4.5.2.3 1 0 test {[1 0[3]] [2 0[3]] [3 0[3]]}
  253. check_doclist fts3d-4.5.2.4 1 0 that {[2 0[0]]}
  254. check_doclist fts3d-4.5.2.5 1 0 this {[1 0[0]] [3 0[0]]}
  255. check_doclist fts3d-4.5.2.6 1 0 was {[2 0[1]]}
  256. # Re-optimizing does nothing.
  257. do_test fts3d-5.0 {
  258. execsql {
  259. SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
  260. SELECT level, idx FROM t1_segdir ORDER BY level, idx;
  261. }
  262. } {{Index already optimal} 1 0}
  263. # Even if we move things around, still does nothing.
  264. do_test fts3d-5.1 {
  265. execsql {
  266. UPDATE t1_segdir SET level = 2 WHERE level = 1 AND idx = 0;
  267. SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
  268. SELECT level, idx FROM t1_segdir ORDER BY level, idx;
  269. }
  270. } {{Index already optimal} 2 0}
  271. # ALTER TABLE RENAME should work regardless of the database encoding.
  272. #
  273. do_test fts3d-6.0 {
  274. db close
  275. forcedelete test.db
  276. sqlite3 db test.db
  277. db eval {
  278. PRAGMA encoding=UTF8;
  279. CREATE VIRTUAL TABLE fts USING fts3(a,b,c);
  280. SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
  281. }
  282. } {fts_content fts_segdir fts_segments}
  283. do_test fts3d-6.1 {
  284. db eval {
  285. ALTER TABLE fts RENAME TO xyz;
  286. SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
  287. }
  288. } {xyz_content xyz_segdir xyz_segments}
  289. do_test fts3d-6.2 {
  290. db close
  291. forcedelete test.db
  292. sqlite3 db test.db
  293. db eval {
  294. PRAGMA encoding=UTF16le;
  295. CREATE VIRTUAL TABLE fts USING fts3(a,b,c);
  296. SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
  297. }
  298. } {fts_content fts_segdir fts_segments}
  299. do_test fts3d-6.3 {
  300. db eval {
  301. ALTER TABLE fts RENAME TO xyz;
  302. SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
  303. }
  304. } {xyz_content xyz_segdir xyz_segments}
  305. do_test fts3d-6.4 {
  306. db close
  307. forcedelete test.db
  308. sqlite3 db test.db
  309. db eval {
  310. PRAGMA encoding=UTF16be;
  311. CREATE VIRTUAL TABLE fts USING fts3(a,b,c);
  312. SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
  313. }
  314. } {fts_content fts_segdir fts_segments}
  315. do_test fts3d-6.5 {
  316. db eval {
  317. ALTER TABLE fts RENAME TO xyz;
  318. SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
  319. }
  320. } {xyz_content xyz_segdir xyz_segments}
  321. finish_test