3 # The author disclaims copyright to this source code. In place of
4 # a legal notice, here is a blessing:
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.
10 #*************************************************************************
12 # The tests in this file test the FTS3 auxillary functions offsets(),
13 # snippet() and matchinfo() work. At time of writing, running this file
14 # provides full coverage of fts3_snippet.c.
17 set testdir [file dirname $argv0]
18 source $testdir/tester.tcl
19 set testprefix fts3snippet
21 # If SQLITE_ENABLE_FTS3 is not defined, omit this file.
22 ifcapable !fts3 { finish_test ; return }
23 source $testdir/fts3_common.tcl
25 set sqlite_fts3_enable_parentheses 1
28 # Transform the list $L to its "normal" form. So that it can be compared to
29 # another list with the same set of elements using [string compare].
33 foreach l $L {lappend ret $l}
37 proc do_offsets_test {name expr args} {
40 lappend result [normalize $a]
42 do_select_test $name {
43 SELECT offsets(ft) FROM ft WHERE ft MATCH $expr
47 # Document text used by a few tests. Contains the English names of all
48 # integers between 1 and 300.
50 set numbers [normalize {
51 one two three four five six seven eight nine ten eleven twelve thirteen
52 fourteen fifteen sixteen seventeen eighteen nineteen twenty twentyone
53 twentytwo twentythree twentyfour twentyfive twentysix twentyseven
54 twentyeight twentynine thirty thirtyone thirtytwo thirtythree thirtyfour
55 thirtyfive thirtysix thirtyseven thirtyeight thirtynine forty fortyone
56 fortytwo fortythree fortyfour fortyfive fortysix fortyseven fortyeight
57 fortynine fifty fiftyone fiftytwo fiftythree fiftyfour fiftyfive fiftysix
58 fiftyseven fiftyeight fiftynine sixty sixtyone sixtytwo sixtythree sixtyfour
59 sixtyfive sixtysix sixtyseven sixtyeight sixtynine seventy seventyone
60 seventytwo seventythree seventyfour seventyfive seventysix seventyseven
61 seventyeight seventynine eighty eightyone eightytwo eightythree eightyfour
62 eightyfive eightysix eightyseven eightyeight eightynine ninety ninetyone
63 ninetytwo ninetythree ninetyfour ninetyfive ninetysix ninetyseven
64 ninetyeight ninetynine onehundred onehundredone onehundredtwo
65 onehundredthree onehundredfour onehundredfive onehundredsix onehundredseven
66 onehundredeight onehundrednine onehundredten onehundredeleven
67 onehundredtwelve onehundredthirteen onehundredfourteen onehundredfifteen
68 onehundredsixteen onehundredseventeen onehundredeighteen onehundrednineteen
69 onehundredtwenty onehundredtwentyone onehundredtwentytwo
70 onehundredtwentythree onehundredtwentyfour onehundredtwentyfive
71 onehundredtwentysix onehundredtwentyseven onehundredtwentyeight
72 onehundredtwentynine onehundredthirty onehundredthirtyone
73 onehundredthirtytwo onehundredthirtythree onehundredthirtyfour
74 onehundredthirtyfive onehundredthirtysix onehundredthirtyseven
75 onehundredthirtyeight onehundredthirtynine onehundredforty
76 onehundredfortyone onehundredfortytwo onehundredfortythree
77 onehundredfortyfour onehundredfortyfive onehundredfortysix
78 onehundredfortyseven onehundredfortyeight onehundredfortynine
79 onehundredfifty onehundredfiftyone onehundredfiftytwo onehundredfiftythree
80 onehundredfiftyfour onehundredfiftyfive onehundredfiftysix
81 onehundredfiftyseven onehundredfiftyeight onehundredfiftynine
82 onehundredsixty onehundredsixtyone onehundredsixtytwo onehundredsixtythree
83 onehundredsixtyfour onehundredsixtyfive onehundredsixtysix
84 onehundredsixtyseven onehundredsixtyeight onehundredsixtynine
85 onehundredseventy onehundredseventyone onehundredseventytwo
86 onehundredseventythree onehundredseventyfour onehundredseventyfive
87 onehundredseventysix onehundredseventyseven onehundredseventyeight
88 onehundredseventynine onehundredeighty onehundredeightyone
89 onehundredeightytwo onehundredeightythree onehundredeightyfour
90 onehundredeightyfive onehundredeightysix onehundredeightyseven
91 onehundredeightyeight onehundredeightynine onehundredninety
92 onehundredninetyone onehundredninetytwo onehundredninetythree
93 onehundredninetyfour onehundredninetyfive onehundredninetysix
94 onehundredninetyseven onehundredninetyeight onehundredninetynine twohundred
95 twohundredone twohundredtwo twohundredthree twohundredfour twohundredfive
96 twohundredsix twohundredseven twohundredeight twohundrednine twohundredten
97 twohundredeleven twohundredtwelve twohundredthirteen twohundredfourteen
98 twohundredfifteen twohundredsixteen twohundredseventeen twohundredeighteen
99 twohundrednineteen twohundredtwenty twohundredtwentyone twohundredtwentytwo
100 twohundredtwentythree twohundredtwentyfour twohundredtwentyfive
101 twohundredtwentysix twohundredtwentyseven twohundredtwentyeight
102 twohundredtwentynine twohundredthirty twohundredthirtyone
103 twohundredthirtytwo twohundredthirtythree twohundredthirtyfour
104 twohundredthirtyfive twohundredthirtysix twohundredthirtyseven
105 twohundredthirtyeight twohundredthirtynine twohundredforty
106 twohundredfortyone twohundredfortytwo twohundredfortythree
107 twohundredfortyfour twohundredfortyfive twohundredfortysix
108 twohundredfortyseven twohundredfortyeight twohundredfortynine
109 twohundredfifty twohundredfiftyone twohundredfiftytwo twohundredfiftythree
110 twohundredfiftyfour twohundredfiftyfive twohundredfiftysix
111 twohundredfiftyseven twohundredfiftyeight twohundredfiftynine
112 twohundredsixty twohundredsixtyone twohundredsixtytwo twohundredsixtythree
113 twohundredsixtyfour twohundredsixtyfive twohundredsixtysix
114 twohundredsixtyseven twohundredsixtyeight twohundredsixtynine
115 twohundredseventy twohundredseventyone twohundredseventytwo
116 twohundredseventythree twohundredseventyfour twohundredseventyfive
117 twohundredseventysix twohundredseventyseven twohundredseventyeight
118 twohundredseventynine twohundredeighty twohundredeightyone
119 twohundredeightytwo twohundredeightythree twohundredeightyfour
120 twohundredeightyfive twohundredeightysix twohundredeightyseven
121 twohundredeightyeight twohundredeightynine twohundredninety
122 twohundredninetyone twohundredninetytwo twohundredninetythree
123 twohundredninetyfour twohundredninetyfive twohundredninetysix
124 twohundredninetyseven twohundredninetyeight twohundredninetynine
128 foreach {DO_MALLOC_TEST enc} {
137 sqlite3_db_config_lookaside db 0 0 0
138 db eval "PRAGMA encoding = \"$enc\""
140 # Set variable $T to the test name prefix for this iteration of the loop.
142 set T "fts3snippet-1.$enc"
144 ##########################################################################
145 # Test the offset function.
149 CREATE VIRTUAL TABLE ft USING fts3;
150 INSERT INTO ft VALUES('xxx xxx xxx xxx');
153 do_offsets_test $T.1.2 {xxx} {0 0 0 3 0 0 4 3 0 0 8 3 0 0 12 3}
154 do_offsets_test $T.1.3 {"xxx xxx"} {
155 0 0 0 3 0 0 4 3 0 1 4 3 0 0 8 3
158 do_offsets_test $T.1.4 {"xxx xxx" xxx} {
159 0 0 0 3 0 2 0 3 0 0 4 3 0 1 4 3
160 0 2 4 3 0 0 8 3 0 1 8 3 0 2 8 3
163 do_offsets_test $T.1.5 {xxx "xxx xxx"} {
164 0 0 0 3 0 1 0 3 0 0 4 3 0 1 4 3
165 0 2 4 3 0 0 8 3 0 1 8 3 0 2 8 3
170 set v1 [lrange $numbers 0 99]
172 DROP TABLE IF EXISTS ft;
173 CREATE VIRTUAL TABLE ft USING fts3(a, b);
174 INSERT INTO ft VALUES($v1, $numbers);
175 INSERT INTO ft VALUES($v1, NULL);
179 set off [string first "twohundred " $numbers]
180 do_offsets_test $T.2.1 {twohundred} [list 1 0 $off 10]
182 set off [string first "onehundred " $numbers]
183 do_offsets_test $T.2.2 {onehundred} \
184 [list 0 0 $off 10 1 0 $off 10] [list 0 0 $off 10]
186 # Test a corruption case:
187 execsql { UPDATE ft_content SET c1b = 'hello world' WHERE c1b = $numbers }
188 do_error_test $T.2.3 {
189 SELECT offsets(ft) FROM ft WHERE ft MATCH 'onehundred'
190 } {database disk image is malformed}
192 ##########################################################################
193 # Test the snippet function.
195 proc do_snippet_test {name expr iCol nTok args} {
197 foreach a $args { lappend res [string trim $a] }
198 do_select_test $name {
199 SELECT snippet(ft,'{','}','...',$iCol,$nTok) FROM ft WHERE ft MATCH $expr
204 DROP TABLE IF EXISTS ft;
205 CREATE VIRTUAL TABLE ft USING fts3;
206 INSERT INTO ft VALUES('one two three four five six seven eight nine ten');
209 do_snippet_test $T.3.2 one 0 5 "{one} two three four five..."
210 do_snippet_test $T.3.3 two 0 5 "one {two} three four five..."
211 do_snippet_test $T.3.4 three 0 5 "one two {three} four five..."
212 do_snippet_test $T.3.5 four 0 5 "...two three {four} five six..."
213 do_snippet_test $T.3.6 five 0 5 "...three four {five} six seven..."
214 do_snippet_test $T.3.7 six 0 5 "...four five {six} seven eight..."
215 do_snippet_test $T.3.8 seven 0 5 "...five six {seven} eight nine..."
216 do_snippet_test $T.3.9 eight 0 5 "...six seven {eight} nine ten"
217 do_snippet_test $T.3.10 nine 0 5 "...six seven eight {nine} ten"
218 do_snippet_test $T.3.11 ten 0 5 "...six seven eight nine {ten}"
222 INSERT INTO ft VALUES(
223 'one two three four five '
224 || 'six seven eight nine ten '
225 || 'eleven twelve thirteen fourteen fifteen '
226 || 'sixteen seventeen eighteen nineteen twenty '
227 || 'one two three four five '
228 || 'six seven eight nine ten '
229 || 'eleven twelve thirteen fourteen fifteen '
230 || 'sixteen seventeen eighteen nineteen twenty'
235 do_snippet_test $T.4.2 {one nine} 0 5 {
236 {one} two three...eight {nine} ten
238 {one} two three...eight {nine} ten...
241 do_snippet_test $T.4.3 {one nine} 0 -5 {
242 {one} two three four five...six seven eight {nine} ten
244 {one} two three four five...seven eight {nine} ten eleven...
246 do_snippet_test $T.4.3 {one nineteen} 0 -5 {
247 ...eighteen {nineteen} twenty {one} two...
249 do_snippet_test $T.4.4 {two nineteen} 0 -5 {
250 ...eighteen {nineteen} twenty one {two}...
252 do_snippet_test $T.4.5 {three nineteen} 0 -5 {
253 ...{nineteen} twenty one two {three}...
256 do_snippet_test $T.4.6 {four nineteen} 0 -5 {
257 ...two three {four} five six...seventeen eighteen {nineteen} twenty one...
259 do_snippet_test $T.4.7 {four NEAR nineteen} 0 -5 {
260 ...seventeen eighteen {nineteen} twenty one...two three {four} five six...
263 do_snippet_test $T.4.8 {four nineteen} 0 5 {
264 ...three {four} five...eighteen {nineteen} twenty...
266 do_snippet_test $T.4.9 {four NEAR nineteen} 0 5 {
267 ...eighteen {nineteen} twenty...three {four} five...
269 do_snippet_test $T.4.10 {four NEAR nineteen} 0 -5 {
270 ...seventeen eighteen {nineteen} twenty one...two three {four} five six...
272 do_snippet_test $T.4.11 {four NOT (nineteen twentyone)} 0 5 {
273 ...two three {four} five six...
275 ...two three {four} five six...
277 do_snippet_test $T.4.12 {four OR nineteen NEAR twentyone} 0 5 {
278 ...two three {four} five six...
280 ...two three {four} five six...
285 DROP TABLE IF EXISTS ft;
286 CREATE VIRTUAL TABLE ft USING fts3(a, b, c);
287 INSERT INTO ft VALUES(
288 'one two three four five',
289 'four five six seven eight',
290 'seven eight nine ten eleven'
295 do_snippet_test $T.5.2 {five} -1 3 {...three four {five}}
296 do_snippet_test $T.5.3 {five} 0 3 {...three four {five}}
297 do_snippet_test $T.5.4 {five} 1 3 {four {five} six...}
298 do_snippet_test $T.5.5 {five} 2 3 {seven eight nine...}
301 execsql { UPDATE ft SET b = NULL }
304 do_snippet_test $T.5.7 {five} -1 3 {...three four {five}}
305 do_snippet_test $T.5.8 {five} 0 3 {...three four {five}}
306 do_snippet_test $T.5.9 {five} 1 3 {}
307 do_snippet_test $T.5.10 {five} 2 3 {seven eight nine...}
309 do_snippet_test $T.5.11 {one "seven eight nine"} -1 -3 {
310 {one} two three...{seven} {eight} {nine}...
315 DROP TABLE IF EXISTS ft;
316 CREATE VIRTUAL TABLE ft USING fts3(x);
317 INSERT INTO ft VALUES($numbers);
320 do_snippet_test $T.6.2 {
321 one fifty onehundred onehundredfifty twohundredfifty threehundred
323 {one}...{fifty}...{onehundred}...{onehundredfifty}...
325 do_snippet_test $T.6.3 {
326 one fifty onehundred onehundredfifty twohundredfifty threehundred
328 {one} two three four...fortyeight fortynine {fifty} fiftyone...ninetyeight ninetynine {onehundred} onehundredone...onehundredfortyeight onehundredfortynine {onehundredfifty} onehundredfiftyone...
334 DROP TABLE IF EXISTS ft;
335 CREATE VIRTUAL TABLE ft USING fts3(x);
337 set testresults [list]
338 for {set i 1} {$i < 150} {incr i} {
339 set commas [string repeat , $i]
340 execsql {INSERT INTO ft VALUES('one' || $commas || 'two')}
341 lappend testresults "{one}$commas{two}"
345 eval [list do_snippet_test $T.7.2 {one two} -1 3] $testresults
347 ##########################################################################
348 # Test the matchinfo function.
351 set scan(littleEndian) i*
352 set scan(bigEndian) I*
353 binary scan $blob $scan($::tcl_platform(byteOrder)) r
357 proc do_matchinfo_test {name expr args} {
359 foreach a $args { lappend res [normalize $a] }
360 do_select_test $name {
361 SELECT mit(matchinfo(ft)) FROM ft WHERE ft MATCH $expr
365 set ten {one two three four five six seven eight nine ten}
367 DROP TABLE IF EXISTS ft;
368 CREATE VIRTUAL TABLE ft USING fts3;
369 INSERT INTO ft VALUES($ten);
370 INSERT INTO ft VALUES($ten || ' ' || $ten);
374 do_matchinfo_test $T.8.2 "one" {1 1 1 3 2} {1 1 2 3 2}
375 do_matchinfo_test $T.8.3 "one NEAR/3 ten" {2 1 1 1 1 1 1 1}
376 do_matchinfo_test $T.8.4 "five NEAR/4 ten" \
377 {2 1 1 3 2 1 3 2} {2 1 2 3 2 2 3 2}
378 do_matchinfo_test $T.8.5 "six NEAR/3 ten NEAR/3 two" \
379 {3 1 1 1 1 1 1 1 1 1 1}
380 do_matchinfo_test $T.8.6 "five NEAR/4 ten NEAR/3 two" \
381 {3 1 2 2 1 1 1 1 1 1 1}
385 DROP TABLE IF EXISTS ft;
386 CREATE VIRTUAL TABLE ft USING fts3(x, y);
389 set v1 [lrange $numbers 0 [expr $n*100]]
390 set v2 [string trim [string repeat "$numbers " $n]]
391 set docid [expr $n * 1000000]
392 execsql { INSERT INTO ft(docid, x, y) VALUES($docid, $v1, $v2) }
395 do_matchinfo_test $T.9.2 {two*} \
396 { 1 2 1 105 3 101 606 3} \
397 { 1 2 3 105 3 202 606 3} \
398 { 1 2 101 105 3 303 606 3}
400 do_matchinfo_test $T.9.4 {"one* two*"} \
401 { 1 2 1 5 3 2 12 3} \
402 { 1 2 2 5 3 4 12 3} \
405 do_matchinfo_test $T.9.5 {twohundredfifty} \
410 do_matchinfo_test $T.9.6 {"threehundred one"} \
414 do_matchinfo_test $T.9.7 {one OR fivehundred} \
415 { 2 2 1 3 3 1 6 3 0 0 0 0 0 0 } \
416 { 2 2 1 3 3 2 6 3 0 0 0 0 0 0 } \
417 { 2 2 1 3 3 3 6 3 0 0 0 0 0 0 }
419 do_matchinfo_test $T.9.8 {two OR "threehundred one"} \
420 { 2 2 1 3 3 1 6 3 0 0 0 0 3 2 } \
421 { 2 2 1 3 3 2 6 3 0 0 0 1 3 2 } \
422 { 2 2 1 3 3 3 6 3 0 0 0 2 3 2 }
424 do_select_test $T.9.9 {
425 SELECT mit(matchinfo(ft)), mit(matchinfo(ft))
426 FROM ft WHERE ft MATCH 'two OR "threehundred one"'
428 {2 2 1 3 3 1 6 3 0 0 0 0 3 2}
429 {2 2 1 3 3 1 6 3 0 0 0 0 3 2}
430 {2 2 1 3 3 2 6 3 0 0 0 1 3 2}
431 {2 2 1 3 3 2 6 3 0 0 0 1 3 2}
432 {2 2 1 3 3 3 6 3 0 0 0 2 3 2}
433 {2 2 1 3 3 3 6 3 0 0 0 2 3 2}
436 # EVIDENCE-OF: R-40630-02268 If used within a SELECT that uses the
437 # "query by rowid" or "linear scan" strategies, then the snippet and
438 # offsets both return an empty string, and the matchinfo function
439 # returns a blob value zero bytes in size.
441 set r 1000000 ;# A rowid that exists in table ft
442 do_select_test $T.10.0 { SELECT rowid FROM ft WHERE rowid = $r } $r
443 do_select_test $T.10.1 {
444 SELECT length(offsets(ft)), typeof(offsets(ft)) FROM ft;
445 } {0 text 0 text 0 text}
446 do_select_test $T.10.2 {
447 SELECT length(offsets(ft)), typeof(offsets(ft)) FROM ft WHERE rowid = $r
449 do_select_test $T.10.3 {
450 SELECT length(snippet(ft)), typeof(snippet(ft)) FROM ft;
451 } {0 text 0 text 0 text}
452 do_select_test $T.10.4 {
453 SELECT length(snippet(ft)), typeof(snippet(ft)) FROM ft WHERE rowid = $r;
455 do_select_test $T.10.5 {
456 SELECT length(matchinfo(ft)), typeof(matchinfo(ft)) FROM ft;
457 } {0 blob 0 blob 0 blob}
458 do_select_test $T.10.6 {
459 SELECT length(matchinfo(ft)), typeof(matchinfo(ft)) FROM ft WHERE rowid = $r
463 #-------------------------------------------------------------------------
464 # Test an interaction between the snippet() function and OR clauses.
466 do_execsql_test 2.1 {
467 CREATE VIRTUAL TABLE t2 USING fts4;
468 INSERT INTO t2 VALUES('one two three four five');
469 INSERT INTO t2 VALUES('two three four five one');
470 INSERT INTO t2 VALUES('three four five one two');
471 INSERT INTO t2 VALUES('four five one two three');
472 INSERT INTO t2 VALUES('five one two three four');
475 do_execsql_test 2.2 {
476 SELECT snippet(t2, '[', ']') FROM t2 WHERE t2 MATCH 'one OR (four AND six)'
478 {[one] two three [four] five}
479 {two three [four] five [one]}
480 {three [four] five [one] two}
481 {[four] five [one] two three}
482 {five [one] two three [four]}
485 do_execsql_test 2.3 {
486 SELECT snippet(t2, '[', ']') FROM t2
487 WHERE t2 MATCH 'one OR (four AND six)'
490 {five [one] two three [four]}
491 {[four] five [one] two three}
492 {three [four] five [one] two}
493 {two three [four] five [one]}
494 {[one] two three [four] five}
497 do_execsql_test 2.4 {
498 INSERT INTO t2 VALUES('six');
501 do_execsql_test 2.5 {
502 SELECT snippet(t2, '[', ']') FROM t2 WHERE t2 MATCH 'one OR (four AND six)'
504 {[one] two three [four] five}
505 {two three [four] five [one]}
506 {three [four] five [one] two}
507 {[four] five [one] two three}
508 {five [one] two three [four]}
511 do_execsql_test 2.6 {
512 SELECT snippet(t2, '[', ']') FROM t2
513 WHERE t2 MATCH 'one OR (four AND six)'
516 {five [one] two three [four]}
517 {[four] five [one] two three}
518 {three [four] five [one] two}
519 {two three [four] five [one]}
520 {[one] two three [four] five}
523 #-------------------------------------------------------------------------
525 CREATE VIRTUAL TABLE t3 USING fts4;
526 INSERT INTO t3 VALUES('[one two three]');
528 do_execsql_test 3.1 {
529 SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'one';
530 } {{[<b>one</b> two three]}}
531 do_execsql_test 3.2 {
532 SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'two';
533 } {{[one <b>two</b> three]}}
534 do_execsql_test 3.3 {
535 SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'three';
536 } {{[one two <b>three</b>]}}
537 do_execsql_test 3.4 {
538 SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'one OR two OR three';
539 } {{[<b>one</b> <b>two</b> <b>three</b>]}}
541 #-------------------------------------------------------------------------
542 # Request a snippet 0 tokens in size. This is always an empty string.
543 do_execsql_test 4.1 {
544 CREATE VIRTUAL TABLE t4 USING fts4;
545 INSERT INTO t4 VALUES('a b c d');
546 SELECT snippet(t4, '[', ']', '...', 0, 0) FROM t4 WHERE t4 MATCH 'b';
550 set x35 [string trim [string repeat "x " 35]]
551 execsql "INSERT INTO t4 VALUES('$x35 E $x35 F $x35 G $x35');"
553 SELECT snippet(t4, '', '', '', 0, 64) FROM t4 WHERE t4 MATCH 'E'
560 set sqlite_fts3_enable_parentheses 0