Add some extra tests for malloc failure during expression parsing and execution using fuzzily generated SQL. (CVS 4043)

FossilOrigin-Name: 7522d2fb3204d107b8b4816d7f39c88741f20230
This commit is contained in:
danielk1977 2007-05-30 10:36:47 +00:00
parent 5453b8da9e
commit c9cf901d8c
13 changed files with 624 additions and 500 deletions

View File

@ -1,5 +1,5 @@
C Add\sthe\sstart\sof\sthe\ssoak-test\sinfrastructure.\s(CVS\s4042)
D 2007-05-30T08:18:04
C Add\ssome\sextra\stests\sfor\smalloc\sfailure\sduring\sexpression\sparsing\sand\sexecution\susing\sfuzzily\sgenerated\sSQL.\s(CVS\s4043)
D 2007-05-30T10:36:47
F Makefile.in a42354804b50c2708ce72cf79e4daa30f50191b5
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@ -71,7 +71,7 @@ F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675
F src/date.c 6049db7d5a8fdf2c677ff7d58fa31d4f6593c988
F src/delete.c 5c0d89b3ef7d48fe1f5124bfe8341f982747fe29
F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b
F src/expr.c 7243b6c01b976662873bbba397640fb4cbed76bc
F src/expr.c fb386ee80026e221869f49159c0963e851c184c9
F src/func.c dfd0dd496dac46c2b14a88292cd9e141aae3ba63
F src/hash.c 67b23e14f0257b69a3e8aa663e4eeadc1a2b6fd5
F src/hash.h 1b3f7e2609141fd571f62199fc38687d262e9564
@ -95,7 +95,7 @@ F src/os_win.c d868d5f9e95ec9c1b9e2a30c54c996053db6dddd
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
F src/pager.c a193f8f0783e33ea0aa1965e951c3daa2b2c9c4e
F src/pager.h 94110a5570dca30d54a883e880a3633b2e4c05ae
F src/parse.y e276a0c35d6579938708f0842d0eba4e9b6866e6
F src/parse.y e3d8e3f748bd3915523f77f704f303c7f0de613b
F src/pragma.c 0d25dad58bdfd6789943a10f1b9663c2eb85b96d
F src/prepare.c 87c23644986b5e41a58bc76f05abebd899e00089
F src/printf.c cd91e057fa7e2661673eecd4eeecf4900b1e5cfe
@ -136,7 +136,7 @@ F src/vacuum.c 8bd895d29e7074e78d4e80f948e35ddc9cf2beef
F src/vdbe.c 74a82e8dc0cd84416fcca63d158c5ab8715f158d
F src/vdbe.h 001c5b257567c1d3de7feb2203aac71d0d7b16a3
F src/vdbeInt.h 7d2bf163d6d4e815724a457f2216dd8e38c3955c
F src/vdbeapi.c f89d6bc5264e66f44589e454fbeeee96854d0dd3
F src/vdbeapi.c 3747e4c3bc3139ff688bb3df462b10e42c084d16
F src/vdbeaux.c a978d170b2ca99c8ff3da8a91f116a66da2600ac
F src/vdbeblob.c 96f3572fdc45eda5be06e6372b612bc30742d9f0
F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
@ -250,8 +250,10 @@ F test/fts2l.test 4c53c89ce3919003765ff4fd8d98ecf724d97dd3
F test/fts2m.test 4b30142ead6f3ed076e880a2a464064c5ad58c51
F test/fts2n.test a70357e72742681eaebfdbe9007b87ff3b771638
F test/func.test 605989453d1b42cec1d05c17aa232dc98e3e04e6
F test/fuzz.test 9fdb668167bd38eb7137d49c8e9c250994bdbd5b
F test/fuzz.test 62fc19dd36a427777fd671b569df07166548628a
F test/fuzz2.test ea38692ce2da99ad79fe0be5eb1a452c1c4d37bb
F test/fuzz_common.tcl ff4bc2dfc465f6878f8e2d819620914365382731
F test/fuzz_malloc.test 41ef1135f2c319b4963b1524f1bdd4a3553b827d
F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
F test/icu.test e6bfae7f625c88fd14df6f540fe835bdfc1e4329
F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
@ -289,7 +291,7 @@ F test/lock2.test b95915eb4b6f8420acb30c68170d8f0ed9364efe
F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
F test/lock4.test 49e22396b9be6e047b3d35469f233be79153c9d5
F test/main.test e7212ce1023957c7209778cc87fa932bd79ba89a
F test/malloc.test c468c018eecba78d1832cb9aaa23a7f824f07179
F test/malloc.test 45a81af6328381d0630ab0a3d8cb8c58551603f7
F test/malloc2.test 4ed7d719542c4570dec9c2ebe2bbdf3a9f3b0d05
F test/malloc3.test e965954b6f808876a63d3101fd70370320b509a7
F test/malloc4.test 59cd02f71b363302a04c4e77b97c0a1572eaa210
@ -299,6 +301,8 @@ F test/malloc7.test 1cf52834509eac7ebeb92105dacd4669f9ca9869
F test/malloc8.test e4054ca2a87ab1d42255bec009b177ba20b5a487
F test/malloc9.test 8381041fd89c31fba60c8a1a1c776bb022108572
F test/mallocA.test 525674e6e0775a9bf85a33f1da1c6bbddc712c30
F test/mallocB.test 5a96951a5f06cfdbd48d61ac3add8aa77461b812
F test/malloc_common.tcl 3cda97d63fbf370061ffa9795a24e5027367fef3
F test/manydb.test 8de36b8d33aab5ef295b11d9e95310aeded31af8
F test/memdb.test a67bda4ff90a38f2b19f6c7f95aa7289e051d893
F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
@ -322,7 +326,7 @@ F test/pragma.test b0e73879206934a835856a8b8c4cc884cd8562f3
F test/printf.test 69d8cb0771a1a5e4d9d5dece12fc2c16179ac5e5
F test/progress.test 8b22b4974b0a95272566385f8cb8c341c7130df8 x
F test/ptrchng.test 1c712dd6516e1377471744fa765e41c79a357da6
F test/quick.test 41052ccf861fa49145496c9e9db62df4b0a2b6b8
F test/quick.test 73a81a29fc28661c9c3fee2dcc3ded83cb1f1834
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/rdonly.test b34db316525440d3b42c32e83942c02c37d28ef0
F test/reindex.test 38b138abe36bf9a08c791ed44d9f76cd6b97b78b
@ -343,7 +347,7 @@ F test/shared.test 5c39f216ce85d723eda5875804bbf5ef8a03fcfc
F test/shared2.test 8b48f8d33494413ef4cf250110d89403e2bf6b23
F test/shared3.test 01e3e124dbb3859788aabc7cfb82f7ea04421749
F test/shared_err.test cc528f6e78665787e93d9ce3a782a2ce5179d821
F test/soak.test dac7ec07bdedd8f924de69ba029ed1dacbc1082a
F test/soak.test 64f9b27fbcdec43335a88c546ce1983e6ba40d7b
F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
F test/speed1.test 22e1b27af0683ed44dcd2f93ed817a9c3e65084a
F test/speed2.test 53177056baf6556dcbdcf032bbdfc41c1aa74ded
@ -496,7 +500,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
P 03750a2a6b5186689f7db6650c0a889429790b03
R cd63e6e2215c48b174d5e94a02b48d9a
P 5d0b247ca1667f5d773bda337cb6f58c3b14676a
R a618a8ac57ff1c991566b3aa8912d34a
U danielk1977
Z 17f10a57e39853a2feb0d4c1a0988d68
Z 5fc3b63bae0e892acacf6621f7949f54

View File

@ -1 +1 @@
5d0b247ca1667f5d773bda337cb6f58c3b14676a
7522d2fb3204d107b8b4816d7f39c88741f20230

View File

@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.295 2007/05/29 12:11:30 danielk1977 Exp $
** $Id: expr.c,v 1.296 2007/05/30 10:36:47 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -1627,13 +1627,16 @@ void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
** text z[0..n-1] on the stack.
*/
static void codeInteger(Vdbe *v, const char *z, int n){
int i;
if( sqlite3GetInt32(z, &i) ){
sqlite3VdbeAddOp(v, OP_Integer, i, 0);
}else if( sqlite3FitsIn64Bits(z) ){
sqlite3VdbeOp3(v, OP_Int64, 0, 0, z, n);
}else{
sqlite3VdbeOp3(v, OP_Real, 0, 0, z, n);
assert( z || sqlite3MallocFailed() );
if( z ){
int i;
if( sqlite3GetInt32(z, &i) ){
sqlite3VdbeAddOp(v, OP_Integer, i, 0);
}else if( sqlite3FitsIn64Bits(z) ){
sqlite3VdbeOp3(v, OP_Int64, 0, 0, z, n);
}else{
sqlite3VdbeOp3(v, OP_Real, 0, 0, z, n);
}
}
}

View File

@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.228 2007/05/15 16:51:37 drh Exp $
** @(#) $Id: parse.y,v 1.229 2007/05/30 10:36:47 danielk1977 Exp $
*/
// All token codes are small integers with #defines that begin with "TK_"
@ -659,7 +659,7 @@ expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
}
%endif SQLITE_OMIT_CAST
expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). {
if( Y->nExpr>SQLITE_MAX_FUNCTION_ARG ){
if( Y && Y->nExpr>SQLITE_MAX_FUNCTION_ARG ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X);
}
A = sqlite3ExprFunction(Y, &X);

View File

@ -498,6 +498,11 @@ static void columnMallocFailure(sqlite3_stmt *pStmt)
const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){
const void *val;
val = sqlite3_value_blob( columnMem(pStmt,i) );
/* Even though there is no encoding conversion, value_blob() might
** need to call malloc() to expand the result of a zeroblob()
** expression.
*/
columnMallocFailure(pStmt);
return val;
}
int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){

View File

@ -19,7 +19,7 @@
#
# The most complicated trees are for SELECT statements.
#
# $Id: fuzz.test,v 1.13 2007/05/30 08:18:04 danielk1977 Exp $
# $Id: fuzz.test,v 1.14 2007/05/30 10:36:47 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -31,384 +31,7 @@ if {[info exists ::ISQUICK]} {
if {$::ISQUICK} { set ::REPEATS 20 }
}
proc fuzz {TemplateList} {
set n [llength $TemplateList]
set i [expr {int(rand()*$n)}]
set r [uplevel 1 subst -novar [list [lindex $TemplateList $i]]]
string map {"\n" " "} $r
}
# Fuzzy generation primitives:
#
# Literal
# UnaryOp
# BinaryOp
# Expr
# Table
# Select
# Insert
#
# Returns a string representing an SQL literal.
#
proc Literal {} {
set TemplateList {
456 0 -456 1 -1
2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649
'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection'
zeroblob(1000)
NULL
56.1 -56.1
123456789.1234567899
}
fuzz $TemplateList
}
# Returns a string containing an SQL unary operator (e.g. "+" or "NOT").
#
proc UnaryOp {} {
set TemplateList {+ - NOT ~}
fuzz $TemplateList
}
# Returns a string containing an SQL binary operator (e.g. "*" or "/").
#
proc BinaryOp {} {
set TemplateList {
|| * / % + - << >> & | < <= > >= = == != <> AND OR
LIKE GLOB {NOT LIKE}
}
fuzz $TemplateList
}
# Return the complete text of an SQL expression.
#
set ::ExprDepth 0
proc Expr { {c {}} } {
incr ::ExprDepth
set TemplateList [concat $c $c $c {[Literal]}]
if {$::ExprDepth < 3} {
lappend TemplateList \
{[Expr $c] [BinaryOp] [Expr $c]} \
{[UnaryOp] [Expr $c]} \
{[Expr $c] ISNULL} \
{[Expr $c] NOTNULL} \
{CAST([Expr $c] AS blob)} \
{CAST([Expr $c] AS text)} \
{CAST([Expr $c] AS integer)} \
{CAST([Expr $c] AS real)} \
{abs([Expr])} \
{coalesce([Expr], [Expr])} \
{hex([Expr])} \
{length([Expr])} \
{lower([Expr])} \
{upper([Expr])} \
{quote([Expr])} \
{random()} \
{randomblob(min(max([Expr],1), 500))} \
{typeof([Expr])} \
{substr([Expr],[Expr],[Expr])} \
{CASE WHEN [Expr $c] THEN [Expr $c] ELSE [Expr $c] END} \
{[Literal]} {[Literal]} {[Literal]} \
{[Literal]} {[Literal]} {[Literal]} \
{[Literal]} {[Literal]} {[Literal]} \
{[Literal]} {[Literal]} {[Literal]}
}
if {$::SelectDepth < 4} {
lappend TemplateList \
{([Select 1])} \
{[Expr $c] IN ([Select 1])} \
{[Expr $c] NOT IN ([Select 1])} \
{EXISTS ([Select 1])} \
}
set res [fuzz $TemplateList]
incr ::ExprDepth -1
return $res
}
# Return a valid table name.
#
set ::TableList [list]
proc Table {} {
set TemplateList [concat sqlite_master $::TableList]
fuzz $TemplateList
}
# Return one of:
#
# "SELECT DISTINCT", "SELECT ALL" or "SELECT"
#
proc SelectKw {} {
set TemplateList {
"SELECT DISTINCT"
"SELECT ALL"
"SELECT"
}
fuzz $TemplateList
}
# Return a result set for a SELECT statement.
#
proc ResultSet {{nRes 0} {c ""}} {
if {$nRes == 0} {
set nRes [expr {rand()*2 + 1}]
}
set aRes [list]
for {set ii 0} {$ii < $nRes} {incr ii} {
lappend aRes [Expr $c]
}
join $aRes ", "
}
set ::SelectDepth 0
set ::ColumnList [list]
proc SimpleSelect {{nRes 0}} {
set TemplateList {
{[SelectKw] [ResultSet $nRes]}
}
# The ::SelectDepth variable contains the number of ancestor SELECT
# statements (i.e. for a top level SELECT it is set to 0, for a
# sub-select 1, for a sub-select of a sub-select 2 etc.).
#
# If this is already greater than 3, do not generate a complicated
# SELECT statement. This tends to cause parser stack overflow (too
# boring to bother with).
#
if {$::SelectDepth < 4} {
lappend TemplateList \
{[SelectKw] [ResultSet $nRes $::ColumnList] FROM ([Select])} \
{[SelectKw] [ResultSet $nRes] FROM ([Select])} \
{[SelectKw] [ResultSet $nRes $::ColumnList] FROM [Table]} \
{
[SelectKw] [ResultSet $nRes $::ColumnList]
FROM ([Select])
GROUP BY [Expr]
HAVING [Expr]
} \
if {0 == $nRes} {
lappend TemplateList \
{[SelectKw] * FROM ([Select])} \
{[SelectKw] * FROM [Table]} \
{[SelectKw] * FROM [Table] WHERE [Expr $::ColumnList]} \
{
[SelectKw] *
FROM [Table],[Table] AS t2
WHERE [Expr $::ColumnList]
} {
[SelectKw] *
FROM [Table] LEFT OUTER JOIN [Table] AS t2
ON [Expr $::ColumnList]
WHERE [Expr $::ColumnList]
}
}
}
fuzz $TemplateList
}
# Return a SELECT statement.
#
# If boolean parameter $isExpr is set to true, make sure the
# returned SELECT statement returns a single column of data.
#
proc Select {{nMulti 0}} {
set TemplateList {
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti] ORDER BY [Expr] DESC}
{[SimpleSelect $nMulti] ORDER BY [Expr] ASC}
{[SimpleSelect $nMulti] ORDER BY [Expr] ASC, [Expr] DESC}
{[SimpleSelect $nMulti] ORDER BY [Expr] LIMIT [Expr] OFFSET [Expr]}
}
if {$::SelectDepth < 4} {
if {$nMulti == 0} {
set nMulti [expr {(rand()*2)+1}]
}
lappend TemplateList \
{[SimpleSelect $nMulti] UNION [Select $nMulti]} \
{[SimpleSelect $nMulti] UNION ALL [Select $nMulti]} \
{[SimpleSelect $nMulti] EXCEPT [Select $nMulti]} \
{[SimpleSelect $nMulti] INTERSECT [Select $nMulti]}
}
incr ::SelectDepth
set res [fuzz $TemplateList]
incr ::SelectDepth -1
set res
}
# Generate and return a fuzzy INSERT statement.
#
proc Insert {} {
set TemplateList {
{INSERT INTO [Table] VALUES([Expr], [Expr], [Expr]);}
{INSERT INTO [Table] VALUES([Expr], [Expr], [Expr], [Expr]);}
{INSERT INTO [Table] VALUES([Expr], [Expr]);}
}
fuzz $TemplateList
}
proc Column {} {
fuzz $::ColumnList
}
# Generate and return a fuzzy UPDATE statement.
#
proc Update {} {
set TemplateList {
{UPDATE [Table]
SET [Column] = [Expr $::ColumnList]
WHERE [Expr $::ColumnList]}
}
fuzz $TemplateList
}
proc Delete {} {
set TemplateList {
{DELETE FROM [Table] WHERE [Expr $::ColumnList]}
}
fuzz $TemplateList
}
proc Statement {} {
set TemplateList {
{[Update]}
{[Insert]}
{[Select]}
{[Delete]}
}
fuzz $TemplateList
}
# Return an identifier. This just chooses randomly from a fixed set
# of strings.
proc Identifier {} {
set TemplateList {
This just chooses randomly a fixed
We would also thank the developers
for their analysis Samba
}
fuzz $TemplateList
}
proc Check {} {
# Use a large value for $::SelectDepth, because sub-selects are
# not allowed in expressions used by CHECK constraints.
#
set sd $::SelectDepth
set ::SelectDepth 500
set TemplateList {
{}
{CHECK ([Expr])}
}
set res [fuzz $TemplateList]
set ::SelectDepth $sd
set res
}
proc Coltype {} {
set TemplateList {
{INTEGER PRIMARY KEY}
{VARCHAR [Check]}
{PRIMARY KEY}
}
fuzz $TemplateList
}
proc DropTable {} {
set TemplateList {
{DROP TABLE IF EXISTS [Identifier]}
}
fuzz $TemplateList
}
proc CreateView {} {
set TemplateList {
{CREATE VIEW [Identifier] AS [Select]}
}
fuzz $TemplateList
}
proc DropView {} {
set TemplateList {
{DROP VIEW IF EXISTS [Identifier]}
}
fuzz $TemplateList
}
proc CreateTable {} {
set TemplateList {
{CREATE TABLE [Identifier]([Identifier] [Coltype], [Identifier] [Coltype])}
{CREATE TEMP TABLE [Identifier]([Identifier] [Coltype])}
}
fuzz $TemplateList
}
proc CreateOrDropTableOrView {} {
set TemplateList {
{[CreateTable]}
{[DropTable]}
{[CreateView]}
{[DropView]}
}
fuzz $TemplateList
}
########################################################################
set ::log [open fuzzy.log w]
#
# Usage: do_fuzzy_test <testname> ?<options>?
#
# -template
# -errorlist
# -repeats
#
proc do_fuzzy_test {testname args} {
set ::fuzzyopts(-errorlist) [list]
set ::fuzzyopts(-repeats) $::REPEATS
array set ::fuzzyopts $args
lappend ::fuzzyopts(-errorlist) {parser stack overflow}
lappend ::fuzzyopts(-errorlist) {ORDER BY}
lappend ::fuzzyopts(-errorlist) {GROUP BY}
lappend ::fuzzyopts(-errorlist) {datatype mismatch}
for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} {
do_test ${testname}.$ii {
set ::sql [subst $::fuzzyopts(-template)]
puts $::log $::sql
flush $::log
set rc [catch {execsql $::sql} msg]
set e 1
if {$rc} {
set e 0
foreach error $::fuzzyopts(-errorlist) {
if {0 == [string first $error $msg]} {
set e 1
break
}
}
}
if {$e == 0} {
puts ""
puts $::sql
puts $msg
}
set e
} {1}
}
}
source $testdir/fuzz_common.tcl
#----------------------------------------------------------------
# These tests caused errors that were first caught by the tests

392
test/fuzz_common.tcl Normal file
View File

@ -0,0 +1,392 @@
# 2007 May 10
#
# 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.
#
#***********************************************************************
#
# $Id: fuzz_common.tcl,v 1.1 2007/05/30 10:36:47 danielk1977 Exp $
proc fuzz {TemplateList} {
set n [llength $TemplateList]
set i [expr {int(rand()*$n)}]
set r [uplevel 1 subst -novar [list [lindex $TemplateList $i]]]
string map {"\n" " "} $r
}
# Fuzzy generation primitives:
#
# Literal
# UnaryOp
# BinaryOp
# Expr
# Table
# Select
# Insert
#
# Returns a string representing an SQL literal.
#
proc Literal {} {
set TemplateList {
456 0 -456 1 -1
2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649
'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection'
zeroblob(1000)
NULL
56.1 -56.1
123456789.1234567899
}
fuzz $TemplateList
}
# Returns a string containing an SQL unary operator (e.g. "+" or "NOT").
#
proc UnaryOp {} {
set TemplateList {+ - NOT ~}
fuzz $TemplateList
}
# Returns a string containing an SQL binary operator (e.g. "*" or "/").
#
proc BinaryOp {} {
set TemplateList {
|| * / % + - << >> & | < <= > >= = == != <> AND OR
LIKE GLOB {NOT LIKE}
}
fuzz $TemplateList
}
# Return the complete text of an SQL expression.
#
set ::ExprDepth 0
proc Expr { {c {}} } {
incr ::ExprDepth
set TemplateList [concat $c $c $c {[Literal]}]
if {$::ExprDepth < 3} {
lappend TemplateList \
{[Expr $c] [BinaryOp] [Expr $c]} \
{[UnaryOp] [Expr $c]} \
{[Expr $c] ISNULL} \
{[Expr $c] NOTNULL} \
{CAST([Expr $c] AS blob)} \
{CAST([Expr $c] AS text)} \
{CAST([Expr $c] AS integer)} \
{CAST([Expr $c] AS real)} \
{abs([Expr])} \
{coalesce([Expr], [Expr])} \
{hex([Expr])} \
{length([Expr])} \
{lower([Expr])} \
{upper([Expr])} \
{quote([Expr])} \
{random()} \
{randomblob(min(max([Expr],1), 500))} \
{typeof([Expr])} \
{substr([Expr],[Expr],[Expr])} \
{CASE WHEN [Expr $c] THEN [Expr $c] ELSE [Expr $c] END} \
{[Literal]} {[Literal]} {[Literal]} \
{[Literal]} {[Literal]} {[Literal]} \
{[Literal]} {[Literal]} {[Literal]} \
{[Literal]} {[Literal]} {[Literal]}
}
if {$::SelectDepth < 4} {
lappend TemplateList \
{([Select 1])} \
{[Expr $c] IN ([Select 1])} \
{[Expr $c] NOT IN ([Select 1])} \
{EXISTS ([Select 1])} \
}
set res [fuzz $TemplateList]
incr ::ExprDepth -1
return $res
}
# Return a valid table name.
#
set ::TableList [list]
proc Table {} {
set TemplateList [concat sqlite_master $::TableList]
fuzz $TemplateList
}
# Return one of:
#
# "SELECT DISTINCT", "SELECT ALL" or "SELECT"
#
proc SelectKw {} {
set TemplateList {
"SELECT DISTINCT"
"SELECT ALL"
"SELECT"
}
fuzz $TemplateList
}
# Return a result set for a SELECT statement.
#
proc ResultSet {{nRes 0} {c ""}} {
if {$nRes == 0} {
set nRes [expr {rand()*2 + 1}]
}
set aRes [list]
for {set ii 0} {$ii < $nRes} {incr ii} {
lappend aRes [Expr $c]
}
join $aRes ", "
}
set ::SelectDepth 0
set ::ColumnList [list]
proc SimpleSelect {{nRes 0}} {
set TemplateList {
{[SelectKw] [ResultSet $nRes]}
}
# The ::SelectDepth variable contains the number of ancestor SELECT
# statements (i.e. for a top level SELECT it is set to 0, for a
# sub-select 1, for a sub-select of a sub-select 2 etc.).
#
# If this is already greater than 3, do not generate a complicated
# SELECT statement. This tends to cause parser stack overflow (too
# boring to bother with).
#
if {$::SelectDepth < 4} {
lappend TemplateList \
{[SelectKw] [ResultSet $nRes $::ColumnList] FROM ([Select])} \
{[SelectKw] [ResultSet $nRes] FROM ([Select])} \
{[SelectKw] [ResultSet $nRes $::ColumnList] FROM [Table]} \
{
[SelectKw] [ResultSet $nRes $::ColumnList]
FROM ([Select])
GROUP BY [Expr]
HAVING [Expr]
} \
if {0 == $nRes} {
lappend TemplateList \
{[SelectKw] * FROM ([Select])} \
{[SelectKw] * FROM [Table]} \
{[SelectKw] * FROM [Table] WHERE [Expr $::ColumnList]} \
{
[SelectKw] *
FROM [Table],[Table] AS t2
WHERE [Expr $::ColumnList]
} {
[SelectKw] *
FROM [Table] LEFT OUTER JOIN [Table] AS t2
ON [Expr $::ColumnList]
WHERE [Expr $::ColumnList]
}
}
}
fuzz $TemplateList
}
# Return a SELECT statement.
#
# If boolean parameter $isExpr is set to true, make sure the
# returned SELECT statement returns a single column of data.
#
proc Select {{nMulti 0}} {
set TemplateList {
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]} {[SimpleSelect $nMulti]}
{[SimpleSelect $nMulti] ORDER BY [Expr] DESC}
{[SimpleSelect $nMulti] ORDER BY [Expr] ASC}
{[SimpleSelect $nMulti] ORDER BY [Expr] ASC, [Expr] DESC}
{[SimpleSelect $nMulti] ORDER BY [Expr] LIMIT [Expr] OFFSET [Expr]}
}
if {$::SelectDepth < 4} {
if {$nMulti == 0} {
set nMulti [expr {(rand()*2)+1}]
}
lappend TemplateList \
{[SimpleSelect $nMulti] UNION [Select $nMulti]} \
{[SimpleSelect $nMulti] UNION ALL [Select $nMulti]} \
{[SimpleSelect $nMulti] EXCEPT [Select $nMulti]} \
{[SimpleSelect $nMulti] INTERSECT [Select $nMulti]}
}
incr ::SelectDepth
set res [fuzz $TemplateList]
incr ::SelectDepth -1
set res
}
# Generate and return a fuzzy INSERT statement.
#
proc Insert {} {
set TemplateList {
{INSERT INTO [Table] VALUES([Expr], [Expr], [Expr]);}
{INSERT INTO [Table] VALUES([Expr], [Expr], [Expr], [Expr]);}
{INSERT INTO [Table] VALUES([Expr], [Expr]);}
}
fuzz $TemplateList
}
proc Column {} {
fuzz $::ColumnList
}
# Generate and return a fuzzy UPDATE statement.
#
proc Update {} {
set TemplateList {
{UPDATE [Table]
SET [Column] = [Expr $::ColumnList]
WHERE [Expr $::ColumnList]}
}
fuzz $TemplateList
}
proc Delete {} {
set TemplateList {
{DELETE FROM [Table] WHERE [Expr $::ColumnList]}
}
fuzz $TemplateList
}
proc Statement {} {
set TemplateList {
{[Update]}
{[Insert]}
{[Select]}
{[Delete]}
}
fuzz $TemplateList
}
# Return an identifier. This just chooses randomly from a fixed set
# of strings.
proc Identifier {} {
set TemplateList {
This just chooses randomly a fixed
We would also thank the developers
for their analysis Samba
}
fuzz $TemplateList
}
proc Check {} {
# Use a large value for $::SelectDepth, because sub-selects are
# not allowed in expressions used by CHECK constraints.
#
set sd $::SelectDepth
set ::SelectDepth 500
set TemplateList {
{}
{CHECK ([Expr])}
}
set res [fuzz $TemplateList]
set ::SelectDepth $sd
set res
}
proc Coltype {} {
set TemplateList {
{INTEGER PRIMARY KEY}
{VARCHAR [Check]}
{PRIMARY KEY}
}
fuzz $TemplateList
}
proc DropTable {} {
set TemplateList {
{DROP TABLE IF EXISTS [Identifier]}
}
fuzz $TemplateList
}
proc CreateView {} {
set TemplateList {
{CREATE VIEW [Identifier] AS [Select]}
}
fuzz $TemplateList
}
proc DropView {} {
set TemplateList {
{DROP VIEW IF EXISTS [Identifier]}
}
fuzz $TemplateList
}
proc CreateTable {} {
set TemplateList {
{CREATE TABLE [Identifier]([Identifier] [Coltype], [Identifier] [Coltype])}
{CREATE TEMP TABLE [Identifier]([Identifier] [Coltype])}
}
fuzz $TemplateList
}
proc CreateOrDropTableOrView {} {
set TemplateList {
{[CreateTable]}
{[DropTable]}
{[CreateView]}
{[DropView]}
}
fuzz $TemplateList
}
########################################################################
set ::log [open fuzzy.log w]
#
# Usage: do_fuzzy_test <testname> ?<options>?
#
# -template
# -errorlist
# -repeats
#
proc do_fuzzy_test {testname args} {
set ::fuzzyopts(-errorlist) [list]
set ::fuzzyopts(-repeats) $::REPEATS
array set ::fuzzyopts $args
lappend ::fuzzyopts(-errorlist) {parser stack overflow}
lappend ::fuzzyopts(-errorlist) {ORDER BY}
lappend ::fuzzyopts(-errorlist) {GROUP BY}
lappend ::fuzzyopts(-errorlist) {datatype mismatch}
for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} {
do_test ${testname}.$ii {
set ::sql [subst $::fuzzyopts(-template)]
puts $::log $::sql
flush $::log
set rc [catch {execsql $::sql} msg]
set e 1
if {$rc} {
set e 0
foreach error $::fuzzyopts(-errorlist) {
if {0 == [string first $error $msg]} {
set e 1
break
}
}
}
if {$e == 0} {
puts ""
puts $::sql
puts $msg
}
set e
} {1}
}
}

53
test/fuzz_malloc.test Normal file
View File

@ -0,0 +1,53 @@
#
# 2007 May 10
#
# 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 tests malloc failures in concert with fuzzy SQL generation.
#
# $Id: fuzz_malloc.test,v 1.1 2007/05/30 10:36:47 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/fuzz_common.tcl
source $testdir/malloc_common.tcl
set ::REPEATS 20
#
# Usage: do_fuzzy_malloc_test <testname> ?<options>?
#
# -template
# -repeats
#
proc do_fuzzy_malloc_test {testname args} {
set ::fuzzyopts(-repeats) $::REPEATS
array set ::fuzzyopts $args
for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} {
set ::sql [subst $::fuzzyopts(-template)]
# puts $::sql
foreach {rc res} [catchsql $::sql] {}
if {$rc==0} {
do_malloc_test $testname-$ii -sqlbody $::sql
} else {
incr ii -1
}
}
}
#----------------------------------------------------------------
# Test malloc failure during parsing (and execution) of a fuzzily
# generated expressions.
#
do_fuzzy_malloc_test fuzzy_malloc-1 -template {Select [Expr]}
sqlite_malloc_fail 0
finish_test

View File

@ -14,7 +14,7 @@
# special feature is used to see what happens in the library if a malloc
# were to really fail due to an out-of-memory situation.
#
# $Id: malloc.test,v 1.41 2007/04/19 11:09:02 danielk1977 Exp $
# $Id: malloc.test,v 1.42 2007/05/30 10:36:47 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -27,101 +27,7 @@ if {[info command sqlite_malloc_stat]==""} {
return
}
# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
# -tclprep TCL script to run to prepare test.
# -sqlprep SQL script to run to prepare test.
# -tclbody TCL script to run with malloc failure simulation.
# -sqlbody TCL script to run with malloc failure simulation.
# -cleanup TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
array unset ::mallocopts
array set ::mallocopts $args
set ::go 1
for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
do_test malloc-$tn.$::n {
# Remove all traces of database files test.db and test2.db from the files
# system. Then open (empty database) "test.db" with the handle [db].
#
sqlite_malloc_fail 0
catch {db close}
catch {file delete -force test.db}
catch {file delete -force test.db-journal}
catch {file delete -force test2.db}
catch {file delete -force test2.db-journal}
catch {sqlite3 db test.db}
set ::DB [sqlite3_connection_pointer db]
# Execute any -tclprep and -sqlprep scripts.
#
if {[info exists ::mallocopts(-tclprep)]} {
eval $::mallocopts(-tclprep)
}
if {[info exists ::mallocopts(-sqlprep)]} {
execsql $::mallocopts(-sqlprep)
}
# Now set the ${::n}th malloc() to fail and execute the -tclbody and
# -sqlbody scripts.
#
sqlite_malloc_fail $::n
set ::mallocbody {}
if {[info exists ::mallocopts(-tclbody)]} {
append ::mallocbody "$::mallocopts(-tclbody)\n"
}
if {[info exists ::mallocopts(-sqlbody)]} {
append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
}
set v [catch $::mallocbody msg]
# If the test fails (if $v!=0) and the database connection actually
# exists, make sure the failure code is SQLITE_NOMEM.
if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
&& [db errorcode]!=7} {
set v 999
}
set leftover [lindex [sqlite_malloc_stat] 2]
if {$leftover>0} {
if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"}
set ::go 0
if {$v} {
puts "\nError message returned: $msg"
} else {
set v {1 1}
}
} else {
set v2 [expr {$msg=="" || $msg=="out of memory"}]
if {!$v2} {puts "\nError message returned: $msg"}
lappend v $v2
}
} {1 1}
if {[info exists ::mallocopts(-cleanup)]} {
catch [list uplevel #0 $::mallocopts(-cleanup)] msg
}
}
unset ::mallocopts
}
source $testdir/malloc_common.tcl
do_malloc_test 1 -tclprep {
db close

35
test/mallocB.test Normal file
View File

@ -0,0 +1,35 @@
# 2007 May 30
#
# 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 additional out-of-memory checks (see malloc.tcl).
# These were all discovered by fuzzy generation of SQL. Apart from
# that they have little in common.
#
# $Id: mallocB.test,v 1.1 2007/05/30 10:36:47 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
# Only run these tests if memory debugging is turned on.
#
if {[info command sqlite_malloc_stat]==""} {
puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
finish_test
return
}
do_malloc_test mallocB-1 -sqlbody {SELECT - 456}
do_malloc_test mallocB-2 -sqlbody {SELECT - 456.1}
do_malloc_test mallocB-3 -sqlbody {SELECT random()}
do_malloc_test mallocB-4 -sqlbody {SELECT zeroblob(1000)}
sqlite_malloc_fail 0
finish_test

101
test/malloc_common.tcl Normal file
View File

@ -0,0 +1,101 @@
# Usage: do_malloc_test <test number> <options...>
#
# The first argument, <test number>, is an integer used to name the
# tests executed by this proc. Options are as follows:
#
# -tclprep TCL script to run to prepare test.
# -sqlprep SQL script to run to prepare test.
# -tclbody TCL script to run with malloc failure simulation.
# -sqlbody TCL script to run with malloc failure simulation.
# -cleanup TCL script to run after the test.
#
# This command runs a series of tests to verify SQLite's ability
# to handle an out-of-memory condition gracefully. It is assumed
# that if this condition occurs a malloc() call will return a
# NULL pointer. Linux, for example, doesn't do that by default. See
# the "BUGS" section of malloc(3).
#
# Each iteration of a loop, the TCL commands in any argument passed
# to the -tclbody switch, followed by the SQL commands in any argument
# passed to the -sqlbody switch are executed. Each iteration the
# Nth call to sqliteMalloc() is made to fail, where N is increased
# each time the loop runs starting from 1. When all commands execute
# successfully, the loop ends.
#
proc do_malloc_test {tn args} {
array unset ::mallocopts
array set ::mallocopts $args
if {[string is integer $tn]} {
set tn malloc-$tn
}
set ::go 1
for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
do_test $tn.$::n {
# Remove all traces of database files test.db and test2.db from the files
# system. Then open (empty database) "test.db" with the handle [db].
#
sqlite_malloc_fail 0
catch {db close}
catch {file delete -force test.db}
catch {file delete -force test.db-journal}
catch {file delete -force test2.db}
catch {file delete -force test2.db-journal}
catch {sqlite3 db test.db}
set ::DB [sqlite3_connection_pointer db]
# Execute any -tclprep and -sqlprep scripts.
#
if {[info exists ::mallocopts(-tclprep)]} {
eval $::mallocopts(-tclprep)
}
if {[info exists ::mallocopts(-sqlprep)]} {
execsql $::mallocopts(-sqlprep)
}
# Now set the ${::n}th malloc() to fail and execute the -tclbody and
# -sqlbody scripts.
#
sqlite_malloc_fail $::n
set ::mallocbody {}
if {[info exists ::mallocopts(-tclbody)]} {
append ::mallocbody "$::mallocopts(-tclbody)\n"
}
if {[info exists ::mallocopts(-sqlbody)]} {
append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
}
set v [catch $::mallocbody msg]
# If the test fails (if $v!=0) and the database connection actually
# exists, make sure the failure code is SQLITE_NOMEM.
if {$v && [info command db]=="db" && [info exists ::mallocopts(-sqlbody)]
&& [db errorcode]!=7} {
set v 999
}
set leftover [lindex [sqlite_malloc_stat] 2]
if {$leftover>0} {
if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"}
set ::go 0
if {$v} {
puts "\nError message returned: $msg"
} else {
set v {1 1}
}
} else {
set v2 [expr {$msg=="" || $msg=="out of memory"}]
if {!$v2} {puts "\nError message returned: $msg"}
lappend v $v2
}
} {1 1}
if {[info exists ::mallocopts(-cleanup)]} {
catch [list uplevel #0 $::mallocopts(-cleanup)] msg
}
}
unset ::mallocopts
}

View File

@ -6,7 +6,7 @@
#***********************************************************************
# This file runs all tests.
#
# $Id: quick.test,v 1.57 2007/05/30 08:18:04 danielk1977 Exp $
# $Id: quick.test,v 1.58 2007/05/30 10:36:47 danielk1977 Exp $
proc lshift {lvar} {
upvar $lvar l
@ -46,6 +46,7 @@ set EXCLUDE {
crash2.test
exclusive3.test
fuzz.test
fuzz_malloc.test
in2.test
loadext.test
malloc.test

View File

@ -11,7 +11,7 @@
# This file is the driver for the "soak" tests. It is a peer of the
# quick.test and all.test scripts.
#
# $Id: soak.test,v 1.1 2007/05/30 08:18:04 danielk1977 Exp $
# $Id: soak.test,v 1.2 2007/05/30 10:36:47 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -60,6 +60,7 @@ set argv [list]
#
set SOAKTESTS {
fuzz.test
fuzz_malloc.test
trans.test
}