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-46122-14930 If the join-op is "CROSS JOIN", "INNER |
| 348 # JOIN", "JOIN" or a comma (",") and there is no ON or USING clause, |
| 349 # then the result of the join is simply the cartesian product of the |
| 350 # 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-07544-24155 The "CROSS JOIN" join operator produces the |
| 356 # same data 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-22775-56496 If there is an ON clause specified, then |
| 372 # the ON expression is evaluated for each row of the cartesian product |
| 373 # as a boolean expression. All rows for which the expression evaluates |
| 374 # to false are excluded 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-55824-40976 A sub-select specified in the join-source |
| 508 # following the FROM clause in a simple SELECT statement is handled as |
| 509 # if it was a table containing the data returned by executing the |
| 510 # sub-select statement. |
| 511 # |
| 512 # EVIDENCE-OF: R-42612-06757 Each column of the sub-select dataset |
| 513 # inherits the collation sequence and affinity of the corresponding |
| 514 # expression in the sub-select 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 |