resolve upstream merge conflict in distclean
[sqlcipher.git] / test / fuzz_common.tcl
blob520555bd29b09011035f09e93b81186fbb1015ed
1 # 2007 May 10
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:
24 # Literal
25 # UnaryOp
26 # BinaryOp
27 # Expr
28 # Table
29 # Select
30 # Insert
33 # Returns a string representing an SQL literal.
35 proc Literal {} {
36 set TemplateList {
37 456 0 -456 1 -1
38 2147483648 2147483647 2147483649 -2147483647 -2147483648 -2147483649
39 'The' 'first' 'experiments' 'in' 'hardware' 'fault' 'injection'
40 zeroblob(1000)
41 NULL
42 56.1 -56.1
43 123456789.1234567899
45 fuzz $TemplateList
48 # Returns a string containing an SQL unary operator (e.g. "+" or "NOT").
50 proc UnaryOp {} {
51 set TemplateList {+ - NOT ~}
52 fuzz $TemplateList
55 # Returns a string containing an SQL binary operator (e.g. "*" or "/").
57 proc BinaryOp {} {
58 set TemplateList {
59 || * / % + - << >> & | < <= > >= = == != <> AND OR
60 LIKE GLOB {NOT LIKE}
62 fuzz $TemplateList
65 # Return the complete text of an SQL expression.
67 set ::ExprDepth 0
68 proc Expr { {c {}} } {
69 incr ::ExprDepth
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]} \
76 {[Expr $c] ISNULL} \
77 {[Expr $c] NOTNULL} \
78 {CAST([Expr $c] AS blob)} \
79 {CAST([Expr $c] AS text)} \
80 {CAST([Expr $c] AS integer)} \
81 {CAST([Expr $c] AS real)} \
82 {abs([Expr])} \
83 {coalesce([Expr], [Expr])} \
84 {hex([Expr])} \
85 {length([Expr])} \
86 {lower([Expr])} \
87 {upper([Expr])} \
88 {quote([Expr])} \
89 {random()} \
90 {randomblob(min(max([Expr],1), 500))} \
91 {typeof([Expr])} \
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 \
101 {([Select 1])} \
102 {[Expr $c] IN ([Select 1])} \
103 {[Expr $c] NOT IN ([Select 1])} \
104 {EXISTS ([Select 1])} \
106 set res [fuzz $TemplateList]
107 incr ::ExprDepth -1
108 return $res
111 # Return a valid table name.
113 set ::TableList [list]
114 proc Table {} {
115 set TemplateList [concat sqlite_master $::TableList]
116 fuzz $TemplateList
119 # Return one of:
121 # "SELECT DISTINCT", "SELECT ALL" or "SELECT"
123 proc SelectKw {} {
124 set TemplateList {
125 "SELECT DISTINCT"
126 "SELECT ALL"
127 "SELECT"
129 fuzz $TemplateList
132 # Return a result set for a SELECT statement.
134 proc ResultSet {{nRes 0} {c ""}} {
135 if {$nRes == 0} {
136 set nRes [expr {rand()*2 + 1}]
139 set aRes [list]
140 for {set ii 0} {$ii < $nRes} {incr ii} {
141 lappend aRes [Expr $c]
144 join $aRes ", "
147 set ::SelectDepth 0
148 set ::ColumnList [list]
149 proc SimpleSelect {{nRes 0}} {
151 set TemplateList {
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]
170 FROM ([Select])
171 GROUP BY [Expr]
172 HAVING [Expr]
175 if {0 == $nRes} {
176 lappend TemplateList \
177 {[SelectKw] * FROM ([Select])} \
178 {[SelectKw] * FROM [Table]} \
179 {[SelectKw] * FROM [Table] WHERE [Expr $::ColumnList]} \
181 [SelectKw] *
182 FROM [Table],[Table] AS t2
183 WHERE [Expr $::ColumnList]
185 [SelectKw] *
186 FROM [Table] LEFT OUTER JOIN [Table] AS t2
187 ON [Expr $::ColumnList]
188 WHERE [Expr $::ColumnList]
193 fuzz $TemplateList
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}} {
202 set TemplateList {
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} {
214 if {$nMulti == 0} {
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]}
224 incr ::SelectDepth
225 set res [fuzz $TemplateList]
226 incr ::SelectDepth -1
227 set res
230 # Generate and return a fuzzy INSERT statement.
232 proc Insert {} {
233 set TemplateList {
234 {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr]);}
235 {INSERT INTO [Table] VALUES([Expr], [Expr], [Expr], [Expr]);}
236 {INSERT INTO [Table] VALUES([Expr], [Expr]);}
238 fuzz $TemplateList
241 proc Column {} {
242 fuzz $::ColumnList
245 # Generate and return a fuzzy UPDATE statement.
247 proc Update {} {
248 set TemplateList {
249 {UPDATE [Table]
250 SET [Column] = [Expr $::ColumnList]
251 WHERE [Expr $::ColumnList]}
253 fuzz $TemplateList
256 proc Delete {} {
257 set TemplateList {
258 {DELETE FROM [Table] WHERE [Expr $::ColumnList]}
260 fuzz $TemplateList
263 proc Statement {} {
264 set TemplateList {
265 {[Update]}
266 {[Insert]}
267 {[Select]}
268 {[Delete]}
270 fuzz $TemplateList
273 # Return an identifier. This just chooses randomly from a fixed set
274 # of strings.
275 proc Identifier {} {
276 set TemplateList {
277 This just chooses randomly a fixed
278 We would also thank the developers
279 for their analysis Samba
281 fuzz $TemplateList
284 proc Check {} {
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
290 set TemplateList {
292 {CHECK ([Expr])}
294 set res [fuzz $TemplateList]
295 set ::SelectDepth $sd
296 set res
299 proc Coltype {} {
300 set TemplateList {
301 {INTEGER PRIMARY KEY}
302 {VARCHAR [Check]}
303 {PRIMARY KEY}
305 fuzz $TemplateList
308 proc DropTable {} {
309 set TemplateList {
310 {DROP TABLE IF EXISTS [Identifier]}
312 fuzz $TemplateList
315 proc CreateView {} {
316 set TemplateList {
317 {CREATE VIEW [Identifier] AS [Select]}
319 fuzz $TemplateList
321 proc DropView {} {
322 set TemplateList {
323 {DROP VIEW IF EXISTS [Identifier]}
325 fuzz $TemplateList
328 proc CreateTable {} {
329 set TemplateList {
330 {CREATE TABLE [Identifier]([Identifier] [Coltype], [Identifier] [Coltype])}
331 {CREATE TEMP TABLE [Identifier]([Identifier] [Coltype])}
333 fuzz $TemplateList
336 proc CreateOrDropTableOrView {} {
337 set TemplateList {
338 {[CreateTable]}
339 {[DropTable]}
340 {[CreateView]}
341 {[DropView]}
343 fuzz $TemplateList
346 ########################################################################
348 set ::log [open fuzzy.log w]
351 # Usage: do_fuzzy_test <testname> ?<options>?
353 # -template
354 # -errorlist
355 # -repeats
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}
366 lappend ::fuzzyopts(-errorlist) {non-deterministic functions prohibited}
368 for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} {
369 do_test ${testname}.$ii {
370 set ::sql [subst $::fuzzyopts(-template)]
371 puts $::log $::sql
372 flush $::log
373 set rc [catch {execsql $::sql} msg]
374 set e 1
375 if {$rc} {
376 set e 0
377 foreach error $::fuzzyopts(-errorlist) {
378 if {[string first $error $msg]>=0} {
379 set e 1
380 break
384 if {$e == 0} {
385 puts ""
386 puts $::sql
387 puts $msg
389 set e
390 } {1}