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