summaryrefslogtreecommitdiffstats
path: root/ext/fts5/test/fts5integrity.test
blob: 1bb367538d61b776377c77dd45a8e1918ed19f71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# 2015 Jan 13
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file contains tests focused on the integrity-check procedure.
#

source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5integrity

# If SQLITE_ENABLE_FTS5 is defined, omit this file.
ifcapable !fts5 {
  finish_test
  return
}

do_execsql_test 1.0 {
  CREATE VIRTUAL TABLE xx USING fts5(x);
  INSERT INTO xx VALUES('term');
}
do_execsql_test 1.1 {
  INSERT INTO xx(xx) VALUES('integrity-check');
}

do_execsql_test 2.0 {
  CREATE VIRTUAL TABLE yy USING fts5(x, prefix=1);
  INSERT INTO yy VALUES('term');
}
do_execsql_test 2.1 {
  INSERT INTO yy(yy) VALUES('integrity-check');
}

#--------------------------------------------------------------------
#
do_execsql_test 3.0 {
  CREATE VIRTUAL TABLE zz USING fts5(z);
  INSERT INTO zz(zz, rank) VALUES('pgsz', 32);
  INSERT INTO zz VALUES('b b b b b b b b b b b b b b');
  INSERT INTO zz SELECT z FROM zz;
  INSERT INTO zz SELECT z FROM zz;
  INSERT INTO zz SELECT z FROM zz;
  INSERT INTO zz SELECT z FROM zz;
  INSERT INTO zz SELECT z FROM zz;
  INSERT INTO zz SELECT z FROM zz;
  INSERT INTO zz(zz) VALUES('optimize');
}

do_execsql_test 3.1 { INSERT INTO zz(zz) VALUES('integrity-check'); }

#--------------------------------------------------------------------
# Mess around with a docsize record. And the averages record. Then
# check that integrity-check picks it up.
#
do_execsql_test 4.0 {
  CREATE VIRTUAL TABLE aa USING fts5(zz);
  INSERT INTO aa(zz) VALUES('a b c d e');
  INSERT INTO aa(zz) VALUES('a b c d');
  INSERT INTO aa(zz) VALUES('a b c');
  INSERT INTO aa(zz) VALUES('a b');
  INSERT INTO aa(zz) VALUES('a');
  SELECT length(sz) FROM aa_docsize;
} {1 1 1 1 1}
do_execsql_test 4.1 { 
  INSERT INTO aa(aa) VALUES('integrity-check'); 
}

sqlite3_db_config db DEFENSIVE 0
do_catchsql_test 4.2 { 
  BEGIN;
    UPDATE aa_docsize SET sz = X'44' WHERE rowid = 3;
    INSERT INTO aa(aa) VALUES('integrity-check'); 
} {1 {database disk image is malformed}}
do_execsql_test 4.2.1 {
  PRAGMA integrity_check(aa);
} {{malformed inverted index for FTS5 table main.aa}}

do_catchsql_test 4.3 { 
  ROLLBACK;
  BEGIN;
    UPDATE aa_data SET block = X'44' WHERE rowid = 1;
    INSERT INTO aa(aa) VALUES('integrity-check'); 
} {1 {database disk image is malformed}}

do_catchsql_test 4.4 { 
  ROLLBACK;
  BEGIN;
    INSERT INTO aa_docsize VALUES(23, X'04');
    INSERT INTO aa(aa) VALUES('integrity-check'); 
} {1 {database disk image is malformed}}

do_catchsql_test 4.5 { 
  ROLLBACK;
  BEGIN;
    INSERT INTO aa_docsize VALUES(23, X'00');
    INSERT INTO aa_content VALUES(23, '');
    INSERT INTO aa(aa) VALUES('integrity-check'); 
} {1 {database disk image is malformed}}

#db eval {SELECT rowid, fts5_decode(rowid, block) aS r FROM zz_data} {puts $r}
#exit

execsql { ROLLBACK }


#-------------------------------------------------------------------------
# Test that integrity-check works on a reasonably large db with many
# different terms.

# Document generator command.
proc rnddoc {n} {
  set doc [list]
  for {set i 0} {$i<$n} {incr i} {
    lappend doc [format %.5d [expr int(rand()*10000)]]
  }
  return $doc
}
db func rnddoc rnddoc

expr srand(0)
do_execsql_test 5.0 {
  CREATE VIRTUAL TABLE gg USING fts5(a, prefix="1,2,3");
  INSERT INTO gg(gg, rank) VALUES('pgsz', 256);
  INSERT INTO gg VALUES(rnddoc(20));
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
  INSERT INTO gg SELECT rnddoc(20) FROM gg;
}

do_execsql_test 5.1 {
  INSERT INTO gg(gg) VALUES('integrity-check');
}

do_execsql_test 5.2 {
  INSERT INTO gg(gg) VALUES('optimize');
}

do_execsql_test 5.3 {
  INSERT INTO gg(gg) VALUES('integrity-check');
}

do_test 5.4.1 {
  set ok 0
  for {set i 0} {$i < 10000} {incr i} {
    set T [format %.5d $i]
    set res  [db eval { SELECT rowid FROM gg($T) ORDER BY rowid ASC  }]
    set res2 [db eval { SELECT rowid FROM gg($T) ORDER BY rowid DESC }]
    if {$res == [lsort -integer $res2]} { incr ok }
  }
  set ok
} {10000}

do_test 5.4.2 {
  set ok 0
  for {set i 0} {$i < 100} {incr i} {
    set T "[format %.3d $i]*"
    set res  [db eval { SELECT rowid FROM gg($T) ORDER BY rowid ASC  }]
    set res2 [db eval { SELECT rowid FROM gg($T) ORDER BY rowid DESC }]
    if {$res == [lsort -integer $res2]} { incr ok }
  }
  set ok
} {100}

#-------------------------------------------------------------------------
# Similar to 5.*.
#
foreach {tn pgsz} {
  1  32
  2  36
  3  40
  4  44
  5  48
} {
  do_execsql_test 6.$tn.1 {
    DROP TABLE IF EXISTS hh;
    CREATE VIRTUAL TABLE hh USING fts5(y);
    INSERT INTO hh(hh, rank) VALUES('pgsz', $pgsz);

    WITH s(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<999)
     INSERT INTO hh SELECT printf('%.3d%.3d%.3d %.3d%.3d%.3d',i,i,i,i+1,i+1,i+1)
     FROM s;

    WITH s(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<999)
     INSERT INTO hh SELECT printf('%.3d%.3d%.3d %.3d%.3d%.3d',i,i,i,i+1,i+1,i+1)
     FROM s;

    INSERT INTO hh(hh) VALUES('optimize');
  }

  do_test 6.$tn.2 {
    set ok 0
    for {set i 0} {$i < 1000} {incr i} {
      set T [format %.3d%.3d%.3d $i $i $i]
      set res  [db eval { SELECT rowid FROM hh($T) ORDER BY rowid ASC  }]
      set res2 [db eval { SELECT rowid FROM hh($T) ORDER BY rowid DESC }]
      if {$res == [lsort -integer $res2]} { incr ok }
    }
    set ok
  } {1000}
}

#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 7.0 {
  PRAGMA encoding = 'UTF-16';
  CREATE VIRTUAL TABLE vt0 USING fts5(c0);
  INSERT INTO vt0 VALUES (x'46f0');
  SELECT quote(c0) FROM vt0;
} {X'46F0'}
do_execsql_test 7.1 {
  INSERT INTO vt0(vt0) VALUES('integrity-check');
}
do_execsql_test 7.2 {
  INSERT INTO vt0(vt0) VALUES('rebuild');
}
do_execsql_test 7.3 {
  INSERT INTO vt0(vt0) VALUES('integrity-check');
}
do_execsql_test 7.4 {
  UPDATE vt0 SET c0='';
}
do_execsql_test 7.5 {
  INSERT INTO vt0(vt0) VALUES('integrity-check');
}

#-------------------------------------------------------------------------
# Ticket 7a458c2a5f4
#
reset_db
do_execsql_test 8.0 {
  PRAGMA locking_mode = EXCLUSIVE;
  PRAGMA journal_mode = PERSIST;
  CREATE VIRTUAL TABLE vt0 USING fts5(c0);
} {exclusive persist}
do_execsql_test 8.1 {
  PRAGMA data_version
} {1}
do_execsql_test 8.2 {
  INSERT INTO vt0(vt0) VALUES('integrity-check');
  PRAGMA data_version;
} {1}
do_execsql_test 8.1 {
  INSERT INTO vt0(vt0, rank) VALUES('usermerge', 2);
}

#-------------------------------------------------------------------------
# Ticket [771fe617]
#
reset_db
do_execsql_test 9.0 {
  PRAGMA encoding = 'UTF16';
  CREATE VIRTUAL TABLE vt0 USING fts5(c0);
}

#explain_i { SELECT quote(SUBSTR(x'37', 0)); }
#execsql { PRAGMA vdbe_trace = 1 }
do_execsql_test 9.1.1 {
  SELECT quote(SUBSTR(x'37', 0));
} {X'37'}
do_execsql_test 9.1.2 {
  SELECT quote(x'37');
} {X'37'}

do_execsql_test 9.2 {
  INSERT INTO vt0 VALUES (SUBSTR(x'37', 0));
--  INSERT INTO vt0 VALUES (x'37');
}
do_execsql_test 9.3 {
  INSERT INTO vt0(vt0) VALUES('integrity-check');
}

#-------------------------------------------------------------------------
reset_db
do_execsql_test 10.0 {
  CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
  CREATE VIRTUAL TABLE vt0 USING fts5(a, b, content=t1);
  INSERT INTO vt0(rowid, a, b) VALUES(1, 'abc', 'def');
}
do_catchsql_test 10.1 {
  INSERT INTO vt0(vt0) VALUES('integrity-check');
} {0 {}}
do_catchsql_test 10.2 {
  INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
} {0 {}}
do_catchsql_test 10.3 {
  INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
} {1 {database disk image is malformed}}
do_catchsql_test 10.3 {
  INSERT INTO t1 VALUES(1, 'abc', 'def');
  INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
} {0 {}}

do_execsql_test 10.4 {
  CREATE VIRTUAL TABLE vt1 USING fts5(a, b, content=);
  INSERT INTO vt1(rowid, a, b) VALUES(1, 'abc', 'def');
}

do_catchsql_test 10.5.1 {
  INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 0);
} {0 {}}
do_catchsql_test 10.5.2 {
  INSERT INTO vt0(vt0, rank) VALUES('integrity-check', 1);
} {0 {}}
do_catchsql_test 10.5.3 {
  INSERT INTO vt0(vt0) VALUES('integrity-check');
} {0 {}}

reset_db
proc slang {in} {return [string map {th d e eh} $in]}
db function slang -deterministic -innocuous slang
do_execsql_test 11.0 {
  CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT AS (slang(b)));
  INSERT INTO t1(b) VALUES('the quick fox jumps over the lazy brown dog');
  SELECT c FROM t1;
} {{deh quick fox jumps ovehr deh lazy brown dog}}

do_execsql_test 11.1 {
  CREATE VIRTUAL TABLE t2 USING fts5(content="t1", c);
  INSERT INTO t2(t2) VALUES('rebuild');
  SELECT rowid FROM t2 WHERE t2 MATCH 'deh';
} {1}

do_execsql_test 11.2 {
  PRAGMA integrity_check(t2);
} {ok}
db close
sqlite3 db test.db

# FIX ME?
#
# FTS5 integrity-check does not care if the content table is unreadable or
# does not exist.  It only looks for internal inconsistencies in the
# inverted index.
#
do_execsql_test 11.3 {
  PRAGMA integrity_check(t2);
} {ok}
do_execsql_test 11.4 {
  DROP TABLE t1;
  PRAGMA integrity_check(t2);
} {ok}

#-------------------------------------------------------------------
reset_db

do_execsql_test 12.1 {
  CREATE VIRTUAL TABLE x1 USING fts5(a, b);
  INSERT INTO x1 VALUES('one', 'two');
  INSERT INTO x1 VALUES('three', 'four');
  INSERT INTO x1 VALUES('five', 'six');
}

do_execsql_test 12.2 {
  PRAGMA integrity_check
} {ok}

db close
sqlite3 db test.db -readonly 1

explain_i {
  PRAGMA integrity_check
  }
do_execsql_test 12.3 {
  PRAGMA integrity_check
} {ok}



finish_test