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.
46 {{EXPR | EXPR
} {EXPR | EXPR
}}
47 {(abs
(EXPR
)/abs
(EXPR
)) (abs
(EXPR
)/abs
(EXPR
))}
49 {case when BOOL then EXPR
else EXPR end
}
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
)}
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.
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
)}
113 {not exists
(select
1 from t1 where 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
}}
125 # Aggregate expressions
129 {{count
(distinct EXPR
)} {[one
{EXPR
}]}}
130 {{cast
(avg
(EXPR
) AS integer
)} (EXPR
)}
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
160 # Test all templates to make sure the number of EXPR, AGG, and BOOL
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
175 proc generate_expr
{{e EXPR
}} {
177 set ne
[llength $::intexpr]
178 set nb
[llength $::boolexpr]
179 set na
[llength $::aggexpr]
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
198 set v1
[extract_vars
$e]
199 if {$v1!=[extract_vars
$tcle]} {
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}]
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
217 proc inop
{lhs args
} {
219 if {$a==$lhs} {return 1}
223 proc betweenop
{lhs first second
} {
224 return [expr {$lhs>=$first && $lhs<=$second}]
226 proc coalesce_subquery
{a b e
} {
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
265 do_test randexpr1-1.1
{
267 CREATE TABLE t1
(a
,b
,c
,d
,e
,f
);
268 INSERT INTO t1 VALUES
(100,200,300,400,500,600);
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.
286 proc make_test_case
{sql result
} {
289 puts "do_test randexpr-2.$tn {\n db eval {$sql}\n} {$result}"
292 # Generate many random test cases.
295 for {set i
0} {$i<1000} {incr i
} {
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
304 set len
[string length
$sqle]
305 if {$len<100 ||
$len>2000} continue
306 if {[info exists seen
($sqle)]} continue
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
321 make_test_case
"SELECT $sqle FROM t1 WHERE $sqlb" $ans
322 make_test_case
"SELECT $sqle FROM t1 WHERE NOT ($sqlb)" {}
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} {
332 make_test_case
"SELECT $sqle FROM t1 WHERE $sqlb" $ans
334 make_test_case
"SELECT $sqle FROM t1 WHERE NOT ($sqlb)" $ans
340 # Terminate the test script