cipher_migrate will use MoveFileExW on windows platforms
[sqlcipher.git] / test / randexpr1.tcl
blob37ebf531e8509ebd61fa8498a1ab4f8b0e743977
1 # Run this TCL script to generate thousands of test cases containing
2 # complicated expressions.
4 # The generated tests are intended to verify expression evaluation
5 # in SQLite against expression evaluation TCL.
8 # Terms of the $intexpr list each contain two sub-terms.
10 # * An SQL expression template
11 # * The equivalent TCL expression
13 # EXPR is replaced by an integer subexpression. BOOL is replaced
14 # by a boolean subexpression.
16 set intexpr {
17 {11 wide(11)}
18 {13 wide(13)}
19 {17 wide(17)}
20 {19 wide(19)}
21 {a $a}
22 {b $b}
23 {c $c}
24 {d $d}
25 {e $e}
26 {f $f}
27 {t1.a $a}
28 {t1.b $b}
29 {t1.c $c}
30 {t1.d $d}
31 {t1.e $e}
32 {t1.f $f}
33 {(EXPR) (EXPR)}
34 {{ -EXPR} {-EXPR}}
35 {+EXPR +EXPR}
36 {~EXPR ~EXPR}
37 {EXPR+EXPR EXPR+EXPR}
38 {EXPR-EXPR EXPR-EXPR}
39 {EXPR*EXPR EXPR*EXPR}
40 {EXPR+EXPR EXPR+EXPR}
41 {EXPR-EXPR EXPR-EXPR}
42 {EXPR*EXPR EXPR*EXPR}
43 {EXPR+EXPR EXPR+EXPR}
44 {EXPR-EXPR EXPR-EXPR}
45 {EXPR*EXPR EXPR*EXPR}
46 {{EXPR | EXPR} {EXPR | EXPR}}
47 {(abs(EXPR)/abs(EXPR)) (abs(EXPR)/abs(EXPR))}
49 {case when BOOL then EXPR else EXPR end}
50 {((BOOL)?EXPR:EXPR)}
53 {case when BOOL then EXPR when BOOL then EXPR else EXPR end}
54 {((BOOL)?EXPR:((BOOL)?EXPR:EXPR))}
57 {case EXPR when EXPR then EXPR else EXPR end}
58 {(((EXPR)==(EXPR))?EXPR:EXPR)}
61 {(select AGG from t1)}
62 {(AGG)}
65 {coalesce((select max(EXPR) from t1 where BOOL),EXPR)}
66 {[coalesce_subquery [expr {EXPR}] [expr {BOOL}] [expr {EXPR}]]}
69 {coalesce((select EXPR from t1 where BOOL),EXPR)}
70 {[coalesce_subquery [expr {EXPR}] [expr {BOOL}] [expr {EXPR}]]}
74 # The $boolexpr list contains terms that show both an SQL boolean
75 # expression and its equivalent TCL.
77 set boolexpr {
78 {EXPR=EXPR ((EXPR)==(EXPR))}
79 {EXPR<EXPR ((EXPR)<(EXPR))}
80 {EXPR>EXPR ((EXPR)>(EXPR))}
81 {EXPR<=EXPR ((EXPR)<=(EXPR))}
82 {EXPR>=EXPR ((EXPR)>=(EXPR))}
83 {EXPR<>EXPR ((EXPR)!=(EXPR))}
85 {EXPR between EXPR and EXPR}
86 {[betweenop [expr {EXPR}] [expr {EXPR}] [expr {EXPR}]]}
89 {EXPR not between EXPR and EXPR}
90 {(![betweenop [expr {EXPR}] [expr {EXPR}] [expr {EXPR}]])}
93 {EXPR in (EXPR,EXPR,EXPR)}
94 {([inop [expr {EXPR}] [expr {EXPR}] [expr {EXPR}] [expr {EXPR}]])}
97 {EXPR not in (EXPR,EXPR,EXPR)}
98 {(![inop [expr {EXPR}] [expr {EXPR}] [expr {EXPR}] [expr {EXPR}]])}
101 {EXPR in (select EXPR from t1 union select EXPR from t1)}
102 {[inop [expr {EXPR}] [expr {EXPR}] [expr {EXPR}]]}
105 {EXPR in (select AGG from t1 union select AGG from t1)}
106 {[inop [expr {EXPR}] [expr {AGG}] [expr {AGG}]]}
109 {exists(select 1 from t1 where BOOL)}
110 {(BOOL)}
113 {not exists(select 1 from t1 where BOOL)}
114 {!(BOOL)}
116 {{not BOOL} !BOOL}
117 {{BOOL and BOOL} {BOOL tcland BOOL}}
118 {{BOOL or BOOL} {BOOL || BOOL}}
119 {{BOOL and BOOL} {BOOL tcland BOOL}}
120 {{BOOL or BOOL} {BOOL || BOOL}}
121 {(BOOL) (BOOL)}
122 {(BOOL) (BOOL)}
125 # Aggregate expressions
127 set aggexpr {
128 {count(*) wide(1)}
129 {{count(distinct EXPR)} {[one {EXPR}]}}
130 {{cast(avg(EXPR) AS integer)} (EXPR)}
131 {min(EXPR) (EXPR)}
132 {max(EXPR) (EXPR)}
133 {(AGG) (AGG)}
134 {{ -AGG} {-AGG}}
135 {+AGG +AGG}
136 {~AGG ~AGG}
137 {abs(AGG) abs(AGG)}
138 {AGG+AGG AGG+AGG}
139 {AGG-AGG AGG-AGG}
140 {AGG*AGG AGG*AGG}
141 {{AGG | AGG} {AGG | AGG}}
143 {case AGG when AGG then AGG else AGG end}
144 {(((AGG)==(AGG))?AGG:AGG)}
148 # Convert a string containing EXPR, AGG, and BOOL into a string
149 # that contains nothing but X, Y, and Z.
151 proc extract_vars {a} {
152 regsub -all {EXPR} $a X a
153 regsub -all {AGG} $a Y a
154 regsub -all {BOOL} $a Z a
155 regsub -all {[^XYZ]} $a {} a
156 return $a
160 # Test all templates to make sure the number of EXPR, AGG, and BOOL
161 # expressions match.
163 foreach term [concat $aggexpr $intexpr $boolexpr] {
164 foreach {a b} $term break
165 if {[extract_vars $a]!=[extract_vars $b]} {
166 error "mismatch: $term"
170 # Generate a random expression according to the templates given above.
171 # If the argument is EXPR or omitted, then an integer expression is
172 # generated. If the argument is BOOL then a boolean expression is
173 # produced.
175 proc generate_expr {{e EXPR}} {
176 set tcle $e
177 set ne [llength $::intexpr]
178 set nb [llength $::boolexpr]
179 set na [llength $::aggexpr]
180 set div 2
181 set mx 50
182 set i 0
183 while {1} {
184 set cnt 0
185 set re [lindex $::intexpr [expr {int(rand()*$ne)}]]
186 incr cnt [regsub {EXPR} $e [lindex $re 0] e]
187 regsub {EXPR} $tcle [lindex $re 1] tcle
188 set rb [lindex $::boolexpr [expr {int(rand()*$nb)}]]
189 incr cnt [regsub {BOOL} $e [lindex $rb 0] e]
190 regsub {BOOL} $tcle [lindex $rb 1] tcle
191 set ra [lindex $::aggexpr [expr {int(rand()*$na)}]]
192 incr cnt [regsub {AGG} $e [lindex $ra 0] e]
193 regsub {AGG} $tcle [lindex $ra 1] tcle
195 if {$cnt==0} break
196 incr i $cnt
198 set v1 [extract_vars $e]
199 if {$v1!=[extract_vars $tcle]} {
200 exit
203 if {$i+[string length $v1]>=$mx} {
204 set ne [expr {$ne/$div}]
205 set nb [expr {$nb/$div}]
206 set na [expr {$na/$div}]
207 set div 1
208 set mx [expr {$mx*1000}]
211 regsub -all { tcland } $tcle { \&\& } tcle
212 return [list $e $tcle]
215 # Implementation of routines used to implement the IN and BETWEEN
216 # operators.
217 proc inop {lhs args} {
218 foreach a $args {
219 if {$a==$lhs} {return 1}
221 return 0
223 proc betweenop {lhs first second} {
224 return [expr {$lhs>=$first && $lhs<=$second}]
226 proc coalesce_subquery {a b e} {
227 if {$b} {
228 return $a
229 } else {
230 return $e
233 proc one {args} {
234 return 1
237 # Begin generating the test script:
239 puts {# 2008 December 16
241 # The author disclaims copyright to this source code. In place of
242 # a legal notice, here is a blessing:
244 # May you do good and not evil.
245 # May you find forgiveness for yourself and forgive others.
246 # May you share freely, never taking more than you give.
248 #***********************************************************************
249 # This file implements regression tests for SQLite library.
251 # This file tests randomly generated SQL expressions. The expressions
252 # are generated by a TCL script. The same TCL script also computes the
253 # correct value of the expression. So, from one point of view, this
254 # file verifies the expression evaluation logic of SQLite against the
255 # expression evaluation logic of TCL.
257 # An early version of this script is how bug #3541 was detected.
259 # $Id: randexpr1.tcl,v 1.1 2008/12/15 16:33:30 drh Exp $
260 set testdir [file dirname $argv0]
261 source $testdir/tester.tcl
263 # Create test data
265 do_test randexpr1-1.1 {
266 db eval {
267 CREATE TABLE t1(a,b,c,d,e,f);
268 INSERT INTO t1 VALUES(100,200,300,400,500,600);
269 SELECT * FROM t1
271 } {100 200 300 400 500 600}
274 # Test data for TCL evaluation.
276 set a [expr {wide(100)}]
277 set b [expr {wide(200)}]
278 set c [expr {wide(300)}]
279 set d [expr {wide(400)}]
280 set e [expr {wide(500)}]
281 set f [expr {wide(600)}]
283 # A procedure to generate a test case.
285 set tn 0
286 proc make_test_case {sql result} {
287 global tn
288 incr tn
289 puts "do_test randexpr-2.$tn {\n db eval {$sql}\n} {$result}"
292 # Generate many random test cases.
294 expr srand(0)
295 for {set i 0} {$i<1000} {incr i} {
296 while {1} {
297 foreach {sqle tcle} [generate_expr EXPR] break;
298 if {[catch {expr $tcle} ans]} {
299 #puts stderr [list $tcle]
300 #puts stderr ans=$ans
301 if {![regexp {divide by zero} $ans]} exit
302 continue
304 set len [string length $sqle]
305 if {$len<100 || $len>2000} continue
306 if {[info exists seen($sqle)]} continue
307 set seen($sqle) 1
308 break
310 while {1} {
311 foreach {sqlb tclb} [generate_expr BOOL] break;
312 if {[catch {expr $tclb} bans]} {
313 #puts stderr [list $tclb]
314 #puts stderr bans=$bans
315 if {![regexp {divide by zero} $bans]} exit
316 continue
318 break
320 if {$bans} {
321 make_test_case "SELECT $sqle FROM t1 WHERE $sqlb" $ans
322 make_test_case "SELECT $sqle FROM t1 WHERE NOT ($sqlb)" {}
323 } else {
324 make_test_case "SELECT $sqle FROM t1 WHERE $sqlb" {}
325 make_test_case "SELECT $sqle FROM t1 WHERE NOT ($sqlb)" $ans
327 if {[regexp { \| } $sqle]} {
328 regsub -all { \| } $sqle { \& } sqle
329 regsub -all { \| } $tcle { \& } tcle
330 if {[catch {expr $tcle} ans]==0} {
331 if {$bans} {
332 make_test_case "SELECT $sqle FROM t1 WHERE $sqlb" $ans
333 } else {
334 make_test_case "SELECT $sqle FROM t1 WHERE NOT ($sqlb)" $ans
340 # Terminate the test script
342 puts {finish_test}