1# 2007 May 10 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 implements regression tests for SQLite library. The 12# focus of this file is generating semi-random strings of SQL 13# (a.k.a. "fuzz") and sending it into the parser to try to 14# generate errors. 15# 16# The tests in this file are really about testing fuzzily generated 17# SQL parse-trees. The majority of the fuzzily generated SQL is 18# valid as far as the parser is concerned. 19# 20# The most complicated trees are for SELECT statements. 21# 22# $Id: fuzz.test,v 1.9 2007/05/14 15:49:44 danielk1977 Exp $ 23 24set testdir [file dirname $argv0] 25source $testdir/tester.tcl 26 27# set ::REPEATS 20 28set ::REPEATS 5000 29 30proc fuzz {TemplateList} { 31 set n [llength $TemplateList] 32 set i [expr {int(rand()*$n)}] 33 set r [uplevel 1 subst -novar [list [lindex $TemplateList $i]]] 34 35 string map {"\n" " "} $r 36} 37 38# Fuzzy generation primitives: 39# 40# Literal 41# UnaryOp 42# BinaryOp 43# Expr 44# Table 45# Select 46# Insert 47# 48 49# Returns a string representing an SQL literal. 50# 51proc Literal {} { 52 set TemplateList { 53 456 0 -456 1 -1 54 2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649 55 'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection' 56 zeroblob(1000) 57 NULL 58 56.1 -56.1 59 123456789.1234567899 60 } 61 fuzz $TemplateList 62} 63 64# Returns a string containing an SQL unary operator (e.g. "+" or "NOT"). 65# 66proc UnaryOp {} { 67 set TemplateList {+ - NOT ~} 68 fuzz $TemplateList 69} 70 71# Returns a string containing an SQL binary operator (e.g. "*" or "/"). 72# 73proc BinaryOp {} { 74 set TemplateList { 75 || * / % + - << >> & | < <= > >= = == != <> AND OR 76 LIKE GLOB {NOT LIKE} 77 } 78 fuzz $TemplateList 79} 80 81# Return the complete text of an SQL expression. 82# 83set ::ExprDepth 0 84proc Expr { {c {}} } { 85 incr ::ExprDepth 86 87 set TemplateList [concat $c $c $c {[Literal]}] 88 if {$::ExprDepth < 3} { 89 lappend TemplateList \ 90 {[Expr $c] [BinaryOp] [Expr $c]} \ 91 {[UnaryOp] [Expr $c]} \ 92 {[Expr $c] ISNULL} \ 93 {[Expr $c] NOTNULL} \ 94 {CAST([Expr $c] AS blob)} \ 95 {CAST([Expr $c] AS text)} \ 96 {CAST([Expr $c] AS integer)} \ 97 {CAST([Expr $c] AS real)} \ 98 {abs([Expr])} \ 99 {coalesce([Expr], [Expr])} \ 100 {hex([Expr])} \ 101 {length([Expr])} \ 102 {lower([Expr])} \ 103 {upper([Expr])} \ 104 {quote([Expr])} \ 105 {random()} \ 106 {randomblob(min(max([Expr],1), 500))} \ 107 {typeof([Expr])} \ 108 {substr([Expr],[Expr],[Expr])} \ 109 {CASE WHEN [Expr $c] THEN [Expr $c] ELSE [Expr $c] END} \ 110 {[Literal]} {[Literal]} {[Literal]} \ 111 {[Literal]} {[Literal]} {[Literal]} \ 112 {[Literal]} {[Literal]} {[Literal]} \ 113 {[Literal]} {[Literal]} {[Literal]} 114 } 115 if {$::SelectDepth < 4} { 116 lappend TemplateList \ 117 {([Select 1])} \ 118 {[Expr $c] IN ([Select 1])} \ 119 {[Expr $c] NOT IN ([Select 1])} \ 120 {EXISTS ([Select 1])} \ 121 } 122 set res [fuzz $TemplateList] 123 incr ::ExprDepth -1 124 return $res 125} 126 127# Return a valid table name. 128# 129set ::TableList [list] 130proc Table {} { 131 set TemplateList [concat sqlite_master $::TableList] 132 fuzz $TemplateList 133} 134 135# Return one of: 136# 137# "SELECT DISTINCT", "SELECT ALL" or "SELECT" 138# 139proc SelectKw {} { 140 set TemplateList { 141 "SELECT DISTINCT" 142 "SELECT ALL" 143 "SELECT" 144 } 145 fuzz $TemplateList 146} 147 148# Return a result set for a SELECT statement. 149# 150proc ResultSet {{nRes 0} {c ""}} { 151 if {$nRes == 0} { 152 set nRes [expr {rand()*2 + 1}] 153 } 154 155 set aRes [list] 156 for {set ii 0} {$ii < $nRes} {incr ii} { 157 lappend aRes [Expr $c] 158 } 159 160 join $aRes ", " 161} 162 163set ::SelectDepth 0 164set ::ColumnList [list] 165proc SimpleSelect {{nRes 0}} { 166 167 set TemplateList { 168 {[SelectKw] [ResultSet $nRes]} 169 } 170 171 # The ::SelectDepth variable contains the number of ancestor SELECT 172 # statements (i.e. for a top level SELECT it is set to 0, for a 173 # sub-select 1, for a sub-select of a sub-select 2 etc.). 174 # 175 # If this is already greater than 3, do not generate a complicated 176 # SELECT statement. This tends to cause parser stack overflow (too 177 # boring to bother with). 178 # 179 if {$::SelectDepth < 4} { 180 lappend TemplateList \ 181 {[SelectKw] [ResultSet $nRes $::ColumnList] FROM ([Select])} \ 182 {[SelectKw] [ResultSet $nRes] FROM ([Select])} \ 183 {[SelectKw] [ResultSet $nRes $::ColumnList] FROM [Table]} \ 184 { 185 [SelectKw] [ResultSet $nRes $::ColumnList] 186 FROM ([Select]) 187 GROUP BY [Expr] 188 HAVING [Expr] 189 } \ 190 191 if {0 == $nRes} { 192 lappend TemplateList \ 193 {[SelectKw] * FROM ([Select])} \ 194 {[SelectKw] * FROM [Table]} \ 195 {[SelectKw] * FROM [Table] WHERE [Expr $::ColumnList]} \ 196 { 197 [SelectKw] * 198 FROM [Table],[Table] AS t2 199 WHERE [Expr $::ColumnList] 200 } { 201 [SelectKw] * 202 FROM [Table] LEFT OUTER JOIN [Table] AS t2 203 ON [Expr $::ColumnList] 204 WHERE [Expr $::ColumnList] 205 } 206 } 207 } 208 209 fuzz $TemplateList 210} 211 212# Return a SELECT statement. 213# 214# If boolean parameter $isExpr is set to true, make sure the 215# returned SELECT statement returns a single column of data. 216# 217proc Select {{nMulti 0}} { 218 set TemplateList { 219 {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} 220 {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} 221 {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} 222 {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} 223 {[SimpleSelect $nMulti] ORDER BY [Expr] DESC} 224 {[SimpleSelect $nMulti] ORDER BY [Expr] ASC} 225 {[SimpleSelect $nMulti] ORDER BY [Expr] ASC, [Expr] DESC} 226 {[SimpleSelect $nMulti] ORDER BY [Expr] LIMIT [Expr] OFFSET [Expr]} 227 } 228 229 if {$::SelectDepth < 4} { 230 if {$nMulti == 0} { 231 set nMulti [expr {(rand()*2)+1}] 232 } 233 lappend TemplateList \ 234 {[SimpleSelect $nMulti] UNION [Select $nMulti]} \ 235 {[SimpleSelect $nMulti] UNION ALL [Select $nMulti]} \ 236 {[SimpleSelect $nMulti] EXCEPT [Select $nMulti]} \ 237 {[SimpleSelect $nMulti] INTERSECT [Select $nMulti]} 238 } 239 240 incr ::SelectDepth 241 set res [fuzz $TemplateList] 242 incr ::SelectDepth -1 243 set res 244} 245 246# Generate and return a fuzzy INSERT statement. 247# 248proc Insert {} { 249 set TemplateList { 250 {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr]);} 251 {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr], [Expr]);} 252 {INSERT INTO [Table] VALUES([Expr], [Expr]);} 253 } 254 fuzz $TemplateList 255} 256 257proc Column {} { 258 fuzz $::ColumnList 259} 260 261# Generate and return a fuzzy UPDATE statement. 262# 263proc Update {} { 264 set TemplateList { 265 {UPDATE [Table] 266 SET [Column] = [Expr $::ColumnList] 267 WHERE [Expr $::ColumnList]} 268 } 269 fuzz $TemplateList 270} 271 272proc Delete {} { 273 set TemplateList { 274 {DELETE FROM [Table] WHERE [Expr $::ColumnList]} 275 } 276 fuzz $TemplateList 277} 278 279proc Statement {} { 280 set TemplateList { 281 {[Update]} 282 {[Insert]} 283 {[Select]} 284 {[Delete]} 285 } 286 fuzz $TemplateList 287} 288 289# Return an identifier. This just chooses randomly from a fixed set 290# of strings. 291proc Identifier {} { 292 set TemplateList { 293 This just chooses randomly a fixed 294 We would also thank the developers 295 for their analysis Samba 296 } 297 fuzz $TemplateList 298} 299 300proc Check {} { 301 # Use a large value for $::SelectDepth, because sub-selects are 302 # not allowed in expressions used by CHECK constraints. 303 # 304 set sd $::SelectDepth 305 set ::SelectDepth 500 306 set TemplateList { 307 {} 308 {CHECK ([Expr])} 309 } 310 set res [fuzz $TemplateList] 311 set ::SelectDepth $sd 312 set res 313} 314 315proc Coltype {} { 316 set TemplateList { 317 {INTEGER PRIMARY KEY} 318 {VARCHAR [Check]} 319 {PRIMARY KEY} 320 } 321 fuzz $TemplateList 322} 323 324proc CreateTable {} { 325 set TemplateList { 326 {CREATE TABLE [Identifier]([Identifier] [Coltype], [Identifier] [Coltype])} 327 {CREATE TEMP TABLE [Identifier]([Identifier] [Coltype])} 328 } 329 fuzz $TemplateList 330} 331 332######################################################################## 333 334set ::log [open fuzzy.log w] 335 336# 337# Usage: do_fuzzy_test <testname> ?<options>? 338# 339# -template 340# -errorlist 341# -repeats 342# 343proc do_fuzzy_test {testname args} { 344 set ::fuzzyopts(-errorlist) [list] 345 set ::fuzzyopts(-repeats) $::REPEATS 346 array set ::fuzzyopts $args 347 348 lappend ::fuzzyopts(-errorlist) {parser stack overflow} 349 lappend ::fuzzyopts(-errorlist) {ORDER BY} 350 lappend ::fuzzyopts(-errorlist) {GROUP BY} 351 lappend ::fuzzyopts(-errorlist) {datatype mismatch} 352 353 for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} { 354 do_test ${testname}.$ii { 355 set ::sql [subst $::fuzzyopts(-template)] 356 puts $::log $::sql 357 flush $::log 358 set rc [catch {execsql $::sql} msg] 359 set e 1 360 if {$rc} { 361 set e 0 362 foreach error $::fuzzyopts(-errorlist) { 363 if {0 == [string first $error $msg]} { 364 set e 1 365 break 366 } 367 } 368 } 369 if {$e == 0} { 370 puts "" 371 puts $::sql 372 puts $msg 373 } 374 set e 375 } {1} 376 } 377} 378 379#---------------------------------------------------------------- 380# These tests caused errors that were first caught by the tests 381# in this file. They are still here. 382do_test fuzz-1.1 { 383 execsql { 384 SELECT 'abc' LIKE X'ABCD'; 385 } 386} {0} 387do_test fuzz-1.2 { 388 execsql { 389 SELECT 'abc' LIKE zeroblob(10); 390 } 391} {0} 392do_test fuzz-1.3 { 393 execsql { 394 SELECT zeroblob(10) LIKE 'abc'; 395 } 396} {0} 397do_test fuzz-1.4 { 398 execsql { 399 SELECT (- -21) % NOT (456 LIKE zeroblob(10)); 400 } 401} {0} 402do_test fuzz-1.5 { 403 execsql { 404 SELECT (SELECT ( 405 SELECT (SELECT -2147483648) FROM (SELECT 1) ORDER BY 1 406 )) 407 } 408} {-2147483648} 409do_test fuzz-1.6 { 410 execsql { 411 SELECT 'abc', zeroblob(1) FROM (SELECT 1) ORDER BY 1 412 } 413} [execsql {SELECT 'abc', zeroblob(1)}] 414 415do_test fuzz-1.7 { 416 execsql { 417 SELECT ( SELECT zeroblob(1000) FROM ( 418 SELECT * FROM (SELECT 'first') ORDER BY NOT 'in') 419 ) 420 } 421} [execsql {SELECT zeroblob(1000)}] 422 423do_test fuzz-1.8 { 424 # Problems with opcode OP_ToText (did not account for MEM_Zero). 425 # Also MemExpandBlob() was marking expanded blobs as nul-terminated. 426 # They are not. 427 execsql { 428 SELECT CAST(zeroblob(1000) AS text); 429 } 430} {{}} 431 432do_test fuzz-1.9 { 433 # This was causing a NULL pointer dereference of Expr.pList. 434 execsql { 435 SELECT 1 FROM (SELECT * FROM sqlite_master WHERE random()) 436 } 437} {} 438 439do_test fuzz-1.10 { 440 # Bug in calculation of Parse.ckOffset causing an assert() 441 # to fail. Probably harmless. 442 execsql { 443 SELECT coalesce(1, substr( 1, 2, length('in' IN (SELECT 1)))) 444 } 445} {1} 446 447do_test fuzz-1.11 { 448 # The literals (A, B, C, D) are not important, they are just used 449 # to make the EXPLAIN output easier to read. 450 # 451 # The problem here is that the EXISTS(...) expression leaves an 452 # extra value on the VDBE stack. This is confusing the parent and 453 # leads to an assert() failure when OP_Insert encounters an integer 454 # when it expects a record blob. 455 # 456 # Update: Any query with (LIMIT 0) was leaking stack. 457 # 458 execsql { 459 SELECT 'A' FROM (SELECT 'B') ORDER BY EXISTS ( 460 SELECT 'C' FROM (SELECT 'D' LIMIT 0) 461 ) 462 } 463} {A} 464 465do_test fuzz-1.12.1 { 466 # Create a table with a single row. 467 execsql { 468 CREATE TABLE abc(b); 469 INSERT INTO abc VALUES('ABCDE'); 470 } 471 472 # The following query was crashing. The later subquery (in the FROM) 473 # clause was flattened into the parent, but the code was not repairng 474 # the "b" reference in the other sub-query. When the query was executed, 475 # that "b" refered to a non-existant vdbe table-cursor. 476 # 477 execsql { 478 SELECT 1 IN ( SELECT b UNION SELECT 1 ) FROM (SELECT b FROM abc); 479 } 480} {1} 481do_test fuzz-1.12.2 { 482 # Clean up after the previous query. 483 execsql { 484 DROP TABLE abc; 485 } 486} {} 487 488#---------------------------------------------------------------- 489# Test some fuzzily generated expressions. 490# 491do_fuzzy_test fuzz-2 -template { SELECT [Expr] } 492 493do_test fuzz-3.1 { 494 execsql { 495 CREATE TABLE abc(a, b, c); 496 CREATE TABLE def(a, b, c); 497 CREATE TABLE ghi(a, b, c); 498 } 499} {} 500set ::TableList [list abc def ghi] 501 502#---------------------------------------------------------------- 503# Test some fuzzily generated SELECT statements. 504# 505do_fuzzy_test fuzz-3.2 -template {[Select]} 506 507#---------------------------------------------------------------- 508# Insert a small amount of data into the database and then run 509# some more generated SELECT statements. 510# 511do_test fuzz-4.1 { 512 execsql { 513 INSERT INTO abc VALUES(1, 2, 3); 514 INSERT INTO abc VALUES(4, 5, 6); 515 INSERT INTO abc VALUES(7, 8, 9); 516 INSERT INTO def VALUES(1, 2, 3); 517 INSERT INTO def VALUES(4, 5, 6); 518 INSERT INTO def VALUES(7, 8, 9); 519 INSERT INTO ghi VALUES(1, 2, 3); 520 INSERT INTO ghi VALUES(4, 5, 6); 521 INSERT INTO ghi VALUES(7, 8, 9); 522 CREATE INDEX abc_i ON abc(a, b, c); 523 CREATE INDEX def_i ON def(c, a, b); 524 CREATE INDEX ghi_i ON ghi(b, c, a); 525 } 526} {} 527do_fuzzy_test fuzz-4.2 -template {[Select]} 528 529#---------------------------------------------------------------- 530# Test some fuzzy INSERT statements: 531# 532do_test fuzz-5.1 {execsql BEGIN} {} 533do_fuzzy_test fuzz-5.2 -template {[Insert]} -errorlist table 534integrity_check fuzz-5.2.integrity 535do_test fuzz-5.3 {execsql COMMIT} {} 536integrity_check fuzz-5.4.integrity 537 538#---------------------------------------------------------------- 539# Now that there is data in the database, run some more SELECT 540# statements 541# 542set ::ColumnList [list a b c] 543set E {{no such col} {ambiguous column name}} 544do_fuzzy_test fuzz-6.1 -template {[Select]} -errorlist $E 545 546#---------------------------------------------------------------- 547# Run some SELECTs, INSERTs, UPDATEs and DELETEs in a transaction. 548# 549set E {{no such col} {ambiguous column name} {table}} 550do_test fuzz-7.1 {execsql BEGIN} {} 551do_fuzzy_test fuzz-7.2 -template {[Statement]} -errorlist $E 552integrity_check fuzz-7.3.integrity 553do_test fuzz-7.4 {execsql COMMIT} {} 554integrity_check fuzz-7.5.integrity 555 556#---------------------------------------------------------------- 557# Many CREATE TABLE statements: 558# 559do_fuzzy_test fuzz-8.1 -template {[CreateTable]} \ 560 -errorlist {table duplicate} -repeats 1000 561 562close $::log 563finish_test 564