000001  # 2010 September 24
000002  #
000003  # The author disclaims copyright to this source code.  In place of
000004  # a legal notice, here is a blessing:
000005  #
000006  #    May you do good and not evil.
000007  #    May you find forgiveness for yourself and forgive others.
000008  #    May you share freely, never taking more than you give.
000009  #
000010  #***********************************************************************
000011  #
000012  # This file implements tests to verify that the "testable statements" in 
000013  # the lang_select.html document are correct.
000014  #
000015  
000016  set testdir [file dirname $argv0]
000017  source $testdir/tester.tcl
000018  
000019  #-------------------------------------------------------------------------
000020  # te_* commands:
000021  #
000022  #
000023  #   te_read_sql DB SELECT-STATEMENT
000024  #   te_read_tbl DB TABLENAME
000025  #
000026  # These two commands are used to read a dataset from the database. A dataset
000027  # consists of N rows of M named columns of values each, where each value has a
000028  # type (null, integer, real, text or blob) and a value within the types domain.
000029  # The tcl format for a "dataset" is a list of two elements:
000030  #
000031  #   * A list of the column names.
000032  #   * A list of data rows. Each row is itself a list, where each element is
000033  #     the contents of a column of the row. Each of these is a list of two
000034  #     elements, the type name and the actual value.
000035  #
000036  # For example, the contents of table [t1] as a dataset is:
000037  #
000038  #   CREATE TABLE t1(a, b);
000039  #   INSERT INTO t1 VALUES('abc', NULL);
000040  #   INSERT INTO t1 VALUES(43.1, 22);
000041  #
000042  #   {a b} {{{TEXT abc} {NULL {}}} {{REAL 43.1} {INTEGER 22}}}
000043  #
000044  # The [te_read_tbl] command returns a dataset read from a table. The
000045  # [te_read_sql] returns the dataset that results from executing a SELECT
000046  # command.
000047  #
000048  #
000049  #   te_tbljoin ?SWITCHES? LHS-TABLE RHS-TABLE
000050  #   te_join ?SWITCHES? LHS-DATASET RHS-DATASET
000051  #
000052  # This command joins the two datasets and returns the resulting dataset. If 
000053  # there are no switches specified, then the results is the cartesian product
000054  # of the two inputs.  The [te_tbljoin] command reads the left and right-hand
000055  # datasets from the specified tables. The [te_join] command is passed the
000056  # datasets directly.
000057  #
000058  # Optional switches are as follows:
000059  #
000060  #   -on SCRIPT
000061  #   -using COLUMN-LIST
000062  #   -left
000063  #
000064  # The -on option specifies a tcl script that is executed for each row in the
000065  # cartesian product of the two datasets. The script has 4 arguments appended
000066  # to it, in the following order:
000067  #
000068  #   * The list of column-names from the left-hand dataset.
000069  #   * A single row from the left-hand dataset (one "data row" list as 
000070  #     described above.
000071  #   * The list of column-names from the right-hand dataset.
000072  #   * A single row from the right-hand dataset.
000073  #
000074  # The script must return a boolean value - true if the combination of rows
000075  # should be included in the output dataset, or false otherwise.
000076  #
000077  # The -using option specifies a list of the columns from the right-hand
000078  # dataset that should be omitted from the output dataset.
000079  #
000080  # If the -left option is present, the join is done LEFT JOIN style. 
000081  # Specifically, an extra row is inserted if after the -on script is run there
000082  # exist rows in the left-hand dataset that have no corresponding rows in
000083  # the output. See the implementation for more specific comments.
000084  #
000085  #
000086  #   te_equals ?SWITCHES? COLNAME1 COLNAME2 <-on script args>
000087  #
000088  # The only supported switch is "-nocase". If it is present, then text values
000089  # are compared in a case-independent fashion. Otherwise, they are compared
000090  # as if using the SQLite BINARY collation sequence.
000091  #
000092  #
000093  #   te_and ONSCRIPT1 ONSCRIPT2...
000094  #
000095  #
000096  
000097  
000098  #
000099  #   te_read_tbl DB TABLENAME
000100  #   te_read_sql DB SELECT-STATEMENT
000101  #
000102  # These two procs are used to extract datasets from the database, either
000103  # by reading the contents of a named table (te_read_tbl), or by executing
000104  # a SELECT statement (t3_read_sql).  
000105  #
000106  # See the comment above, describing "te_* commands", for details of the
000107  # return values.
000108  #
000109  proc te_read_tbl {db tbl} {
000110   te_read_sql $db "SELECT * FROM '$tbl'"
000111  }
000112  proc te_read_sql {db sql} {
000113    set S [sqlite3_prepare_v2 $db $sql -1 DUMMY]
000114  
000115    set cols [list]
000116    for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} {
000117      lappend cols [sqlite3_column_name $S $i]
000118    }
000119  
000120    set rows [list]
000121    while {[sqlite3_step $S] == "SQLITE_ROW"} {
000122      set r [list]
000123      for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} {
000124        lappend r [list [sqlite3_column_type $S $i] [sqlite3_column_text $S $i]]
000125      }
000126      lappend rows $r
000127    }
000128    sqlite3_finalize $S
000129  
000130    return [list $cols $rows]
000131  }
000132  
000133  #-------
000134  # Usage:   te_join <table-data1> <table-data2> <join spec>...
000135  #
000136  # Where a join-spec is an optional list of arguments as follows:
000137  #
000138  #   ?-left?
000139  #   ?-using colname-list?
000140  #   ?-on on-expr-proc?
000141  #
000142  proc te_join {data1 data2 args} {
000143  
000144    set testproc ""
000145    set usinglist [list]
000146    set isleft 0
000147    for {set i 0} {$i < [llength $args]} {incr i} {
000148      set a [lindex $args $i]
000149      switch -- $a {
000150        -on     { set testproc [lindex $args [incr i]] }
000151        -using  { set usinglist [lindex $args [incr i]] }
000152        -left   { set isleft 1 }
000153        default {
000154          error "Unknown argument: $a"
000155        }
000156      }
000157    }
000158  
000159    set c1 [lindex $data1 0]
000160    set c2 [lindex $data2 0]
000161    set omitlist [list]
000162    set nullrowlist [list]
000163    set cret $c1
000164  
000165    set cidx 0
000166    foreach col $c2 {
000167      set idx [lsearch $usinglist $col]
000168      if {$idx>=0} {lappend omitlist $cidx}
000169      if {$idx<0} {
000170        lappend nullrowlist {NULL {}}
000171        lappend cret $col
000172      }
000173      incr cidx
000174    }
000175    set omitlist [lsort -integer -decreasing $omitlist]
000176  
000177  
000178    set rret [list]
000179    foreach r1 [lindex $data1 1] {
000180      set one 0
000181      foreach r2 [lindex $data2 1] {
000182        set ok 1
000183        if {$testproc != ""} {
000184          set ok [eval $testproc [list $c1 $r1 $c2 $r2]]
000185        }
000186        if {$ok} {
000187          set one 1
000188          foreach idx $omitlist {set r2 [lreplace $r2 $idx $idx]}
000189          lappend rret [concat $r1 $r2]
000190        }
000191      }
000192  
000193      if {$isleft && $one==0} {
000194        lappend rret [concat $r1 $nullrowlist]
000195      }
000196    }
000197    
000198    list $cret $rret
000199  }
000200  
000201  proc te_tbljoin {db t1 t2 args} {
000202    te_join [te_read_tbl $db $t1] [te_read_tbl $db $t2] {*}$args
000203  }
000204  
000205  proc te_apply_affinity {affinity typevar valvar} {
000206    upvar $typevar type
000207    upvar $valvar val
000208  
000209    switch -- $affinity {
000210      integer {
000211        if {[string is double $val]} { set type REAL }
000212        if {[string is wideinteger $val]} { set type INTEGER }
000213        if {$type == "REAL" && int($val)==$val} { 
000214          set type INTEGER 
000215          set val [expr {int($val)}]
000216        }
000217      }
000218      text {
000219        set type TEXT
000220      }
000221      none { }
000222  
000223      default { error "invalid affinity: $affinity" }
000224    }
000225  }
000226  
000227  #----------
000228  # te_equals ?SWITCHES? c1 c2 cols1 row1 cols2 row2
000229  #
000230  proc te_equals {args} {
000231  
000232    if {[llength $args]<6} {error "invalid arguments to te_equals"}
000233    foreach {c1 c2 cols1 row1 cols2 row2} [lrange $args end-5 end] break
000234  
000235    set nocase 0
000236    set affinity none
000237  
000238    for {set i 0} {$i < ([llength $args]-6)} {incr i} {
000239      set a [lindex $args $i]
000240      switch -- $a {
000241        -nocase {
000242          set nocase 1
000243        }
000244        -affinity {
000245          set affinity [string tolower [lindex $args [incr i]]]
000246        }
000247        default {
000248          error "invalid arguments to te_equals"
000249        }
000250      }
000251    }
000252  
000253    set idx2 [if {[string is integer $c2]} { set c2 } else { lsearch $cols2 $c2 }]
000254    set idx1 [if {[string is integer $c1]} { set c1 } else { lsearch $cols1 $c1 }]
000255  
000256    set t1 [lindex $row1 $idx1 0]
000257    set t2 [lindex $row2 $idx2 0]
000258    set v1 [lindex $row1 $idx1 1]
000259    set v2 [lindex $row2 $idx2 1]
000260  
000261    te_apply_affinity $affinity t1 v1
000262    te_apply_affinity $affinity t2 v2
000263  
000264    if {$t1 == "NULL" || $t2 == "NULL"} { return 0 }
000265    if {$nocase && $t1 == "TEXT"} { set v1 [string tolower $v1] }
000266    if {$nocase && $t2 == "TEXT"} { set v2 [string tolower $v2] }
000267  
000268  
000269    set res [expr {$t1 == $t2 && [string equal $v1 $v2]}]
000270    return $res
000271  }
000272  
000273  proc te_false {args} { return 0 }
000274  proc te_true  {args} { return 1 }
000275  
000276  proc te_and {args} {
000277    foreach a [lrange $args 0 end-4] {
000278      set res [eval $a [lrange $args end-3 end]]
000279      if {$res == 0} {return 0}
000280    }
000281    return 1
000282  }
000283  
000284  
000285  proc te_dataset_eq {testname got expected} {
000286    uplevel #0 [list do_test $testname [list set {} $got] $expected]
000287  }
000288  proc te_dataset_eq_unordered {testname got expected} {
000289    lset got      1 [lsort [lindex $got 1]]
000290    lset expected 1 [lsort [lindex $expected 1]]
000291    te_dataset_eq $testname $got $expected
000292  }
000293  
000294  proc te_dataset_ne {testname got unexpected} {
000295    uplevel #0 [list do_test $testname [list string equal $got $unexpected] 0]
000296  }
000297  proc te_dataset_ne_unordered {testname got unexpected} {
000298    lset got      1 [lsort [lindex $got 1]]
000299    lset unexpected 1 [lsort [lindex $unexpected 1]]
000300    te_dataset_ne $testname $got $unexpected
000301  }
000302  
000303  
000304  #-------------------------------------------------------------------------
000305  #
000306  proc test_join {tn sqljoin tbljoinargs} {
000307    set sql [te_read_sql db "SELECT * FROM $sqljoin"]
000308    set te  [te_tbljoin db {*}$tbljoinargs]
000309    te_dataset_eq_unordered $tn $sql $te
000310  }
000311  
000312  drop_all_tables
000313  do_execsql_test e_select-2.0 {
000314    CREATE TABLE t1(a, b);
000315    CREATE TABLE t2(a, b);
000316    CREATE TABLE t3(b COLLATE nocase);
000317  
000318    INSERT INTO t1 VALUES(2, 'B');
000319    INSERT INTO t1 VALUES(1, 'A');
000320    INSERT INTO t1 VALUES(4, 'D');
000321    INSERT INTO t1 VALUES(NULL, NULL);
000322    INSERT INTO t1 VALUES(3, NULL);
000323  
000324    INSERT INTO t2 VALUES(1, 'A');
000325    INSERT INTO t2 VALUES(2, NULL);
000326    INSERT INTO t2 VALUES(5, 'E');
000327    INSERT INTO t2 VALUES(NULL, NULL);
000328    INSERT INTO t2 VALUES(3, 'C');
000329  
000330    INSERT INTO t3 VALUES('a');
000331    INSERT INTO t3 VALUES('c');
000332    INSERT INTO t3 VALUES('b');
000333  } {}
000334  
000335  foreach {tn indexes} {
000336    e_select-2.1.1 { }
000337    e_select-2.1.2 { CREATE INDEX i1 ON t1(a) }
000338    e_select-2.1.3 { CREATE INDEX i1 ON t2(a) }
000339    e_select-2.1.4 { CREATE INDEX i1 ON t3(b) }
000340  } {
000341  
000342    catchsql { DROP INDEX i1 }
000343    catchsql { DROP INDEX i2 }
000344    catchsql { DROP INDEX i3 }
000345    execsql $indexes
000346  
000347    # EVIDENCE-OF: R-49872-03192 If the join-operator is "CROSS JOIN",
000348    # "INNER JOIN", "JOIN" or a comma (",") and there is no ON or USING
000349    # clause, then the result of the join is simply the cartesian product of
000350    # the left and right-hand datasets.
000351    #
000352    # EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER
000353    # JOIN", "JOIN" and "," join operators.
000354    #
000355    # EVIDENCE-OF: R-25071-21202 The "CROSS JOIN" join operator produces the
000356    # same result as the "INNER JOIN", "JOIN" and "," operators
000357    #
000358    test_join $tn.1.1  "t1, t2"                {t1 t2}
000359    test_join $tn.1.2  "t1 INNER JOIN t2"      {t1 t2}
000360    test_join $tn.1.3  "t1 CROSS JOIN t2"      {t1 t2}
000361    test_join $tn.1.4  "t1 JOIN t2"            {t1 t2}
000362    test_join $tn.1.5  "t2, t3"                {t2 t3}
000363    test_join $tn.1.6  "t2 INNER JOIN t3"      {t2 t3}
000364    test_join $tn.1.7  "t2 CROSS JOIN t3"      {t2 t3}
000365    test_join $tn.1.8  "t2 JOIN t3"            {t2 t3}
000366    test_join $tn.1.9  "t2, t2 AS x"           {t2 t2}
000367    test_join $tn.1.10 "t2 INNER JOIN t2 AS x" {t2 t2}
000368    test_join $tn.1.11 "t2 CROSS JOIN t2 AS x" {t2 t2}
000369    test_join $tn.1.12 "t2 JOIN t2 AS x"       {t2 t2}
000370  
000371    # EVIDENCE-OF: R-38465-03616 If there is an ON clause then the ON
000372    # expression is evaluated for each row of the cartesian product as a
000373    # boolean expression. Only rows for which the expression evaluates to
000374    # true are included from the dataset.
000375    #
000376    test_join $tn.2.1  "t1, t2 ON (t1.a=t2.a)"  {t1 t2 -on {te_equals a a}}
000377    test_join $tn.2.2  "t2, t1 ON (t1.a=t2.a)"  {t2 t1 -on {te_equals a a}}
000378    test_join $tn.2.3  "t2, t1 ON (1)"          {t2 t1 -on te_true}
000379    test_join $tn.2.4  "t2, t1 ON (NULL)"       {t2 t1 -on te_false}
000380    test_join $tn.2.5  "t2, t1 ON (1.1-1.1)"    {t2 t1 -on te_false}
000381    test_join $tn.2.6  "t1, t2 ON (1.1-1.0)"    {t1 t2 -on te_true}
000382  
000383  
000384    test_join $tn.3 "t1 LEFT JOIN t2 ON (t1.a=t2.a)" {t1 t2 -left -on {te_equals a a}}
000385    test_join $tn.4 "t1 LEFT JOIN t2 USING (a)" {
000386      t1 t2 -left -using a -on {te_equals a a}
000387    }
000388    test_join $tn.5 "t1 CROSS JOIN t2 USING(b, a)" {
000389      t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000390    }
000391    test_join $tn.6 "t1 NATURAL JOIN t2" {
000392      t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000393    }
000394    test_join $tn.7 "t1 NATURAL INNER JOIN t2" {
000395      t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000396    }
000397    test_join $tn.8 "t1 NATURAL CROSS JOIN t2" {
000398      t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000399    }
000400    test_join $tn.9 "t1 NATURAL INNER JOIN t2" {
000401      t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000402    }
000403    test_join $tn.10 "t1 NATURAL LEFT JOIN t2" {
000404      t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000405    }
000406    test_join $tn.11 "t1 NATURAL LEFT OUTER JOIN t2" {
000407      t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000408    }
000409    test_join $tn.12 "t2 NATURAL JOIN t1" {
000410      t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000411    }
000412    test_join $tn.13 "t2 NATURAL INNER JOIN t1" {
000413      t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000414    }
000415    test_join $tn.14 "t2 NATURAL CROSS JOIN t1" {
000416      t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000417    }
000418    test_join $tn.15 "t2 NATURAL INNER JOIN t1" {
000419      t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000420    }
000421    test_join $tn.16 "t2 NATURAL LEFT JOIN t1" {
000422      t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000423    }
000424    test_join $tn.17 "t2 NATURAL LEFT OUTER JOIN t1" {
000425      t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
000426    }
000427    test_join $tn.18 "t1 LEFT JOIN t2 USING (b)" {
000428      t1 t2 -left -using b -on {te_equals b b}
000429    }
000430    test_join $tn.19 "t1 JOIN t3 USING(b)" {t1 t3 -using b -on {te_equals b b}}
000431    test_join $tn.20 "t3 JOIN t1 USING(b)" {
000432      t3 t1 -using b -on {te_equals -nocase b b}
000433    }
000434    test_join $tn.21 "t1 NATURAL JOIN t3"  {
000435      t1 t3 -using b -on {te_equals b b}
000436    }
000437    test_join $tn.22 "t3 NATURAL JOIN t1"  {
000438      t3 t1 -using b -on {te_equals -nocase b b}
000439    }
000440    test_join $tn.23 "t1 NATURAL LEFT JOIN t3" {
000441      t1 t3 -left -using b -on {te_equals b b}
000442    }
000443    test_join $tn.24 "t3 NATURAL LEFT JOIN t1" {
000444      t3 t1 -left -using b -on {te_equals -nocase b b}
000445    }
000446    test_join $tn.25 "t1 LEFT JOIN t3 ON (t3.b=t1.b)" {
000447      t1 t3 -left -on {te_equals -nocase b b}
000448    }
000449    test_join $tn.26 "t1 LEFT JOIN t3 ON (t1.b=t3.b)" {
000450      t1 t3 -left -on {te_equals b b}
000451    }
000452    test_join $tn.27 "t1 JOIN t3 ON (t1.b=t3.b)" { t1 t3 -on {te_equals b b} }
000453  
000454    # EVIDENCE-OF: R-28760-53843 When more than two tables are joined
000455    # together as part of a FROM clause, the join operations are processed
000456    # in order from left to right. In other words, the FROM clause (A
000457    # join-op-1 B join-op-2 C) is computed as ((A join-op-1 B) join-op-2 C).
000458    #
000459    #   Tests 28a and 28b show that the statement above is true for this case.
000460    #   Test 28c shows that if the parenthesis force a different order of
000461    #   evaluation the result is different. Test 28d verifies that the result
000462    #   of the query with the parenthesis forcing a different order of evaluation
000463    #   is as calculated by the [te_*] procs.
000464    #
000465    set t3_natural_left_join_t2 [
000466      te_tbljoin db t3 t2 -left -using {b} -on {te_equals -nocase b b}
000467    ]
000468    set t1 [te_read_tbl db t1]
000469    te_dataset_eq_unordered $tn.28a [
000470      te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1"
000471    ] [te_join $t3_natural_left_join_t2 $t1                                \
000472        -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}}  \
000473    ]
000474  
000475    te_dataset_eq_unordered $tn.28b [
000476      te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1"
000477    ] [te_join $t3_natural_left_join_t2 $t1                                \
000478        -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}}  \
000479    ]
000480  
000481    te_dataset_ne_unordered $tn.28c [
000482      te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1"
000483    ] [
000484      te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)"
000485    ]
000486  
000487    set t2_natural_join_t1 [te_tbljoin db t2 t1 -using {a b}                 \
000488          -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}}  \
000489    ]
000490    set t3 [te_read_tbl db t3]
000491    te_dataset_eq_unordered $tn.28d [
000492      te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)"
000493    ] [te_join $t3 $t2_natural_join_t1                                       \
000494        -left -using {b} -on {te_equals -nocase b b}                         \
000495    ]
000496  }
000497  
000498  do_execsql_test e_select-2.2.0 {
000499    CREATE TABLE t4(x TEXT COLLATE nocase);
000500    CREATE TABLE t5(y INTEGER, z TEXT COLLATE binary);
000501  
000502    INSERT INTO t4 VALUES('2.0');
000503    INSERT INTO t4 VALUES('TWO');
000504    INSERT INTO t5 VALUES(2, 'two');
000505  } {}
000506  
000507  # EVIDENCE-OF: R-59237-46742 A subquery specified in the
000508  # table-or-subquery following the FROM clause in a simple SELECT
000509  # statement is handled as if it was a table containing the data returned
000510  # by executing the subquery statement.
000511  #
000512  # EVIDENCE-OF: R-27438-53558 Each column of the subquery has the
000513  # collation sequence and affinity of the corresponding expression in the
000514  # subquery statement.
000515  #
000516  foreach {tn subselect select spec} {
000517    1   "SELECT * FROM t2"   "SELECT * FROM t1 JOIN %ss%" 
000518        {t1 %ss%}
000519  
000520    2   "SELECT * FROM t2"   "SELECT * FROM t1 JOIN %ss% AS x ON (t1.a=x.a)" 
000521        {t1 %ss% -on {te_equals 0 0}}
000522  
000523    3   "SELECT * FROM t2"   "SELECT * FROM %ss% AS x JOIN t1 ON (t1.a=x.a)" 
000524        {%ss% t1 -on {te_equals 0 0}}
000525  
000526    4   "SELECT * FROM t1, t2" "SELECT * FROM %ss% AS x JOIN t3"
000527        {%ss% t3}
000528  
000529    5   "SELECT * FROM t1, t2" "SELECT * FROM %ss% NATURAL JOIN t3"
000530        {%ss% t3 -using b -on {te_equals 1 0}}
000531  
000532    6   "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL JOIN %ss%"
000533        {t3 %ss% -using b -on {te_equals -nocase 0 1}}
000534  
000535    7   "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL LEFT JOIN %ss%"
000536        {t3 %ss% -left -using b -on {te_equals -nocase 0 1}}
000537  
000538    8   "SELECT count(*) AS y FROM t4"   "SELECT * FROM t5, %ss% USING (y)"
000539        {t5 %ss% -using y -on {te_equals -affinity text 0 0}}
000540  
000541    9   "SELECT count(*) AS y FROM t4"   "SELECT * FROM %ss%, t5 USING (y)"
000542        {%ss% t5 -using y -on {te_equals -affinity text 0 0}}
000543  
000544    10  "SELECT x AS y FROM t4"   "SELECT * FROM %ss% JOIN t5 USING (y)"
000545        {%ss% t5 -using y -on {te_equals -nocase -affinity integer 0 0}}
000546  
000547    11  "SELECT x AS y FROM t4"   "SELECT * FROM t5 JOIN %ss% USING (y)"
000548        {t5 %ss% -using y -on {te_equals -nocase -affinity integer 0 0}}
000549  
000550    12  "SELECT y AS x FROM t5"   "SELECT * FROM %ss% JOIN t4 USING (x)"
000551        {%ss% t4 -using x -on {te_equals -nocase -affinity integer 0 0}}
000552  
000553    13  "SELECT y AS x FROM t5"   "SELECT * FROM t4 JOIN %ss% USING (x)"
000554        {t4 %ss% -using x -on {te_equals -nocase -affinity integer 0 0}}
000555  
000556    14  "SELECT +y AS x FROM t5"   "SELECT * FROM %ss% JOIN t4 USING (x)"
000557        {%ss% t4 -using x -on {te_equals -nocase -affinity text 0 0}}
000558  
000559    15  "SELECT +y AS x FROM t5"   "SELECT * FROM t4 JOIN %ss% USING (x)"
000560        {t4 %ss% -using x -on {te_equals -nocase -affinity text 0 0}}
000561  } {
000562  
000563    # Create a temporary table named %ss% containing the data returned by
000564    # the sub-select. Then have the [te_tbljoin] proc use this table to
000565    # compute the expected results of the $select query. Drop the temporary
000566    # table before continuing.
000567    #
000568    execsql "CREATE TEMP TABLE '%ss%' AS $subselect"
000569    set te [eval te_tbljoin db $spec]
000570    execsql "DROP TABLE '%ss%'"
000571  
000572    # Check that the actual data returned by the $select query is the same
000573    # as the expected data calculated using [te_tbljoin] above.
000574    #
000575    te_dataset_eq_unordered e_select-2.2.1.$tn [
000576      te_read_sql db [string map [list %ss% "($subselect)"] $select]
000577    ] $te
000578  }
000579  
000580  finish_test