3 # The author disclaims copyright to this source code. In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
12 # $Id: fuzz_common.tcl,v 1.2 2009/01/05 19:36:30 drh Exp $
14 proc fuzz
{TemplateList
} {
15 set n
[llength $TemplateList]
16 set i
[expr {int
(rand
()*$n)}]
17 set r
[uplevel 1 subst -novar [list [lindex $TemplateList $i]]]
19 string map
{"\n" " "} $r
22 # Fuzzy generation primitives:
33 # Returns a string representing an SQL literal.
38 2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649
39 'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection'
48 # Returns a string containing an SQL unary operator (e.g. "+" or "NOT").
51 set TemplateList
{+ - NOT ~
}
55 # Returns a string containing an SQL binary operator (e.g. "*" or "/").
59 ||
* / % + - << >> & |
< <= > >= = == != <> AND OR
65 # Return the complete text of an SQL expression.
68 proc Expr
{ {c
{}} } {
71 set TemplateList
[concat $c $c $c {[Literal
]}]
72 if {$::ExprDepth < 3} {
73 lappend TemplateList
\
74 {[Expr
$c] [BinaryOp
] [Expr
$c]} \
75 {[UnaryOp
] [Expr
$c]} \
78 {CAST
([Expr
$c] AS blob
)} \
79 {CAST
([Expr
$c] AS
text)} \
80 {CAST
([Expr
$c] AS integer
)} \
81 {CAST
([Expr
$c] AS real
)} \
83 {coalesce
([Expr
], [Expr
])} \
90 {randomblob
(min
(max
([Expr
],1), 500))} \
92 {substr
([Expr
],[Expr
],[Expr
])} \
93 {CASE WHEN
[Expr
$c] THEN
[Expr
$c] ELSE
[Expr
$c] END
} \
94 {[Literal
]} {[Literal
]} {[Literal
]} \
95 {[Literal
]} {[Literal
]} {[Literal
]} \
96 {[Literal
]} {[Literal
]} {[Literal
]} \
97 {[Literal
]} {[Literal
]} {[Literal
]}
99 if {$::SelectDepth < 4} {
100 lappend TemplateList
\
102 {[Expr
$c] IN
([Select
1])} \
103 {[Expr
$c] NOT IN
([Select
1])} \
104 {EXISTS
([Select
1])} \
106 set res
[fuzz
$TemplateList]
111 # Return a valid table name.
113 set ::TableList [list]
115 set TemplateList
[concat sqlite_master
$::TableList]
121 # "SELECT DISTINCT", "SELECT ALL" or "SELECT"
132 # Return a result set for a SELECT statement.
134 proc ResultSet
{{nRes
0} {c
""}} {
136 set nRes
[expr {rand
()*2 + 1}]
140 for {set ii
0} {$ii < $nRes} {incr ii
} {
141 lappend aRes
[Expr
$c]
148 set ::ColumnList [list]
149 proc SimpleSelect
{{nRes
0}} {
152 {[SelectKw
] [ResultSet
$nRes]}
155 # The ::SelectDepth variable contains the number of ancestor SELECT
156 # statements (i.e. for a top level SELECT it is set to 0, for a
157 # sub-select 1, for a sub-select of a sub-select 2 etc.).
159 # If this is already greater than 3, do not generate a complicated
160 # SELECT statement. This tends to cause parser stack overflow (too
161 # boring to bother with).
163 if {$::SelectDepth < 4} {
164 lappend TemplateList
\
165 {[SelectKw
] [ResultSet
$nRes $::ColumnList] FROM
([Select
])} \
166 {[SelectKw
] [ResultSet
$nRes] FROM
([Select
])} \
167 {[SelectKw
] [ResultSet
$nRes $::ColumnList] FROM
[Table
]} \
169 [SelectKw
] [ResultSet
$nRes $::ColumnList]
176 lappend TemplateList
\
177 {[SelectKw
] * FROM
([Select
])} \
178 {[SelectKw
] * FROM
[Table
]} \
179 {[SelectKw
] * FROM
[Table
] WHERE
[Expr
$::ColumnList]} \
182 FROM
[Table
],[Table
] AS t2
183 WHERE
[Expr
$::ColumnList]
186 FROM
[Table
] LEFT OUTER JOIN
[Table
] AS t2
187 ON
[Expr
$::ColumnList]
188 WHERE
[Expr
$::ColumnList]
196 # Return a SELECT statement.
198 # If boolean parameter $isExpr is set to true, make sure the
199 # returned SELECT statement returns a single column of data.
201 proc Select
{{nMulti
0}} {
203 {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]}
204 {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]}
205 {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]}
206 {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]} {[SimpleSelect
$nMulti]}
207 {[SimpleSelect
$nMulti] ORDER BY
[Expr
] DESC
}
208 {[SimpleSelect
$nMulti] ORDER BY
[Expr
] ASC
}
209 {[SimpleSelect
$nMulti] ORDER BY
[Expr
] ASC
, [Expr
] DESC
}
210 {[SimpleSelect
$nMulti] ORDER BY
[Expr
] LIMIT
[Expr
] OFFSET
[Expr
]}
213 if {$::SelectDepth < 4} {
215 set nMulti
[expr {(rand
()*2)+1}]
217 lappend TemplateList
\
218 {[SimpleSelect
$nMulti] UNION
[Select
$nMulti]} \
219 {[SimpleSelect
$nMulti] UNION ALL
[Select
$nMulti]} \
220 {[SimpleSelect
$nMulti] EXCEPT
[Select
$nMulti]} \
221 {[SimpleSelect
$nMulti] INTERSECT
[Select
$nMulti]}
225 set res
[fuzz
$TemplateList]
226 incr ::SelectDepth -1
230 # Generate and return a fuzzy INSERT statement.
234 {INSERT INTO
[Table
] VALUES
([Expr
], [Expr
], [Expr
]);}
235 {INSERT INTO
[Table
] VALUES
([Expr
], [Expr
], [Expr
], [Expr
]);}
236 {INSERT INTO
[Table
] VALUES
([Expr
], [Expr
]);}
245 # Generate and return a fuzzy UPDATE statement.
250 SET
[Column
] = [Expr
$::ColumnList]
251 WHERE
[Expr
$::ColumnList]}
258 {DELETE FROM
[Table
] WHERE
[Expr
$::ColumnList]}
273 # Return an identifier. This just chooses randomly from a fixed set
277 This just chooses randomly a fixed
278 We would also thank the developers
279 for their analysis Samba
285 # Use a large value for $::SelectDepth, because sub-selects are
286 # not allowed in expressions used by CHECK constraints.
288 set sd
$::SelectDepth
289 set ::SelectDepth 500
294 set res
[fuzz
$TemplateList]
295 set ::SelectDepth $sd
301 {INTEGER PRIMARY KEY
}
310 {DROP TABLE IF EXISTS
[Identifier
]}
317 {CREATE VIEW
[Identifier
] AS
[Select
]}
323 {DROP VIEW IF EXISTS
[Identifier
]}
328 proc CreateTable
{} {
330 {CREATE TABLE
[Identifier
]([Identifier
] [Coltype
], [Identifier
] [Coltype
])}
331 {CREATE TEMP TABLE
[Identifier
]([Identifier
] [Coltype
])}
336 proc CreateOrDropTableOrView
{} {
346 ########################################################################
348 set ::log [open fuzzy.log w
]
351 # Usage: do_fuzzy_test <testname> ?<options>?
357 proc do_fuzzy_test
{testname args
} {
358 set ::fuzzyopts(-errorlist) [list]
359 set ::fuzzyopts(-repeats) $::REPEATS
360 array set ::fuzzyopts $args
362 lappend ::fuzzyopts(-errorlist) {parser stack overflow
}
363 lappend ::fuzzyopts(-errorlist) {ORDER BY
}
364 lappend ::fuzzyopts(-errorlist) {GROUP BY
}
365 lappend ::fuzzyopts(-errorlist) {datatype mismatch
}
367 for {set ii
0} {$ii < $::fuzzyopts(-repeats)} {incr ii
} {
368 do_test
${testname
}.
$ii {
369 set ::sql [subst $::fuzzyopts(-template)]
372 set rc
[catch {execsql
$::sql} msg
]
376 foreach error $::fuzzyopts(-errorlist) {
377 if {[string first
$error $msg]>=0} {