1# 2010 September 22 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 contains tests for the r-tree module. Specifically, it tests 12# that corrupt or inconsistent databases do not cause crashes in the r-tree 13# module. 14# 15 16if {![info exists testdir]} { 17 set testdir [file join [file dirname [info script]] .. .. test] 18} 19source $testdir/tester.tcl 20ifcapable !rtree { finish_test ; return } 21 22proc create_t1 {} { 23 db close 24 forcedelete test.db 25 sqlite3 db test.db 26 execsql { 27 PRAGMA page_size = 1024; 28 CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2, y1, y2); 29 } 30} 31proc populate_t1 {} { 32 execsql BEGIN 33 for {set i 0} {$i < 500} {incr i} { 34 set x2 [expr $i+5] 35 set y2 [expr $i+5] 36 execsql { INSERT INTO t1 VALUES($i, $i, $x2, $i, $y2) } 37 } 38 execsql COMMIT 39 sqlite3_db_config db DEFENSIVE 0 40} 41 42proc truncate_node {nodeno nTrunc} { 43 set blob [db one {SELECT data FROM t1_node WHERE nodeno=$nodeno}] 44 if {$nTrunc<0} {set nTrunc "end-$nTrunc"} 45 set blob [string range $blob 0 $nTrunc] 46 db eval { UPDATE t1_node SET data = $blob WHERE nodeno=$nodeno } 47} 48 49proc set_tree_depth {tbl {newvalue ""}} { 50 set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=1"] 51 52 if {$newvalue == ""} { 53 binary scan $blob Su oldvalue 54 return $oldvalue 55 } 56 57 set blob [binary format Sua* $newvalue [string range $blob 2 end]] 58 db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=1" 59 return [set_tree_depth $tbl] 60} 61 62proc set_entry_count {tbl nodeno {newvalue ""}} { 63 set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=$nodeno"] 64 65 if {$newvalue == ""} { 66 binary scan [string range $blob 2 end] Su oldvalue 67 return $oldvalue 68 } 69 70 set blob [binary format a*Sua* \ 71 [string range $blob 0 1] $newvalue [string range $blob 4 end] 72 ] 73 db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=$nodeno" 74 return [set_entry_count $tbl $nodeno] 75} 76 77 78proc do_corruption_tests {prefix args} { 79 set testarray [lindex $args end] 80 set errormsg {database disk image is malformed} 81 82 foreach {z value} [lrange $args 0 end-1] { 83 set n [string length $z] 84 if {$n>=2 && [string equal -length $n $z "-error"]} { 85 set errormsg $value 86 } 87 } 88 89 foreach {tn sql} $testarray { 90 do_catchsql_test $prefix.$tn $sql [list 1 $errormsg] 91 } 92} 93 94#------------------------------------------------------------------------- 95# Test the libraries response if the %_node table is completely empty 96# (i.e. the root node is missing), or has been removed from the database 97# entirely. 98# 99create_t1 100populate_t1 101do_execsql_test rtreeA-1.0 { 102 DELETE FROM t1_node; 103} {} 104 105do_corruption_tests rtreeA-1.1 { 106 1 "SELECT * FROM t1" 107 2 "SELECT * FROM t1 WHERE rowid=5" 108 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 109 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" 110} 111 112do_execsql_test rtreeA-1.1.1 { 113 SELECT rtreecheck('main', 't1') 114} {{Node 1 missing from database 115Wrong number of entries in %_rowid table - expected 0, actual 500 116Wrong number of entries in %_parent table - expected 0, actual 23}} 117 118do_execsql_test rtreeA-1.2.0 { DROP TABLE t1_node } {} 119do_corruption_tests rtreeA-1.2 -error "database disk image is malformed" { 120 1 "SELECT * FROM t1" 121 2 "SELECT * FROM t1 WHERE rowid=5" 122 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 123 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" 124} 125 126#------------------------------------------------------------------------- 127# Test the libraries response if some of the entries in the %_node table 128# are the wrong size. 129# 130create_t1 131populate_t1 132do_test rtreeA-2.1.0 { 133 set nodes [db eval {select nodeno FROM t1_node}] 134 foreach {a b c} $nodes { truncate_node $c 200 } 135} {} 136do_corruption_tests rtreeA-2.1 { 137 1 "SELECT * FROM t1" 138 2 "SELECT * FROM t1 WHERE rowid=5" 139 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 140 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" 141} 142 143create_t1 144populate_t1 145do_test rtreeA-2.2.0 { truncate_node 1 200 } {} 146do_corruption_tests rtreeA-2.2 { 147 1 "SELECT * FROM t1" 148 2 "SELECT * FROM t1 WHERE +rowid=5" 149 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 150 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" 151} 152 153#------------------------------------------------------------------------- 154# Set the "depth" of the tree stored on the root node incorrectly. Test 155# that this does not cause any problems. 156# 157create_t1 158populate_t1 159do_test rtreeA-3.1.0.1 { set_tree_depth t1 } {1} 160do_test rtreeA-3.1.0.2 { set_tree_depth t1 3 } {3} 161do_corruption_tests rtreeA-3.1 { 162 1 "SELECT * FROM t1" 163 2 "SELECT * FROM t1 WHERE +rowid=5" 164 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 165} 166 167do_execsql_test rtreeA-3.1.0.3 { 168 SELECT rtreecheck('main', 't1')!='ok' 169} {1} 170 171do_test rtreeA-3.2.0 { set_tree_depth t1 1000 } {1000} 172do_corruption_tests rtreeA-3.2 { 173 1 "SELECT * FROM t1" 174 2 "SELECT * FROM t1 WHERE +rowid=5" 175 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 176} 177 178create_t1 179populate_t1 180do_test rtreeA-3.3.0 { 181 execsql { DELETE FROM t1 WHERE rowid = 0 } 182 set_tree_depth t1 65535 183} {65535} 184do_corruption_tests rtreeA-3.3 { 185 1 "SELECT * FROM t1" 186 2 "SELECT * FROM t1 WHERE +rowid=5" 187 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 188} 189 190do_execsql_test rtreeA-3.3.3.4 { 191 SELECT rtreecheck('main', 't1') 192} {{Rtree depth out of range (65535) 193Wrong number of entries in %_rowid table - expected 0, actual 499 194Wrong number of entries in %_parent table - expected 0, actual 23}} 195 196#------------------------------------------------------------------------- 197# Set the "number of entries" field on some nodes incorrectly. 198# 199create_t1 200populate_t1 201do_test rtreeA-4.1.0 { 202 set_entry_count t1 1 4000 203} {4000} 204do_corruption_tests rtreeA-4.1 { 205 1 "SELECT * FROM t1" 206 2 "SELECT * FROM t1 WHERE +rowid=5" 207 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 208 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" 209} 210 211#------------------------------------------------------------------------- 212# Remove entries from the %_parent table and check that this does not 213# cause a crash. 214# 215create_t1 216populate_t1 217do_execsql_test rtreeA-5.1.0 { DELETE FROM t1_parent } {} 218do_corruption_tests rtreeA-5.1 { 219 1 "DELETE FROM t1 WHERE +rowid = 5" 220 2 "DELETE FROM t1" 221} 222 223do_execsql_test rtreeA-5.2 { 224 SELECT rtreecheck('main', 't1')!='ok' 225} {1} 226 227#------------------------------------------------------------------------- 228# Add some bad entries to the %_parent table. 229# 230create_t1 231populate_t1 232do_execsql_test rtreeA-6.1.0 { 233 UPDATE t1_parent set parentnode = parentnode+1 234} {} 235do_corruption_tests rtreeA-6.1 { 236 1 "DELETE FROM t1 WHERE rowid = 5" 237 2 "UPDATE t1 SET x1=x1+1, x2=x2+1" 238} 239 240do_execsql_test rtreeA-6.2 { 241 SELECT rtreecheck('main', 't1')!='ok' 242} {1} 243 244#------------------------------------------------------------------------- 245# Truncated blobs in the _node table. 246# 247create_t1 248populate_t1 249sqlite3 db test.db 250sqlite3_db_config db DEFENSIVE 0 251do_execsql_test rtreeA-7.100 { 252 UPDATE t1_node SET data=x'' WHERE rowid=1; 253} {} 254do_catchsql_test rtreeA-7.110 { 255 SELECT * FROM t1 WHERE x1>0 AND x1<100 AND x2>0 AND x2<100; 256} {1 {undersize RTree blobs in "t1_node"}} 257do_test rtreeA-7.120 { 258 sqlite3_extended_errcode db 259} {SQLITE_CORRUPT_VTAB} 260 261 262finish_test 263