]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add new test file e_insert.test.
authordan <dan@noemail.net>
Sat, 18 Sep 2010 19:00:12 +0000 (19:00 +0000)
committerdan <dan@noemail.net>
Sat, 18 Sep 2010 19:00:12 +0000 (19:00 +0000)
FossilOrigin-Name: 8023a3091b32d304eaf7be41bb1d0bd33517e5f6

manifest
manifest.uuid
test/e_insert.test [new file with mode: 0644]
test/e_select.test
test/tester.tcl

index e327b8fe96a7848accfa89ecc1daa34a556e6fa5..a42d639bc3c4b62f42ba680f9793e8e893f6001a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\scouple\sof\sstale\sevidence\smarks\sin\se_select.test.
-D 2010-09-18T15:15:02
+C Add\snew\stest\sfile\se_insert.test.
+D 2010-09-18T19:00:13
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in c599a15d268b1db2aeadea19df2adc3bf2eb6bee
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -350,7 +350,8 @@ F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
 F test/e_expr.test 164e87c1d7b40ceb47c57c3bffa384c81d009aa7
 F test/e_fkey.test 6721a741c6499b3ab7e5385923233343c8f1ad05
 F test/e_fts3.test 75bb0aee26384ef586165e21018a17f7cd843469
-F test/e_select.test 8b1747f86c043b74c71a60627dfa524927457711
+F test/e_insert.test 066926c10eeffbde3890a84d5f8b63dbbb2b6aa9
+F test/e_select.test 64b9cc44f8df454f45bc8df032e12032c8bf8670
 F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
 F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398
 F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041
@@ -634,7 +635,7 @@ F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
 F test/tempdb.test 800c36623d67a2ad1f58784b9c5644e0405af6e6
 F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
 F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
-F test/tester.tcl dd05c5302a149632ec692c6175f02ef2e4be13fd
+F test/tester.tcl 4c7bf5a1a9382de2ebd5d072c08aca909cc5ac2b
 F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
 F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
 F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@@ -858,7 +859,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 0ee9e755719c45e6047f9f004030716029b886ca
-R 7ce5425128484c50a370d11dc779202c
+P 14227724a81c31c6bb9381b210f8fabf4b1154fd
+R 436252de876752929fe669b19572343f
 U dan
-Z cd027a321f9813d8938b660e447c7f58
+Z 0eab070401d21ee3c063299339d2883e
index 5c97f0b123449d2abc5d59ab28809d8751759f92..bdddff5d9de1362750ec0625ac60ebe960e225f7 100644 (file)
@@ -1 +1 @@
-14227724a81c31c6bb9381b210f8fabf4b1154fd
\ No newline at end of file
+8023a3091b32d304eaf7be41bb1d0bd33517e5f6
\ No newline at end of file
diff --git a/test/e_insert.test b/test/e_insert.test
new file mode 100644 (file)
index 0000000..ece745d
--- /dev/null
@@ -0,0 +1,282 @@
+# 2010 September 18
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# This file implements tests to verify that the "testable statements" in 
+# the lang_insert.html document are correct.
+#
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Organization of tests:
+#
+#   e_insert-0.*: Test the syntax diagram.
+#
+#   e_insert-1.*: Test statements of the form "INSERT ... VALUES(...)".
+#   
+#   e_insert-2.*: Test statements of the form "INSERT ... SELECT ...".
+#
+#   e_insert-3.*: Test statements of the form "INSERT ... DEFAULT VALUES".
+#
+
+do_execsql_test e_insert-0.0 {
+  CREATE TABLE a1(a, b);
+  CREATE TABLE a2(a, b, c DEFAULT 'xyz');
+
+  CREATE TABLE a3(x DEFAULT 1.0, y DEFAULT 'string', z);
+} {}
+
+proc delete_all_data {} {
+  db eval {SELECT tbl_name AS t FROM sqlite_master WHERE type = 'table'} {
+    db eval "DELETE FROM '[string map {' ''} $t]'"
+  }
+}
+
+proc do_insert_tests {args} {
+  uplevel do_select_tests $args
+}
+
+# EVIDENCE-OF: R-41448-54465 -- syntax diagram insert-stmt
+#
+do_insert_tests e_insert-0 {
+     1  "INSERT             INTO a1 DEFAULT VALUES"                   {}
+     2  "INSERT             INTO main.a1 DEFAULT VALUES"              {}
+     3  "INSERT OR ROLLBACK INTO main.a1 DEFAULT VALUES"              {}
+     4  "INSERT OR ROLLBACK INTO a1 DEFAULT VALUES"                   {}
+     5  "INSERT OR ABORT    INTO main.a1 DEFAULT VALUES"              {}
+     6  "INSERT OR ABORT    INTO a1 DEFAULT VALUES"                   {}
+     7  "INSERT OR REPLACE  INTO main.a1 DEFAULT VALUES"              {}
+     8  "INSERT OR REPLACE  INTO a1 DEFAULT VALUES"                   {}
+     9  "INSERT OR FAIL     INTO main.a1 DEFAULT VALUES"              {}
+    10  "INSERT OR FAIL     INTO a1 DEFAULT VALUES"                   {}
+    11  "INSERT OR FAIL     INTO main.a1 DEFAULT VALUES"              {}
+    12  "INSERT OR IGNORE   INTO a1 DEFAULT VALUES"                   {}
+    13  "REPLACE            INTO a1 DEFAULT VALUES"                   {}
+    14  "REPLACE            INTO main.a1 DEFAULT VALUES"              {}
+    15  "INSERT             INTO a1      VALUES(1, 2)"                {}
+    16  "INSERT             INTO main.a1 VALUES(1, 2)"                {}
+    17  "INSERT OR ROLLBACK INTO main.a1 VALUES(1, 2)"                {}
+    18  "INSERT OR ROLLBACK INTO a1      VALUES(1, 2)"                {}
+    19  "INSERT OR ABORT    INTO main.a1 VALUES(1, 2)"                {}
+    20  "INSERT OR ABORT    INTO a1      VALUES(1, 2)"                {}
+    21  "INSERT OR REPLACE  INTO main.a1 VALUES(1, 2)"                {}
+    22  "INSERT OR REPLACE  INTO a1      VALUES(1, 2)"                {}
+    23  "INSERT OR FAIL     INTO main.a1 VALUES(1, 2)"                {}
+    24  "INSERT OR FAIL     INTO a1      VALUES(1, 2)"                {}
+    25  "INSERT OR FAIL     INTO main.a1 VALUES(1, 2)"                {}
+    26  "INSERT OR IGNORE   INTO a1      VALUES(1, 2)"                {}
+    27  "REPLACE            INTO a1      VALUES(1, 2)"                {}
+    28  "REPLACE            INTO main.a1 VALUES(1, 2)"                {}
+    29  "INSERT             INTO a1      (b, a) VALUES(1, 2)"         {}
+    30  "INSERT             INTO main.a1 (b, a) VALUES(1, 2)"         {}
+    31  "INSERT OR ROLLBACK INTO main.a1 (b, a) VALUES(1, 2)"         {}
+    32  "INSERT OR ROLLBACK INTO a1      (b, a) VALUES(1, 2)"         {}
+    33  "INSERT OR ABORT    INTO main.a1 (b, a) VALUES(1, 2)"         {}
+    34  "INSERT OR ABORT    INTO a1      (b, a) VALUES(1, 2)"         {}
+    35  "INSERT OR REPLACE  INTO main.a1 (b, a) VALUES(1, 2)"         {}
+    36  "INSERT OR REPLACE  INTO a1      (b, a) VALUES(1, 2)"         {}
+    37  "INSERT OR FAIL     INTO main.a1 (b, a) VALUES(1, 2)"         {}
+    38  "INSERT OR FAIL     INTO a1      (b, a) VALUES(1, 2)"         {}
+    39  "INSERT OR FAIL     INTO main.a1 (b, a) VALUES(1, 2)"         {}
+    40  "INSERT OR IGNORE   INTO a1      (b, a) VALUES(1, 2)"         {}
+    41  "REPLACE            INTO a1      (b, a) VALUES(1, 2)"         {}
+    42  "REPLACE            INTO main.a1 (b, a) VALUES(1, 2)"         {}
+    43  "INSERT             INTO a1      SELECT c, b FROM a2"         {}
+    44  "INSERT             INTO main.a1 SELECT c, b FROM a2"         {}
+    45  "INSERT OR ROLLBACK INTO main.a1 SELECT c, b FROM a2"         {}
+    46  "INSERT OR ROLLBACK INTO a1      SELECT c, b FROM a2"         {}
+    47  "INSERT OR ABORT    INTO main.a1 SELECT c, b FROM a2"         {}
+    48  "INSERT OR ABORT    INTO a1      SELECT c, b FROM a2"         {}
+    49  "INSERT OR REPLACE  INTO main.a1 SELECT c, b FROM a2"         {}
+    50  "INSERT OR REPLACE  INTO a1      SELECT c, b FROM a2"         {}
+    51  "INSERT OR FAIL     INTO main.a1 SELECT c, b FROM a2"         {}
+    52  "INSERT OR FAIL     INTO a1      SELECT c, b FROM a2"         {}
+    53  "INSERT OR FAIL     INTO main.a1 SELECT c, b FROM a2"         {}
+    54  "INSERT OR IGNORE   INTO a1      SELECT c, b FROM a2"         {}
+    55  "REPLACE            INTO a1      SELECT c, b FROM a2"         {}
+    56  "REPLACE            INTO main.a1 SELECT c, b FROM a2"         {}
+    57  "INSERT             INTO a1      (b, a) SELECT c, b FROM a2"  {}
+    58  "INSERT             INTO main.a1 (b, a) SELECT c, b FROM a2"  {}
+    59  "INSERT OR ROLLBACK INTO main.a1 (b, a) SELECT c, b FROM a2"  {}
+    60  "INSERT OR ROLLBACK INTO a1      (b, a) SELECT c, b FROM a2"  {}
+    61  "INSERT OR ABORT    INTO main.a1 (b, a) SELECT c, b FROM a2"  {}
+    62  "INSERT OR ABORT    INTO a1      (b, a) SELECT c, b FROM a2"  {}
+    63  "INSERT OR REPLACE  INTO main.a1 (b, a) SELECT c, b FROM a2"  {}
+    64  "INSERT OR REPLACE  INTO a1      (b, a) SELECT c, b FROM a2"  {}
+    65  "INSERT OR FAIL     INTO main.a1 (b, a) SELECT c, b FROM a2"  {}
+    66  "INSERT OR FAIL     INTO a1      (b, a) SELECT c, b FROM a2"  {}
+    67  "INSERT OR FAIL     INTO main.a1 (b, a) SELECT c, b FROM a2"  {}
+    68  "INSERT OR IGNORE   INTO a1      (b, a) SELECT c, b FROM a2"  {}
+    69  "REPLACE            INTO a1      (b, a) SELECT c, b FROM a2"  {}
+    70  "REPLACE            INTO main.a1 (b, a) SELECT c, b FROM a2"  {}
+}
+
+delete_all_data
+
+# EVIDENCE-OF: R-20288-20462 The first form (with the "VALUES" keyword)
+# creates a single new row in an existing table.
+#
+do_insert_tests e_insert-1.1 {
+    0    "SELECT count(*) FROM a2"           {0}
+
+    1a   "INSERT INTO a2 VALUES(1, 2, 3)"    {}
+    1b   "SELECT count(*) FROM a2"           {1}
+
+    2a   "INSERT INTO a2(a, b) VALUES(1, 2)" {}
+    2b   "SELECT count(*) FROM a2"           {2}
+}
+
+# EVIDENCE-OF: R-36040-20870 If no column-list is specified then the
+# number of values must be the same as the number of columns in the
+# table.
+#
+#   A test in the block above verifies that if the VALUES list has the
+#   correct number of columns (for table a2, 3 columns) works. So these
+#   tests just show that other values cause an error.
+#
+do_insert_tests e_insert-1.2 -error { 
+  table %s has %d columns but %d values were supplied
+} {
+    1    "INSERT INTO a2 VALUES(1)"         {a2 3 1}
+    2    "INSERT INTO a2 VALUES(1,2)"       {a2 3 2}
+    3    "INSERT INTO a2 VALUES(1,2,3,4)"   {a2 3 4}
+    4    "INSERT INTO a2 VALUES(1,2,3,4,5)" {a2 3 5}
+}
+
+# EVIDENCE-OF: R-52422-65517 In this case the result of evaluting the
+# left-most expression in the VALUES list is inserted into the left-most
+# column of the new row, and so on.
+#
+delete_all_data
+do_insert_tests e_insert-1.3 {
+    1a   "INSERT INTO a2 VALUES(1, 2, 3)"    {}
+    1b   "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {1 2 3}
+
+    2a   "INSERT INTO a2 VALUES('abc', NULL, 3*3+1)"      {}
+    2b   "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {abc {} 10}
+
+    3a   "INSERT INTO a2 VALUES((SELECT count(*) FROM a2), 'x', 'y')" {}
+    3b   "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {2 x y}
+}
+
+# EVIDENCE-OF: R-62524-00361 If a column-list is specified, then the
+# number of values must match the number of specified columns.
+#
+do_insert_tests e_insert-1.4 -error { 
+  %d values for %d columns
+} {
+    1    "INSERT INTO a2(a, b, c) VALUES(1)"         {1 3}
+    2    "INSERT INTO a2(a, b, c) VALUES(1,2)"       {2 3}
+    3    "INSERT INTO a2(a, b, c) VALUES(1,2,3,4)"   {4 3}
+    4    "INSERT INTO a2(a, b, c) VALUES(1,2,3,4,5)" {5 3}
+
+    5    "INSERT INTO a2(c, a) VALUES(1)"            {1 2}
+    6    "INSERT INTO a2(c, a) VALUES(1,2,3)"        {3 2}
+    7    "INSERT INTO a2(c, a) VALUES(1,2,3,4)"      {4 2}
+    8    "INSERT INTO a2(c, a) VALUES(1,2,3,4,5)"    {5 2}
+}
+
+# EVIDENCE-OF: R-07016-26442 Each of the named columns of the new row is
+# populated with the results of evaluating the corresponding VALUES
+# expression.
+#
+# EVIDENCE-OF: R-12183-43719 Table columns that do not appear in the
+# column list are populated with the default column value (specified as
+# part of the CREATE TABLE statement), or with NULL if no default value
+# is specified.
+#
+delete_all_data
+do_insert_tests e_insert-1.5 {
+    1a   "INSERT INTO a2(b, c) VALUES('b', 'c')"     {}
+    1b   "SELECT * FROM a2"                          {{} b c}
+
+    2a   "INSERT INTO a2(a, b) VALUES('a', 'b')"     {}
+    2b   "SELECT * FROM a2"                          {{} b c  a b xyz}
+}
+
+delete_all_data
+
+
+# EVIDENCE-OF: R-63614-47421 If a column-list is specified, the number
+# of columns in the result of the SELECT must be the same as the number
+# of items in the column-list.
+#
+do_insert_tests e_insert-2.2 -error {
+  %d values for %d columns
+} {
+    1    "INSERT INTO a3(x, y) SELECT a, b, c FROM a2"            {3 2}
+    2    "INSERT INTO a3(x, y) SELECT * FROM a2"                  {3 2}
+    3    "INSERT INTO a3(x, y) SELECT * FROM a2 CROSS JOIN a1"    {5 2}
+    4    "INSERT INTO a3(x, y) SELECT * FROM a2 NATURAL JOIN a1"  {3 2}
+    5    "INSERT INTO a3(x, y) SELECT a2.a FROM a2,a1"            {1 2}
+
+    6    "INSERT INTO a3(z) SELECT a, b, c FROM a2"               {3 1}
+    7    "INSERT INTO a3(z) SELECT * FROM a2"                     {3 1}
+    8    "INSERT INTO a3(z) SELECT * FROM a2 CROSS JOIN a1"       {5 1}
+    9    "INSERT INTO a3(z) SELECT * FROM a2 NATURAL JOIN a1"     {3 1}
+    10   "INSERT INTO a3(z) SELECT a1.* FROM a2,a1"               {2 1}
+}
+
+# EVIDENCE-OF: R-58951-07798 Otherwise, if no column-list is specified,
+# the number of columns in the result of the SELECT must be the same as
+# the number of columns in the table.
+#
+do_insert_tests e_insert-2.3 -error {
+  table %s has %d columns but %d values were supplied
+} {
+    1    "INSERT INTO a1 SELECT a, b, c FROM a2"            {a1 2 3}
+    2    "INSERT INTO a1 SELECT * FROM a2"                  {a1 2 3}
+    3    "INSERT INTO a1 SELECT * FROM a2 CROSS JOIN a1"    {a1 2 5}
+    4    "INSERT INTO a1 SELECT * FROM a2 NATURAL JOIN a1"  {a1 2 3}
+    5    "INSERT INTO a1 SELECT a2.a FROM a2,a1"            {a1 2 1}
+}
+
+
+# EVIDENCE-OF: R-25149-22012 The INSERT ... DEFAULT VALUES statement
+# inserts a single new row into the named table.
+#
+delete_all_data
+do_insert_tests e_insert-3.1 {
+    1    "SELECT count(*) FROM a3"           {0}
+    2a   "INSERT INTO a3 DEFAULT VALUES"     {}
+    2b   "SELECT count(*) FROM a3"           {1}
+}
+
+# EVIDENCE-OF: R-18927-01951 Each column of the new row is populated
+# with its default value, or with a NULL if no default value is
+# specified as part of the column definition in the CREATE TABLE
+# statement.
+#
+delete_all_data
+do_insert_tests e_insert-3.2 {
+    1.1    "INSERT INTO a3 DEFAULT VALUES"     {}
+    1.2    "SELECT * FROM a3"                  {1.0 string {}}
+
+    2.1    "INSERT INTO a3 DEFAULT VALUES"     {}
+    2.2    "SELECT * FROM a3"                  {1.0 string {} 1.0 string {}}
+
+    3.1    "INSERT INTO a2 DEFAULT VALUES"     {}
+    3.2    "SELECT * FROM a2"                  {{} {} xyz}
+
+    4.1    "INSERT INTO a2 DEFAULT VALUES"     {}
+    4.2    "SELECT * FROM a2"                  {{} {} xyz {} {} xyz}
+
+    5.1    "INSERT INTO a1 DEFAULT VALUES"     {}
+    5.2    "SELECT * FROM a1"                  {{} {}}
+
+    6.1    "INSERT INTO a1 DEFAULT VALUES"     {}
+    6.2    "SELECT * FROM a1"                  {{} {} {} {}}
+}
+
+
+
+delete_all_data
+
+finish_test
index a242a2a2dbc2e547869443b212b77a1a6a08983f..973cf1279f7fcf4fc26bad7fb9e5bca4bdbd6b20 100644 (file)
@@ -74,55 +74,6 @@ proc do_join_test {tn select res} {
   }
 }
 
-#
-#   Usage: do_select_tests PREFIX ?SWITCHES? TESTLIST
-#
-# Where switches are:
-#
-#   -errorformat FMTSTRING
-#
-proc do_select_tests {prefix args} {
-
-  set testlist [lindex $args end]
-  set switches [lrange $args 0 end-1]
-
-  set errfmt ""
-  set countonly 0
-
-  for {set i 0} {$i < [llength $switches]} {incr i} {
-    set s [lindex $switches $i]
-    set n [string length $s]
-    if {$n>=2 && [string equal -length $n $s "-errorformat"]} {
-      set errfmt [lindex $switches [incr i]]
-    } elseif {$n>=2 && [string equal -length $n $s "-count"]} {
-      set countonly 1
-    } else {
-      error "unknown switch: $s"
-    }
-  }
-
-  if {$countonly && $errfmt!=""} {
-    error "Cannot use -count and -errorformat together"
-  }
-  set nTestlist [llength $testlist]
-  if {$nTestlist%3 || $nTestlist==0 } {
-    error "SELECT test list contains [llength $testlist] elements"
-  }
-
-  foreach {tn sql res} $testlist {
-    if {$countonly} {
-      set nRow 0
-      db eval $sql {incr nRow}
-      uplevel do_test ${prefix}.$tn [list [list set {} $nRow]] [list $res]
-    } elseif {$errfmt==""} {
-      uplevel do_execsql_test ${prefix}.${tn} [list $sql] [list [list {*}$res]]
-    } else {
-      set res [list 1 [string trim [format $errfmt $res]]]
-      uplevel do_catchsql_test ${prefix}.${tn} [list $sql] [list $res]
-    }
-  }
-}
-
 #-------------------------------------------------------------------------
 # The following tests check that all paths on the syntax diagrams on
 # the lang_select.html page may be taken.
@@ -1894,11 +1845,11 @@ do_execsql_test e_select-7.1.0 {
 do_select_tests e_select-7.1 -error {
   SELECTs to the left and right of %s do not have the same number of result columns
 } {
-  1   "SELECT a, b FROM j1    UNION ALL SELECT g FROM j3"    {UNION ALL}
-  2   "SELECT *    FROM j1    UNION ALL SELECT * FROM j3"    {UNION ALL}
-  3   "SELECT a, b FROM j1    UNION ALL SELECT g FROM j3"    {UNION ALL}
-  4   "SELECT a, b FROM j1    UNION ALL SELECT * FROM j3,j2" {UNION ALL}
-  5   "SELECT *    FROM j3,j2 UNION ALL SELECT a, b FROM j1" {UNION ALL}
+  1   "SELECT a, b FROM j1    UNION ALL SELECT g FROM j3"    {{UNION ALL}}
+  2   "SELECT *    FROM j1    UNION ALL SELECT * FROM j3"    {{UNION ALL}}
+  3   "SELECT a, b FROM j1    UNION ALL SELECT g FROM j3"    {{UNION ALL}}
+  4   "SELECT a, b FROM j1    UNION ALL SELECT * FROM j3,j2" {{UNION ALL}}
+  5   "SELECT *    FROM j3,j2 UNION ALL SELECT a, b FROM j1" {{UNION ALL}}
 
   6   "SELECT a, b FROM j1    UNION SELECT g FROM j3"        {UNION}
   7   "SELECT *    FROM j1    UNION SELECT * FROM j3"        {UNION}
index f7cad5ec721ed32084fbf832dedc2395f5c08612..023a67f5832a21879ab3fc9146d3fa815ae622d1 100644 (file)
@@ -343,6 +343,56 @@ proc do_catchsql_test {testname sql result} {
   uplevel do_test $testname [list "catchsql {$sql}"] [list $result]
 }
 
+#-------------------------------------------------------------------------
+#   Usage: do_select_tests PREFIX ?SWITCHES? TESTLIST
+#
+# Where switches are:
+#
+#   -errorformat FMTSTRING
+#   -count
+#
+proc do_select_tests {prefix args} {
+
+  set testlist [lindex $args end]
+  set switches [lrange $args 0 end-1]
+
+  set errfmt ""
+  set countonly 0
+
+  for {set i 0} {$i < [llength $switches]} {incr i} {
+    set s [lindex $switches $i]
+    set n [string length $s]
+    if {$n>=2 && [string equal -length $n $s "-errorformat"]} {
+      set errfmt [lindex $switches [incr i]]
+    } elseif {$n>=2 && [string equal -length $n $s "-count"]} {
+      set countonly 1
+    } else {
+      error "unknown switch: $s"
+    }
+  }
+
+  if {$countonly && $errfmt!=""} {
+    error "Cannot use -count and -errorformat together"
+  }
+  set nTestlist [llength $testlist]
+  if {$nTestlist%3 || $nTestlist==0 } {
+    error "SELECT test list contains [llength $testlist] elements"
+  }
+
+  foreach {tn sql res} $testlist {
+    if {$countonly} {
+      set nRow 0
+      db eval $sql {incr nRow}
+      uplevel do_test ${prefix}.$tn [list [list set {} $nRow]] [list $res]
+    } elseif {$errfmt==""} {
+      uplevel do_execsql_test ${prefix}.${tn} [list $sql] [list [list {*}$res]]
+    } else {
+      set res [list 1 [string trim [format $errfmt {*}$res]]]
+      uplevel do_catchsql_test ${prefix}.${tn} [list $sql] [list $res]
+    }
+  }
+}
+
 
 # Run an SQL script.  
 # Return the number of microseconds per statement.