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 581