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