| OLD | NEW |
| (Empty) |
| 1 # 2010 September 24 | |
| 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 # This file implements tests to verify that the "testable statements" in | |
| 13 # the lang_select.html document are correct. | |
| 14 # | |
| 15 | |
| 16 set testdir [file dirname $argv0] | |
| 17 source $testdir/tester.tcl | |
| 18 | |
| 19 #------------------------------------------------------------------------- | |
| 20 # te_* commands: | |
| 21 # | |
| 22 # | |
| 23 # te_read_sql DB SELECT-STATEMENT | |
| 24 # te_read_tbl DB TABLENAME | |
| 25 # | |
| 26 # These two commands are used to read a dataset from the database. A dataset | |
| 27 # consists of N rows of M named columns of values each, where each value has a | |
| 28 # type (null, integer, real, text or blob) and a value within the types domain. | |
| 29 # The tcl format for a "dataset" is a list of two elements: | |
| 30 # | |
| 31 # * A list of the column names. | |
| 32 # * A list of data rows. Each row is itself a list, where each element is | |
| 33 # the contents of a column of the row. Each of these is a list of two | |
| 34 # elements, the type name and the actual value. | |
| 35 # | |
| 36 # For example, the contents of table [t1] as a dataset is: | |
| 37 # | |
| 38 # CREATE TABLE t1(a, b); | |
| 39 # INSERT INTO t1 VALUES('abc', NULL); | |
| 40 # INSERT INTO t1 VALUES(43.1, 22); | |
| 41 # | |
| 42 # {a b} {{{TEXT abc} {NULL {}}} {{REAL 43.1} {INTEGER 22}}} | |
| 43 # | |
| 44 # The [te_read_tbl] command returns a dataset read from a table. The | |
| 45 # [te_read_sql] returns the dataset that results from executing a SELECT | |
| 46 # command. | |
| 47 # | |
| 48 # | |
| 49 # te_tbljoin ?SWITCHES? LHS-TABLE RHS-TABLE | |
| 50 # te_join ?SWITCHES? LHS-DATASET RHS-DATASET | |
| 51 # | |
| 52 # This command joins the two datasets and returns the resulting dataset. If | |
| 53 # there are no switches specified, then the results is the cartesian product | |
| 54 # of the two inputs. The [te_tbljoin] command reads the left and right-hand | |
| 55 # datasets from the specified tables. The [te_join] command is passed the | |
| 56 # datasets directly. | |
| 57 # | |
| 58 # Optional switches are as follows: | |
| 59 # | |
| 60 # -on SCRIPT | |
| 61 # -using COLUMN-LIST | |
| 62 # -left | |
| 63 # | |
| 64 # The -on option specifies a tcl script that is executed for each row in the | |
| 65 # cartesian product of the two datasets. The script has 4 arguments appended | |
| 66 # to it, in the following order: | |
| 67 # | |
| 68 # * The list of column-names from the left-hand dataset. | |
| 69 # * A single row from the left-hand dataset (one "data row" list as | |
| 70 # described above. | |
| 71 # * The list of column-names from the right-hand dataset. | |
| 72 # * A single row from the right-hand dataset. | |
| 73 # | |
| 74 # The script must return a boolean value - true if the combination of rows | |
| 75 # should be included in the output dataset, or false otherwise. | |
| 76 # | |
| 77 # The -using option specifies a list of the columns from the right-hand | |
| 78 # dataset that should be omitted from the output dataset. | |
| 79 # | |
| 80 # If the -left option is present, the join is done LEFT JOIN style. | |
| 81 # Specifically, an extra row is inserted if after the -on script is run there | |
| 82 # exist rows in the left-hand dataset that have no corresponding rows in | |
| 83 # the output. See the implementation for more specific comments. | |
| 84 # | |
| 85 # | |
| 86 # te_equals ?SWITCHES? COLNAME1 COLNAME2 <-on script args> | |
| 87 # | |
| 88 # The only supported switch is "-nocase". If it is present, then text values | |
| 89 # are compared in a case-independent fashion. Otherwise, they are compared | |
| 90 # as if using the SQLite BINARY collation sequence. | |
| 91 # | |
| 92 # | |
| 93 # te_and ONSCRIPT1 ONSCRIPT2... | |
| 94 # | |
| 95 # | |
| 96 | |
| 97 | |
| 98 # | |
| 99 # te_read_tbl DB TABLENAME | |
| 100 # te_read_sql DB SELECT-STATEMENT | |
| 101 # | |
| 102 # These two procs are used to extract datasets from the database, either | |
| 103 # by reading the contents of a named table (te_read_tbl), or by executing | |
| 104 # a SELECT statement (t3_read_sql). | |
| 105 # | |
| 106 # See the comment above, describing "te_* commands", for details of the | |
| 107 # return values. | |
| 108 # | |
| 109 proc te_read_tbl {db tbl} { | |
| 110 te_read_sql $db "SELECT * FROM '$tbl'" | |
| 111 } | |
| 112 proc te_read_sql {db sql} { | |
| 113 set S [sqlite3_prepare_v2 $db $sql -1 DUMMY] | |
| 114 | |
| 115 set cols [list] | |
| 116 for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} { | |
| 117 lappend cols [sqlite3_column_name $S $i] | |
| 118 } | |
| 119 | |
| 120 set rows [list] | |
| 121 while {[sqlite3_step $S] == "SQLITE_ROW"} { | |
| 122 set r [list] | |
| 123 for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} { | |
| 124 lappend r [list [sqlite3_column_type $S $i] [sqlite3_column_text $S $i]] | |
| 125 } | |
| 126 lappend rows $r | |
| 127 } | |
| 128 sqlite3_finalize $S | |
| 129 | |
| 130 return [list $cols $rows] | |
| 131 } | |
| 132 | |
| 133 #------- | |
| 134 # Usage: te_join <table-data1> <table-data2> <join spec>... | |
| 135 # | |
| 136 # Where a join-spec is an optional list of arguments as follows: | |
| 137 # | |
| 138 # ?-left? | |
| 139 # ?-using colname-list? | |
| 140 # ?-on on-expr-proc? | |
| 141 # | |
| 142 proc te_join {data1 data2 args} { | |
| 143 | |
| 144 set testproc "" | |
| 145 set usinglist [list] | |
| 146 set isleft 0 | |
| 147 for {set i 0} {$i < [llength $args]} {incr i} { | |
| 148 set a [lindex $args $i] | |
| 149 switch -- $a { | |
| 150 -on { set testproc [lindex $args [incr i]] } | |
| 151 -using { set usinglist [lindex $args [incr i]] } | |
| 152 -left { set isleft 1 } | |
| 153 default { | |
| 154 error "Unknown argument: $a" | |
| 155 } | |
| 156 } | |
| 157 } | |
| 158 | |
| 159 set c1 [lindex $data1 0] | |
| 160 set c2 [lindex $data2 0] | |
| 161 set omitlist [list] | |
| 162 set nullrowlist [list] | |
| 163 set cret $c1 | |
| 164 | |
| 165 set cidx 0 | |
| 166 foreach col $c2 { | |
| 167 set idx [lsearch $usinglist $col] | |
| 168 if {$idx>=0} {lappend omitlist $cidx} | |
| 169 if {$idx<0} { | |
| 170 lappend nullrowlist {NULL {}} | |
| 171 lappend cret $col | |
| 172 } | |
| 173 incr cidx | |
| 174 } | |
| 175 set omitlist [lsort -integer -decreasing $omitlist] | |
| 176 | |
| 177 | |
| 178 set rret [list] | |
| 179 foreach r1 [lindex $data1 1] { | |
| 180 set one 0 | |
| 181 foreach r2 [lindex $data2 1] { | |
| 182 set ok 1 | |
| 183 if {$testproc != ""} { | |
| 184 set ok [eval $testproc [list $c1 $r1 $c2 $r2]] | |
| 185 } | |
| 186 if {$ok} { | |
| 187 set one 1 | |
| 188 foreach idx $omitlist {set r2 [lreplace $r2 $idx $idx]} | |
| 189 lappend rret [concat $r1 $r2] | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 if {$isleft && $one==0} { | |
| 194 lappend rret [concat $r1 $nullrowlist] | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 list $cret $rret | |
| 199 } | |
| 200 | |
| 201 proc te_tbljoin {db t1 t2 args} { | |
| 202 te_join [te_read_tbl $db $t1] [te_read_tbl $db $t2] {*}$args | |
| 203 } | |
| 204 | |
| 205 proc te_apply_affinity {affinity typevar valvar} { | |
| 206 upvar $typevar type | |
| 207 upvar $valvar val | |
| 208 | |
| 209 switch -- $affinity { | |
| 210 integer { | |
| 211 if {[string is double $val]} { set type REAL } | |
| 212 if {[string is wideinteger $val]} { set type INTEGER } | |
| 213 if {$type == "REAL" && int($val)==$val} { | |
| 214 set type INTEGER | |
| 215 set val [expr {int($val)}] | |
| 216 } | |
| 217 } | |
| 218 text { | |
| 219 set type TEXT | |
| 220 } | |
| 221 none { } | |
| 222 | |
| 223 default { error "invalid affinity: $affinity" } | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 #---------- | |
| 228 # te_equals ?SWITCHES? c1 c2 cols1 row1 cols2 row2 | |
| 229 # | |
| 230 proc te_equals {args} { | |
| 231 | |
| 232 if {[llength $args]<6} {error "invalid arguments to te_equals"} | |
| 233 foreach {c1 c2 cols1 row1 cols2 row2} [lrange $args end-5 end] break | |
| 234 | |
| 235 set nocase 0 | |
| 236 set affinity none | |
| 237 | |
| 238 for {set i 0} {$i < ([llength $args]-6)} {incr i} { | |
| 239 set a [lindex $args $i] | |
| 240 switch -- $a { | |
| 241 -nocase { | |
| 242 set nocase 1 | |
| 243 } | |
| 244 -affinity { | |
| 245 set affinity [string tolower [lindex $args [incr i]]] | |
| 246 } | |
| 247 default { | |
| 248 error "invalid arguments to te_equals" | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 set idx2 [if {[string is integer $c2]} { set c2 } else { lsearch $cols2 $c2 }] | |
| 254 set idx1 [if {[string is integer $c1]} { set c1 } else { lsearch $cols1 $c1 }] | |
| 255 | |
| 256 set t1 [lindex $row1 $idx1 0] | |
| 257 set t2 [lindex $row2 $idx2 0] | |
| 258 set v1 [lindex $row1 $idx1 1] | |
| 259 set v2 [lindex $row2 $idx2 1] | |
| 260 | |
| 261 te_apply_affinity $affinity t1 v1 | |
| 262 te_apply_affinity $affinity t2 v2 | |
| 263 | |
| 264 if {$t1 == "NULL" || $t2 == "NULL"} { return 0 } | |
| 265 if {$nocase && $t1 == "TEXT"} { set v1 [string tolower $v1] } | |
| 266 if {$nocase && $t2 == "TEXT"} { set v2 [string tolower $v2] } | |
| 267 | |
| 268 | |
| 269 set res [expr {$t1 == $t2 && [string equal $v1 $v2]}] | |
| 270 return $res | |
| 271 } | |
| 272 | |
| 273 proc te_false {args} { return 0 } | |
| 274 proc te_true {args} { return 1 } | |
| 275 | |
| 276 proc te_and {args} { | |
| 277 foreach a [lrange $args 0 end-4] { | |
| 278 set res [eval $a [lrange $args end-3 end]] | |
| 279 if {$res == 0} {return 0} | |
| 280 } | |
| 281 return 1 | |
| 282 } | |
| 283 | |
| 284 | |
| 285 proc te_dataset_eq {testname got expected} { | |
| 286 uplevel #0 [list do_test $testname [list set {} $got] $expected] | |
| 287 } | |
| 288 proc te_dataset_eq_unordered {testname got expected} { | |
| 289 lset got 1 [lsort [lindex $got 1]] | |
| 290 lset expected 1 [lsort [lindex $expected 1]] | |
| 291 te_dataset_eq $testname $got $expected | |
| 292 } | |
| 293 | |
| 294 proc te_dataset_ne {testname got unexpected} { | |
| 295 uplevel #0 [list do_test $testname [list string equal $got $unexpected] 0] | |
| 296 } | |
| 297 proc te_dataset_ne_unordered {testname got unexpected} { | |
| 298 lset got 1 [lsort [lindex $got 1]] | |
| 299 lset unexpected 1 [lsort [lindex $unexpected 1]] | |
| 300 te_dataset_ne $testname $got $unexpected | |
| 301 } | |
| 302 | |
| 303 | |
| 304 #------------------------------------------------------------------------- | |
| 305 # | |
| 306 proc test_join {tn sqljoin tbljoinargs} { | |
| 307 set sql [te_read_sql db "SELECT * FROM $sqljoin"] | |
| 308 set te [te_tbljoin db {*}$tbljoinargs] | |
| 309 te_dataset_eq_unordered $tn $sql $te | |
| 310 } | |
| 311 | |
| 312 drop_all_tables | |
| 313 do_execsql_test e_select-2.0 { | |
| 314 CREATE TABLE t1(a, b); | |
| 315 CREATE TABLE t2(a, b); | |
| 316 CREATE TABLE t3(b COLLATE nocase); | |
| 317 | |
| 318 INSERT INTO t1 VALUES(2, 'B'); | |
| 319 INSERT INTO t1 VALUES(1, 'A'); | |
| 320 INSERT INTO t1 VALUES(4, 'D'); | |
| 321 INSERT INTO t1 VALUES(NULL, NULL); | |
| 322 INSERT INTO t1 VALUES(3, NULL); | |
| 323 | |
| 324 INSERT INTO t2 VALUES(1, 'A'); | |
| 325 INSERT INTO t2 VALUES(2, NULL); | |
| 326 INSERT INTO t2 VALUES(5, 'E'); | |
| 327 INSERT INTO t2 VALUES(NULL, NULL); | |
| 328 INSERT INTO t2 VALUES(3, 'C'); | |
| 329 | |
| 330 INSERT INTO t3 VALUES('a'); | |
| 331 INSERT INTO t3 VALUES('c'); | |
| 332 INSERT INTO t3 VALUES('b'); | |
| 333 } {} | |
| 334 | |
| 335 foreach {tn indexes} { | |
| 336 e_select-2.1.1 { } | |
| 337 e_select-2.1.2 { CREATE INDEX i1 ON t1(a) } | |
| 338 e_select-2.1.3 { CREATE INDEX i1 ON t2(a) } | |
| 339 e_select-2.1.4 { CREATE INDEX i1 ON t3(b) } | |
| 340 } { | |
| 341 | |
| 342 catchsql { DROP INDEX i1 } | |
| 343 catchsql { DROP INDEX i2 } | |
| 344 catchsql { DROP INDEX i3 } | |
| 345 execsql $indexes | |
| 346 | |
| 347 # EVIDENCE-OF: R-49872-03192 If the join-operator is "CROSS JOIN", | |
| 348 # "INNER JOIN", "JOIN" or a comma (",") and there is no ON or USING | |
| 349 # clause, then the result of the join is simply the cartesian product of | |
| 350 # the left and right-hand datasets. | |
| 351 # | |
| 352 # EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER | |
| 353 # JOIN", "JOIN" and "," join operators. | |
| 354 # | |
| 355 # EVIDENCE-OF: R-25071-21202 The "CROSS JOIN" join operator produces the | |
| 356 # same result as the "INNER JOIN", "JOIN" and "," operators | |
| 357 # | |
| 358 test_join $tn.1.1 "t1, t2" {t1 t2} | |
| 359 test_join $tn.1.2 "t1 INNER JOIN t2" {t1 t2} | |
| 360 test_join $tn.1.3 "t1 CROSS JOIN t2" {t1 t2} | |
| 361 test_join $tn.1.4 "t1 JOIN t2" {t1 t2} | |
| 362 test_join $tn.1.5 "t2, t3" {t2 t3} | |
| 363 test_join $tn.1.6 "t2 INNER JOIN t3" {t2 t3} | |
| 364 test_join $tn.1.7 "t2 CROSS JOIN t3" {t2 t3} | |
| 365 test_join $tn.1.8 "t2 JOIN t3" {t2 t3} | |
| 366 test_join $tn.1.9 "t2, t2 AS x" {t2 t2} | |
| 367 test_join $tn.1.10 "t2 INNER JOIN t2 AS x" {t2 t2} | |
| 368 test_join $tn.1.11 "t2 CROSS JOIN t2 AS x" {t2 t2} | |
| 369 test_join $tn.1.12 "t2 JOIN t2 AS x" {t2 t2} | |
| 370 | |
| 371 # EVIDENCE-OF: R-38465-03616 If there is an ON clause then the ON | |
| 372 # expression is evaluated for each row of the cartesian product as a | |
| 373 # boolean expression. Only rows for which the expression evaluates to | |
| 374 # true are included from the dataset. | |
| 375 # | |
| 376 test_join $tn.2.1 "t1, t2 ON (t1.a=t2.a)" {t1 t2 -on {te_equals a a}} | |
| 377 test_join $tn.2.2 "t2, t1 ON (t1.a=t2.a)" {t2 t1 -on {te_equals a a}} | |
| 378 test_join $tn.2.3 "t2, t1 ON (1)" {t2 t1 -on te_true} | |
| 379 test_join $tn.2.4 "t2, t1 ON (NULL)" {t2 t1 -on te_false} | |
| 380 test_join $tn.2.5 "t2, t1 ON (1.1-1.1)" {t2 t1 -on te_false} | |
| 381 test_join $tn.2.6 "t1, t2 ON (1.1-1.0)" {t1 t2 -on te_true} | |
| 382 | |
| 383 | |
| 384 test_join $tn.3 "t1 LEFT JOIN t2 ON (t1.a=t2.a)" {t1 t2 -left -on {te_equals a
a}} | |
| 385 test_join $tn.4 "t1 LEFT JOIN t2 USING (a)" { | |
| 386 t1 t2 -left -using a -on {te_equals a a} | |
| 387 } | |
| 388 test_join $tn.5 "t1 CROSS JOIN t2 USING(b, a)" { | |
| 389 t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 390 } | |
| 391 test_join $tn.6 "t1 NATURAL JOIN t2" { | |
| 392 t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 393 } | |
| 394 test_join $tn.7 "t1 NATURAL INNER JOIN t2" { | |
| 395 t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 396 } | |
| 397 test_join $tn.8 "t1 NATURAL CROSS JOIN t2" { | |
| 398 t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 399 } | |
| 400 test_join $tn.9 "t1 NATURAL INNER JOIN t2" { | |
| 401 t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 402 } | |
| 403 test_join $tn.10 "t1 NATURAL LEFT JOIN t2" { | |
| 404 t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 405 } | |
| 406 test_join $tn.11 "t1 NATURAL LEFT OUTER JOIN t2" { | |
| 407 t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 408 } | |
| 409 test_join $tn.12 "t2 NATURAL JOIN t1" { | |
| 410 t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 411 } | |
| 412 test_join $tn.13 "t2 NATURAL INNER JOIN t1" { | |
| 413 t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 414 } | |
| 415 test_join $tn.14 "t2 NATURAL CROSS JOIN t1" { | |
| 416 t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 417 } | |
| 418 test_join $tn.15 "t2 NATURAL INNER JOIN t1" { | |
| 419 t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 420 } | |
| 421 test_join $tn.16 "t2 NATURAL LEFT JOIN t1" { | |
| 422 t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 423 } | |
| 424 test_join $tn.17 "t2 NATURAL LEFT OUTER JOIN t1" { | |
| 425 t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} | |
| 426 } | |
| 427 test_join $tn.18 "t1 LEFT JOIN t2 USING (b)" { | |
| 428 t1 t2 -left -using b -on {te_equals b b} | |
| 429 } | |
| 430 test_join $tn.19 "t1 JOIN t3 USING(b)" {t1 t3 -using b -on {te_equals b b}} | |
| 431 test_join $tn.20 "t3 JOIN t1 USING(b)" { | |
| 432 t3 t1 -using b -on {te_equals -nocase b b} | |
| 433 } | |
| 434 test_join $tn.21 "t1 NATURAL JOIN t3" { | |
| 435 t1 t3 -using b -on {te_equals b b} | |
| 436 } | |
| 437 test_join $tn.22 "t3 NATURAL JOIN t1" { | |
| 438 t3 t1 -using b -on {te_equals -nocase b b} | |
| 439 } | |
| 440 test_join $tn.23 "t1 NATURAL LEFT JOIN t3" { | |
| 441 t1 t3 -left -using b -on {te_equals b b} | |
| 442 } | |
| 443 test_join $tn.24 "t3 NATURAL LEFT JOIN t1" { | |
| 444 t3 t1 -left -using b -on {te_equals -nocase b b} | |
| 445 } | |
| 446 test_join $tn.25 "t1 LEFT JOIN t3 ON (t3.b=t1.b)" { | |
| 447 t1 t3 -left -on {te_equals -nocase b b} | |
| 448 } | |
| 449 test_join $tn.26 "t1 LEFT JOIN t3 ON (t1.b=t3.b)" { | |
| 450 t1 t3 -left -on {te_equals b b} | |
| 451 } | |
| 452 test_join $tn.27 "t1 JOIN t3 ON (t1.b=t3.b)" { t1 t3 -on {te_equals b b} } | |
| 453 | |
| 454 # EVIDENCE-OF: R-28760-53843 When more than two tables are joined | |
| 455 # together as part of a FROM clause, the join operations are processed | |
| 456 # in order from left to right. In other words, the FROM clause (A | |
| 457 # join-op-1 B join-op-2 C) is computed as ((A join-op-1 B) join-op-2 C). | |
| 458 # | |
| 459 # Tests 28a and 28b show that the statement above is true for this case. | |
| 460 # Test 28c shows that if the parenthesis force a different order of | |
| 461 # evaluation the result is different. Test 28d verifies that the result | |
| 462 # of the query with the parenthesis forcing a different order of evaluation | |
| 463 # is as calculated by the [te_*] procs. | |
| 464 # | |
| 465 set t3_natural_left_join_t2 [ | |
| 466 te_tbljoin db t3 t2 -left -using {b} -on {te_equals -nocase b b} | |
| 467 ] | |
| 468 set t1 [te_read_tbl db t1] | |
| 469 te_dataset_eq_unordered $tn.28a [ | |
| 470 te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1" | |
| 471 ] [te_join $t3_natural_left_join_t2 $t1 \ | |
| 472 -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \ | |
| 473 ] | |
| 474 | |
| 475 te_dataset_eq_unordered $tn.28b [ | |
| 476 te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1" | |
| 477 ] [te_join $t3_natural_left_join_t2 $t1 \ | |
| 478 -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \ | |
| 479 ] | |
| 480 | |
| 481 te_dataset_ne_unordered $tn.28c [ | |
| 482 te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1" | |
| 483 ] [ | |
| 484 te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)" | |
| 485 ] | |
| 486 | |
| 487 set t2_natural_join_t1 [te_tbljoin db t2 t1 -using {a b} \ | |
| 488 -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \ | |
| 489 ] | |
| 490 set t3 [te_read_tbl db t3] | |
| 491 te_dataset_eq_unordered $tn.28d [ | |
| 492 te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)" | |
| 493 ] [te_join $t3 $t2_natural_join_t1 \ | |
| 494 -left -using {b} -on {te_equals -nocase b b} \ | |
| 495 ] | |
| 496 } | |
| 497 | |
| 498 do_execsql_test e_select-2.2.0 { | |
| 499 CREATE TABLE t4(x TEXT COLLATE nocase); | |
| 500 CREATE TABLE t5(y INTEGER, z TEXT COLLATE binary); | |
| 501 | |
| 502 INSERT INTO t4 VALUES('2.0'); | |
| 503 INSERT INTO t4 VALUES('TWO'); | |
| 504 INSERT INTO t5 VALUES(2, 'two'); | |
| 505 } {} | |
| 506 | |
| 507 # EVIDENCE-OF: R-59237-46742 A subquery specified in the | |
| 508 # table-or-subquery following the FROM clause in a simple SELECT | |
| 509 # statement is handled as if it was a table containing the data returned | |
| 510 # by executing the subquery statement. | |
| 511 # | |
| 512 # EVIDENCE-OF: R-27438-53558 Each column of the subquery has the | |
| 513 # collation sequence and affinity of the corresponding expression in the | |
| 514 # subquery statement. | |
| 515 # | |
| 516 foreach {tn subselect select spec} { | |
| 517 1 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss%" | |
| 518 {t1 %ss%} | |
| 519 | |
| 520 2 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss% AS x ON (t1.a=x.a)" | |
| 521 {t1 %ss% -on {te_equals 0 0}} | |
| 522 | |
| 523 3 "SELECT * FROM t2" "SELECT * FROM %ss% AS x JOIN t1 ON (t1.a=x.a)" | |
| 524 {%ss% t1 -on {te_equals 0 0}} | |
| 525 | |
| 526 4 "SELECT * FROM t1, t2" "SELECT * FROM %ss% AS x JOIN t3" | |
| 527 {%ss% t3} | |
| 528 | |
| 529 5 "SELECT * FROM t1, t2" "SELECT * FROM %ss% NATURAL JOIN t3" | |
| 530 {%ss% t3 -using b -on {te_equals 1 0}} | |
| 531 | |
| 532 6 "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL JOIN %ss%" | |
| 533 {t3 %ss% -using b -on {te_equals -nocase 0 1}} | |
| 534 | |
| 535 7 "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL LEFT JOIN %ss%" | |
| 536 {t3 %ss% -left -using b -on {te_equals -nocase 0 1}} | |
| 537 | |
| 538 8 "SELECT count(*) AS y FROM t4" "SELECT * FROM t5, %ss% USING (y)" | |
| 539 {t5 %ss% -using y -on {te_equals -affinity text 0 0}} | |
| 540 | |
| 541 9 "SELECT count(*) AS y FROM t4" "SELECT * FROM %ss%, t5 USING (y)" | |
| 542 {%ss% t5 -using y -on {te_equals -affinity text 0 0}} | |
| 543 | |
| 544 10 "SELECT x AS y FROM t4" "SELECT * FROM %ss% JOIN t5 USING (y)" | |
| 545 {%ss% t5 -using y -on {te_equals -nocase -affinity integer 0 0}} | |
| 546 | |
| 547 11 "SELECT x AS y FROM t4" "SELECT * FROM t5 JOIN %ss% USING (y)" | |
| 548 {t5 %ss% -using y -on {te_equals -nocase -affinity integer 0 0}} | |
| 549 | |
| 550 12 "SELECT y AS x FROM t5" "SELECT * FROM %ss% JOIN t4 USING (x)" | |
| 551 {%ss% t4 -using x -on {te_equals -nocase -affinity integer 0 0}} | |
| 552 | |
| 553 13 "SELECT y AS x FROM t5" "SELECT * FROM t4 JOIN %ss% USING (x)" | |
| 554 {t4 %ss% -using x -on {te_equals -nocase -affinity integer 0 0}} | |
| 555 | |
| 556 14 "SELECT +y AS x FROM t5" "SELECT * FROM %ss% JOIN t4 USING (x)" | |
| 557 {%ss% t4 -using x -on {te_equals -nocase -affinity text 0 0}} | |
| 558 | |
| 559 15 "SELECT +y AS x FROM t5" "SELECT * FROM t4 JOIN %ss% USING (x)" | |
| 560 {t4 %ss% -using x -on {te_equals -nocase -affinity text 0 0}} | |
| 561 } { | |
| 562 | |
| 563 # Create a temporary table named %ss% containing the data returned by | |
| 564 # the sub-select. Then have the [te_tbljoin] proc use this table to | |
| 565 # compute the expected results of the $select query. Drop the temporary | |
| 566 # table before continuing. | |
| 567 # | |
| 568 execsql "CREATE TEMP TABLE '%ss%' AS $subselect" | |
| 569 set te [eval te_tbljoin db $spec] | |
| 570 execsql "DROP TABLE '%ss%'" | |
| 571 | |
| 572 # Check that the actual data returned by the $select query is the same | |
| 573 # as the expected data calculated using [te_tbljoin] above. | |
| 574 # | |
| 575 te_dataset_eq_unordered e_select-2.2.1.$tn [ | |
| 576 te_read_sql db [string map [list %ss% "($subselect)"] $select] | |
| 577 ] $te | |
| 578 } | |
| 579 | |
| 580 finish_test | |
| OLD | NEW |