Home | History | Annotate | Download | only in test
      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