fts5merge.test 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. # 2014 Dec 20
  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. # Test that focus on incremental merges of segments.
  13. #
  14. source [file join [file dirname [info script]] fts5_common.tcl]
  15. set testprefix fts5merge
  16. # If SQLITE_ENABLE_FTS5 is not defined, omit this file.
  17. ifcapable !fts5 {
  18. finish_test
  19. return
  20. }
  21. db func repeat [list string repeat]
  22. #-------------------------------------------------------------------------
  23. # Create an fts index so that:
  24. #
  25. # * the index consists of two top-level segments
  26. # * each segment contains records related to $nRowPerSeg rows
  27. # * all rows consist of tokens "x" and "y" only.
  28. #
  29. # Then run ('merge', 1) until everything is completely merged.
  30. #
  31. proc do_merge1_test {testname nRowPerSeg} {
  32. set ::nRowPerSeg [expr $nRowPerSeg]
  33. do_execsql_test $testname.0 {
  34. DROP TABLE IF EXISTS x8;
  35. CREATE VIRTUAL TABLE x8 USING fts5(i);
  36. INSERT INTO x8(x8, rank) VALUES('pgsz', 32);
  37. WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<$::nRowPerSeg)
  38. INSERT INTO x8 SELECT repeat('x y ', i % 16) FROM ii;
  39. WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<$::nRowPerSeg)
  40. INSERT INTO x8 SELECT repeat('x y ', i % 16) FROM ii;
  41. INSERT INTO x8(x8, rank) VALUES('usermerge', 2);
  42. }
  43. for {set tn 1} {[lindex [fts5_level_segs x8] 0]>0} {incr tn} {
  44. do_execsql_test $testname.$tn {
  45. INSERT INTO x8(x8, rank) VALUES('merge', 1);
  46. INSERT INTO x8(x8) VALUES('integrity-check');
  47. }
  48. if {$tn>5} break
  49. }
  50. do_test $testname.x [list expr "$tn < 5"] 1
  51. }
  52. do_merge1_test 1.1 1
  53. do_merge1_test 1.2 2
  54. do_merge1_test 1.3 3
  55. do_merge1_test 1.4 4
  56. do_merge1_test 1.5 10
  57. do_merge1_test 1.6 20
  58. do_merge1_test 1.7 100
  59. #-------------------------------------------------------------------------
  60. #
  61. proc do_merge2_test {testname nRow} {
  62. db func rnddoc fts5_rnddoc
  63. do_execsql_test $testname.0 {
  64. DROP TABLE IF EXISTS x8;
  65. CREATE VIRTUAL TABLE x8 USING fts5(i);
  66. INSERT INTO x8(x8, rank) VALUES('pgsz', 32);
  67. }
  68. set ::nRow $nRow
  69. do_test $testname.1 {
  70. for {set i 0} {$i < $::nRow} {incr i} {
  71. execsql { INSERT INTO x8 VALUES( rnddoc(($i%16) + 5) ) }
  72. while {[not_merged x8]} {
  73. execsql {
  74. INSERT INTO x8(x8, rank) VALUES('usermerge', 2);
  75. INSERT INTO x8(x8, rank) VALUES('merge', 1);
  76. INSERT INTO x8(x8, rank) VALUES('usermerge', 16);
  77. INSERT INTO x8(x8) VALUES('integrity-check');
  78. }
  79. }
  80. }
  81. } {}
  82. }
  83. proc not_merged {tbl} {
  84. set segs [fts5_level_segs $tbl]
  85. foreach s $segs { if {$s>1} { return 1 } }
  86. return 0
  87. }
  88. do_merge2_test 2.1 5
  89. do_merge2_test 2.2 10
  90. do_merge2_test 2.3 20
  91. #-------------------------------------------------------------------------
  92. # Test that a merge will complete any merge that has already been
  93. # started, even if the number of input segments is less than the current
  94. # value of the 'usermerge' configuration parameter.
  95. #
  96. db func rnddoc fts5_rnddoc
  97. do_execsql_test 3.1 {
  98. DROP TABLE IF EXISTS x8;
  99. CREATE VIRTUAL TABLE x8 USING fts5(i);
  100. INSERT INTO x8(x8, rank) VALUES('pgsz', 32);
  101. INSERT INTO x8 VALUES(rnddoc(100));
  102. INSERT INTO x8 VALUES(rnddoc(100));
  103. }
  104. do_test 3.2 {
  105. execsql {
  106. INSERT INTO x8(x8, rank) VALUES('usermerge', 4);
  107. INSERT INTO x8(x8, rank) VALUES('merge', 1);
  108. }
  109. fts5_level_segs x8
  110. } {2}
  111. do_test 3.3 {
  112. execsql {
  113. INSERT INTO x8(x8, rank) VALUES('usermerge', 2);
  114. INSERT INTO x8(x8, rank) VALUES('merge', 1);
  115. }
  116. fts5_level_segs x8
  117. } {2 1}
  118. do_test 3.4 {
  119. execsql { INSERT INTO x8(x8, rank) VALUES('usermerge', 4) }
  120. while {[not_merged x8]} {
  121. execsql { INSERT INTO x8(x8, rank) VALUES('merge', 1) }
  122. }
  123. fts5_level_segs x8
  124. } {0 1}
  125. #-------------------------------------------------------------------------
  126. #
  127. proc mydoc {} {
  128. set x [lindex {a b c d e f g h i j} [expr int(rand()*10)]]
  129. return [string repeat "$x " 30]
  130. }
  131. db func mydoc mydoc
  132. proc mycount {} {
  133. set res [list]
  134. foreach x {a b c d e f g h i j} {
  135. lappend res [db one {SELECT count(*) FROM x8 WHERE x8 MATCH $x}]
  136. }
  137. set res
  138. }
  139. #1 32
  140. foreach {tn pgsz} {
  141. 2 1000
  142. } {
  143. do_execsql_test 4.$tn.1 {
  144. DROP TABLE IF EXISTS x8;
  145. CREATE VIRTUAL TABLE x8 USING fts5(i);
  146. INSERT INTO x8(x8, rank) VALUES('pgsz', $pgsz);
  147. }
  148. do_execsql_test 4.$tn.2 {
  149. INSERT INTO x8(x8, rank) VALUES('merge', 1);
  150. }
  151. do_execsql_test 4.$tn.3 {
  152. WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<100)
  153. INSERT INTO x8 SELECT mydoc() FROM ii;
  154. WITH ii(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM ii WHERE i<100)
  155. INSERT INTO x8 SELECT mydoc() FROM ii;
  156. INSERT INTO x8(x8, rank) VALUES('usermerge', 2);
  157. }
  158. set expect [mycount]
  159. for {set i 0} {$i < 20} {incr i} {
  160. do_test 4.$tn.4.$i {
  161. execsql { INSERT INTO x8(x8, rank) VALUES('merge', 1); }
  162. mycount
  163. } $expect
  164. break
  165. }
  166. # db eval {SELECT fts5_decode(rowid, block) AS r FROM x8_data} { puts $r }
  167. }
  168. #-------------------------------------------------------------------------
  169. # Test that the 'merge' command does not modify the database if there is
  170. # no work to do.
  171. do_execsql_test 5.1 {
  172. CREATE VIRTUAL TABLE x9 USING fts5(one, two);
  173. INSERT INTO x9(x9, rank) VALUES('pgsz', 32);
  174. INSERT INTO x9(x9, rank) VALUES('automerge', 2);
  175. INSERT INTO x9(x9, rank) VALUES('usermerge', 2);
  176. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  177. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  178. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  179. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  180. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  181. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  182. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  183. INSERT INTO x9 VALUES(rnddoc(100), rnddoc(100));
  184. }
  185. do_test 5.2 {
  186. while 1 {
  187. set nChange [db total_changes]
  188. execsql { INSERT INTO x9(x9, rank) VALUES('merge', 1); }
  189. set nChange [expr [db total_changes] - $nChange]
  190. #puts $nChange
  191. if {$nChange<2} break
  192. }
  193. } {}
  194. #--------------------------------------------------------------------------
  195. # Test that running 'merge' on an empty database does not cause a
  196. # problem.
  197. #
  198. reset_db
  199. do_execsql_test 6.0 {
  200. CREATE VIRTUAL TABLE g1 USING fts5(a, b);
  201. }
  202. do_execsql_test 6.1 {
  203. INSERT INTO g1(g1, rank) VALUES('merge', 10);
  204. }
  205. do_execsql_test 6.2 {
  206. INSERT INTO g1(g1, rank) VALUES('merge', -10);
  207. }
  208. do_execsql_test 6.3 {
  209. INSERT INTO g1(g1) VALUES('integrity-check');
  210. }
  211. finish_test