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 |