OLD | NEW |
| (Empty) |
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 # | |
12 # $Id: fuzz_common.tcl,v 1.2 2009/01/05 19:36:30 drh Exp $ | |
13 | |
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]]] | |
18 | |
19 string map {"\n" " "} $r | |
20 } | |
21 | |
22 # Fuzzy generation primitives: | |
23 # | |
24 # Literal | |
25 # UnaryOp | |
26 # BinaryOp | |
27 # Expr | |
28 # Table | |
29 # Select | |
30 # Insert | |
31 # | |
32 | |
33 # Returns a string representing an SQL literal. | |
34 # | |
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 | |
44 } | |
45 fuzz $TemplateList | |
46 } | |
47 | |
48 # Returns a string containing an SQL unary operator (e.g. "+" or "NOT"). | |
49 # | |
50 proc UnaryOp {} { | |
51 set TemplateList {+ - NOT ~} | |
52 fuzz $TemplateList | |
53 } | |
54 | |
55 # Returns a string containing an SQL binary operator (e.g. "*" or "/"). | |
56 # | |
57 proc BinaryOp {} { | |
58 set TemplateList { | |
59 || * / % + - << >> & | < <= > >= = == != <> AND OR | |
60 LIKE GLOB {NOT LIKE} | |
61 } | |
62 fuzz $TemplateList | |
63 } | |
64 | |
65 # Return the complete text of an SQL expression. | |
66 # | |
67 set ::ExprDepth 0 | |
68 proc Expr { {c {}} } { | |
69 incr ::ExprDepth | |
70 | |
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]} | |
98 } | |
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])} \ | |
105 } | |
106 set res [fuzz $TemplateList] | |
107 incr ::ExprDepth -1 | |
108 return $res | |
109 } | |
110 | |
111 # Return a valid table name. | |
112 # | |
113 set ::TableList [list] | |
114 proc Table {} { | |
115 set TemplateList [concat sqlite_master $::TableList] | |
116 fuzz $TemplateList | |
117 } | |
118 | |
119 # Return one of: | |
120 # | |
121 # "SELECT DISTINCT", "SELECT ALL" or "SELECT" | |
122 # | |
123 proc SelectKw {} { | |
124 set TemplateList { | |
125 "SELECT DISTINCT" | |
126 "SELECT ALL" | |
127 "SELECT" | |
128 } | |
129 fuzz $TemplateList | |
130 } | |
131 | |
132 # Return a result set for a SELECT statement. | |
133 # | |
134 proc ResultSet {{nRes 0} {c ""}} { | |
135 if {$nRes == 0} { | |
136 set nRes [expr {rand()*2 + 1}] | |
137 } | |
138 | |
139 set aRes [list] | |
140 for {set ii 0} {$ii < $nRes} {incr ii} { | |
141 lappend aRes [Expr $c] | |
142 } | |
143 | |
144 join $aRes ", " | |
145 } | |
146 | |
147 set ::SelectDepth 0 | |
148 set ::ColumnList [list] | |
149 proc SimpleSelect {{nRes 0}} { | |
150 | |
151 set TemplateList { | |
152 {[SelectKw] [ResultSet $nRes]} | |
153 } | |
154 | |
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.). | |
158 # | |
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). | |
162 # | |
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]} \ | |
168 { | |
169 [SelectKw] [ResultSet $nRes $::ColumnList] | |
170 FROM ([Select]) | |
171 GROUP BY [Expr] | |
172 HAVING [Expr] | |
173 } \ | |
174 | |
175 if {0 == $nRes} { | |
176 lappend TemplateList \ | |
177 {[SelectKw] * FROM ([Select])} \ | |
178 {[SelectKw] * FROM [Table]} \ | |
179 {[SelectKw] * FROM [Table] WHERE [Expr $::ColumnList]} \ | |
180 { | |
181 [SelectKw] * | |
182 FROM [Table],[Table] AS t2 | |
183 WHERE [Expr $::ColumnList] | |
184 } { | |
185 [SelectKw] * | |
186 FROM [Table] LEFT OUTER JOIN [Table] AS t2 | |
187 ON [Expr $::ColumnList] | |
188 WHERE [Expr $::ColumnList] | |
189 } | |
190 } | |
191 } | |
192 | |
193 fuzz $TemplateList | |
194 } | |
195 | |
196 # Return a SELECT statement. | |
197 # | |
198 # If boolean parameter $isExpr is set to true, make sure the | |
199 # returned SELECT statement returns a single column of data. | |
200 # | |
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]} | |
211 } | |
212 | |
213 if {$::SelectDepth < 4} { | |
214 if {$nMulti == 0} { | |
215 set nMulti [expr {(rand()*2)+1}] | |
216 } | |
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]} | |
222 } | |
223 | |
224 incr ::SelectDepth | |
225 set res [fuzz $TemplateList] | |
226 incr ::SelectDepth -1 | |
227 set res | |
228 } | |
229 | |
230 # Generate and return a fuzzy INSERT statement. | |
231 # | |
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]);} | |
237 } | |
238 fuzz $TemplateList | |
239 } | |
240 | |
241 proc Column {} { | |
242 fuzz $::ColumnList | |
243 } | |
244 | |
245 # Generate and return a fuzzy UPDATE statement. | |
246 # | |
247 proc Update {} { | |
248 set TemplateList { | |
249 {UPDATE [Table] | |
250 SET [Column] = [Expr $::ColumnList] | |
251 WHERE [Expr $::ColumnList]} | |
252 } | |
253 fuzz $TemplateList | |
254 } | |
255 | |
256 proc Delete {} { | |
257 set TemplateList { | |
258 {DELETE FROM [Table] WHERE [Expr $::ColumnList]} | |
259 } | |
260 fuzz $TemplateList | |
261 } | |
262 | |
263 proc Statement {} { | |
264 set TemplateList { | |
265 {[Update]} | |
266 {[Insert]} | |
267 {[Select]} | |
268 {[Delete]} | |
269 } | |
270 fuzz $TemplateList | |
271 } | |
272 | |
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 | |
280 } | |
281 fuzz $TemplateList | |
282 } | |
283 | |
284 proc Check {} { | |
285 # Use a large value for $::SelectDepth, because sub-selects are | |
286 # not allowed in expressions used by CHECK constraints. | |
287 # | |
288 set sd $::SelectDepth | |
289 set ::SelectDepth 500 | |
290 set TemplateList { | |
291 {} | |
292 {CHECK ([Expr])} | |
293 } | |
294 set res [fuzz $TemplateList] | |
295 set ::SelectDepth $sd | |
296 set res | |
297 } | |
298 | |
299 proc Coltype {} { | |
300 set TemplateList { | |
301 {INTEGER PRIMARY KEY} | |
302 {VARCHAR [Check]} | |
303 {PRIMARY KEY} | |
304 } | |
305 fuzz $TemplateList | |
306 } | |
307 | |
308 proc DropTable {} { | |
309 set TemplateList { | |
310 {DROP TABLE IF EXISTS [Identifier]} | |
311 } | |
312 fuzz $TemplateList | |
313 } | |
314 | |
315 proc CreateView {} { | |
316 set TemplateList { | |
317 {CREATE VIEW [Identifier] AS [Select]} | |
318 } | |
319 fuzz $TemplateList | |
320 } | |
321 proc DropView {} { | |
322 set TemplateList { | |
323 {DROP VIEW IF EXISTS [Identifier]} | |
324 } | |
325 fuzz $TemplateList | |
326 } | |
327 | |
328 proc CreateTable {} { | |
329 set TemplateList { | |
330 {CREATE TABLE [Identifier]([Identifier] [Coltype], [Identifier] [Coltype])} | |
331 {CREATE TEMP TABLE [Identifier]([Identifier] [Coltype])} | |
332 } | |
333 fuzz $TemplateList | |
334 } | |
335 | |
336 proc CreateOrDropTableOrView {} { | |
337 set TemplateList { | |
338 {[CreateTable]} | |
339 {[DropTable]} | |
340 {[CreateView]} | |
341 {[DropView]} | |
342 } | |
343 fuzz $TemplateList | |
344 } | |
345 | |
346 ######################################################################## | |
347 | |
348 set ::log [open fuzzy.log w] | |
349 | |
350 # | |
351 # Usage: do_fuzzy_test <testname> ?<options>? | |
352 # | |
353 # -template | |
354 # -errorlist | |
355 # -repeats | |
356 # | |
357 proc do_fuzzy_test {testname args} { | |
358 set ::fuzzyopts(-errorlist) [list] | |
359 set ::fuzzyopts(-repeats) $::REPEATS | |
360 array set ::fuzzyopts $args | |
361 | |
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 | |
367 for {set ii 0} {$ii < $::fuzzyopts(-repeats)} {incr ii} { | |
368 do_test ${testname}.$ii { | |
369 set ::sql [subst $::fuzzyopts(-template)] | |
370 puts $::log $::sql | |
371 flush $::log | |
372 set rc [catch {execsql $::sql} msg] | |
373 set e 1 | |
374 if {$rc} { | |
375 set e 0 | |
376 foreach error $::fuzzyopts(-errorlist) { | |
377 if {[string first $error $msg]>=0} { | |
378 set e 1 | |
379 break | |
380 } | |
381 } | |
382 } | |
383 if {$e == 0} { | |
384 puts "" | |
385 puts $::sql | |
386 puts $msg | |
387 } | |
388 set e | |
389 } {1} | |
390 } | |
391 } | |
OLD | NEW |