modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/fts5/test/fts5corrupt3.test (about) 1 # 2015 Apr 24 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 tests that FTS5 handles corrupt databases (i.e. internal 13 # inconsistencies in the backing tables) correctly. In this case 14 # "correctly" means without crashing. 15 # 16 17 source [file join [file dirname [info script]] fts5_common.tcl] 18 set testprefix fts5corrupt3 19 20 # If SQLITE_ENABLE_FTS5 is defined, omit this file. 21 ifcapable !fts5 { 22 finish_test 23 return 24 } 25 sqlite3_fts5_may_be_corrupt 1 26 27 proc create_t1 {} { 28 expr srand(0) 29 db func rnddoc fts5_rnddoc 30 db eval { 31 CREATE VIRTUAL TABLE t1 USING fts5(x); 32 INSERT INTO t1(t1, rank) VALUES('pgsz', 64); 33 WITH ii(i) AS (SELECT 1 UNION SELECT i+1 FROM ii WHERE i<100) 34 INSERT INTO t1 SELECT rnddoc(10) FROM ii; 35 } 36 } 37 38 if 1 { 39 40 # Create a simple FTS5 table containing 100 documents. Each document 41 # contains 10 terms, each of which start with the character "x". 42 # 43 do_test 1.0 { create_t1 } {} 44 45 do_test 1.1 { 46 # Pick out the rowid of the right-most b-tree leaf in the new segment. 47 set rowid [db one { 48 SELECT max(rowid) FROM t1_data WHERE ((rowid>>31) & 0x0F)==1 49 }] 50 set L [db one {SELECT length(block) FROM t1_data WHERE rowid = $rowid}] 51 set {} {} 52 } {} 53 54 for {set i 0} {$i < $L} {incr i} { 55 do_test 1.2.$i { 56 catchsql { 57 BEGIN; 58 UPDATE t1_data SET block = substr(block, 1, $i) WHERE id = $rowid; 59 INSERT INTO t1(t1) VALUES('integrity-check'); 60 } 61 } {1 {database disk image is malformed}} 62 catchsql ROLLBACK 63 } 64 65 #------------------------------------------------------------------------- 66 # Test that trailing bytes appended to the averages record are ignored. 67 # 68 do_execsql_test 2.1 { 69 CREATE VIRTUAL TABLE t2 USING fts5(x); 70 INSERT INTO t2 VALUES(rnddoc(10)); 71 INSERT INTO t2 VALUES(rnddoc(10)); 72 SELECT length(block) FROM t2_data WHERE id=1; 73 } {2} 74 do_execsql_test 2.2 { 75 UPDATE t2_data SET block = block || 'abcd' WHERE id=1; 76 SELECT length(block) FROM t2_data WHERE id=1; 77 } {6} 78 do_execsql_test 2.2 { 79 INSERT INTO t2 VALUES(rnddoc(10)); 80 SELECT length(block) FROM t2_data WHERE id=1; 81 } {2} 82 83 84 #------------------------------------------------------------------------- 85 # Test that missing leaf pages are recognized as corruption. 86 # 87 reset_db 88 do_test 3.0 { create_t1 } {} 89 90 do_execsql_test 3.1 { 91 SELECT count(*) FROM t1_data; 92 } {105} 93 94 proc do_3_test {tn} { 95 set i 0 96 foreach ::rowid [db eval "SELECT rowid FROM t1_data WHERE rowid>100"] { 97 incr i 98 do_test $tn.$i { 99 db eval BEGIN 100 db eval {DELETE FROM t1_data WHERE rowid = $::rowid} 101 list [ 102 catch { db eval {SELECT rowid FROM t1 WHERE t1 MATCH 'x*'} } msg 103 ] $msg 104 } {1 {database disk image is malformed}} 105 catch { db eval ROLLBACK } 106 } 107 } 108 109 do_3_test 3.2 110 111 do_execsql_test 3.3 { 112 INSERT INTO t1(t1, rank) VALUES('pgsz', 32); 113 INSERT INTO t1 SELECT x FROM t1; 114 INSERT INTO t1(t1) VALUES('optimize'); 115 } {} 116 117 do_3_test 3.4 118 119 do_test 3.5 { 120 execsql { 121 DELETE FROM t1; 122 INSERT INTO t1(t1, rank) VALUES('pgsz', 40); 123 } 124 for {set i 0} {$i < 1000} {incr i} { 125 set rnd [expr int(rand() * 1000)] 126 set doc [string repeat "x$rnd " [expr int(rand() * 3) + 1]] 127 execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) } 128 } 129 } {} 130 131 do_3_test 3.6 132 133 do_test 3.7 { 134 execsql { 135 INSERT INTO t1(t1, rank) VALUES('pgsz', 40); 136 INSERT INTO t1 SELECT x FROM t1; 137 INSERT INTO t1(t1) VALUES('optimize'); 138 } 139 } {} 140 141 do_3_test 3.8 142 143 do_test 3.9 { 144 execsql { 145 DELETE FROM t1; 146 INSERT INTO t1(t1, rank) VALUES('pgsz', 32); 147 } 148 for {set i 0} {$i < 100} {incr i} { 149 set rnd [expr int(rand() * 100)] 150 set doc "x[string repeat $rnd 20]" 151 execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) } 152 } 153 } {} 154 155 do_3_test 3.10 156 157 #------------------------------------------------------------------------- 158 # Test that segments that end unexpectedly are identified as corruption. 159 # 160 reset_db 161 do_test 4.0 { 162 execsql { 163 CREATE VIRTUAL TABLE t1 USING fts5(x); 164 INSERT INTO t1(t1, rank) VALUES('pgsz', 32); 165 } 166 for {set i 0} {$i < 100} {incr i} { 167 set rnd [expr int(rand() * 100)] 168 set doc "x[string repeat $rnd 20]" 169 execsql { INSERT INTO t1(rowid, x) VALUES($i, $doc) } 170 } 171 execsql { INSERT INTO t1(t1) VALUES('optimize') } 172 } {} 173 174 set nErr 0 175 for {set i 1} {1} {incr i} { 176 set struct [db one {SELECT block FROM t1_data WHERE id=10}] 177 binary scan $struct c* var 178 set end [lindex $var end] 179 if {$end<=$i} break 180 lset var end [expr $end - $i] 181 set struct [binary format c* $var] 182 183 db close 184 sqlite3 db test.db 185 186 db eval { 187 BEGIN; 188 UPDATE t1_data SET block = $struct WHERE id=10; 189 } 190 do_test 4.1.$i { 191 incr nErr [catch { db eval { SELECT rowid FROM t1 WHERE t1 MATCH 'x*' } }] 192 set {} {} 193 } {} 194 catch { db eval ROLLBACK } 195 } 196 do_test 4.1.x { expr $nErr>45 } 1 197 198 #------------------------------------------------------------------------- 199 # 200 201 # The first argument passed to this command must be a binary blob 202 # containing an FTS5 leaf page. This command returns a copy of this 203 # blob, with the pgidx of the leaf page replaced by a single varint 204 # containing value $iVal. 205 # 206 proc rewrite_pgidx {blob iVal} { 207 binary scan $blob SS off1 szLeaf 208 if {$iVal<0 || $iVal>=128} { 209 error "$iVal out of range!" 210 } else { 211 set pgidx [binary format c $iVal] 212 } 213 214 binary format a${szLeaf}a* $blob $pgidx 215 } 216 217 reset_db 218 do_execsql_test 5.1 { 219 CREATE VIRTUAL TABLE x1 USING fts5(x); 220 INSERT INTO x1(x1, rank) VALUES('pgsz', 40); 221 BEGIN; 222 INSERT INTO x1 VALUES('xaaa xabb xccc xcdd xeee xeff xggg xghh xiii xijj'); 223 INSERT INTO x1 SELECT x FROM x1; 224 INSERT INTO x1 SELECT x FROM x1; 225 INSERT INTO x1 SELECT x FROM x1; 226 INSERT INTO x1 SELECT x FROM x1; 227 INSERT INTO x1(x1) VALUES('optimize'); 228 COMMIT; 229 } 230 231 #db eval { SELECT fts5_decode(id, block) b from x1_data } { puts $b } 232 # 233 db func rewrite_pgidx rewrite_pgidx 234 set i 0 235 foreach rowid [db eval {SELECT rowid FROM x1_data WHERE rowid>100}] { 236 foreach val {2 100} { 237 do_test 5.2.$val.[incr i] { 238 catchsql { 239 BEGIN; 240 UPDATE x1_data SET block=rewrite_pgidx(block, $val) WHERE id=$rowid; 241 SELECT rowid FROM x1 WHERE x1 MATCH 'xa*'; 242 SELECT rowid FROM x1 WHERE x1 MATCH 'xb*'; 243 SELECT rowid FROM x1 WHERE x1 MATCH 'xc*'; 244 SELECT rowid FROM x1 WHERE x1 MATCH 'xd*'; 245 SELECT rowid FROM x1 WHERE x1 MATCH 'xe*'; 246 SELECT rowid FROM x1 WHERE x1 MATCH 'xf*'; 247 SELECT rowid FROM x1 WHERE x1 MATCH 'xg*'; 248 SELECT rowid FROM x1 WHERE x1 MATCH 'xh*'; 249 SELECT rowid FROM x1 WHERE x1 MATCH 'xi*'; 250 } 251 set {} {} 252 } {} 253 catch { db eval ROLLBACK } 254 } 255 } 256 257 #------------------------------------------------------------------------ 258 # 259 reset_db 260 do_execsql_test 6.1.0 { 261 CREATE VIRTUAL TABLE t1 USING fts5(a); 262 INSERT INTO t1 VALUES('bbbbb ccccc'); 263 SELECT quote(block) FROM t1_data WHERE rowid>100; 264 } {X'000000180630626262626201020201056363636363010203040A'} 265 do_execsql_test 6.1.1 { 266 UPDATE t1_data SET block = 267 X'000000180630626262626201020201056161616161010203040A' 268 WHERE rowid>100; 269 } 270 do_catchsql_test 6.1.2 { 271 INSERT INTO t1(t1) VALUES('integrity-check'); 272 } {1 {database disk image is malformed}} 273 274 #------- 275 reset_db 276 do_execsql_test 6.2.0 { 277 CREATE VIRTUAL TABLE t1 USING fts5(a); 278 INSERT INTO t1(t1, rank) VALUES('pgsz', 32); 279 INSERT INTO t1 VALUES('aa bb cc dd ee'); 280 SELECT pgno, quote(term) FROM t1_idx; 281 } {2 X'' 4 X'3064'} 282 do_execsql_test 6.2.1 { 283 UPDATE t1_idx SET term = X'3065' WHERE pgno=4; 284 } 285 do_catchsql_test 6.2.2 { 286 INSERT INTO t1(t1) VALUES('integrity-check'); 287 } {1 {database disk image is malformed}} 288 289 #------- 290 reset_db 291 do_execsql_test 6.3.0 { 292 CREATE VIRTUAL TABLE t1 USING fts5(a); 293 INSERT INTO t1 VALUES('abc abcdef abcdefghi'); 294 SELECT quote(block) FROM t1_data WHERE id>100; 295 } {X'0000001C043061626301020204036465660102030703676869010204040808'} 296 do_execsql_test 6.3.1 { 297 BEGIN; 298 UPDATE t1_data SET block = 299 X'0000001C043061626301020204036465660102035003676869010204040808' 300 ------------------------------------------^^--------------------- 301 WHERE id>100; 302 } 303 do_catchsql_test 6.3.2 { 304 INSERT INTO t1(t1) VALUES('integrity-check'); 305 } {1 {database disk image is malformed}} 306 do_execsql_test 6.3.3 { 307 ROLLBACK; 308 BEGIN; 309 UPDATE t1_data SET block = 310 X'0000001C043061626301020204036465660102030750676869010204040808' 311 --------------------------------------------^^------------------- 312 WHERE id>100; 313 } 314 do_catchsql_test 6.3.3 { 315 INSERT INTO t1(t1) VALUES('integrity-check'); 316 } {1 {database disk image is malformed}} 317 do_execsql_test 6.3.4 { 318 ROLLBACK; 319 BEGIN; 320 UPDATE t1_data SET block = 321 X'0000001C043061626301020204036465660102030707676869010204040850' 322 --------------------------------------------------------------^^- 323 WHERE id>100; 324 } 325 do_catchsql_test 6.3.5 { 326 INSERT INTO t1(t1) VALUES('integrity-check'); 327 } {1 {database disk image is malformed}} 328 do_execsql_test 6.3.6 { 329 ROLLBACK; 330 BEGIN; 331 UPDATE t1_data SET block = 332 X'0000001C503061626301020204036465660102030707676869010204040808' 333 ----------^^----------------------------------------------------- 334 WHERE id>100; 335 } 336 do_catchsql_test 6.3.5 { 337 INSERT INTO t1(t1) VALUES('integrity-check'); 338 } {1 {database disk image is malformed}} 339 340 341 #------------------------------------------------------------------------ 342 # 343 reset_db 344 proc rnddoc {n} { 345 set map [list a b c d] 346 set doc [list] 347 for {set i 0} {$i < $n} {incr i} { 348 lappend doc "x[lindex $map [expr int(rand()*4)]]" 349 } 350 set doc 351 } 352 353 db func rnddoc rnddoc 354 do_test 7.0 { 355 execsql { 356 CREATE VIRTUAL TABLE t5 USING fts5(x); 357 INSERT INTO t5 VALUES( rnddoc(10000) ); 358 INSERT INTO t5 VALUES( rnddoc(10000) ); 359 INSERT INTO t5 VALUES( rnddoc(10000) ); 360 INSERT INTO t5 VALUES( rnddoc(10000) ); 361 INSERT INTO t5(t5) VALUES('optimize'); 362 } 363 } {} 364 365 do_test 7.1 { 366 foreach i [db eval { SELECT rowid FROM t5_data WHERE rowid>100 }] { 367 db eval BEGIN 368 db eval {DELETE FROM t5_data WHERE rowid = $i} 369 set r [catchsql { INSERT INTO t5(t5) VALUES('integrity-check')} ] 370 if {$r != "1 {database disk image is malformed}"} { error $r } 371 db eval ROLLBACK 372 } 373 } {} 374 375 } 376 377 #------------------------------------------------------------------------ 378 # Corruption within the structure record. 379 # 380 reset_db 381 do_execsql_test 8.1 { 382 CREATE VIRTUAL TABLE t1 USING fts5(x, y); 383 INSERT INTO t1 VALUES('one', 'two'); 384 } 385 386 do_test 9.1.1 { 387 set blob "12345678" ;# cookie 388 append blob "0105" ;# 1 level, total of 5 segments 389 append blob "06" ;# write counter 390 append blob "0002" ;# first level has 0 segments merging, 2 other. 391 append blob "450108" ;# first segment 392 execsql "REPLACE INTO t1_data VALUES(10, X'$blob')" 393 } {} 394 do_catchsql_test 9.1.2 { 395 SELECT * FROM t1('one AND two'); 396 } {1 {database disk image is malformed}} 397 398 do_test 9.2.1 { 399 set blob "12345678" ;# cookie 400 append blob "0205" ;# 2 levels, total of 5 segments 401 append blob "06" ;# write counter 402 append blob "0001" ;# first level has 0 segments merging, 1 other. 403 append blob "450108" ;# first segment 404 execsql "REPLACE INTO t1_data VALUES(10, X'$blob')" 405 } {} 406 do_catchsql_test 9.2.2 { 407 SELECT * FROM t1('one AND two'); 408 } {1 {database disk image is malformed}} 409 410 sqlite3_fts5_may_be_corrupt 0 411 finish_test