• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 2009 November 28
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 implements tests to verify the "testable statements" in the
13# fts3.in document.
14#
15
16set testdir [file dirname $argv0]
17source $testdir/tester.tcl
18
19# If this build does not include FTS3, skip the tests in this file.
20#
21ifcapable !fts3 { finish_test ; return }
22source $testdir/fts3_common.tcl
23source $testdir/malloc_common.tcl
24
25# Procs used to make the tests in this file easier to read.
26#
27proc ddl_test {tn ddl} {
28  uplevel [list do_write_test e_fts3-$tn sqlite_master $ddl]
29}
30proc write_test {tn tbl sql} {
31  uplevel [list do_write_test e_fts3-$tn $tbl $sql]
32}
33proc read_test {tn sql result} {
34  uplevel [list do_select_test e_fts3-$tn $sql $result]
35}
36proc error_test {tn sql result} {
37  uplevel [list do_error_test e_fts3-$tn $sql $result]
38}
39
40
41#-------------------------------------------------------------------------
42# The body of the following [foreach] block contains test cases to verify
43# that the example code in fts3.html works as expected. The tests run three
44# times, with different values for DO_MALLOC_TEST.
45#
46#   DO_MALLOC_TEST=0: Run tests with no OOM errors.
47#   DO_MALLOC_TEST=1: Run tests with transient OOM errors.
48#   DO_MALLOC_TEST=2: Run tests with persistent OOM errors.
49#
50foreach {DO_MALLOC_TEST enc} {
51  0 utf-8
52  1 utf-8
53  2 utf-8
54  1 utf-16
55} {
56
57#if {$DO_MALLOC_TEST} break
58
59# Reset the database and database connection. If this iteration of the
60# [foreach] loop is testing with OOM errors, disable the lookaside buffer.
61#
62db close
63file delete -force test.db test.db-journal
64sqlite3 db test.db
65if {$DO_MALLOC_TEST} { sqlite3_db_config_lookaside db 0 0 0 }
66db eval "PRAGMA encoding = '$enc'"
67
68proc mit {blob} {
69  set scan(littleEndian) i*
70  set scan(bigEndian) I*
71  binary scan $blob $scan($::tcl_platform(byteOrder)) r
72  return $r
73}
74db func mit mit
75
76##########################################################################
77# Test the example CREATE VIRTUAL TABLE statements in section 1.1
78# of fts3.in.
79#
80ddl_test   1.1.1.1 {CREATE VIRTUAL TABLE data USING fts3()}
81read_test  1.1.1.2 {PRAGMA table_info(data)} {0 content {} 0 {} 0}
82
83ddl_test   1.1.2.1 {
84  CREATE VIRTUAL TABLE pages USING fts3(title, keywords, body)
85}
86read_test  1.1.2.2 {
87  PRAGMA table_info(pages)
88} {0 title {} 0 {} 0 1 keywords {} 0 {} 0 2 body {} 0 {} 0}
89
90ddl_test   1.1.3.1 {
91  CREATE VIRTUAL TABLE mail USING fts3(
92      subject VARCHAR(256) NOT NULL,
93      body TEXT CHECK(length(body)<10240)
94  )
95}
96read_test  1.1.3.2 {
97  PRAGMA table_info(mail)
98} {0 subject {} 0 {} 0 1 body {} 0 {} 0}
99
100# A very large string. Used to test if the constraint on column "body" of
101# table "mail" is enforced (it should not be - FTS3 tables do not support
102# constraints).
103set largetext [string repeat "the quick brown fox " 5000]
104write_test 1.1.3.3 mail_content { INSERT INTO mail VALUES(NULL, $largetext) }
105read_test  1.1.3.4 {
106  SELECT subject IS NULL, length(body) FROM mail
107} [list 1 100000]
108
109ddl_test   1.1.4.1 {
110  CREATE VIRTUAL TABLE papers USING fts3(author, document, tokenize=porter)
111}
112read_test  1.1.4.2 {
113  PRAGMA table_info(papers)
114} {0 author {} 0 {} 0 1 document {} 0 {} 0}
115
116ddl_test   1.1.5.1 {
117  CREATE VIRTUAL TABLE simpledata USING fts3(tokenize=simple)
118}
119read_test  1.1.5.2 {
120  PRAGMA table_info(simpledata)
121} {0 content {} 0 {} 0}
122
123ifcapable icu {
124  ddl_test 1.1.6.1 {
125    CREATE VIRTUAL TABLE names USING fts3(a, b, tokenize=icu en_AU)
126  }
127  read_test  1.1.6.2 {
128    PRAGMA table_info(names)
129  } {0 a {} 0 {} 0 1 b {} 0 {} 0}
130}
131
132ddl_test   1.1.7.1 {DROP TABLE data}
133ddl_test   1.1.7.2 {DROP TABLE pages}
134ddl_test   1.1.7.3 {DROP TABLE mail}
135ddl_test   1.1.7.4 {DROP TABLE papers}
136ddl_test   1.1.7.5 {DROP TABLE simpledata}
137read_test  1.1.7.6 {SELECT * FROM sqlite_master} {}
138
139# The following is not one of the examples in section 1.1. It tests
140# specifying an FTS3 table with no module arguments using a slightly
141# different syntax.
142ddl_test   1.1.8.1 {CREATE VIRTUAL TABLE data USING fts3;}
143read_test  1.1.8.2 {PRAGMA table_info(data)} {0 content {} 0 {} 0}
144ddl_test   1.1.8.3 {DROP TABLE data}
145
146##########################################################################
147# Test the examples in section 1.2 (populating fts3 tables)
148#
149ddl_test   1.2.1.1 {
150  CREATE VIRTUAL TABLE pages USING fts3(title, body);
151}
152write_test 1.2.1.2 pages_content {
153  INSERT INTO pages(docid, title, body)
154  VALUES(53, 'Home Page', 'SQLite is a software...');
155}
156read_test  1.2.1.3 {
157  SELECT docid, * FROM pages
158} {53 {Home Page} {SQLite is a software...}}
159
160write_test 1.2.1.4 pages_content {
161  INSERT INTO pages(title, body)
162  VALUES('Download', 'All SQLite source code...');
163}
164read_test  1.2.1.5 {
165  SELECT docid, * FROM pages
166} {53 {Home Page} {SQLite is a software...} 54 Download {All SQLite source code...}}
167
168write_test 1.2.1.6 pages_content {
169  UPDATE pages SET title = 'Download SQLite' WHERE rowid = 54
170}
171read_test  1.2.1.7 {
172  SELECT docid, * FROM pages
173} {53 {Home Page} {SQLite is a software...} 54 {Download SQLite} {All SQLite source code...}}
174
175write_test 1.2.1.8 pages_content { DELETE FROM pages }
176read_test  1.2.1.9 { SELECT docid, * FROM pages } {}
177
178do_error_test fts3-1.2.1.10 {
179  INSERT INTO pages(rowid, docid, title, body) VALUES(1, 2, 'A title', 'A document body');
180} {SQL logic error or missing database}
181
182# Test the optimize() function example:
183ddl_test   1.2.2.1 { CREATE VIRTUAL TABLE docs USING fts3 }
184write_test 1.2.2.2 docs_content {
185  INSERT INTO docs VALUES('Others translate the first clause as');
186}
187write_test 1.2.2.3 docs_content {
188  INSERT INTO docs VALUES('"which is for Solomon," meaning that');
189}
190write_test 1.2.2.4 docs_content {
191  INSERT INTO docs VALUES('the book is dedicated to Solomon.');
192}
193read_test  1.2.2.5 { SELECT count(*) FROM docs_segdir } {3}
194write_test 1.2.2.6 docs_segdir {
195  INSERT INTO docs(docs) VALUES('optimize');
196}
197read_test  1.2.2.7 { SELECT count(*) FROM docs_segdir } {1}
198ddl_test   1.2.2.8 { DROP TABLE docs }
199
200##########################################################################
201# Test the examples in section 1.3 (querying FTS3 tables)
202#
203ddl_test   1.3.1.1 { CREATE VIRTUAL TABLE mail USING fts3(subject, body) }
204read_test  1.3.1.2 {
205  SELECT * FROM mail WHERE rowid = 15;                -- Fast. Rowid lookup.
206  SELECT * FROM mail WHERE body MATCH 'sqlite';       -- Fast. Full-text query.
207  SELECT * FROM mail WHERE mail MATCH 'search';       -- Fast. Full-text query.
208  SELECT * FROM mail WHERE rowid BETWEEN 15 AND 20;   -- Slow. Linear scan.
209  SELECT * FROM mail WHERE subject = 'database';      -- Slow. Linear scan.
210  SELECT * FROM mail WHERE subject MATCH 'database';  -- Fast. Full-text query.
211} {}
212ddl_test   1.3.1.3 { DROP TABLE mail }
213
214ddl_test   1.3.2.1 { CREATE VIRTUAL TABLE mail USING fts3(subject, body) }
215
216write_test 1.3.2.2 mail_content {
217  INSERT INTO mail(docid, subject, body)
218  VALUES(1, 'software feedback', 'found it too slow')
219}
220write_test 1.3.2.3 mail_content {
221  INSERT INTO mail(docid, subject, body)
222  VALUES(2, 'software feedback', 'no feedback')
223}
224write_test 1.3.2.4 mail_content {
225  INSERT INTO mail(docid, subject, body)
226  VALUES(3, 'slow lunch order',  'was a software problem')
227}
228read_test  1.3.2.5 {
229  SELECT * FROM mail WHERE subject MATCH 'software'
230} {{software feedback} {found it too slow} {software feedback} {no feedback}}
231read_test  1.3.2.6 {
232  SELECT * FROM mail WHERE body MATCH 'feedback'
233} {{software feedback} {no feedback}}
234read_test  1.3.2.7 {
235  SELECT * FROM mail WHERE mail MATCH 'software'
236} {{software feedback} {found it too slow} {software feedback} {no feedback} {slow lunch order} {was a software problem}}
237read_test  1.3.2.7 {
238  SELECT * FROM mail WHERE mail MATCH 'slow'
239} {{software feedback} {found it too slow} {slow lunch order} {was a software problem}}
240ddl_test   1.3.2.8 { DROP TABLE mail }
241
242ddl_test   1.3.3.1 { CREATE VIRTUAL TABLE docs USING fts3(content) }
243read_test  1.3.3.2 { SELECT * FROM docs WHERE docs MATCH 'sqlite' } {}
244read_test  1.3.3.3 { SELECT * FROM docs WHERE docs.docs MATCH 'sqlite' } {}
245read_test  1.3.3.4 { SELECT * FROM docs WHERE main.docs.docs MATCH 'sqlite' } {}
246do_error_test e_fts3-1.3.3.5 {
247  SELECT * FROM docs WHERE main.docs MATCH 'sqlite'
248} {no such column: main.docs}
249ddl_test   1.3.2.8 { DROP TABLE docs }
250
251##########################################################################
252# Test the examples in section 3 (full-text index queries).
253#
254ddl_test   1.4.1.1 { CREATE VIRTUAL TABLE docs USING fts3(title, body) }
255unset -nocomplain R
256foreach {tn title body} {
257  2 "linux driver" "a device"
258  3 "driver"       "linguistic trick"
259  4 "problems"     "linux problems"
260  5 "linux"        "big problems"
261  6 "linux driver" "a device driver problem"
262  7 "good times"   "applications for linux"
263  8 "not so good"  "linux applications"
264  9 "alternative"  "linoleum appliances"
265 10 "no L I N"     "to be seen"
266} {
267  write_test 1.4.1.$tn docs_content { INSERT INTO docs VALUES($title,$body) }
268  set R($tn) [list $title $body]
269}
270
271read_test  1.4.1.11 {
272  SELECT * FROM docs WHERE docs MATCH 'linux'
273} [concat $R(2) $R(4) $R(5) $R(6) $R(7) $R(8)]
274read_test  1.4.1.12 {
275  SELECT * FROM docs WHERE docs MATCH 'lin*'
276} [concat $R(2) $R(3) $R(4) $R(5) $R(6) $R(7) $R(8) $R(9)]
277read_test  1.4.1.13 {
278  SELECT * FROM docs WHERE docs MATCH 'title:linux problems'
279} [concat $R(5)]
280read_test  1.4.1.14 {
281  SELECT * FROM docs WHERE body MATCH 'title:linux driver'
282} [concat $R(6)]
283read_test  1.4.1.15 {
284  SELECT * FROM docs WHERE docs MATCH '"linux applications"'
285} [concat $R(8)]
286read_test  1.4.1.16 {
287  SELECT * FROM docs WHERE docs MATCH '"lin* app*"'
288} [concat $R(8) $R(9)]
289ddl_test   1.4.1.17 { DROP TABLE docs }
290unset R
291
292ddl_test   1.4.2.1 { CREATE VIRTUAL TABLE docs USING fts3() }
293write_test 1.4.2.2 docs_content {
294  INSERT INTO docs VALUES(
295  'SQLite is an ACID compliant embedded relational database management system')
296}
297foreach {tn query hit} {
2983 {SELECT * FROM docs WHERE docs MATCH 'sqlite NEAR database'} 1
2994 {SELECT * FROM docs WHERE docs MATCH 'database NEAR/6 sqlite'} 1
3005 {SELECT * FROM docs WHERE docs MATCH 'database NEAR/5 sqlite'} 0
3016 {SELECT * FROM docs WHERE docs MATCH 'database NEAR/2 "ACID compliant"'} 1
3027 {SELECT * FROM docs WHERE docs MATCH '"ACID compliant" NEAR/2 sqlite'} 1
3038 {SELECT * FROM docs WHERE docs MATCH 'sqlite NEAR/2 acid NEAR/2 relational'} 1
3049 {SELECT * FROM docs WHERE docs MATCH 'acid NEAR/2 sqlite NEAR/2 relational'} 0
305} {
306  set res [db eval {SELECT * FROM docs WHERE $hit}]
307  read_test 1.4.2.$tn $query $res
308}
309ddl_test 1.4.2.10 { DROP TABLE docs }
310
311##########################################################################
312# Test the example in section 3.1 (set operators with enhanced syntax).
313#
314set sqlite_fts3_enable_parentheses 1
315ddl_test 1.5.1.1 { CREATE VIRTUAL TABLE docs USING fts3() }
316foreach {tn docid content} {
317  2 1 "a database is a software system"
318  3 2 "sqlite is a software system"
319  4 3 "sqlite is a database"
320} {
321  set R($docid) $content
322  write_test 1.5.1.$tn docs_content {
323    INSERT INTO docs(docid, content) VALUES($docid, $content)
324  }
325}
326read_test 1.5.1.4 {
327  SELECT * FROM docs WHERE docs MATCH 'sqlite AND database'
328} [list $R(3)]
329read_test 1.5.1.5 {
330  SELECT * FROM docs WHERE docs MATCH 'database sqlite'
331} [list $R(3)]
332read_test 1.5.1.6 {
333  SELECT * FROM docs WHERE docs MATCH 'sqlite OR database'
334} [list $R(1) $R(2) $R(3)]
335read_test 1.5.1.7 {
336  SELECT * FROM docs WHERE docs MATCH 'database NOT sqlite'
337} [list $R(1)]
338read_test 1.5.1.8 {
339  SELECT * FROM docs WHERE docs MATCH 'database and sqlite'
340} {}
341
342write_test 1.5.2.1 docs_content {
343  INSERT INTO docs
344    SELECT 'sqlite is also a library' UNION ALL
345    SELECT 'library software'
346}
347read_test 1.5.2.2 {
348  SELECT docid FROM docs WHERE docs MATCH 'sqlite AND database OR library'
349} {3 4 5}
350read_test 1.5.2.3 {
351  SELECT docid FROM docs WHERE docs MATCH 'sqlite AND database'
352    UNION
353  SELECT docid FROM docs WHERE docs MATCH 'library'
354} {3 4 5}
355write_test 1.5.2.4 docs_content {
356  INSERT INTO docs
357    SELECT 'the sqlite library runs on linux' UNION ALL
358    SELECT 'as does the sqlite database (on linux)' UNION ALL
359    SELECT 'the sqlite database is accessed by the sqlite library'
360}
361read_test 1.5.2.2 {
362  SELECT docid FROM docs
363  WHERE docs MATCH '("sqlite database" OR "sqlite library") AND linux';
364} {6 7}
365read_test 1.5.2.3 {
366  SELECT docid FROM docs WHERE docs MATCH 'linux'
367    INTERSECT
368  SELECT docid FROM (
369    SELECT docid FROM docs WHERE docs MATCH '"sqlite library"'
370      UNION
371    SELECT docid FROM docs WHERE docs MATCH '"sqlite database"'
372  );
373} {6 7}
374
375##########################################################################
376# Test the examples in section 3.2 (set operators with standard syntax).
377# These tests reuse the table populated by the block above.
378#
379set sqlite_fts3_enable_parentheses 0
380read_test 1.6.1.1 {
381  SELECT * FROM docs WHERE docs MATCH 'sqlite -database'
382} {{sqlite is a software system} {sqlite is also a library} {the sqlite library runs on linux}}
383read_test 1.6.1.2 {
384  SELECT * FROM docs WHERE docs MATCH 'sqlite OR database library'
385} {{sqlite is also a library} {the sqlite library runs on linux} {the sqlite database is accessed by the sqlite library}}
386
387set sqlite_fts3_enable_parentheses 1
388read_test 1.6.1.3 {
389  SELECT * FROM docs WHERE docs MATCH 'sqlite OR database library'
390} {{sqlite is a software system} {sqlite is a database} {sqlite is also a library} {the sqlite library runs on linux} {as does the sqlite database (on linux)} {the sqlite database is accessed by the sqlite library}}
391read_test 1.6.1.4 {
392  SELECT * FROM docs WHERE docs MATCH '(sqlite OR database) library'
393} {{sqlite is also a library} {the sqlite library runs on linux} {the sqlite database is accessed by the sqlite library}}
394set sqlite_fts3_enable_parentheses 0
395ddl_test  1.6.1.5 { DROP TABLE docs }
396
397##########################################################################
398# Test the examples in section 4 (auxillary functions).
399#
400ddl_test   1.7.1.1 { CREATE VIRTUAL TABLE mail USING fts3(subject, body) }
401
402write_test 1.7.1.2 mail_content {
403  INSERT INTO mail VALUES(
404    'hello world', 'This message is a hello world message.');
405}
406write_test 1.7.1.3 mail_content {
407  INSERT INTO mail VALUES(
408    'urgent: serious', 'This mail is seen as a more serious mail');
409}
410
411read_test  1.7.1.4 {
412  SELECT offsets(mail) FROM mail WHERE mail MATCH 'world';
413} {{0 0 6 5 1 0 24 5}}
414read_test  1.7.1.5 {
415  SELECT offsets(mail) FROM mail WHERE mail MATCH 'message'
416} {{1 0 5 7 1 0 30 7}}
417read_test  1.7.1.6 {
418  SELECT offsets(mail) FROM mail WHERE mail MATCH '"serious mail"'
419} {{1 0 28 7 1 1 36 4}}
420
421ddl_test   1.7.2.1 { CREATE VIRTUAL TABLE text USING fts3() }
422
423write_test 1.7.2.2 text_content {
424  INSERT INTO text VALUES('
425    During 30 Nov-1 Dec, 2-3oC drops. Cool in the upper portion, minimum temperature 14-16oC and cool elsewhere, minimum temperature 17-20oC. Cold to very cold on mountaintops, minimum temperature 6-12oC. Northeasterly winds 15-30 km/hr. After that, temperature increases. Northeasterly winds 15-30 km/hr.
426  ');
427}
428
429read_test  1.7.2.3 {
430  SELECT snippet(text) FROM text WHERE text MATCH 'cold'
431} {{<b>...</b>cool elsewhere, minimum temperature 17-20oC. <b>Cold</b> to very <b>cold</b> on mountaintops, minimum temperature 6<b>...</b>}}
432
433read_test  1.7.2.4 {
434  SELECT snippet(text, '[', ']', '...') FROM text WHERE text MATCH '"min* tem*"'
435} {{...the upper portion, [minimum] [temperature] 14-16oC and cool elsewhere, [minimum] [temperature] 17-20oC. Cold...}}
436
437ddl_test   1.7.3.1 { DROP TABLE IF EXISTS t1 }
438ddl_test   1.7.3.2 { CREATE VIRTUAL TABLE t1 USING fts3(a, b) }
439write_test 1.7.3.3 t1_content {
440  INSERT INTO t1 VALUES(
441    'transaction default models default', 'Non transaction reads');
442}
443write_test 1.7.3.4 t1_content {
444  INSERT INTO t1 VALUES('the default transaction', 'these semantics present');
445}
446write_test 1.7.3.5 t1_content {
447  INSERT INTO t1 VALUES('single request', 'default data');
448}
449read_test  1.7.3.6 {
450  SELECT mit(matchinfo(t1)) FROM t1
451    WHERE t1 MATCH 'default transaction "these semantics"';
452} {{3 2 1 3 2 0 1 1 1 2 2 0 1 1 0 0 0 1 1 1}}
453
454##########################################################################
455# Test the example in section 5 (custom tokenizers).
456#
457ddl_test   1.8.1.1 { CREATE VIRTUAL TABLE simple USING fts3(tokenize=simple) }
458write_test 1.8.1.2 simple_content {
459  INSERT INTO simple VALUES('Right now they''re very frustrated')
460}
461read_test 1.8.1.3 {SELECT docid FROM simple WHERE simple MATCH 'Frustrated'} {1}
462read_test 1.8.1.4 {SELECT docid FROM simple WHERE simple MATCH 'Frustration'} {}
463
464ddl_test   1.8.2.1 { CREATE VIRTUAL TABLE porter USING fts3(tokenize=porter) }
465write_test 1.8.2.2 porter_content {
466  INSERT INTO porter VALUES('Right now they''re very frustrated')
467}
468read_test 1.8.2.4 {
469  SELECT docid FROM porter WHERE porter MATCH 'Frustration'
470} {1}
471
472}
473# End of tests of example code in fts3.html
474#-------------------------------------------------------------------------
475
476#-------------------------------------------------------------------------
477# Test that errors in the arguments passed to the snippet and offsets
478# functions are handled correctly.
479#
480set DO_MALLOC_TEST 0
481ddl_test   2.1.0 { DROP TABLE IF EXISTS t1 }
482ddl_test   2.1.1 { CREATE VIRTUAL TABLE t1 USING fts3(a, b) }
483write_test 2.1.2 t1_content {
484  INSERT INTO t1 VALUES('one two three', x'A1B2C3D4E5F6');
485}
486error_test 2.1.3 {
487  SELECT offsets(a) FROM t1 WHERE a MATCH 'one'
488} {illegal first argument to offsets}
489error_test 2.1.4 {
490  SELECT offsets(b) FROM t1 WHERE a MATCH 'one'
491} {illegal first argument to offsets}
492error_test 2.1.5 {
493  SELECT optimize(a) FROM t1 LIMIT 1
494} {illegal first argument to optimize}
495error_test 2.1.6 {
496  SELECT snippet(a) FROM t1 WHERE a MATCH 'one'
497} {illegal first argument to snippet}
498error_test 2.1.7 {
499  SELECT snippet() FROM t1 WHERE a MATCH 'one'
500} {unable to use function snippet in the requested context}
501error_test 2.1.8 {
502  SELECT snippet(a, b, 'A', 'B', 'C', 'D', 'E') FROM t1 WHERE a MATCH 'one'
503} {wrong number of arguments to function snippet()}
504#-------------------------------------------------------------------------
505
506#-------------------------------------------------------------------------
507# Test the effect of an OOM error while installing the FTS3 module (i.e.
508# opening a database handle). This case was not tested by the OOM testing
509# of the document examples above.
510#
511do_malloc_test e_fts3-3 -tclbody {
512  if {[catch {sqlite3 db test.db}]} { error "out of memory" }
513}
514#-------------------------------------------------------------------------
515
516#-------------------------------------------------------------------------
517# Verify the return values of the optimize() function. If no error occurs,
518# the returned value should be "Index optimized" if the data structure
519# was modified, or "Index already optimal" if it were not.
520#
521set DO_MALLOC_TEST 0
522ddl_test   4.1 { CREATE VIRTUAL TABLE t4 USING fts3(a, b) }
523write_test 4.2 t4_content {
524  INSERT INTO t4 VALUES('In Xanadu', 'did Kubla Khan');
525}
526write_test 4.3 t4_content {
527  INSERT INTO t4 VALUES('a stately pleasure', 'dome decree');
528}
529do_test e_fts3-4.4 {
530  execsql { SELECT optimize(t4) FROM t4 LIMIT 1 }
531} {{Index optimized}}
532do_test e_fts3-4.5 {
533  execsql { SELECT optimize(t4) FROM t4 LIMIT 1 }
534} {{Index already optimal}}
535#-------------------------------------------------------------------------
536
537#-------------------------------------------------------------------------
538# Test that the snippet function appears to work correctly with 1, 2, 3
539# or 4 arguments passed to it.
540#
541set DO_MALLOC_TEST 0
542ddl_test   5.1 { CREATE VIRTUAL TABLE t5 USING fts3(x) }
543write_test 5.2 t5_content {
544  INSERT INTO t5 VALUES('In Xanadu did Kubla Khan A stately pleasure-dome decree Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea.  So twice five miles of fertile ground With walls and towers were girdled round : And there were gardens bright with sinuous rills, Where blossomed many an incense-bearing tree ; And here were forests ancient as the hills, Enfolding sunny spots of greenery.');
545}
546read_test 5.3 {
547  SELECT snippet(t5) FROM t5 WHERE t5 MATCH 'miles'
548} {{<b>...</b>to a sunless sea.  So twice five <b>miles</b> of fertile ground With walls and towers<b>...</b>}}
549read_test 5.4 {
550  SELECT snippet(t5, '<i>') FROM t5 WHERE t5 MATCH 'miles'
551} {{<b>...</b>to a sunless sea.  So twice five <i>miles</b> of fertile ground With walls and towers<b>...</b>}}
552read_test 5.5 {
553  SELECT snippet(t5, '<i>', '</i>') FROM t5 WHERE t5 MATCH 'miles'
554} {{<b>...</b>to a sunless sea.  So twice five <i>miles</i> of fertile ground With walls and towers<b>...</b>}}
555read_test 5.6 {
556  SELECT snippet(t5, '<i>', '</i>', 'XXX') FROM t5 WHERE t5 MATCH 'miles'
557} {{XXXto a sunless sea.  So twice five <i>miles</i> of fertile ground With walls and towersXXX}}
558#-------------------------------------------------------------------------
559
560#-------------------------------------------------------------------------
561# Test that an empty MATCH expression returns an empty result set. As
562# does passing a NULL value as a MATCH expression.
563#
564set DO_MALLOC_TEST 0
565ddl_test   6.1 { CREATE VIRTUAL TABLE t6 USING fts3(x) }
566write_test 6.2 t5_content { INSERT INTO t6 VALUES('a'); }
567write_test 6.3 t5_content { INSERT INTO t6 VALUES('b'); }
568write_test 6.4 t5_content { INSERT INTO t6 VALUES('c'); }
569read_test  6.5 { SELECT * FROM t6 WHERE t6 MATCH '' } {}
570read_test  6.6 { SELECT * FROM t6 WHERE x MATCH '' } {}
571read_test  6.7 { SELECT * FROM t6 WHERE t6 MATCH NULL } {}
572read_test  6.8 { SELECT * FROM t6 WHERE x MATCH NULL } {}
573#-------------------------------------------------------------------------
574
575#-------------------------------------------------------------------------
576# Test a few facets of the FTS3 xFilter() callback implementation:
577#
578#   1. That the sqlite3_index_constraint.usable flag is respected.
579#
580#   2. That it is an error to use the "docid" or "rowid" column of
581#      an FTS3 table as the LHS of a MATCH operator.
582#
583#   3. That it is an error to AND together two MATCH expressions in
584#      that refer to a single FTS3 table in a WHERE clause.
585#
586#
587set DO_MALLOC_TEST 0
588ddl_test   7.1.1 { CREATE VIRTUAL TABLE t7 USING fts3(a) }
589ddl_test   7.1.2 { CREATE VIRTUAL TABLE t8 USING fts3(b) }
590write_test 7.1.3 t7_content { INSERT INTO t7(docid, a) VALUES(4,'number four') }
591write_test 7.1.4 t7_content { INSERT INTO t7(docid, a) VALUES(5,'number five') }
592write_test 7.1.5 t8_content { INSERT INTO t8(docid, b) VALUES(4,'letter D') }
593write_test 7.1.6 t8_content { INSERT INTO t8(docid, b) VALUES(5,'letter E') }
594read_test  7.1.7 {
595  SELECT a || ':' || b FROM t7 JOIN t8 USING(docid)
596} {{number four:letter D} {number five:letter E}}
597
598error_test 7.2.1 {
599  SELECT * FROM t7 WHERE docid MATCH 'number'
600} {unable to use function MATCH in the requested context}
601error_test 7.2.2 {
602  SELECT * FROM t7 WHERE rowid MATCH 'number'
603} {unable to use function MATCH in the requested context}
604
605error_test 7.3.1 {
606  SELECT * FROM t7 WHERE a MATCH 'number' AND a MATCH 'four'
607} {unable to use function MATCH in the requested context}
608error_test 7.3.2 {
609  SELECT * FROM t7, t8 WHERE a MATCH 'number' AND a MATCH 'four'
610} {unable to use function MATCH in the requested context}
611error_test 7.3.3 {
612  SELECT * FROM t7, t8 WHERE b MATCH 'letter' AND b MATCH 'd'
613} {unable to use function MATCH in the requested context}
614read_test 7.3.4 {
615  SELECT * FROM t7, t8 WHERE a MATCH 'number' AND b MATCH 'letter'
616} {{number four} {letter D} {number four} {letter E} {number five} {letter D} {number five} {letter E}}
617read_test 7.3.5 {
618  SELECT * FROM t7 WHERE a MATCH 'number' AND docid = 4
619} {{number four}}
620
621#-------------------------------------------------------------------------
622# Test the quoting of FTS3 table column names. Names may be quoted using
623# any of "", '', ``` or [].
624#
625set DO_MALLOC_TEST 0
626ddl_test  8.1.1 { CREATE VIRTUAL TABLE t9a USING fts3("c1", [c2]) }
627ddl_test  8.1.2 { CREATE VIRTUAL TABLE t9b USING fts3('c1', `c2`) }
628read_test 8.1.3 { PRAGMA table_info(t9a) } {0 c1 {} 0 {} 0 1 c2 {} 0 {} 0}
629read_test 8.1.4 { PRAGMA table_info(t9b) } {0 c1 {} 0 {} 0 1 c2 {} 0 {} 0}
630ddl_test  8.2.1 { CREATE VIRTUAL TABLE t9c USING fts3("c""1", 'c''2') }
631read_test 8.2.2 { PRAGMA table_info(t9c) } {0 c\"1 {} 0 {} 0 1 c'2 {} 0 {} 0}
632#-------------------------------------------------------------------------
633
634#-------------------------------------------------------------------------
635# Test that FTS3 tables can be renamed using the ALTER RENAME command.
636# OOM errors are tested during ALTER RENAME commands also.
637#
638foreach DO_MALLOC_TEST {0 1 2} {
639  db close
640  file delete -force test.db test.db-journal
641  sqlite3 db test.db
642  if {$DO_MALLOC_TEST} { sqlite3_db_config_lookaside db 0 0 0 }
643
644  ddl_test   9.1.1             { CREATE VIRTUAL TABLE t10 USING fts3(x) }
645  write_test 9.1.2 t10_content { INSERT INTO t10 VALUES('fts3 tables') }
646  write_test 9.1.3 t10_content { INSERT INTO t10 VALUES('are renameable') }
647
648  read_test  9.1.4 {
649    SELECT * FROM t10 WHERE t10 MATCH 'table*'
650  } {{fts3 tables}}
651  read_test  9.1.5 {
652    SELECT * FROM t10 WHERE x MATCH 'rename*'
653  } {{are renameable}}
654
655  ddl_test   9.1.6             { ALTER TABLE t10 RENAME TO t11 }
656
657  read_test  9.1.7 {
658    SELECT * FROM t11 WHERE t11 MATCH 'table*'
659  } {{fts3 tables}}
660  read_test  9.1.8 {
661    SELECT * FROM t11 WHERE x MATCH 'rename*'
662  } {{are renameable}}
663}
664#-------------------------------------------------------------------------
665
666#-------------------------------------------------------------------------
667# Test a couple of cases involving corrupt data structures:
668#
669#   1) A case where a document referenced by the full-text index is
670#      not present in the %_content table.
671#
672#   2) A badly formatted b-tree segment node.
673#
674set DO_MALLOC_TEST 0
675ddl_test   10.1.1 { CREATE VIRTUAL TABLE ta USING fts3 }
676write_test 10.1.2 ta_content {
677  INSERT INTO ta VALUES('During a summer vacation in 1790') }
678write_test 10.1.3 ta_content {
679  INSERT INTO ta VALUES('Wordsworth went on a walking tour') }
680write_test 10.1.4 ta_content { DELETE FROM ta_content WHERE rowid = 2 }
681read_test  10.1.5 {
682  SELECT * FROM ta WHERE ta MATCH 'summer'
683} {{During a summer vacation in 1790}}
684error_test 10.1.6 {
685  SELECT * FROM ta WHERE ta MATCH 'walking'
686} {database disk image is malformed}
687
688write_test 10.2.1 ta_content { DELETE FROM ta }
689write_test 10.2.2 ta_content {
690  INSERT INTO ta VALUES('debate demonstrated the rising difficulty') }
691write_test 10.2.3 ta_content {
692  INSERT INTO ta VALUES('Google released its browser beta') }
693
694set blob [db one {SELECT root FROM ta_segdir WHERE rowid = 2}]
695binary scan $blob "a6 a3 a*" start middle end
696set middle "\x0E\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x06\x06"
697set blob [binary format "a6 a* a*" $start $middle $end]
698write_test 10.2.4 ta_segdir {
699  UPDATE ta_segdir SET root = $blob WHERE rowid = 2
700}
701error_test 10.2.5 {
702  SELECT * FROM ta WHERE ta MATCH 'beta'
703} {database disk image is malformed}
704
705
706finish_test
707