mirror of
https://github.com/status-im/sqlcipher.git
synced 2026-08-31 06:21:07 +00:00
Merge branch 'sqlite-release' into v2-integration
Conflicts: Makefile.in
This commit is contained in:
@@ -16,6 +16,7 @@ source $testdir/permutations.test
|
||||
|
||||
run_test_suite full
|
||||
|
||||
run_test_suite no_optimization
|
||||
run_test_suite memsubsys1
|
||||
run_test_suite memsubsys2
|
||||
run_test_suite singlethread
|
||||
|
||||
@@ -27,11 +27,6 @@ ifcapable {!pragma} return
|
||||
#
|
||||
do_not_use_codec
|
||||
|
||||
# These tests do not work if there is a codec.
|
||||
#
|
||||
#if {[catch {sqlite3 -has_codec} r] || $r} return
|
||||
#
|
||||
|
||||
# The file format change affects the way row-records stored in tables (but
|
||||
# not indices) are interpreted. Before version 3.1.3, a row-record for a
|
||||
# table with N columns was guaranteed to contain exactly N fields. As
|
||||
|
||||
+3
-1
@@ -28,7 +28,7 @@ ifcapable !altertable {
|
||||
|
||||
# Determine if there is a codec available on this test.
|
||||
#
|
||||
if {[catch {sqlite3 -has_codec} r] || $r} {
|
||||
if {[catch {sqlite3 -has-codec} r] || $r} {
|
||||
set has_codec 1
|
||||
} else {
|
||||
set has_codec 0
|
||||
@@ -55,6 +55,7 @@ proc get_file_format {{fname test.db}} {
|
||||
|
||||
do_test alter3-1.1 {
|
||||
execsql {
|
||||
PRAGMA legacy_file_format=ON;
|
||||
CREATE TABLE abc(a, b, c);
|
||||
SELECT sql FROM sqlite_master;
|
||||
}
|
||||
@@ -198,6 +199,7 @@ do_test alter3-4.1 {
|
||||
file delete -force test.db
|
||||
set ::DB [sqlite3 db test.db]
|
||||
execsql {
|
||||
PRAGMA legacy_file_format=ON;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 100);
|
||||
INSERT INTO t1 VALUES(2, 300);
|
||||
|
||||
@@ -26,14 +26,6 @@ ifcapable !altertable {
|
||||
return
|
||||
}
|
||||
|
||||
# Determine if there is a codec available on this test.
|
||||
#
|
||||
if {[catch {sqlite3 -has_codec} r] || $r} {
|
||||
set has_codec 1
|
||||
} else {
|
||||
set has_codec 0
|
||||
}
|
||||
|
||||
|
||||
# Test Organisation:
|
||||
# ------------------
|
||||
@@ -47,12 +39,6 @@ if {[catch {sqlite3 -has_codec} r] || $r} {
|
||||
# alter4-7.*: Test that VACUUM resets the file-format.
|
||||
#
|
||||
|
||||
# This procedure returns the value of the file-format in file 'test.db'.
|
||||
#
|
||||
proc get_file_format {{fname test.db}} {
|
||||
return [hexio_get_int [hexio_read $fname 44 4]]
|
||||
}
|
||||
|
||||
do_test alter4-1.1 {
|
||||
execsql {
|
||||
CREATE TEMP TABLE abc(a, b, c);
|
||||
@@ -182,11 +168,6 @@ do_test alter4-3.2 {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 {} 2 300 {}}
|
||||
if {!$has_codec} {
|
||||
do_test alter4-3.3 {
|
||||
get_file_format
|
||||
} {3}
|
||||
}
|
||||
ifcapable schema_version {
|
||||
do_test alter4-3.4 {
|
||||
execsql {
|
||||
@@ -217,11 +198,6 @@ do_test alter4-4.2 {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 100 {hello world} 2 300 {hello world}}
|
||||
if {!$has_codec} {
|
||||
do_test alter4-4.3 {
|
||||
get_file_format
|
||||
} {3}
|
||||
}
|
||||
ifcapable schema_version {
|
||||
do_test alter4-4.4 {
|
||||
execsql {
|
||||
@@ -267,11 +243,6 @@ ifcapable attach {
|
||||
}
|
||||
} {31}
|
||||
}
|
||||
if {!$has_codec} {
|
||||
do_test alter4-5.5 {
|
||||
list [get_file_format test2.db] [get_file_format]
|
||||
} {2 3}
|
||||
}
|
||||
do_test alter4-5.6 {
|
||||
execsql {
|
||||
ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000;
|
||||
@@ -333,42 +304,6 @@ ifcapable trigger&&tempdb {
|
||||
} {b 1 2 a 1 2 b 3 4 a 3 4}
|
||||
}
|
||||
|
||||
if {!$has_codec} {
|
||||
ifcapable vacuum {
|
||||
do_test alter4-7.1 {
|
||||
execsql {
|
||||
VACUUM;
|
||||
}
|
||||
get_file_format
|
||||
} {1}
|
||||
do_test alter4-7.2 {
|
||||
execsql {
|
||||
CREATE TEMP TABLE abc(a, b, c);
|
||||
ALTER TABLE abc ADD d DEFAULT NULL;
|
||||
}
|
||||
get_file_format
|
||||
} {2}
|
||||
do_test alter4-7.3 {
|
||||
execsql {
|
||||
ALTER TABLE abc ADD e DEFAULT 10;
|
||||
}
|
||||
get_file_format
|
||||
} {3}
|
||||
do_test alter4-7.4 {
|
||||
execsql {
|
||||
ALTER TABLE abc ADD f DEFAULT NULL;
|
||||
}
|
||||
get_file_format
|
||||
} {3}
|
||||
do_test alter4-7.5 {
|
||||
execsql {
|
||||
VACUUM;
|
||||
}
|
||||
get_file_format
|
||||
} {1}
|
||||
}
|
||||
}
|
||||
|
||||
# Ticket #1183 - Make sure adding columns to large tables does not cause
|
||||
# memory corruption (as was the case before this bug was fixed).
|
||||
do_test alter4-8.1 {
|
||||
|
||||
+7
-7
@@ -73,7 +73,7 @@ do_test analyze-1.6.3 {
|
||||
} {1 {table sqlite_stat1 may not be indexed}}
|
||||
do_test analyze-1.7 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1
|
||||
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
|
||||
}
|
||||
} {}
|
||||
do_test analyze-1.8 {
|
||||
@@ -83,7 +83,7 @@ do_test analyze-1.8 {
|
||||
} {0 {}}
|
||||
do_test analyze-1.9 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1
|
||||
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
|
||||
}
|
||||
} {}
|
||||
do_test analyze-1.10 {
|
||||
@@ -96,7 +96,7 @@ do_test analyze-1.11 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1
|
||||
}
|
||||
} {}
|
||||
} {t1 {} 0}
|
||||
do_test analyze-1.12 {
|
||||
catchsql {
|
||||
ANALYZE t1;
|
||||
@@ -106,7 +106,7 @@ do_test analyze-1.13 {
|
||||
execsql {
|
||||
SELECT * FROM sqlite_stat1
|
||||
}
|
||||
} {}
|
||||
} {t1 {} 0}
|
||||
|
||||
# Create some indices that can be analyzed. But do not yet add
|
||||
# data. Without data in the tables, no analysis is done.
|
||||
@@ -117,21 +117,21 @@ do_test analyze-2.1 {
|
||||
ANALYZE main.t1;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {}
|
||||
} {t1 {} 0}
|
||||
do_test analyze-2.2 {
|
||||
execsql {
|
||||
CREATE INDEX t1i2 ON t1(b);
|
||||
ANALYZE t1;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {}
|
||||
} {t1 {} 0}
|
||||
do_test analyze-2.3 {
|
||||
execsql {
|
||||
CREATE INDEX t1i3 ON t1(a,b);
|
||||
ANALYZE main;
|
||||
SELECT * FROM sqlite_stat1 ORDER BY idx;
|
||||
}
|
||||
} {}
|
||||
} {t1 {} 0}
|
||||
|
||||
# Start adding data to the table. Verify that the analysis
|
||||
# is done correctly.
|
||||
|
||||
+120
-78
@@ -22,6 +22,8 @@ ifcapable !stat2 {
|
||||
return
|
||||
}
|
||||
|
||||
set testprefix analyze2
|
||||
|
||||
# Do not use a codec for tests in this file, as the database file is
|
||||
# manipulated directly using tcl scripts (using the [hexio_write] command).
|
||||
#
|
||||
@@ -119,36 +121,56 @@ do_test analyze2-2.1 {
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
do_test analyze2-2.2 {
|
||||
eqp "SELECT * FROM t1 WHERE x>500 AND y>700"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_test analyze2-2.3 {
|
||||
eqp "SELECT * FROM t1 WHERE x>700 AND y>500"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_x}}
|
||||
do_test analyze2-2.3 {
|
||||
eqp "SELECT * FROM t1 WHERE y>700 AND x>500"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_test analyze2-2.4 {
|
||||
eqp "SELECT * FROM t1 WHERE y>500 AND x>700"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_x}}
|
||||
do_test analyze2-2.5 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_x}}
|
||||
do_test analyze2-2.6 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_test analyze2-2.7 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_x}}
|
||||
do_test analyze2-2.8 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN -400 AND -300"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_test analyze2-2.9 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN 500 AND 100 AND y BETWEEN 100 AND 300"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_x}}
|
||||
do_test analyze2-2.10 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN 500 AND 100"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_eqp_test 2.2 {
|
||||
SELECT * FROM t1 WHERE x>500 AND y>700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.3 {
|
||||
SELECT * FROM t1 WHERE x>700 AND y>500
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.3 {
|
||||
SELECT * FROM t1 WHERE y>700 AND x>500
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.4 {
|
||||
SELECT * FROM t1 WHERE y>500 AND x>700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)}
|
||||
}
|
||||
do_eqp_test 2.5 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~25 rows)}
|
||||
}
|
||||
do_eqp_test 2.6 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~75 rows)}
|
||||
}
|
||||
do_eqp_test 2.7 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~25 rows)}
|
||||
}
|
||||
do_eqp_test 2.8 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN -400 AND -300
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~25 rows)}
|
||||
}
|
||||
do_eqp_test 2.9 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 500 AND 100 AND y BETWEEN 100 AND 300
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~25 rows)}
|
||||
}
|
||||
do_eqp_test 2.10 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN 500 AND 100
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~25 rows)}
|
||||
}
|
||||
|
||||
do_test analyze2-3.1 {
|
||||
set alphabet [list a b c d e f g h i j]
|
||||
@@ -177,21 +199,31 @@ do_test analyze2-3.2 {
|
||||
}
|
||||
} {t1 t1_y {100 299 499 699 899 ajj cjj ejj gjj ijj}}
|
||||
|
||||
do_test analyze2-3.3 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b'"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_test analyze2-3.4 {
|
||||
eqp "SELECT * FROM t1 WHERE x BETWEEN 100 AND 400 AND y BETWEEN 'a' AND 'h'"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_x}}
|
||||
do_test analyze2-3.5 {
|
||||
eqp "SELECT * FROM t1 WHERE x<'a' AND y>'h'"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_test analyze2-3.6 {
|
||||
eqp "SELECT * FROM t1 WHERE x<444 AND y>'h'"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_y}}
|
||||
do_test analyze2-3.7 {
|
||||
eqp "SELECT * FROM t1 WHERE x<221 AND y>'g'"
|
||||
} {0 0 {TABLE t1 WITH INDEX t1_x}}
|
||||
do_eqp_test 3.3 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~50 rows)}
|
||||
}
|
||||
do_eqp_test 3.4 {
|
||||
SELECT * FROM t1 WHERE x BETWEEN 100 AND 400 AND y BETWEEN 'a' AND 'h'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~50 rows)}
|
||||
}
|
||||
do_eqp_test 3.5 {
|
||||
SELECT * FROM t1 WHERE x<'a' AND y>'h'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)}
|
||||
}
|
||||
do_eqp_test 3.6 {
|
||||
SELECT * FROM t1 WHERE x<444 AND y>'h'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)}
|
||||
}
|
||||
do_eqp_test 3.7 {
|
||||
SELECT * FROM t1 WHERE x<221 AND y>'g'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x<?) (~66 rows)}
|
||||
}
|
||||
|
||||
do_test analyze2-4.1 {
|
||||
execsql { CREATE TABLE t3(a COLLATE nocase, b) }
|
||||
@@ -225,12 +257,16 @@ do_test analyze2-4.3 {
|
||||
}
|
||||
} {t3 t3b {AbA CIj EIj GIj IIj bIj dIj fIj hIj jIj}}
|
||||
|
||||
do_test analyze2-4.4 {
|
||||
eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C'"
|
||||
} {0 0 {TABLE t3 WITH INDEX t3b}}
|
||||
do_test analyze2-4.5 {
|
||||
eqp "SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c'"
|
||||
} {0 0 {TABLE t3 WITH INDEX t3a}}
|
||||
do_eqp_test 4.4 {
|
||||
SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t3 USING INDEX t3b (b>? AND b<?) (~11 rows)}
|
||||
}
|
||||
do_eqp_test 4.5 {
|
||||
SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t3 USING INDEX t3a (a>? AND a<?) (~22 rows)}
|
||||
}
|
||||
|
||||
ifcapable utf16 {
|
||||
proc test_collate {enc lhs rhs} {
|
||||
@@ -260,15 +296,21 @@ ifcapable utf16 {
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {t4 t4x {afa bej cej dej eej fej gej hej iej jej}}
|
||||
do_test analyze2-5.3 {
|
||||
eqp "SELECT * FROM t4 WHERE x>'ccc'"
|
||||
} {0 0 {TABLE t4 WITH INDEX t4x}}
|
||||
do_test analyze2-5.4 {
|
||||
eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg'"
|
||||
} {0 1 {TABLE t4 AS t42 WITH INDEX t4x} 1 0 {TABLE t4 AS t41 WITH INDEX t4x}}
|
||||
do_test analyze2-5.5 {
|
||||
eqp "SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc'"
|
||||
} {0 0 {TABLE t4 AS t41 WITH INDEX t4x} 1 1 {TABLE t4 AS t42 WITH INDEX t4x}}
|
||||
do_eqp_test 5.3 {
|
||||
SELECT * FROM t4 WHERE x>'ccc'
|
||||
} {0 0 0 {SEARCH TABLE t4 USING COVERING INDEX t4x (x>?) (~800 rows)}}
|
||||
do_eqp_test 5.4 {
|
||||
SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg'
|
||||
} {
|
||||
0 0 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~300 rows)}
|
||||
0 1 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~800 rows)}
|
||||
}
|
||||
do_eqp_test 5.5 {
|
||||
SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc'
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~700 rows)}
|
||||
0 1 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~800 rows)}
|
||||
}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
@@ -306,7 +348,7 @@ do_test analyze2-6.1.1 {
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.2 {
|
||||
db cache flush
|
||||
execsql ANALYZE
|
||||
@@ -314,14 +356,14 @@ do_test analyze2-6.1.2 {
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.3 {
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.4 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
@@ -332,7 +374,7 @@ do_test analyze2-6.1.4 {
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.5 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
@@ -343,7 +385,7 @@ do_test analyze2-6.1.5 {
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.1.6 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
@@ -354,7 +396,7 @@ do_test analyze2-6.1.6 {
|
||||
t5.a = 1 AND
|
||||
t6.a = 1 AND t6.b = 1
|
||||
}
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
do_test analyze2-6.2.1 {
|
||||
execsql {
|
||||
@@ -366,7 +408,7 @@ do_test analyze2-6.2.1 {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~110000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.2 {
|
||||
db cache flush
|
||||
execsql ANALYZE
|
||||
@@ -374,14 +416,14 @@ do_test analyze2-6.2.2 {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.3 {
|
||||
sqlite3 db test.db
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.4 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
@@ -392,7 +434,7 @@ do_test analyze2-6.2.4 {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~110000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.5 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
@@ -403,7 +445,7 @@ do_test analyze2-6.2.5 {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~110000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-6.2.6 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = 1;
|
||||
@@ -415,7 +457,7 @@ do_test analyze2-6.2.6 {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
}
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# These tests, analyze2-7.*, test that the sqlite_stat2 functionality
|
||||
@@ -459,7 +501,7 @@ ifcapable shared_cache {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-7.6 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT * FROM sqlite_master } db2
|
||||
@@ -467,7 +509,7 @@ ifcapable shared_cache {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db2
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-7.7 {
|
||||
incr_schema_cookie test.db
|
||||
execsql { SELECT * FROM sqlite_master } db1
|
||||
@@ -475,7 +517,7 @@ ifcapable shared_cache {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
do_test analyze2-7.8 {
|
||||
execsql { DELETE FROM sqlite_stat2 } db2
|
||||
@@ -484,14 +526,14 @@ ifcapable shared_cache {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
do_test analyze2-7.9 {
|
||||
execsql { SELECT * FROM sqlite_master } db2
|
||||
eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db2
|
||||
} {0 1 {TABLE t6 WITH INDEX t6i} 1 0 {TABLE t5 USING PRIMARY KEY}}
|
||||
} {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
do_test analyze2-7.10 {
|
||||
incr_schema_cookie test.db
|
||||
@@ -500,7 +542,7 @@ ifcapable shared_cache {
|
||||
t5.a>1 AND t5.a<15 AND
|
||||
t6.a>1
|
||||
} db1
|
||||
} {0 0 {TABLE t5 WITH INDEX t5i} 1 1 {TABLE t6 USING PRIMARY KEY}}
|
||||
} {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~2 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
|
||||
|
||||
db1 close
|
||||
db2 close
|
||||
|
||||
+24
-24
@@ -95,12 +95,12 @@ do_test analyze3-1.1.1 {
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test analyze3-1.1.2 {
|
||||
eqp { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test analyze3-1.1.3 {
|
||||
eqp { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 }
|
||||
} {0 0 {TABLE t1}}
|
||||
do_eqp_test analyze3-1.1.2 {
|
||||
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~100 rows)}}
|
||||
do_eqp_test analyze3-1.1.3 {
|
||||
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SCAN TABLE t1 (~111 rows)}}
|
||||
|
||||
do_test analyze3-1.1.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
|
||||
@@ -144,12 +144,12 @@ do_test analyze3-1.2.1 {
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
do_test analyze3-1.2.2 {
|
||||
eqp { SELECT sum(y) FROM t2 WHERE x>1 AND x<2 }
|
||||
} {0 0 {TABLE t2 WITH INDEX i2}}
|
||||
do_test analyze3-1.2.3 {
|
||||
eqp { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 }
|
||||
} {0 0 {TABLE t2}}
|
||||
do_eqp_test analyze3-1.2.2 {
|
||||
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
|
||||
} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~200 rows)}}
|
||||
do_eqp_test analyze3-1.2.3 {
|
||||
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
|
||||
} {0 0 0 {SCAN TABLE t2 (~111 rows)}}
|
||||
do_test analyze3-1.2.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
|
||||
} {161 0 4760}
|
||||
@@ -191,12 +191,12 @@ do_test analyze3-1.3.1 {
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
do_test analyze3-1.3.2 {
|
||||
eqp { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
|
||||
} {0 0 {TABLE t3 WITH INDEX i3}}
|
||||
do_test analyze3-1.3.3 {
|
||||
eqp { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 }
|
||||
} {0 0 {TABLE t3}}
|
||||
do_eqp_test analyze3-1.3.2 {
|
||||
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~100 rows)}}
|
||||
do_eqp_test analyze3-1.3.3 {
|
||||
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
|
||||
} {0 0 0 {SCAN TABLE t3 (~111 rows)}}
|
||||
|
||||
do_test analyze3-1.3.4 {
|
||||
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
|
||||
@@ -246,12 +246,12 @@ do_test analyze3-2.1 {
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_test analyze3-2.2 {
|
||||
eqp { SELECT count(a) FROM t1 WHERE b LIKE 'a%' }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test analyze3-2.3 {
|
||||
eqp { SELECT count(a) FROM t1 WHERE b LIKE '%a' }
|
||||
} {0 0 {TABLE t1}}
|
||||
do_eqp_test analyze3-2.2 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE 'a%'
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~55000 rows)}}
|
||||
do_eqp_test analyze3-2.3 {
|
||||
SELECT count(a) FROM t1 WHERE b LIKE '%a'
|
||||
} {0 0 0 {SCAN TABLE t1 (~500000 rows)}}
|
||||
|
||||
do_test analyze3-2.4 {
|
||||
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' }
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
# 2011 January 04
|
||||
#
|
||||
# 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 regression tests for SQLite library. This file
|
||||
# implements tests for ANALYZE to verify that multiple rows containing
|
||||
# a NULL value count as distinct rows for the purposes of analyze
|
||||
# statistics.
|
||||
#
|
||||
# Also include test cases for collating sequences on indices.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test analyze4-1.0 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a,b);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
INSERT INTO t1 VALUES(1,NULL);
|
||||
INSERT INTO t1 SELECT a+1, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+2, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+4, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+8, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+16, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+32, b FROM t1;
|
||||
INSERT INTO t1 SELECT a+64, b FROM t1;
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
# Should choose the t1a index since it is more specific than t1b.
|
||||
db eval {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=5 AND b IS NULL}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
|
||||
|
||||
# Verify that the t1b index shows that it does not narrow down the
|
||||
# search any at all.
|
||||
#
|
||||
do_test analyze4-1.1 {
|
||||
db eval {
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
|
||||
}
|
||||
} {t1a {128 1} t1b {128 128}}
|
||||
|
||||
# Change half of the b values from NULL to a constant. Verify
|
||||
# that the number of rows selected in stat1 is half the total
|
||||
# number of rows.
|
||||
#
|
||||
do_test analyze4-1.2 {
|
||||
db eval {
|
||||
UPDATE t1 SET b='x' WHERE a%2;
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
|
||||
}
|
||||
} {t1a {128 1} t1b {128 64}}
|
||||
|
||||
# Change the t1.b values all back to NULL. Add columns t1.c and t1.d.
|
||||
# Create a multi-column indices using t1.b and verify that ANALYZE
|
||||
# processes them correctly.
|
||||
#
|
||||
do_test analyze4-1.3 {
|
||||
db eval {
|
||||
UPDATE t1 SET b=NULL;
|
||||
ALTER TABLE t1 ADD COLUMN c;
|
||||
ALTER TABLE t1 ADD COLUMN d;
|
||||
UPDATE t1 SET c=a/4, d=a/2;
|
||||
CREATE INDEX t1bcd ON t1(b,c,d);
|
||||
CREATE INDEX t1cdb ON t1(c,d,b);
|
||||
CREATE INDEX t1cbd ON t1(c,b,d);
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t1' ORDER BY idx;
|
||||
}
|
||||
} {t1a {128 1} t1b {128 128} t1bcd {128 128 4 2} t1cbd {128 4 4 2} t1cdb {128 4 2 2}}
|
||||
|
||||
# Verify that collating sequences are taken into account when computing
|
||||
# ANALYZE statistics.
|
||||
#
|
||||
do_test analyze4-2.0 {
|
||||
db eval {
|
||||
CREATE TABLE t2(
|
||||
x INTEGER PRIMARY KEY,
|
||||
a TEXT COLLATE nocase,
|
||||
b TEXT COLLATE rtrim,
|
||||
c TEXT COLLATE binary
|
||||
);
|
||||
CREATE INDEX t2a ON t2(a);
|
||||
CREATE INDEX t2b ON t2(b);
|
||||
CREATE INDEX t2c ON t2(c);
|
||||
CREATE INDEX t2c2 ON t2(c COLLATE nocase);
|
||||
CREATE INDEX t2c3 ON t2(c COLLATE rtrim);
|
||||
INSERT INTO t2 VALUES(1, 'abc', 'abc', 'abc');
|
||||
INSERT INTO t2 VALUES(2, 'abC', 'abC', 'abC');
|
||||
INSERT INTO t2 VALUES(3, 'abc ', 'abc ', 'abc ');
|
||||
INSERT INTO t2 VALUES(4, 'abC ', 'abC ', 'abC ');
|
||||
INSERT INTO t2 VALUES(5, 'aBc', 'aBc', 'aBc');
|
||||
INSERT INTO t2 VALUES(6, 'aBC', 'aBC', 'aBC');
|
||||
INSERT INTO t2 VALUES(7, 'aBc ', 'aBc ', 'aBc ');
|
||||
INSERT INTO t2 VALUES(8, 'aBC ', 'aBC ', 'aBC ');
|
||||
ANALYZE;
|
||||
SELECT idx, stat FROM sqlite_stat1 WHERE tbl='t2' ORDER BY idx;
|
||||
}
|
||||
} {t2a {8 4} t2b {8 2} t2c {8 1} t2c2 {8 4} t2c3 {8 2}}
|
||||
|
||||
finish_test
|
||||
@@ -26,8 +26,9 @@ ifcapable !memdebug||!attach {
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
do_malloc_test attachmalloc-1 -tclprep {
|
||||
db close
|
||||
catch { db close }
|
||||
for {set i 2} {$i<=4} {incr i} {
|
||||
catch { db$i close }
|
||||
file delete -force test$i.db
|
||||
file delete -force test$i.db-journal
|
||||
}
|
||||
@@ -61,13 +62,13 @@ do_malloc_test attachmalloc-2 -tclprep {
|
||||
}
|
||||
|
||||
set enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
sqlite3 dbaux test2.db
|
||||
sqlite3 dbaux test3.db
|
||||
dbaux eval {SELECT * FROM sqlite_master}
|
||||
do_malloc_test attachmalloc-3 -sqlbody {
|
||||
SELECT * FROM sqlite_master;
|
||||
ATTACH 'test2.db' AS two;
|
||||
ATTACH 'test3.db' AS three;
|
||||
} -cleanup {
|
||||
db eval { DETACH two }
|
||||
db eval { DETACH three }
|
||||
}
|
||||
dbaux close
|
||||
sqlite3_enable_shared_cache $enable_shared_cache
|
||||
|
||||
+3
-3
@@ -1976,12 +1976,12 @@ ifcapable analyze {
|
||||
ANALYZE;
|
||||
}
|
||||
set ::authargs
|
||||
} {t4 {} main {}}
|
||||
} {t4 {} main {} t2 {} main {}}
|
||||
do_test auth-1.295 {
|
||||
execsql {
|
||||
SELECT count(*) FROM sqlite_stat1;
|
||||
}
|
||||
} 2
|
||||
} 3
|
||||
proc auth {code args} {
|
||||
if {$code=="SQLITE_ANALYZE"} {
|
||||
set ::authargs [concat $::authargs $args]
|
||||
@@ -1999,7 +1999,7 @@ ifcapable analyze {
|
||||
execsql {
|
||||
SELECT count(*) FROM sqlite_stat1;
|
||||
}
|
||||
} 2
|
||||
} 3
|
||||
} ;# ifcapable analyze
|
||||
|
||||
|
||||
|
||||
+107
-25
@@ -140,30 +140,112 @@ do_test autoindex1-401 {
|
||||
# Make sure automatic indices are not created for the RHS of an IN expression
|
||||
# that is not a correlated subquery.
|
||||
#
|
||||
do_test autoindex1-500 {
|
||||
db eval {
|
||||
CREATE TABLE t501(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t502(x INTEGER PRIMARY KEY, y);
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t501
|
||||
WHERE t501.a IN (SELECT x FROM t502 WHERE y=?);
|
||||
}
|
||||
} {0 0 {TABLE t501 USING PRIMARY KEY} 0 0 {TABLE t502}}
|
||||
do_test autoindex1-501 {
|
||||
db eval {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t501
|
||||
WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
|
||||
}
|
||||
} {0 0 {TABLE t501} 0 0 {TABLE t502 WITH AUTOMATIC INDEX}}
|
||||
do_test autoindex1-502 {
|
||||
db eval {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t501
|
||||
WHERE t501.a=123
|
||||
AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
|
||||
}
|
||||
} {0 0 {TABLE t501 USING PRIMARY KEY} 0 0 {TABLE t502}}
|
||||
|
||||
do_execsql_test autoindex1-500 {
|
||||
CREATE TABLE t501(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t502(x INTEGER PRIMARY KEY, y);
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t501
|
||||
WHERE t501.a IN (SELECT x FROM t502 WHERE y=?);
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)}
|
||||
0 0 0 {EXECUTE LIST SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t502 (~100000 rows)}
|
||||
}
|
||||
do_execsql_test autoindex1-501 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t501
|
||||
WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t501 (~500000 rows)}
|
||||
0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
|
||||
1 0 0 {SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?) (~7 rows)}
|
||||
}
|
||||
do_execsql_test autoindex1-502 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT b FROM t501
|
||||
WHERE t501.a=123
|
||||
AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t502 (~100000 rows)}
|
||||
}
|
||||
|
||||
|
||||
# The following code checks a performance regression reported on the
|
||||
# mailing list on 2010-10-19. The problem is that the nRowEst field
|
||||
# of ephermeral tables was not being initialized correctly and so no
|
||||
# automatic index was being created for the emphemeral table when it was
|
||||
# used as part of a join.
|
||||
#
|
||||
do_execsql_test autoindex1-600 {
|
||||
CREATE TABLE flock_owner(
|
||||
owner_rec_id INTEGER CONSTRAINT flock_owner_key PRIMARY KEY,
|
||||
flock_no VARCHAR(6) NOT NULL REFERENCES flock (flock_no),
|
||||
owner_person_id INTEGER NOT NULL REFERENCES person (person_id),
|
||||
owner_change_date TEXT, last_changed TEXT NOT NULL,
|
||||
CONSTRAINT fo_owner_date UNIQUE (flock_no, owner_change_date)
|
||||
);
|
||||
CREATE TABLE sheep (
|
||||
Sheep_No char(7) NOT NULL,
|
||||
Date_of_Birth char(8),
|
||||
Sort_DoB text,
|
||||
Flock_Book_Vol char(2),
|
||||
Breeder_No char(6),
|
||||
Breeder_Person integer,
|
||||
Originating_Flock char(6),
|
||||
Registering_Flock char(6),
|
||||
Tag_Prefix char(9),
|
||||
Tag_No char(15),
|
||||
Sort_Tag_No integer,
|
||||
Breeders_Temp_Tag char(15),
|
||||
Sex char(1),
|
||||
Sheep_Name char(32),
|
||||
Sire_No char(7),
|
||||
Dam_No char(7),
|
||||
Register_Code char(1),
|
||||
Colour char(48),
|
||||
Colour_Code char(2),
|
||||
Pattern_Code char(8),
|
||||
Horns char(1),
|
||||
Litter_Size char(1),
|
||||
Coeff_of_Inbreeding real,
|
||||
Date_of_Registration text,
|
||||
Date_Last_Changed text,
|
||||
UNIQUE(Sheep_No));
|
||||
CREATE INDEX fo_flock_no_index
|
||||
ON flock_owner (flock_no);
|
||||
CREATE INDEX fo_owner_change_date_index
|
||||
ON flock_owner (owner_change_date);
|
||||
CREATE INDEX fo_owner_person_id_index
|
||||
ON flock_owner (owner_person_id);
|
||||
CREATE INDEX sheep_org_flock_index
|
||||
ON sheep (originating_flock);
|
||||
CREATE INDEX sheep_reg_flock_index
|
||||
ON sheep (registering_flock);
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT x.sheep_no, x.registering_flock, x.date_of_registration
|
||||
FROM sheep x LEFT JOIN
|
||||
(SELECT s.sheep_no, prev.flock_no, prev.owner_person_id,
|
||||
s.date_of_registration, prev.owner_change_date
|
||||
FROM sheep s JOIN flock_owner prev ON s.registering_flock =
|
||||
prev.flock_no
|
||||
AND (prev.owner_change_date <= s.date_of_registration || ' 00:00:00')
|
||||
WHERE NOT EXISTS
|
||||
(SELECT 'x' FROM flock_owner later
|
||||
WHERE prev.flock_no = later.flock_no
|
||||
AND later.owner_change_date > prev.owner_change_date
|
||||
AND later.owner_change_date <= s.date_of_registration||' 00:00:00')
|
||||
) y ON x.sheep_no = y.sheep_no
|
||||
WHERE y.sheep_no IS NULL
|
||||
ORDER BY x.registering_flock;
|
||||
} {
|
||||
1 0 0 {SCAN TABLE sheep AS s (~1000000 rows)}
|
||||
1 1 1 {SEARCH TABLE flock_owner AS prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?) (~2 rows)}
|
||||
1 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2}
|
||||
2 0 0 {SEARCH TABLE flock_owner AS later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?) (~1 rows)}
|
||||
0 0 0 {SCAN TABLE sheep AS x USING INDEX sheep_reg_flock_index (~1000000 rows)}
|
||||
0 1 1 {SEARCH SUBQUERY 1 AS y USING AUTOMATIC COVERING INDEX (sheep_no=?) (~8 rows)}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+99
-1
@@ -39,7 +39,7 @@ if {$tcl_platform(platform)=="windows"} {
|
||||
set pattern [string map {\.exe {}} $pattern]
|
||||
}
|
||||
foreach file [glob -nocomplain $pattern] {
|
||||
if {[file executable $file]} {lappend binaries $file}
|
||||
if {[file executable $file] && [file isfile $file]} {lappend binaries $file}
|
||||
}
|
||||
if {[llength $binaries]==0} {
|
||||
puts "WARNING: No historical binaries to test against."
|
||||
@@ -271,4 +271,102 @@ do_allbackcompat_test {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that FTS3 tables may be read/written by different versions of
|
||||
# SQLite.
|
||||
#
|
||||
set contents {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3(a, b);
|
||||
}
|
||||
foreach {num doc} {
|
||||
one "jk zm jk eczkjblu urvysbnykk sk gnl jk ttvgf hmjf"
|
||||
two "jk bnhc jjrxpjkb mjpavjuhw fibokdry igju jk zm zm xh"
|
||||
three "wxe ogttbykvt uhzq xr iaf zf urvysbnykk aayxpmve oacaxgjoo mjpavjuhw"
|
||||
four "gazrt jk ephknonq myjp uenvbm wuvajhwqz jk zm xnxhf nvfasfh"
|
||||
five "zm aayxpmve csjqxhgj xnxhf xr jk aayxpmve xnxhf zm zm"
|
||||
six "sokcyf zm ogyavjvv jk zm fibokdry zm jk igju igju"
|
||||
seven "vgsld bvgimjik xuprtlyle jk akmikrqyt jk aayxpmve hkfoudzftq ddjj"
|
||||
eight "zm uhzq ovkyevlgv zk uenvbm csjqxhgj jk vgsld pgybs jk"
|
||||
nine "zm agmckuiu zexh fibokdry jk uhzq bu tugflixoex xnxhf sk"
|
||||
} {
|
||||
append contents "INSERT INTO t1 VALUES('$num', '$doc');"
|
||||
}
|
||||
do_allbackcompat_test {
|
||||
if {[code1 {set ::sqlite_options(fts3)}]
|
||||
&& [code2 {set ::sqlite_options(fts3)}]
|
||||
} {
|
||||
|
||||
do_test backcompat-3.1 { sql1 $contents } {}
|
||||
|
||||
foreach {n q} {
|
||||
1 "SELECT * FROM t1 ORDER BY a, b"
|
||||
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
|
||||
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
|
||||
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
|
||||
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
|
||||
} {
|
||||
do_test backcompat-3.2 [list sql1 $q] [sql2 $q]
|
||||
}
|
||||
|
||||
do_test backcompat-3.3 { sql1 {
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
INSERT INTO t1 SELECT * FROM t1;
|
||||
} } {}
|
||||
|
||||
foreach {n q} {
|
||||
1 "SELECT * FROM t1 ORDER BY a, b"
|
||||
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
|
||||
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
|
||||
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
|
||||
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
|
||||
} {
|
||||
do_test backcompat-3.4 [list sql1 $q] [sql2 $q]
|
||||
}
|
||||
|
||||
set alphabet "a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4"
|
||||
for {set i 0} {$i < 900} {incr i} {
|
||||
set term "[lindex $alphabet [expr $i/30]][lindex $alphabet [expr $i%30]] "
|
||||
sql1 "INSERT INTO t1 VALUES($i, '[string repeat $term 14]')"
|
||||
}
|
||||
|
||||
foreach {n q} {
|
||||
1 "SELECT * FROM t1 ORDER BY a, b"
|
||||
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
|
||||
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
|
||||
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
|
||||
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
|
||||
|
||||
6 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'aa'"
|
||||
7 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH '44'"
|
||||
8 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'a*'"
|
||||
} {
|
||||
do_test backcompat-3.5 [list sql1 $q] [sql2 $q]
|
||||
}
|
||||
|
||||
do_test backcompat-3.6 {
|
||||
sql1 "SELECT optimize(t1) FROM t1 LIMIT 1"
|
||||
} {{Index optimized}}
|
||||
|
||||
foreach {n q} {
|
||||
1 "SELECT * FROM t1 ORDER BY a, b"
|
||||
2 "SELECT rowid FROM t1 WHERE a MATCH 'five'"
|
||||
3 "SELECT * FROM t1 WHERE a MATCH 'five'"
|
||||
4 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'jk'"
|
||||
5 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'tug* OR eight'"
|
||||
|
||||
6 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'aa'"
|
||||
7 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH '44'"
|
||||
8 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'a*'"
|
||||
} {
|
||||
do_test backcompat-3.7 [list sql1 $q] [sql2 $q]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+1
-1
@@ -177,7 +177,7 @@ foreach nPagePerStep {1 200} {
|
||||
# page size is the same as the source page size (in this case 1024 bytes).
|
||||
#
|
||||
set isMemDest [expr {
|
||||
$zDestFile eq ":memory:" || $file_dest eq "temp" && $TEMP_STORE==3
|
||||
$zDestFile eq ":memory:" || $file_dest eq "temp" && $TEMP_STORE>=2
|
||||
}]
|
||||
|
||||
if { $isMemDest==0 || $pgsz_dest == 1024 } {
|
||||
|
||||
+80
-3
@@ -51,13 +51,90 @@ do_test cache-1.2 {
|
||||
#
|
||||
set cache_size [pager_cache_size db]
|
||||
for {set ii 0} {$ii < 10} {incr ii} {
|
||||
|
||||
do_test cache-1.3.$ii {
|
||||
execsql {SELECT * FROM abc}
|
||||
pager_cache_size db
|
||||
} $::cache_size
|
||||
|
||||
}
|
||||
sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This block of tests checks that it is possible to set the cache_size of a
|
||||
# database to a small (< 10) value. More specifically:
|
||||
#
|
||||
# cache-2.1.*: Test that "PRAGMA cache_size" appears to work with small
|
||||
# values.
|
||||
# cache-2.2.*: Test that "PRAGMA main.cache_size" appears to work with
|
||||
# small values.
|
||||
# cache-2.3.*: Test cache_size=1 correctly spills/flushes the cache.
|
||||
# cache-2.4.*: Test cache_size=0 correctly spills/flushes the cache.
|
||||
#
|
||||
#
|
||||
db_delete_and_reopen
|
||||
do_execsql_test cache-2.0 {
|
||||
PRAGMA auto_vacuum=OFF;
|
||||
PRAGMA journal_mode=DELETE;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(c, d);
|
||||
INSERT INTO t1 VALUES('x', 'y');
|
||||
INSERT INTO t2 VALUES('i', 'j');
|
||||
} {delete}
|
||||
|
||||
for {set i 0} {$i < 20} {incr i} {
|
||||
do_execsql_test cache-2.1.$i.1 "PRAGMA cache_size = $i"
|
||||
do_execsql_test cache-2.1.$i.2 "PRAGMA cache_size" $i
|
||||
do_execsql_test cache-2.1.$i.3 "SELECT * FROM t1" {x y}
|
||||
do_execsql_test cache-2.1.$i.4 "PRAGMA cache_size" $i
|
||||
}
|
||||
for {set i 0} {$i < 20} {incr i} {
|
||||
do_execsql_test cache-2.2.$i.1 "PRAGMA main.cache_size = $i"
|
||||
do_execsql_test cache-2.2.$i.2 "PRAGMA main.cache_size" $i
|
||||
do_execsql_test cache-2.2.$i.3 "SELECT * FROM t1" {x y}
|
||||
do_execsql_test cache-2.2.$i.4 "PRAGMA main.cache_size" $i
|
||||
}
|
||||
|
||||
# Tests for cache_size = 1.
|
||||
#
|
||||
do_execsql_test cache-2.3.1 {
|
||||
PRAGMA cache_size = 1;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
PRAGMA lock_status;
|
||||
} {main reserved temp closed}
|
||||
do_test cache-2.3.2 { pager_cache_size db } 2
|
||||
do_execsql_test cache-2.3.3 {
|
||||
INSERT INTO t2 VALUES(1, 2);
|
||||
PRAGMA lock_status;
|
||||
} {main exclusive temp closed}
|
||||
do_test cache-2.3.4 { pager_cache_size db } 2
|
||||
do_execsql_test cache-2.3.5 COMMIT
|
||||
do_test cache-2.3.6 { pager_cache_size db } 1
|
||||
|
||||
do_execsql_test cache-2.3.7 {
|
||||
SELECT * FROM t1 UNION SELECT * FROM t2;
|
||||
} {1 2 i j x y}
|
||||
do_test cache-2.3.8 { pager_cache_size db } 1
|
||||
|
||||
# Tests for cache_size = 0.
|
||||
#
|
||||
do_execsql_test cache-2.4.1 {
|
||||
PRAGMA cache_size = 0;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
PRAGMA lock_status;
|
||||
} {main reserved temp closed}
|
||||
do_test cache-2.4.2 { pager_cache_size db } 2
|
||||
do_execsql_test cache-2.4.3 {
|
||||
INSERT INTO t2 VALUES(1, 2);
|
||||
PRAGMA lock_status;
|
||||
} {main exclusive temp closed}
|
||||
do_test cache-2.4.4 { pager_cache_size db } 2
|
||||
do_execsql_test cache-2.4.5 COMMIT
|
||||
|
||||
do_test cache-2.4.6 { pager_cache_size db } 0
|
||||
do_execsql_test cache-2.4.7 {
|
||||
SELECT * FROM t1 UNION SELECT * FROM t2;
|
||||
} {1 2 i j x y}
|
||||
do_test cache-2.4.8 { pager_cache_size db } 0
|
||||
|
||||
sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
|
||||
finish_test
|
||||
|
||||
+9
-3
@@ -74,9 +74,15 @@ do_test capi2-1.7 {
|
||||
|
||||
# This used to be SQLITE_MISUSE. But now we automatically reset prepared
|
||||
# statements.
|
||||
do_test capi2-1.8 {
|
||||
sqlite3_step $VM
|
||||
} {SQLITE_ROW}
|
||||
ifcapable autoreset {
|
||||
do_test capi2-1.8 {
|
||||
sqlite3_step $VM
|
||||
} {SQLITE_ROW}
|
||||
} else {
|
||||
do_test capi2-1.8 {
|
||||
sqlite3_step $VM
|
||||
} {SQLITE_MISUSE}
|
||||
}
|
||||
|
||||
# Update: In v2, once SQLITE_MISUSE is returned the statement handle cannot
|
||||
# be interrogated for more information. However in v3, since the column
|
||||
|
||||
+25
-2
@@ -10,7 +10,8 @@
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
# This file is devoted to testing the sqlite3_next_stmt interface.
|
||||
# This file is devoted to testing the sqlite3_next_stmt and
|
||||
# sqlite3_stmt_readonly interfaces.
|
||||
#
|
||||
# $Id: capi3d.test,v 1.2 2008/07/14 15:11:20 drh Exp $
|
||||
#
|
||||
@@ -88,6 +89,28 @@ for {set i 1} {$i<=100} {incr i} {
|
||||
sqlite3_next_stmt db 0
|
||||
} {}
|
||||
}
|
||||
|
||||
|
||||
# Tests for the is-read-only interface.
|
||||
#
|
||||
proc test_is_readonly {testname sql truth} {
|
||||
do_test $testname [format {
|
||||
set DB [sqlite3_connection_pointer db]
|
||||
set STMT [sqlite3_prepare $DB {%s} -1 TAIL]
|
||||
set rc [sqlite3_stmt_readonly $STMT]
|
||||
sqlite3_finalize $STMT
|
||||
set rc
|
||||
} $sql] $truth
|
||||
}
|
||||
|
||||
test_is_readonly capi3d-2.1 {SELECT * FROM sqlite_master} 1
|
||||
test_is_readonly capi3d-2.2 {CREATE TABLE t1(x)} 0
|
||||
db eval {CREATE TABLE t1(x)}
|
||||
test_is_readonly capi3d-2.3 {INSERT INTO t1 VALUES(5)} 0
|
||||
test_is_readonly capi3d-2.4 {UPDATE t1 SET x=x+1 WHERE x<0} 0
|
||||
test_is_readonly capi3d-2.5 {SELECT * FROM t1} 1
|
||||
do_test capi3-2.99 {
|
||||
sqlite3_stmt_readonly 0
|
||||
} 1
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# 2010 Novemeber 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 regression tests for SQLite library. The
|
||||
# focus of this script testing the callback-free C/C++ API.
|
||||
#
|
||||
# $Id: capi3e.test,v 1.70 2009/01/09 02:49:32 drh Exp $
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Make sure the system encoding is utf-8. Otherwise, if the system encoding
|
||||
# is other than utf-8, [file isfile $x] may not refer to the same file
|
||||
# as [sqlite3 db $x].
|
||||
encoding system utf-8
|
||||
|
||||
# Do not use a codec for tests in this file, as the database file is
|
||||
# manipulated directly using tcl scripts (using the [hexio_write] command).
|
||||
#
|
||||
do_not_use_codec
|
||||
|
||||
# Return the UTF-16 representation of the supplied UTF-8 string $str.
|
||||
# If $nt is true, append two 0x00 bytes as a nul terminator.
|
||||
proc utf16 {str {nt 1}} {
|
||||
set r [encoding convertto unicode $str]
|
||||
if {$nt} {
|
||||
append r "\x00\x00"
|
||||
}
|
||||
return $r
|
||||
}
|
||||
|
||||
# Return the UTF-8 representation of the supplied UTF-16 string $str.
|
||||
proc utf8 {str} {
|
||||
# If $str ends in two 0x00 0x00 bytes, knock these off before
|
||||
# converting to UTF-8 using TCL.
|
||||
binary scan $str \c* vals
|
||||
if {[lindex $vals end]==0 && [lindex $vals end-1]==0} {
|
||||
set str [binary format \c* [lrange $vals 0 end-2]]
|
||||
}
|
||||
|
||||
set r [encoding convertfrom unicode $str]
|
||||
return $r
|
||||
}
|
||||
|
||||
# These tests complement those in capi2.test. They are organized
|
||||
# as follows:
|
||||
#
|
||||
# capi3e-1.*: Test sqlite3_open with various UTF8 filenames
|
||||
# capi3e-2.*: Test sqlite3_open16 with various UTF8 filenames
|
||||
# capi3e-3.*: Test ATTACH with various UTF8 filenames
|
||||
|
||||
db close
|
||||
|
||||
# here's the list of file names we're testing
|
||||
set names {t 1 t. 1. t.d 1.d t-1 1-1 t.db ä.db ë.db ö.db ü.db ÿ.db}
|
||||
|
||||
set i 0
|
||||
foreach name $names {
|
||||
incr i
|
||||
do_test capi3e-1.1.$i {
|
||||
set db2 [sqlite3_open $name {}]
|
||||
sqlite3_errcode $db2
|
||||
} {SQLITE_OK}
|
||||
do_test capi3e-1.2.$i {
|
||||
sqlite3_close $db2
|
||||
} {SQLITE_OK}
|
||||
do_test capi3e-1.3.$i {
|
||||
file isfile $name
|
||||
} {1}
|
||||
}
|
||||
|
||||
set i 0
|
||||
foreach name $names {
|
||||
incr i
|
||||
do_test capi3e-2.1.$i {
|
||||
set db2 [sqlite3_open16 [utf16 $name] {}]
|
||||
sqlite3_errcode $db2
|
||||
} {SQLITE_OK}
|
||||
do_test capi3e-2.2.$i {
|
||||
sqlite3_close $db2
|
||||
} {SQLITE_OK}
|
||||
do_test capi3e-2.3.$i {
|
||||
file isfile $name
|
||||
} {1}
|
||||
}
|
||||
|
||||
ifcapable attach {
|
||||
do_test capi3e-3.1 {
|
||||
sqlite3 db2 base.db
|
||||
} {}
|
||||
set i 0
|
||||
foreach name $names {
|
||||
incr i
|
||||
do_test capi3e-3.2.$i {
|
||||
db2 eval "ATTACH DATABASE '$name' AS db$i;"
|
||||
} {}
|
||||
do_test capi3e-3.3.$i {
|
||||
db2 eval "DETACH DATABASE db$i;"
|
||||
} {}
|
||||
}
|
||||
do_test capi3e-3.4 {
|
||||
db2 close
|
||||
} {}
|
||||
}
|
||||
|
||||
# clean up
|
||||
forcedelete base.db
|
||||
foreach name $names {
|
||||
forcedelete $name
|
||||
}
|
||||
|
||||
finish_test
|
||||
+1
-1
@@ -307,7 +307,7 @@ foreach {i conf1 cmd t0 t1 t2 t3 t4} {
|
||||
16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0
|
||||
} {
|
||||
if {$t0} {set t1 {column a is not unique}}
|
||||
if {[info exists TEMP_STORE] && $TEMP_STORE>=2} {
|
||||
if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
|
||||
set t3 0
|
||||
} else {
|
||||
set t3 [expr {$t3+$t4}]
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ for {set i 0} {$i<1000} {incr i} {
|
||||
datetest 2.3 {date('2003-10-22','weekday 0')} 2003-10-26
|
||||
datetest 2.4 {date('2003-10-22','weekday 1')} 2003-10-27
|
||||
datetest 2.4a {date('2003-10-22','weekday 1')} 2003-10-27
|
||||
datetest 2.4b {date('2003-10-22','weekday 1x')} 2003-10-27
|
||||
datetest 2.4b {date('2003-10-22','weekday 1x')} NULL
|
||||
datetest 2.4c {date('2003-10-22','weekday -1')} NULL
|
||||
datetest 2.4d {date('2003-10-22','weakday 1x')} NULL
|
||||
datetest 2.4e {date('2003-10-22','weekday ')} NULL
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Memory statistics must be enabled for this test.
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_memstatus 1
|
||||
sqlite3_initialize
|
||||
sqlite3 db test.db
|
||||
|
||||
|
||||
# Make sure sqlite3_db_config() and sqlite3_db_status are working.
|
||||
#
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,470 @@
|
||||
# 2010 September 21
|
||||
#
|
||||
# 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_delete.html document are correct.
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
proc do_delete_tests {args} {
|
||||
uplevel do_select_tests $args
|
||||
}
|
||||
|
||||
do_execsql_test e_delete-0.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-24177-52883 -- syntax diagram delete-stmt
|
||||
#
|
||||
# EVIDENCE-OF: R-12802-60464 -- syntax diagram qualified-table-name
|
||||
#
|
||||
do_delete_tests e_delete-0.1 {
|
||||
1 "DELETE FROM t1" {}
|
||||
2 "DELETE FROM t1 INDEXED BY i1" {}
|
||||
3 "DELETE FROM t1 NOT INDEXED" {}
|
||||
4 "DELETE FROM main.t1" {}
|
||||
5 "DELETE FROM main.t1 INDEXED BY i1" {}
|
||||
6 "DELETE FROM main.t1 NOT INDEXED" {}
|
||||
7 "DELETE FROM t1 WHERE a>2" {}
|
||||
8 "DELETE FROM t1 INDEXED BY i1 WHERE a>2" {}
|
||||
9 "DELETE FROM t1 NOT INDEXED WHERE a>2" {}
|
||||
10 "DELETE FROM main.t1 WHERE a>2" {}
|
||||
11 "DELETE FROM main.t1 INDEXED BY i1 WHERE a>2" {}
|
||||
12 "DELETE FROM main.t1 NOT INDEXED WHERE a>2" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-20205-17349 If the WHERE clause is not present, all
|
||||
# records in the table are deleted.
|
||||
#
|
||||
drop_all_tables
|
||||
do_test e_delete-1.0 {
|
||||
db transaction {
|
||||
foreach t {t1 t2 t3 t4 t5 t6} {
|
||||
execsql [string map [list %T% $t] {
|
||||
CREATE TABLE %T%(x, y);
|
||||
INSERT INTO %T% VALUES(1, 'one');
|
||||
INSERT INTO %T% VALUES(2, 'two');
|
||||
INSERT INTO %T% VALUES(3, 'three');
|
||||
INSERT INTO %T% VALUES(4, 'four');
|
||||
INSERT INTO %T% VALUES(5, 'five');
|
||||
}]
|
||||
}
|
||||
}
|
||||
} {}
|
||||
do_delete_tests e_delete-1.1 {
|
||||
1 "DELETE FROM t1 ; SELECT * FROM t1" {}
|
||||
2 "DELETE FROM main.t2 ; SELECT * FROM t2" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-30203-16177 If a WHERE clause is supplied, then only
|
||||
# those rows for which the result of evaluating the WHERE clause as a
|
||||
# boolean expression is true are deleted.
|
||||
#
|
||||
do_delete_tests e_delete-1.2 {
|
||||
1 "DELETE FROM t3 WHERE 1 ; SELECT x FROM t3" {}
|
||||
2 "DELETE FROM main.t4 WHERE 0 ; SELECT x FROM t4" {1 2 3 4 5}
|
||||
3 "DELETE FROM t4 WHERE 0.0 ; SELECT x FROM t4" {1 2 3 4 5}
|
||||
4 "DELETE FROM t4 WHERE NULL ; SELECT x FROM t4" {1 2 3 4 5}
|
||||
5 "DELETE FROM t4 WHERE y!='two'; SELECT x FROM t4" {2}
|
||||
6 "DELETE FROM t4 WHERE y='two' ; SELECT x FROM t4" {}
|
||||
7 "DELETE FROM t5 WHERE x=(SELECT max(x) FROM t5);SELECT x FROM t5" {1 2 3 4}
|
||||
8 "DELETE FROM t5 WHERE (SELECT max(x) FROM t4) ;SELECT x FROM t5" {1 2 3 4}
|
||||
9 "DELETE FROM t5 WHERE (SELECT max(x) FROM t6) ;SELECT x FROM t5" {}
|
||||
10 "DELETE FROM t6 WHERE y>'seven' ; SELECT y FROM t6" {one four five}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tests for restrictions on DELETE statements that appear within trigger
|
||||
# programs.
|
||||
#
|
||||
forcedelete test.db2
|
||||
forcedelete test.db3
|
||||
do_execsql_test e_delete-2.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
ATTACH 'test.db3' AS aux2;
|
||||
|
||||
CREATE TABLE temp.t7(a, b); INSERT INTO temp.t7 VALUES(1, 2);
|
||||
CREATE TABLE main.t7(a, b); INSERT INTO main.t7 VALUES(3, 4);
|
||||
CREATE TABLE aux.t7(a, b); INSERT INTO aux.t7 VALUES(5, 6);
|
||||
CREATE TABLE aux2.t7(a, b); INSERT INTO aux2.t7 VALUES(7, 8);
|
||||
|
||||
CREATE TABLE main.t8(a, b); INSERT INTO main.t8 VALUES(1, 2);
|
||||
CREATE TABLE aux.t8(a, b); INSERT INTO aux.t8 VALUES(3, 4);
|
||||
CREATE TABLE aux2.t8(a, b); INSERT INTO aux2.t8 VALUES(5, 6);
|
||||
|
||||
CREATE TABLE aux.t9(a, b); INSERT INTO aux.t9 VALUES(1, 2);
|
||||
CREATE TABLE aux2.t9(a, b); INSERT INTO aux2.t9 VALUES(3, 4);
|
||||
|
||||
CREATE TABLE aux2.t10(a, b); INSERT INTO aux2.t10 VALUES(1, 2);
|
||||
} {}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-09681-58560 The table-name specified as part of a
|
||||
# DELETE statement within a trigger body must be unqualified.
|
||||
#
|
||||
# EVIDENCE-OF: R-36771-43788 In other words, the database-name. prefix
|
||||
# on the table name is not allowed within triggers.
|
||||
#
|
||||
do_delete_tests e_delete-2.1 -error {
|
||||
qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers
|
||||
} {
|
||||
1 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
DELETE FROM main.t2;
|
||||
END;
|
||||
} {}
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER tr1 BEFORE UPDATE ON t2 BEGIN
|
||||
DELETE FROM temp.t7 WHERE a=new.a;
|
||||
END;
|
||||
} {}
|
||||
|
||||
3 {
|
||||
CREATE TRIGGER tr1 AFTER UPDATE ON t8 BEGIN
|
||||
DELETE FROM aux2.t8 WHERE b!=a;
|
||||
END;
|
||||
} {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-28818-63526 If the table to which the trigger is
|
||||
# attached is not in the temp database, then DELETE statements within
|
||||
# the trigger body must operate on tables within the same database as
|
||||
# it.
|
||||
#
|
||||
# This is tested in two parts. First, check that if a table of the
|
||||
# specified name does not exist, an error is raised. Secondly, test
|
||||
# that if tables with the specified name exist in multiple databases,
|
||||
# the local database table is used.
|
||||
#
|
||||
do_delete_tests e_delete-2.2.1 -error { no such table: %s } {
|
||||
1 {
|
||||
CREATE TRIGGER main.tr1 AFTER INSERT ON main.t7 BEGIN
|
||||
DELETE FROM t9;
|
||||
END;
|
||||
INSERT INTO main.t7 VALUES(1, 2);
|
||||
} {main.t9}
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER aux.tr2 BEFORE UPDATE ON t9 BEGIN
|
||||
DELETE FROM t10;
|
||||
END;
|
||||
UPDATE t9 SET a=1;
|
||||
} {aux.t10}
|
||||
}
|
||||
do_execsql_test e_delete-2.2.X {
|
||||
DROP TRIGGER main.tr1;
|
||||
DROP TRIGGER aux.tr2;
|
||||
} {}
|
||||
|
||||
do_delete_tests e_delete-2.2.2 {
|
||||
1 {
|
||||
CREATE TRIGGER aux.tr1 AFTER INSERT ON t8 BEGIN
|
||||
DELETE FROM t9;
|
||||
END;
|
||||
INSERT INTO aux.t8 VALUES(1, 2);
|
||||
|
||||
SELECT count(*) FROM aux.t9
|
||||
UNION ALL
|
||||
SELECT count(*) FROM aux2.t9;
|
||||
} {0 1}
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER main.tr1 AFTER INSERT ON t8 BEGIN
|
||||
DELETE FROM t7;
|
||||
END;
|
||||
INSERT INTO main.t8 VALUES(1, 2);
|
||||
|
||||
SELECT count(*) FROM temp.t7
|
||||
UNION ALL
|
||||
SELECT count(*) FROM main.t7
|
||||
UNION ALL
|
||||
SELECT count(*) FROM aux.t7
|
||||
UNION ALL
|
||||
SELECT count(*) FROM aux2.t7;
|
||||
} {1 0 1 1}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-31567-38587 If the table to which the trigger is
|
||||
# attached is in the TEMP database, then the unqualified name of the
|
||||
# table being deleted is resolved in the same way as it is for a
|
||||
# top-level statement (by searching first the TEMP database, then the
|
||||
# main database, then any other databases in the order they were
|
||||
# attached).
|
||||
#
|
||||
do_execsql_test e_delete-2.3.0 {
|
||||
DROP TRIGGER aux.tr1;
|
||||
DROP TRIGGER main.tr1;
|
||||
DELETE FROM main.t8 WHERE oid>1;
|
||||
DELETE FROM aux.t8 WHERE oid>1;
|
||||
INSERT INTO aux.t9 VALUES(1, 2);
|
||||
INSERT INTO main.t7 VALUES(3, 4);
|
||||
} {}
|
||||
do_execsql_test e_delete-2.3.1 {
|
||||
SELECT count(*) FROM temp.t7 UNION ALL SELECT count(*) FROM main.t7 UNION ALL
|
||||
SELECT count(*) FROM aux.t7 UNION ALL SELECT count(*) FROM aux2.t7;
|
||||
|
||||
SELECT count(*) FROM main.t8 UNION ALL SELECT count(*) FROM aux.t8
|
||||
UNION ALL SELECT count(*) FROM aux2.t8;
|
||||
|
||||
SELECT count(*) FROM aux.t9 UNION ALL SELECT count(*) FROM aux2.t9;
|
||||
|
||||
SELECT count(*) FROM aux2.t10;
|
||||
} {1 1 1 1 1 1 1 1 1 1}
|
||||
do_execsql_test e_delete-2.3.2 {
|
||||
CREATE TRIGGER temp.tr1 AFTER INSERT ON t7 BEGIN
|
||||
DELETE FROM t7;
|
||||
DELETE FROM t8;
|
||||
DELETE FROM t9;
|
||||
DELETE FROM t10;
|
||||
END;
|
||||
INSERT INTO temp.t7 VALUES('hello', 'world');
|
||||
} {}
|
||||
do_execsql_test e_delete-2.3.3 {
|
||||
SELECT count(*) FROM temp.t7 UNION ALL SELECT count(*) FROM main.t7 UNION ALL
|
||||
SELECT count(*) FROM aux.t7 UNION ALL SELECT count(*) FROM aux2.t7;
|
||||
|
||||
SELECT count(*) FROM main.t8 UNION ALL SELECT count(*) FROM aux.t8
|
||||
UNION ALL SELECT count(*) FROM aux2.t8;
|
||||
|
||||
SELECT count(*) FROM aux.t9 UNION ALL SELECT count(*) FROM aux2.t9;
|
||||
|
||||
SELECT count(*) FROM aux2.t10;
|
||||
} {0 1 1 1 0 1 1 0 1 0}
|
||||
|
||||
# EVIDENCE-OF: R-28691-49464 The INDEXED BY and NOT INDEXED clauses are
|
||||
# not allowed on DELETE statements within triggers.
|
||||
#
|
||||
do_execsql_test e_delete-2.4.0 {
|
||||
CREATE INDEX i8 ON t8(a, b);
|
||||
} {}
|
||||
do_delete_tests e_delete-2.4 -error {
|
||||
the %s %s clause is not allowed on UPDATE or DELETE statements within triggers
|
||||
} {
|
||||
1 {
|
||||
CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN
|
||||
DELETE FROM t8 INDEXED BY i8 WHERE a=5;
|
||||
END;
|
||||
} {INDEXED BY}
|
||||
2 {
|
||||
CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN
|
||||
DELETE FROM t8 NOT INDEXED WHERE a=5;
|
||||
END;
|
||||
} {NOT INDEXED}
|
||||
}
|
||||
|
||||
ifcapable update_delete_limit {
|
||||
|
||||
# EVIDENCE-OF: R-64942-06615 The LIMIT and ORDER BY clauses (described
|
||||
# below) are unsupported for DELETE statements within triggers.
|
||||
#
|
||||
do_delete_tests e_delete-2.5 -error { near "%s": syntax error } {
|
||||
1 {
|
||||
CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN
|
||||
DELETE FROM t8 LIMIT 10;
|
||||
END;
|
||||
} {LIMIT}
|
||||
2 {
|
||||
CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN
|
||||
DELETE FROM t8 ORDER BY a LIMIT 5;
|
||||
END;
|
||||
} {ORDER}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-40026-10531 If SQLite is compiled with the
|
||||
# SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile-time option, then the syntax
|
||||
# of the DELETE statement is extended by the addition of optional ORDER
|
||||
# BY and LIMIT clauses:
|
||||
#
|
||||
# EVIDENCE-OF: R-45897-01670 -- syntax diagram delete-stmt-limited
|
||||
#
|
||||
do_delete_tests e_delete-3.1 {
|
||||
1 "DELETE FROM t1 LIMIT 5" {}
|
||||
2 "DELETE FROM t1 LIMIT 5-1 OFFSET 2+2" {}
|
||||
3 "DELETE FROM t1 LIMIT 2+2, 16/4" {}
|
||||
4 "DELETE FROM t1 ORDER BY x LIMIT 5" {}
|
||||
5 "DELETE FROM t1 ORDER BY x LIMIT 5-1 OFFSET 2+2" {}
|
||||
6 "DELETE FROM t1 ORDER BY x LIMIT 2+2, 16/4" {}
|
||||
7 "DELETE FROM t1 WHERE x>2 LIMIT 5" {}
|
||||
8 "DELETE FROM t1 WHERE x>2 LIMIT 5-1 OFFSET 2+2" {}
|
||||
9 "DELETE FROM t1 WHERE x>2 LIMIT 2+2, 16/4" {}
|
||||
10 "DELETE FROM t1 WHERE x>2 ORDER BY x LIMIT 5" {}
|
||||
11 "DELETE FROM t1 WHERE x>2 ORDER BY x LIMIT 5-1 OFFSET 2+2" {}
|
||||
12 "DELETE FROM t1 WHERE x>2 ORDER BY x LIMIT 2+2, 16/4" {}
|
||||
}
|
||||
|
||||
drop_all_tables
|
||||
proc rebuild_t1 {} {
|
||||
catchsql { DROP TABLE t1 }
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-44062-08550 If a DELETE statement has a LIMIT clause,
|
||||
# the maximum number of rows that will be deleted is found by evaluating
|
||||
# the accompanying expression and casting it to an integer value.
|
||||
#
|
||||
rebuild_t1
|
||||
do_delete_tests e_delete-3.2 -repair rebuild_t1 -query {
|
||||
SELECT a FROM t1
|
||||
} {
|
||||
1 "DELETE FROM t1 LIMIT 3" {4 5}
|
||||
2 "DELETE FROM t1 LIMIT 1+1" {3 4 5}
|
||||
3 "DELETE FROM t1 LIMIT '4'" {5}
|
||||
4 "DELETE FROM t1 LIMIT '1.0'" {2 3 4 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-02661-56399 If the result of the evaluating the LIMIT
|
||||
# clause cannot be losslessly converted to an integer value, it is an
|
||||
# error.
|
||||
#
|
||||
do_delete_tests e_delete-3.3 -error { datatype mismatch } {
|
||||
1 "DELETE FROM t1 LIMIT 'abc'" {}
|
||||
2 "DELETE FROM t1 LIMIT NULL" {}
|
||||
3 "DELETE FROM t1 LIMIT X'ABCD'" {}
|
||||
4 "DELETE FROM t1 LIMIT 1.2" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-00598-03741 A negative LIMIT value is interpreted as
|
||||
# "no limit".
|
||||
#
|
||||
do_delete_tests e_delete-3.4 -repair rebuild_t1 -query {
|
||||
SELECT a FROM t1
|
||||
} {
|
||||
1 "DELETE FROM t1 LIMIT -1" {}
|
||||
2 "DELETE FROM t1 LIMIT 2-4" {}
|
||||
3 "DELETE FROM t1 LIMIT -4.0" {}
|
||||
4 "DELETE FROM t1 LIMIT 5*-1" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-26377-49195 If the DELETE statement also has an OFFSET
|
||||
# clause, then it is similarly evaluated and cast to an integer value.
|
||||
# Again, it is an error if the value cannot be losslessly converted to
|
||||
# an integer.
|
||||
#
|
||||
do_delete_tests e_delete-3.5 -error { datatype mismatch } {
|
||||
1 "DELETE FROM t1 LIMIT 1 OFFSET 'abc'" {}
|
||||
2 "DELETE FROM t1 LIMIT 1 OFFSET NULL" {}
|
||||
3 "DELETE FROM t1 LIMIT 1 OFFSET X'ABCD'" {}
|
||||
4 "DELETE FROM t1 LIMIT 1 OFFSET 1.2" {}
|
||||
5 "DELETE FROM t1 LIMIT 'abc', 1" {}
|
||||
6 "DELETE FROM t1 LIMIT NULL, 1" {}
|
||||
7 "DELETE FROM t1 LIMIT X'ABCD', 1" {}
|
||||
8 "DELETE FROM t1 LIMIT 1.2, 1" {}
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-64004-53814 If there is no OFFSET clause, or the
|
||||
# calculated integer value is negative, the effective OFFSET value is
|
||||
# zero.
|
||||
#
|
||||
do_delete_tests e_delete-3.6 -repair rebuild_t1 -query {
|
||||
SELECT a FROM t1
|
||||
} {
|
||||
1a "DELETE FROM t1 LIMIT 3 OFFSET 0" {4 5}
|
||||
1b "DELETE FROM t1 LIMIT 3" {4 5}
|
||||
1c "DELETE FROM t1 LIMIT 3 OFFSET -1" {4 5}
|
||||
2a "DELETE FROM t1 LIMIT 1+1 OFFSET 0" {3 4 5}
|
||||
2b "DELETE FROM t1 LIMIT 1+1" {3 4 5}
|
||||
2c "DELETE FROM t1 LIMIT 1+1 OFFSET 2-5" {3 4 5}
|
||||
3a "DELETE FROM t1 LIMIT '4' OFFSET 0" {5}
|
||||
3b "DELETE FROM t1 LIMIT '4'" {5}
|
||||
3c "DELETE FROM t1 LIMIT '4' OFFSET -1.0" {5}
|
||||
4a "DELETE FROM t1 LIMIT '1.0' OFFSET 0" {2 3 4 5}
|
||||
4b "DELETE FROM t1 LIMIT '1.0'" {2 3 4 5}
|
||||
4c "DELETE FROM t1 LIMIT '1.0' OFFSET -11" {2 3 4 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-48141-52334 If the DELETE statement has an ORDER BY
|
||||
# clause, then all rows that would be deleted in the absence of the
|
||||
# LIMIT clause are sorted according to the ORDER BY. The first M rows,
|
||||
# where M is the value found by evaluating the OFFSET clause expression,
|
||||
# are skipped, and the following N, where N is the value of the LIMIT
|
||||
# expression, are deleted.
|
||||
#
|
||||
do_delete_tests e_delete-3.7 -repair rebuild_t1 -query {
|
||||
SELECT a FROM t1
|
||||
} {
|
||||
1 "DELETE FROM t1 ORDER BY b LIMIT 2" {1 2 3}
|
||||
2 "DELETE FROM t1 ORDER BY length(b), a LIMIT 3" {3 5}
|
||||
3 "DELETE FROM t1 ORDER BY a DESC LIMIT 1 OFFSET 0" {1 2 3 4}
|
||||
4 "DELETE FROM t1 ORDER BY a DESC LIMIT 1 OFFSET 1" {1 2 3 5}
|
||||
5 "DELETE FROM t1 ORDER BY a DESC LIMIT 1 OFFSET 2" {1 2 4 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-64535-08414 If there are less than N rows remaining
|
||||
# after taking the OFFSET clause into account, or if the LIMIT clause
|
||||
# evaluated to a negative value, then all remaining rows are deleted.
|
||||
#
|
||||
do_delete_tests e_delete-3.8 -repair rebuild_t1 -query {
|
||||
SELECT a FROM t1
|
||||
} {
|
||||
1 "DELETE FROM t1 ORDER BY a ASC LIMIT 10" {}
|
||||
2 "DELETE FROM t1 ORDER BY a ASC LIMIT -1" {}
|
||||
3 "DELETE FROM t1 ORDER BY a ASC LIMIT 4 OFFSET 2" {1 2}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-37284-06965 If the DELETE statement has no ORDER BY
|
||||
# clause, then all rows that would be deleted in the absence of the
|
||||
# LIMIT clause are assembled in an arbitrary order before applying the
|
||||
# LIMIT and OFFSET clauses to determine the subset that are actually
|
||||
# deleted.
|
||||
#
|
||||
# In practice, the "arbitrary order" is rowid order.
|
||||
#
|
||||
do_delete_tests e_delete-3.9 -repair rebuild_t1 -query {
|
||||
SELECT a FROM t1
|
||||
} {
|
||||
1 "DELETE FROM t1 LIMIT 2" {3 4 5}
|
||||
2 "DELETE FROM t1 LIMIT 3" {4 5}
|
||||
3 "DELETE FROM t1 LIMIT 1 OFFSET 0" {2 3 4 5}
|
||||
4 "DELETE FROM t1 LIMIT 1 OFFSET 1" {1 3 4 5}
|
||||
5 "DELETE FROM t1 LIMIT 1 OFFSET 2" {1 2 4 5}
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-26627-30313 The ORDER BY clause on an DELETE statement
|
||||
# is used only to determine which rows fall within the LIMIT. The order
|
||||
# in which rows are deleted is arbitrary and is not influenced by the
|
||||
# ORDER BY clause.
|
||||
#
|
||||
# In practice, rows are always deleted in rowid order.
|
||||
#
|
||||
do_delete_tests e_delete-3.10 -repair {
|
||||
rebuild_t1
|
||||
catchsql { DROP TABLE t1log }
|
||||
execsql {
|
||||
CREATE TABLE t1log(x);
|
||||
CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN
|
||||
INSERT INTO t1log VALUES(old.a);
|
||||
END;
|
||||
}
|
||||
} -query {
|
||||
SELECT x FROM t1log
|
||||
} {
|
||||
1 "DELETE FROM t1 ORDER BY a DESC LIMIT 2" {4 5}
|
||||
2 "DELETE FROM t1 ORDER BY a DESC LIMIT -1" {1 2 3 4 5}
|
||||
3 "DELETE FROM t1 ORDER BY a ASC LIMIT 2" {1 2}
|
||||
4 "DELETE FROM t1 ORDER BY a ASC LIMIT -1" {1 2 3 4 5}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,218 @@
|
||||
# 2010 November 29
|
||||
#
|
||||
# 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_droptrigger.html document are correct.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix e_droptrigger
|
||||
|
||||
ifcapable !trigger { finish_test ; return }
|
||||
|
||||
proc do_droptrigger_tests {nm args} {
|
||||
uplevel do_select_tests [list e_createtable-$nm] $args
|
||||
}
|
||||
|
||||
proc list_all_triggers {{db db}} {
|
||||
set res [list]
|
||||
$db eval { PRAGMA database_list } {
|
||||
if {$name == "temp"} {
|
||||
set tbl sqlite_temp_master
|
||||
} else {
|
||||
set tbl "$name.sqlite_master"
|
||||
}
|
||||
lappend res {*}[
|
||||
db eval "SELECT '$name.' || name FROM $tbl WHERE type = 'trigger'"
|
||||
]
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
|
||||
proc droptrigger_reopen_db {{event INSERT}} {
|
||||
db close
|
||||
forcedelete test.db test.db2
|
||||
sqlite3 db test.db
|
||||
|
||||
set ::triggers_fired [list]
|
||||
proc r {x} { lappend ::triggers_fired $x }
|
||||
db func r r
|
||||
|
||||
db eval "
|
||||
ATTACH 'test.db2' AS aux;
|
||||
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
CREATE TRIGGER tr1 AFTER $event ON t1 BEGIN SELECT r('temp.tr1') ; END;
|
||||
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t2 VALUES('a', 'b');
|
||||
CREATE TRIGGER tr1 BEFORE $event ON t2 BEGIN SELECT r('main.tr1') ; END;
|
||||
CREATE TRIGGER tr2 AFTER $event ON t2 BEGIN SELECT r('main.tr2') ; END;
|
||||
|
||||
CREATE TABLE aux.t3(a, b);
|
||||
INSERT INTO t3 VALUES('a', 'b');
|
||||
CREATE TRIGGER aux.tr1 BEFORE $event ON t3 BEGIN SELECT r('aux.tr1') ; END;
|
||||
CREATE TRIGGER aux.tr2 AFTER $event ON t3 BEGIN SELECT r('aux.tr2') ; END;
|
||||
CREATE TRIGGER aux.tr3 AFTER $event ON t3 BEGIN SELECT r('aux.tr3') ; END;
|
||||
"
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-52650-16855 -- syntax diagram drop-trigger-stmt
|
||||
#
|
||||
do_droptrigger_tests 1.1 -repair {
|
||||
droptrigger_reopen_db
|
||||
} -tclquery {
|
||||
list_all_triggers
|
||||
} {
|
||||
1 "DROP TRIGGER main.tr1"
|
||||
{main.tr2 temp.tr1 aux.tr1 aux.tr2 aux.tr3}
|
||||
2 "DROP TRIGGER IF EXISTS main.tr1"
|
||||
{main.tr2 temp.tr1 aux.tr1 aux.tr2 aux.tr3}
|
||||
3 "DROP TRIGGER tr1"
|
||||
{main.tr1 main.tr2 aux.tr1 aux.tr2 aux.tr3}
|
||||
4 "DROP TRIGGER IF EXISTS tr1"
|
||||
{main.tr1 main.tr2 aux.tr1 aux.tr2 aux.tr3}
|
||||
|
||||
5 "DROP TRIGGER aux.tr1"
|
||||
{main.tr1 main.tr2 temp.tr1 aux.tr2 aux.tr3}
|
||||
6 "DROP TRIGGER IF EXISTS aux.tr1"
|
||||
{main.tr1 main.tr2 temp.tr1 aux.tr2 aux.tr3}
|
||||
|
||||
7 "DROP TRIGGER IF EXISTS aux.xxx"
|
||||
{main.tr1 main.tr2 temp.tr1 aux.tr1 aux.tr2 aux.tr3}
|
||||
8 "DROP TRIGGER IF EXISTS aux.xxx"
|
||||
{main.tr1 main.tr2 temp.tr1 aux.tr1 aux.tr2 aux.tr3}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-61172-15671 The DROP TRIGGER statement removes a
|
||||
# trigger created by the CREATE TRIGGER statement.
|
||||
#
|
||||
foreach {tn tbl droptrigger before after} {
|
||||
1 t1 "DROP TRIGGER tr1" {temp.tr1} {}
|
||||
2 t2 "DROP TRIGGER tr1" {main.tr1 main.tr2} {main.tr1 main.tr2}
|
||||
3 t3 "DROP TRIGGER tr1" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr3 aux.tr2}
|
||||
|
||||
4 t1 "DROP TRIGGER tr2" {temp.tr1} {temp.tr1}
|
||||
5 t2 "DROP TRIGGER tr2" {main.tr1 main.tr2} {main.tr1}
|
||||
6 t3 "DROP TRIGGER tr2" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr3 aux.tr2}
|
||||
|
||||
7 t1 "DROP TRIGGER tr3" {temp.tr1} {temp.tr1}
|
||||
8 t2 "DROP TRIGGER tr3" {main.tr1 main.tr2} {main.tr1 main.tr2}
|
||||
9 t3 "DROP TRIGGER tr3" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr2}
|
||||
} {
|
||||
|
||||
do_test 2.$tn.1 {
|
||||
droptrigger_reopen_db
|
||||
execsql " INSERT INTO $tbl VALUES('1', '2') "
|
||||
set ::triggers_fired
|
||||
} $before
|
||||
|
||||
do_test 2.$tn.2 {
|
||||
droptrigger_reopen_db
|
||||
execsql $droptrigger
|
||||
execsql " INSERT INTO $tbl VALUES('1', '2') "
|
||||
set ::triggers_fired
|
||||
} $after
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-50239-29811 Once removed, the trigger definition is no
|
||||
# longer present in the sqlite_master (or sqlite_temp_master) table and
|
||||
# is not fired by any subsequent INSERT, UPDATE or DELETE statements.
|
||||
#
|
||||
# Test cases e_droptrigger-1.* test the first part of this statement
|
||||
# (that dropped triggers do not appear in the schema table), and tests
|
||||
# droptrigger-2.* test that dropped triggers are not fired by INSERT
|
||||
# statements. The following tests verify that they are not fired by
|
||||
# UPDATE or DELETE statements.
|
||||
#
|
||||
foreach {tn tbl droptrigger before after} {
|
||||
1 t1 "DROP TRIGGER tr1" {temp.tr1} {}
|
||||
2 t2 "DROP TRIGGER tr1" {main.tr1 main.tr2} {main.tr1 main.tr2}
|
||||
3 t3 "DROP TRIGGER tr1" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr3 aux.tr2}
|
||||
|
||||
4 t1 "DROP TRIGGER tr2" {temp.tr1} {temp.tr1}
|
||||
5 t2 "DROP TRIGGER tr2" {main.tr1 main.tr2} {main.tr1}
|
||||
6 t3 "DROP TRIGGER tr2" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr3 aux.tr2}
|
||||
|
||||
7 t1 "DROP TRIGGER tr3" {temp.tr1} {temp.tr1}
|
||||
8 t2 "DROP TRIGGER tr3" {main.tr1 main.tr2} {main.tr1 main.tr2}
|
||||
9 t3 "DROP TRIGGER tr3" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr2}
|
||||
} {
|
||||
|
||||
do_test 3.1.$tn.1 {
|
||||
droptrigger_reopen_db UPDATE
|
||||
execsql "UPDATE $tbl SET a = 'abc'"
|
||||
set ::triggers_fired
|
||||
} $before
|
||||
|
||||
do_test 3.1.$tn.2 {
|
||||
droptrigger_reopen_db UPDATE
|
||||
execsql $droptrigger
|
||||
execsql "UPDATE $tbl SET a = 'abc'"
|
||||
set ::triggers_fired
|
||||
} $after
|
||||
}
|
||||
foreach {tn tbl droptrigger before after} {
|
||||
1 t1 "DROP TRIGGER tr1" {temp.tr1} {}
|
||||
2 t2 "DROP TRIGGER tr1" {main.tr1 main.tr2} {main.tr1 main.tr2}
|
||||
3 t3 "DROP TRIGGER tr1" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr3 aux.tr2}
|
||||
|
||||
4 t1 "DROP TRIGGER tr2" {temp.tr1} {temp.tr1}
|
||||
5 t2 "DROP TRIGGER tr2" {main.tr1 main.tr2} {main.tr1}
|
||||
6 t3 "DROP TRIGGER tr2" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr3 aux.tr2}
|
||||
|
||||
7 t1 "DROP TRIGGER tr3" {temp.tr1} {temp.tr1}
|
||||
8 t2 "DROP TRIGGER tr3" {main.tr1 main.tr2} {main.tr1 main.tr2}
|
||||
9 t3 "DROP TRIGGER tr3" {aux.tr1 aux.tr3 aux.tr2} {aux.tr1 aux.tr2}
|
||||
} {
|
||||
|
||||
do_test 3.2.$tn.1 {
|
||||
droptrigger_reopen_db DELETE
|
||||
execsql "DELETE FROM $tbl"
|
||||
set ::triggers_fired
|
||||
} $before
|
||||
|
||||
do_test 3.2.$tn.2 {
|
||||
droptrigger_reopen_db DELETE
|
||||
execsql $droptrigger
|
||||
execsql "DELETE FROM $tbl"
|
||||
set ::triggers_fired
|
||||
} $after
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-37808-62273 Note that triggers are automatically
|
||||
# dropped when the associated table is dropped.
|
||||
#
|
||||
do_test 4.1 {
|
||||
droptrigger_reopen_db
|
||||
list_all_triggers
|
||||
} {main.tr1 main.tr2 temp.tr1 aux.tr1 aux.tr2 aux.tr3}
|
||||
do_test 4.2 {
|
||||
droptrigger_reopen_db
|
||||
execsql "DROP TABLE t1"
|
||||
list_all_triggers
|
||||
} {main.tr1 main.tr2 aux.tr1 aux.tr2 aux.tr3}
|
||||
do_test 4.3 {
|
||||
droptrigger_reopen_db
|
||||
execsql "DROP TABLE t1"
|
||||
list_all_triggers
|
||||
} {main.tr1 main.tr2 aux.tr1 aux.tr2 aux.tr3}
|
||||
do_test 4.4 {
|
||||
droptrigger_reopen_db
|
||||
execsql "DROP TABLE t1"
|
||||
list_all_triggers
|
||||
} {main.tr1 main.tr2 aux.tr1 aux.tr2 aux.tr3}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,192 @@
|
||||
# 2010 November 30
|
||||
#
|
||||
# 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_dropview.html document are correct.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix e_dropview
|
||||
|
||||
proc dropview_reopen_db {} {
|
||||
db close
|
||||
forcedelete test.db test.db2
|
||||
sqlite3 db test.db
|
||||
|
||||
db eval {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES('a main', 'b main');
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
CREATE VIEW v2 AS SELECT * FROM t1;
|
||||
|
||||
CREATE TEMP TABLE t1(a, b);
|
||||
INSERT INTO temp.t1 VALUES('a temp', 'b temp');
|
||||
CREATE VIEW temp.v1 AS SELECT * FROM t1;
|
||||
|
||||
CREATE TABLE aux.t1(a, b);
|
||||
INSERT INTO aux.t1 VALUES('a aux', 'b aux');
|
||||
CREATE VIEW aux.v1 AS SELECT * FROM t1;
|
||||
CREATE VIEW aux.v2 AS SELECT * FROM t1;
|
||||
CREATE VIEW aux.v3 AS SELECT * FROM t1;
|
||||
}
|
||||
}
|
||||
|
||||
proc list_all_views {{db db}} {
|
||||
set res [list]
|
||||
$db eval { PRAGMA database_list } {
|
||||
set tbl "$name.sqlite_master"
|
||||
if {$name == "temp"} { set tbl sqlite_temp_master }
|
||||
|
||||
set sql "SELECT '$name.' || name FROM $tbl WHERE type = 'view'"
|
||||
lappend res {*}[$db eval $sql]
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
proc list_all_data {{db db}} {
|
||||
set res [list]
|
||||
$db eval { PRAGMA database_list } {
|
||||
set tbl "$name.sqlite_master"
|
||||
if {$name == "temp"} { set tbl sqlite_temp_master }
|
||||
|
||||
db eval "SELECT '$name.' || name AS x FROM $tbl WHERE type = 'table'" {
|
||||
lappend res [list $x [db eval "SELECT * FROM $x"]]
|
||||
}
|
||||
}
|
||||
set res
|
||||
}
|
||||
|
||||
proc do_dropview_tests {nm args} {
|
||||
uplevel do_select_tests $nm $args
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-21739-51207 -- syntax diagram drop-view-stmt
|
||||
#
|
||||
# All paths in the syntax diagram for DROP VIEW are tested by tests 1.*.
|
||||
#
|
||||
do_dropview_tests 1 -repair {
|
||||
dropview_reopen_db
|
||||
} -tclquery {
|
||||
list_all_views
|
||||
} {
|
||||
1 "DROP VIEW v1" {main.v1 main.v2 aux.v1 aux.v2 aux.v3}
|
||||
2 "DROP VIEW v2" {main.v1 temp.v1 aux.v1 aux.v2 aux.v3}
|
||||
3 "DROP VIEW main.v1" {main.v2 temp.v1 aux.v1 aux.v2 aux.v3}
|
||||
4 "DROP VIEW main.v2" {main.v1 temp.v1 aux.v1 aux.v2 aux.v3}
|
||||
5 "DROP VIEW IF EXISTS v1" {main.v1 main.v2 aux.v1 aux.v2 aux.v3}
|
||||
6 "DROP VIEW IF EXISTS v2" {main.v1 temp.v1 aux.v1 aux.v2 aux.v3}
|
||||
7 "DROP VIEW IF EXISTS main.v1" {main.v2 temp.v1 aux.v1 aux.v2 aux.v3}
|
||||
8 "DROP VIEW IF EXISTS main.v2" {main.v1 temp.v1 aux.v1 aux.v2 aux.v3}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-27002-52307 The DROP VIEW statement removes a view
|
||||
# created by the CREATE VIEW statement.
|
||||
#
|
||||
dropview_reopen_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE VIEW "new view" AS SELECT * FROM t1 AS x, t1 AS y;
|
||||
SELECT * FROM "new view";
|
||||
} {{a main} {b main} {a main} {b main}}
|
||||
do_execsql_test 2.2 {;
|
||||
SELECT * FROM sqlite_master WHERE name = 'new view';
|
||||
} {
|
||||
view {new view} {new view} 0
|
||||
{CREATE VIEW "new view" AS SELECT * FROM t1 AS x, t1 AS y}
|
||||
}
|
||||
do_execsql_test 2.3 {
|
||||
DROP VIEW "new view";
|
||||
SELECT * FROM sqlite_master WHERE name = 'new view';
|
||||
} {}
|
||||
do_catchsql_test 2.4 {
|
||||
SELECT * FROM "new view"
|
||||
} {1 {no such table: new view}}
|
||||
|
||||
# EVIDENCE-OF: R-00359-41639 The view definition is removed from the
|
||||
# database schema, but no actual data in the underlying base tables is
|
||||
# modified.
|
||||
#
|
||||
# For each view in the database, check that it can be queried. Then drop
|
||||
# it. Check that it can no longer be queried and is no longer listed
|
||||
# in any schema table. Then check that the contents of the db tables have
|
||||
# not changed
|
||||
#
|
||||
set databasedata [list_all_data]
|
||||
|
||||
do_execsql_test 3.1.0 { SELECT * FROM temp.v1 } {{a temp} {b temp}}
|
||||
do_execsql_test 3.1.1 { DROP VIEW temp.v1 } {}
|
||||
do_catchsql_test 3.1.2 { SELECT * FROM temp.v1 } {1 {no such table: temp.v1}}
|
||||
do_test 3.1.3 { list_all_views } {main.v1 main.v2 aux.v1 aux.v2 aux.v3}
|
||||
do_test 3.1.4 { list_all_data } $databasedata
|
||||
|
||||
do_execsql_test 3.2.0 { SELECT * FROM v1 } {{a main} {b main}}
|
||||
do_execsql_test 3.2.1 { DROP VIEW v1 } {}
|
||||
do_catchsql_test 3.2.2 { SELECT * FROM main.v1 } {1 {no such table: main.v1}}
|
||||
do_test 3.2.3 { list_all_views } {main.v2 aux.v1 aux.v2 aux.v3}
|
||||
do_test 3.2.4 { list_all_data } $databasedata
|
||||
|
||||
do_execsql_test 3.3.0 { SELECT * FROM v2 } {{a main} {b main}}
|
||||
do_execsql_test 3.3.1 { DROP VIEW v2 } {}
|
||||
do_catchsql_test 3.3.2 { SELECT * FROM main.v2 } {1 {no such table: main.v2}}
|
||||
do_test 3.3.3 { list_all_views } {aux.v1 aux.v2 aux.v3}
|
||||
do_test 3.3.4 { list_all_data } $databasedata
|
||||
|
||||
do_execsql_test 3.4.0 { SELECT * FROM v1 } {{a aux} {b aux}}
|
||||
do_execsql_test 3.4.1 { DROP VIEW v1 } {}
|
||||
do_catchsql_test 3.4.2 { SELECT * FROM v1 } {1 {no such table: v1}}
|
||||
do_test 3.4.3 { list_all_views } {aux.v2 aux.v3}
|
||||
do_test 3.4.4 { list_all_data } $databasedata
|
||||
|
||||
do_execsql_test 3.4.0 { SELECT * FROM aux.v2 } {{a aux} {b aux}}
|
||||
do_execsql_test 3.4.1 { DROP VIEW aux.v2 } {}
|
||||
do_catchsql_test 3.4.2 { SELECT * FROM aux.v2 } {1 {no such table: aux.v2}}
|
||||
do_test 3.4.3 { list_all_views } {aux.v3}
|
||||
do_test 3.4.4 { list_all_data } $databasedata
|
||||
|
||||
do_execsql_test 3.5.0 { SELECT * FROM v3 } {{a aux} {b aux}}
|
||||
do_execsql_test 3.5.1 { DROP VIEW v3 } {}
|
||||
do_catchsql_test 3.5.2 { SELECT * FROM v3 } {1 {no such table: v3}}
|
||||
do_test 3.5.3 { list_all_views } {}
|
||||
do_test 3.5.4 { list_all_data } $databasedata
|
||||
|
||||
# EVIDENCE-OF: R-25558-37487 If the specified view cannot be found and
|
||||
# the IF EXISTS clause is not present, it is an error.
|
||||
#
|
||||
do_dropview_tests 4 -repair {
|
||||
dropview_reopen_db
|
||||
} -errorformat {
|
||||
no such view: %s
|
||||
} {
|
||||
1 "DROP VIEW xx" xx
|
||||
2 "DROP VIEW main.xx" main.xx
|
||||
3 "DROP VIEW temp.v2" temp.v2
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-07490-32536 If the specified view cannot be found and
|
||||
# an IF EXISTS clause is present in the DROP VIEW statement, then the
|
||||
# statement is a no-op.
|
||||
#
|
||||
do_dropview_tests 5 -repair {
|
||||
dropview_reopen_db
|
||||
} -tclquery {
|
||||
list_all_views
|
||||
expr {[list_all_views] == "main.v1 main.v2 temp.v1 aux.v1 aux.v2 aux.v3"}
|
||||
} {
|
||||
1 "DROP VIEW IF EXISTS xx" 1
|
||||
2 "DROP VIEW IF EXISTS main.xx" 1
|
||||
3 "DROP VIEW IF EXISTS temp.v2" 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
+742
-12
@@ -17,6 +17,17 @@ set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
|
||||
proc do_expr_test {tn expr type value} {
|
||||
uplevel do_execsql_test $tn [list "SELECT typeof($expr), $expr"] [
|
||||
list [list $type $value]
|
||||
]
|
||||
}
|
||||
|
||||
proc do_qexpr_test {tn expr value} {
|
||||
uplevel do_execsql_test $tn [list "SELECT quote($expr)"] [list $value]
|
||||
}
|
||||
|
||||
# Set up three global variables:
|
||||
#
|
||||
# ::opname An array mapping from SQL operator to an easy to parse
|
||||
@@ -413,8 +424,7 @@ do_execsql_test e_expr-10.1.3 { SELECT typeof('5.1') } {text}
|
||||
do_execsql_test e_expr-10.1.4 { SELECT typeof(X'ABCD') } {blob}
|
||||
do_execsql_test e_expr-10.1.5 { SELECT typeof(NULL) } {null}
|
||||
|
||||
# EVIDENCE-OF: R-26921-59298 Scientific notation is supported for
|
||||
# floating point literal values.
|
||||
# "Scientific notation is supported for point literal values."
|
||||
#
|
||||
do_execsql_test e_expr-10.2.1 { SELECT typeof(3.4e-02) } {real}
|
||||
do_execsql_test e_expr-10.2.2 { SELECT typeof(3e+5) } {real}
|
||||
@@ -617,7 +627,7 @@ do_test e_expr-11.7.1 { sqlite3_finalize $stmt } SQLITE_OK
|
||||
#-------------------------------------------------------------------------
|
||||
# "Test" the syntax diagrams in lang_expr.html.
|
||||
#
|
||||
# EVIDENCE-OF: R-04177-20688 -- syntax diagram signed-number
|
||||
# EVIDENCE-OF: R-62067-43884 -- syntax diagram signed-number
|
||||
#
|
||||
do_execsql_test e_expr-12.1.1 { SELECT 0, +0, -0 } {0 0 0}
|
||||
do_execsql_test e_expr-12.1.2 { SELECT 1, +1, -1 } {1 1 -1}
|
||||
@@ -632,7 +642,7 @@ do_execsql_test e_expr-12.1.6 {
|
||||
SELECT 0.0001, +0.0001, -0.0001
|
||||
} {0.0001 0.0001 -0.0001}
|
||||
|
||||
# EVIDENCE-OF: R-30740-26723 -- syntax diagram literal-value
|
||||
# EVIDENCE-OF: R-21258-25489 -- syntax diagram literal-value
|
||||
#
|
||||
set sqlite_current_time 1
|
||||
do_execsql_test e_expr-12.2.1 {SELECT 123} {123}
|
||||
@@ -927,8 +937,14 @@ do_execsql_test e_expr-14.4.3 { SELECT 'ac' LIKE 'aBc' } 0
|
||||
# 'a' LIKE 'A' is TRUE but
|
||||
# 'æ' LIKE 'Æ' is FALSE.
|
||||
#
|
||||
# The restriction to ASCII characters does not apply if the ICU
|
||||
# library is compiled in. When ICU is enabled SQLite does not act
|
||||
# as it does "by default".
|
||||
#
|
||||
do_execsql_test e_expr-14.5.1 { SELECT 'A' LIKE 'a' } 1
|
||||
do_execsql_test e_expr-14.5.2 "SELECT '\u00c6' LIKE '\u00e6'" 0
|
||||
ifcapable !icu {
|
||||
do_execsql_test e_expr-14.5.2 "SELECT '\u00c6' LIKE '\u00e6'" 0
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-56683-13731 If the optional ESCAPE clause is present,
|
||||
# then the expression following the ESCAPE keyword must evaluate to a
|
||||
@@ -975,7 +991,8 @@ proc likefunc {args} {
|
||||
eval lappend ::likeargs $args
|
||||
return 1
|
||||
}
|
||||
db func like likefunc
|
||||
db func like -argcount 2 likefunc
|
||||
db func like -argcount 3 likefunc
|
||||
set ::likeargs [list]
|
||||
do_execsql_test e_expr-15.1.1 { SELECT 'abc' LIKE 'def' } 1
|
||||
do_test e_expr-15.1.2 { set likeargs } {def abc}
|
||||
@@ -1046,12 +1063,16 @@ sqlite3 db test.db
|
||||
# default and so use of the REGEXP operator will normally result in an
|
||||
# error message.
|
||||
#
|
||||
do_catchsql_test e_expr-18.1.1 {
|
||||
SELECT regexp('abc', 'def')
|
||||
} {1 {no such function: regexp}}
|
||||
do_catchsql_test e_expr-18.1.2 {
|
||||
SELECT 'abc' REGEXP 'def'
|
||||
} {1 {no such function: REGEXP}}
|
||||
# There is a regexp function if ICU is enabled though.
|
||||
#
|
||||
ifcapable !icu {
|
||||
do_catchsql_test e_expr-18.1.1 {
|
||||
SELECT regexp('abc', 'def')
|
||||
} {1 {no such function: regexp}}
|
||||
do_catchsql_test e_expr-18.1.2 {
|
||||
SELECT 'abc' REGEXP 'def'
|
||||
} {1 {no such function: REGEXP}}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-33693-50180 The REGEXP operator is a special syntax for
|
||||
# the regexp() user function.
|
||||
@@ -1102,5 +1123,714 @@ do_execsql_test e_expr-19.2.3 { SELECT 'X' NOT MATCH 'Y' } 0
|
||||
do_test e_expr-19.2.4 { set matchargs } {Y X}
|
||||
sqlite3 db test.db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases for the testable statements related to the CASE expression.
|
||||
#
|
||||
# EVIDENCE-OF: R-15199-61389 There are two basic forms of the CASE
|
||||
# expression: those with a base expression and those without.
|
||||
#
|
||||
do_execsql_test e_expr-20.1 {
|
||||
SELECT CASE WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE 'else' END;
|
||||
} {true}
|
||||
do_execsql_test e_expr-20.2 {
|
||||
SELECT CASE 0 WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE 'else' END;
|
||||
} {false}
|
||||
|
||||
proc var {nm} {
|
||||
lappend ::varlist $nm
|
||||
return [set "::$nm"]
|
||||
}
|
||||
db func var var
|
||||
|
||||
# EVIDENCE-OF: R-30638-59954 In a CASE without a base expression, each
|
||||
# WHEN expression is evaluated and the result treated as a boolean,
|
||||
# starting with the leftmost and continuing to the right.
|
||||
#
|
||||
foreach {a b c} {0 0 0} break
|
||||
set varlist [list]
|
||||
do_execsql_test e_expr-21.1.1 {
|
||||
SELECT CASE WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C' END
|
||||
} {{}}
|
||||
do_test e_expr-21.1.2 { set varlist } {a b c}
|
||||
set varlist [list]
|
||||
do_execsql_test e_expr-21.1.3 {
|
||||
SELECT CASE WHEN var('c') THEN 'C'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('a') THEN 'A'
|
||||
ELSE 'no result'
|
||||
END
|
||||
} {{no result}}
|
||||
do_test e_expr-21.1.4 { set varlist } {c b a}
|
||||
|
||||
# EVIDENCE-OF: R-39009-25596 The result of the CASE expression is the
|
||||
# evaluation of the THEN expression that corresponds to the first WHEN
|
||||
# expression that evaluates to true.
|
||||
#
|
||||
foreach {a b c} {0 1 0} break
|
||||
do_execsql_test e_expr-21.2.1 {
|
||||
SELECT CASE WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C'
|
||||
ELSE 'no result'
|
||||
END
|
||||
} {B}
|
||||
foreach {a b c} {0 1 1} break
|
||||
do_execsql_test e_expr-21.2.2 {
|
||||
SELECT CASE WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C'
|
||||
ELSE 'no result'
|
||||
END
|
||||
} {B}
|
||||
foreach {a b c} {0 0 1} break
|
||||
do_execsql_test e_expr-21.2.3 {
|
||||
SELECT CASE WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C'
|
||||
ELSE 'no result'
|
||||
END
|
||||
} {C}
|
||||
|
||||
# EVIDENCE-OF: R-24227-04807 Or, if none of the WHEN expressions
|
||||
# evaluate to true, the result of evaluating the ELSE expression, if
|
||||
# any.
|
||||
#
|
||||
foreach {a b c} {0 0 0} break
|
||||
do_execsql_test e_expr-21.3.1 {
|
||||
SELECT CASE WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C'
|
||||
ELSE 'no result'
|
||||
END
|
||||
} {{no result}}
|
||||
|
||||
# EVIDENCE-OF: R-14168-07579 If there is no ELSE expression and none of
|
||||
# the WHEN expressions are true, then the overall result is NULL.
|
||||
#
|
||||
db nullvalue null
|
||||
do_execsql_test e_expr-21.3.2 {
|
||||
SELECT CASE WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C'
|
||||
END
|
||||
} {null}
|
||||
db nullvalue {}
|
||||
|
||||
# EVIDENCE-OF: R-13943-13592 A NULL result is considered untrue when
|
||||
# evaluating WHEN terms.
|
||||
#
|
||||
do_execsql_test e_expr-21.4.1 {
|
||||
SELECT CASE WHEN NULL THEN 'A' WHEN 1 THEN 'B' END
|
||||
} {B}
|
||||
do_execsql_test e_expr-21.4.2 {
|
||||
SELECT CASE WHEN 0 THEN 'A' WHEN NULL THEN 'B' ELSE 'C' END
|
||||
} {C}
|
||||
|
||||
# EVIDENCE-OF: R-38620-19499 In a CASE with a base expression, the base
|
||||
# expression is evaluated just once and the result is compared against
|
||||
# the evaluation of each WHEN expression from left to right.
|
||||
#
|
||||
# Note: This test case tests the "evaluated just once" part of the above
|
||||
# statement. Tests associated with the next two statements test that the
|
||||
# comparisons take place.
|
||||
#
|
||||
foreach {a b c} [list [expr 3] [expr 4] [expr 5]] break
|
||||
set ::varlist [list]
|
||||
do_execsql_test e_expr-22.1.1 {
|
||||
SELECT CASE var('a') WHEN 1 THEN 'A' WHEN 2 THEN 'B' WHEN 3 THEN 'C' END
|
||||
} {C}
|
||||
do_test e_expr-22.1.2 { set ::varlist } {a}
|
||||
|
||||
# EVIDENCE-OF: R-07667-49537 The result of the CASE expression is the
|
||||
# evaluation of the THEN expression that corresponds to the first WHEN
|
||||
# expression for which the comparison is true.
|
||||
#
|
||||
do_execsql_test e_expr-22.2.1 {
|
||||
SELECT CASE 23 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END
|
||||
} {B}
|
||||
do_execsql_test e_expr-22.2.2 {
|
||||
SELECT CASE 1 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END
|
||||
} {A}
|
||||
|
||||
# EVIDENCE-OF: R-47543-32145 Or, if none of the WHEN expressions
|
||||
# evaluate to a value equal to the base expression, the result of
|
||||
# evaluating the ELSE expression, if any.
|
||||
#
|
||||
do_execsql_test e_expr-22.3.1 {
|
||||
SELECT CASE 24 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' ELSE 'D' END
|
||||
} {D}
|
||||
|
||||
# EVIDENCE-OF: R-54721-48557 If there is no ELSE expression and none of
|
||||
# the WHEN expressions produce a result equal to the base expression,
|
||||
# the overall result is NULL.
|
||||
#
|
||||
do_execsql_test e_expr-22.4.1 {
|
||||
SELECT CASE 24 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END
|
||||
} {{}}
|
||||
db nullvalue null
|
||||
do_execsql_test e_expr-22.4.2 {
|
||||
SELECT CASE 24 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END
|
||||
} {null}
|
||||
db nullvalue {}
|
||||
|
||||
# EVIDENCE-OF: R-11479-62774 When comparing a base expression against a
|
||||
# WHEN expression, the same collating sequence, affinity, and
|
||||
# NULL-handling rules apply as if the base expression and WHEN
|
||||
# expression are respectively the left- and right-hand operands of an =
|
||||
# operator.
|
||||
#
|
||||
proc rev {str} {
|
||||
set ret ""
|
||||
set chars [split $str]
|
||||
for {set i [expr [llength $chars]-1]} {$i>=0} {incr i -1} {
|
||||
append ret [lindex $chars $i]
|
||||
}
|
||||
set ret
|
||||
}
|
||||
proc reverse {lhs rhs} {
|
||||
string compare [rev $lhs] [ref $rhs]
|
||||
}
|
||||
db collate reverse reverse
|
||||
do_execsql_test e_expr-23.1.1 {
|
||||
CREATE TABLE t1(
|
||||
a TEXT COLLATE NOCASE,
|
||||
b COLLATE REVERSE,
|
||||
c INTEGER,
|
||||
d BLOB
|
||||
);
|
||||
INSERT INTO t1 VALUES('abc', 'cba', 55, 34.5);
|
||||
} {}
|
||||
do_execsql_test e_expr-23.1.2 {
|
||||
SELECT CASE a WHEN 'xyz' THEN 'A' WHEN 'AbC' THEN 'B' END FROM t1
|
||||
} {B}
|
||||
do_execsql_test e_expr-23.1.3 {
|
||||
SELECT CASE 'AbC' WHEN 'abc' THEN 'A' WHEN a THEN 'B' END FROM t1
|
||||
} {B}
|
||||
do_execsql_test e_expr-23.1.4 {
|
||||
SELECT CASE a WHEN b THEN 'A' ELSE 'B' END FROM t1
|
||||
} {B}
|
||||
do_execsql_test e_expr-23.1.5 {
|
||||
SELECT CASE b WHEN a THEN 'A' ELSE 'B' END FROM t1
|
||||
} {A}
|
||||
do_execsql_test e_expr-23.1.6 {
|
||||
SELECT CASE 55 WHEN '55' THEN 'A' ELSE 'B' END
|
||||
} {B}
|
||||
do_execsql_test e_expr-23.1.7 {
|
||||
SELECT CASE c WHEN '55' THEN 'A' ELSE 'B' END FROM t1
|
||||
} {A}
|
||||
do_execsql_test e_expr-23.1.8 {
|
||||
SELECT CASE '34.5' WHEN d THEN 'A' ELSE 'B' END FROM t1
|
||||
} {B}
|
||||
do_execsql_test e_expr-23.1.9 {
|
||||
SELECT CASE NULL WHEN NULL THEN 'A' ELSE 'B' END
|
||||
} {B}
|
||||
|
||||
# EVIDENCE-OF: R-37304-39405 If the base expression is NULL then the
|
||||
# result of the CASE is always the result of evaluating the ELSE
|
||||
# expression if it exists, or NULL if it does not.
|
||||
#
|
||||
do_execsql_test e_expr-24.1.1 {
|
||||
SELECT CASE NULL WHEN 'abc' THEN 'A' WHEN 'def' THEN 'B' END;
|
||||
} {{}}
|
||||
do_execsql_test e_expr-24.1.2 {
|
||||
SELECT CASE NULL WHEN 'abc' THEN 'A' WHEN 'def' THEN 'B' ELSE 'C' END;
|
||||
} {C}
|
||||
|
||||
# EVIDENCE-OF: R-56280-17369 Both forms of the CASE expression use lazy,
|
||||
# or short-circuit, evaluation.
|
||||
#
|
||||
set varlist [list]
|
||||
foreach {a b c} {0 1 0} break
|
||||
do_execsql_test e_expr-25.1.1 {
|
||||
SELECT CASE WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C'
|
||||
END
|
||||
} {B}
|
||||
do_test e_expr-25.1.2 { set ::varlist } {a b}
|
||||
set varlist [list]
|
||||
do_execsql_test e_expr-25.1.3 {
|
||||
SELECT CASE '0' WHEN var('a') THEN 'A'
|
||||
WHEN var('b') THEN 'B'
|
||||
WHEN var('c') THEN 'C'
|
||||
END
|
||||
} {A}
|
||||
do_test e_expr-25.1.4 { set ::varlist } {a}
|
||||
|
||||
# EVIDENCE-OF: R-34773-62253 The only difference between the following
|
||||
# two CASE expressions is that the x expression is evaluated exactly
|
||||
# once in the first example but might be evaluated multiple times in the
|
||||
# second: CASE x WHEN w1 THEN r1 WHEN w2 THEN r2 ELSE r3 END CASE WHEN
|
||||
# x=w1 THEN r1 WHEN x=w2 THEN r2 ELSE r3 END
|
||||
#
|
||||
proc ceval {x} {
|
||||
incr ::evalcount
|
||||
return $x
|
||||
}
|
||||
db func ceval ceval
|
||||
set ::evalcount 0
|
||||
|
||||
do_execsql_test e_expr-26.1.1 {
|
||||
CREATE TABLE t2(x, w1, r1, w2, r2, r3);
|
||||
INSERT INTO t2 VALUES(1, 1, 'R1', 2, 'R2', 'R3');
|
||||
INSERT INTO t2 VALUES(2, 1, 'R1', 2, 'R2', 'R3');
|
||||
INSERT INTO t2 VALUES(3, 1, 'R1', 2, 'R2', 'R3');
|
||||
} {}
|
||||
do_execsql_test e_expr-26.1.2 {
|
||||
SELECT CASE x WHEN w1 THEN r1 WHEN w2 THEN r2 ELSE r3 END FROM t2
|
||||
} {R1 R2 R3}
|
||||
do_execsql_test e_expr-26.1.3 {
|
||||
SELECT CASE WHEN x=w1 THEN r1 WHEN x=w2 THEN r2 ELSE r3 END FROM t2
|
||||
} {R1 R2 R3}
|
||||
|
||||
do_execsql_test e_expr-26.1.4 {
|
||||
SELECT CASE ceval(x) WHEN w1 THEN r1 WHEN w2 THEN r2 ELSE r3 END FROM t2
|
||||
} {R1 R2 R3}
|
||||
do_test e_expr-26.1.5 { set ::evalcount } {3}
|
||||
set ::evalcount 0
|
||||
do_execsql_test e_expr-26.1.6 {
|
||||
SELECT CASE
|
||||
WHEN ceval(x)=w1 THEN r1
|
||||
WHEN ceval(x)=w2 THEN r2
|
||||
ELSE r3 END
|
||||
FROM t2
|
||||
} {R1 R2 R3}
|
||||
do_test e_expr-26.1.6 { set ::evalcount } {5}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test statements related to CAST expressions.
|
||||
#
|
||||
# EVIDENCE-OF: R-65079-31758 Application of a CAST expression is
|
||||
# different to application of a column affinity, as with a CAST
|
||||
# expression the storage class conversion is forced even if it is lossy
|
||||
# and irrreversible.
|
||||
#
|
||||
do_execsql_test e_expr-27.1.1 {
|
||||
CREATE TABLE t3(a TEXT, b REAL, c INTEGER);
|
||||
INSERT INTO t3 VALUES(X'555655', '1.23abc', 4.5);
|
||||
SELECT typeof(a), a, typeof(b), b, typeof(c), c FROM t3;
|
||||
} {blob UVU text 1.23abc real 4.5}
|
||||
do_execsql_test e_expr-27.1.2 {
|
||||
SELECT
|
||||
typeof(CAST(X'555655' as TEXT)), CAST(X'555655' as TEXT),
|
||||
typeof(CAST('1.23abc' as REAL)), CAST('1.23abc' as REAL),
|
||||
typeof(CAST(4.5 as INTEGER)), CAST(4.5 as INTEGER)
|
||||
} {text UVU real 1.23 integer 4}
|
||||
|
||||
# EVIDENCE-OF: R-27225-65050 If the value of <expr> is NULL, then
|
||||
# the result of the CAST expression is also NULL.
|
||||
#
|
||||
do_expr_test e_expr-27.2.1 { CAST(NULL AS integer) } null {}
|
||||
do_expr_test e_expr-27.2.2 { CAST(NULL AS text) } null {}
|
||||
do_expr_test e_expr-27.2.3 { CAST(NULL AS blob) } null {}
|
||||
do_expr_test e_expr-27.2.4 { CAST(NULL AS number) } null {}
|
||||
|
||||
# EVIDENCE-OF: R-31076-23575 Casting a value to a <type-name> with
|
||||
# no affinity causes the value to be converted into a BLOB.
|
||||
#
|
||||
do_expr_test e_expr-27.3.1 { CAST('abc' AS blob) } blob abc
|
||||
do_expr_test e_expr-27.3.2 { CAST('def' AS shobblob_x) } blob def
|
||||
do_expr_test e_expr-27.3.3 { CAST('ghi' AS abbLOb10) } blob ghi
|
||||
|
||||
# EVIDENCE-OF: R-22956-37754 Casting to a BLOB consists of first casting
|
||||
# the value to TEXT in the encoding of the database connection, then
|
||||
# interpreting the resulting byte sequence as a BLOB instead of as TEXT.
|
||||
#
|
||||
do_qexpr_test e_expr-27.4.1 { CAST('ghi' AS blob) } X'676869'
|
||||
do_qexpr_test e_expr-27.4.2 { CAST(456 AS blob) } X'343536'
|
||||
do_qexpr_test e_expr-27.4.3 { CAST(1.78 AS blob) } X'312E3738'
|
||||
rename db db2
|
||||
sqlite3 db :memory:
|
||||
db eval { PRAGMA encoding = 'utf-16le' }
|
||||
do_qexpr_test e_expr-27.4.4 { CAST('ghi' AS blob) } X'670068006900'
|
||||
do_qexpr_test e_expr-27.4.5 { CAST(456 AS blob) } X'340035003600'
|
||||
do_qexpr_test e_expr-27.4.6 { CAST(1.78 AS blob) } X'31002E0037003800'
|
||||
db close
|
||||
sqlite3 db :memory:
|
||||
db eval { PRAGMA encoding = 'utf-16be' }
|
||||
do_qexpr_test e_expr-27.4.7 { CAST('ghi' AS blob) } X'006700680069'
|
||||
do_qexpr_test e_expr-27.4.8 { CAST(456 AS blob) } X'003400350036'
|
||||
do_qexpr_test e_expr-27.4.9 { CAST(1.78 AS blob) } X'0031002E00370038'
|
||||
db close
|
||||
rename db2 db
|
||||
|
||||
# EVIDENCE-OF: R-04207-37981 To cast a BLOB value to TEXT, the sequence
|
||||
# of bytes that make up the BLOB is interpreted as text encoded using
|
||||
# the database encoding.
|
||||
#
|
||||
do_expr_test e_expr-28.1.1 { CAST (X'676869' AS text) } text ghi
|
||||
do_expr_test e_expr-28.1.2 { CAST (X'670068006900' AS text) } text g
|
||||
rename db db2
|
||||
sqlite3 db :memory:
|
||||
db eval { PRAGMA encoding = 'utf-16le' }
|
||||
do_expr_test e_expr-28.1.3 { CAST (X'676869' AS text) == 'ghi' } integer 0
|
||||
do_expr_test e_expr-28.1.4 { CAST (X'670068006900' AS text) } text ghi
|
||||
db close
|
||||
rename db2 db
|
||||
|
||||
# EVIDENCE-OF: R-22235-47006 Casting an INTEGER or REAL value into TEXT
|
||||
# renders the value as if via sqlite3_snprintf() except that the
|
||||
# resulting TEXT uses the encoding of the database connection.
|
||||
#
|
||||
do_expr_test e_expr-28.2.1 { CAST (1 AS text) } text 1
|
||||
do_expr_test e_expr-28.2.2 { CAST (45 AS text) } text 45
|
||||
do_expr_test e_expr-28.2.3 { CAST (-45 AS text) } text -45
|
||||
do_expr_test e_expr-28.2.4 { CAST (8.8 AS text) } text 8.8
|
||||
do_expr_test e_expr-28.2.5 { CAST (2.3e+5 AS text) } text 230000.0
|
||||
do_expr_test e_expr-28.2.6 { CAST (-2.3e-5 AS text) } text -2.3e-05
|
||||
do_expr_test e_expr-28.2.7 { CAST (0.0 AS text) } text 0.0
|
||||
do_expr_test e_expr-28.2.7 { CAST (0 AS text) } text 0
|
||||
|
||||
# EVIDENCE-OF: R-26346-36443 When casting a BLOB value to a REAL, the
|
||||
# value is first converted to TEXT.
|
||||
#
|
||||
do_expr_test e_expr-29.1.1 { CAST (X'312E3233' AS REAL) } real 1.23
|
||||
do_expr_test e_expr-29.1.2 { CAST (X'3233302E30' AS REAL) } real 230.0
|
||||
do_expr_test e_expr-29.1.3 { CAST (X'2D392E3837' AS REAL) } real -9.87
|
||||
do_expr_test e_expr-29.1.4 { CAST (X'302E30303031' AS REAL) } real 0.0001
|
||||
rename db db2
|
||||
sqlite3 db :memory:
|
||||
db eval { PRAGMA encoding = 'utf-16le' }
|
||||
do_expr_test e_expr-29.1.5 {
|
||||
CAST (X'31002E0032003300' AS REAL) } real 1.23
|
||||
do_expr_test e_expr-29.1.6 {
|
||||
CAST (X'3200330030002E003000' AS REAL) } real 230.0
|
||||
do_expr_test e_expr-29.1.7 {
|
||||
CAST (X'2D0039002E0038003700' AS REAL) } real -9.87
|
||||
do_expr_test e_expr-29.1.8 {
|
||||
CAST (X'30002E003000300030003100' AS REAL) } real 0.0001
|
||||
db close
|
||||
rename db2 db
|
||||
|
||||
# EVIDENCE-OF: R-54898-34554 When casting a TEXT value to REAL, the
|
||||
# longest possible prefix of the value that can be interpreted as a real
|
||||
# number is extracted from the TEXT value and the remainder ignored.
|
||||
#
|
||||
do_expr_test e_expr-29.2.1 { CAST('1.23abcd' AS REAL) } real 1.23
|
||||
do_expr_test e_expr-29.2.2 { CAST('1.45.23abcd' AS REAL) } real 1.45
|
||||
do_expr_test e_expr-29.2.3 { CAST('-2.12e-01ABC' AS REAL) } real -0.212
|
||||
do_expr_test e_expr-29.2.4 { CAST('1 2 3 4' AS REAL) } real 1.0
|
||||
|
||||
# EVIDENCE-OF: R-11321-47427 Any leading spaces in the TEXT value are
|
||||
# ignored when converging from TEXT to REAL.
|
||||
#
|
||||
do_expr_test e_expr-29.3.1 { CAST(' 1.23abcd' AS REAL) } real 1.23
|
||||
do_expr_test e_expr-29.3.2 { CAST(' 1.45.23abcd' AS REAL) } real 1.45
|
||||
do_expr_test e_expr-29.3.3 { CAST(' -2.12e-01ABC' AS REAL) } real -0.212
|
||||
do_expr_test e_expr-29.3.4 { CAST(' 1 2 3 4' AS REAL) } real 1.0
|
||||
|
||||
# EVIDENCE-OF: R-22662-28218 If there is no prefix that can be
|
||||
# interpreted as a real number, the result of the conversion is 0.0.
|
||||
#
|
||||
do_expr_test e_expr-29.4.1 { CAST('' AS REAL) } real 0.0
|
||||
do_expr_test e_expr-29.4.2 { CAST('not a number' AS REAL) } real 0.0
|
||||
do_expr_test e_expr-29.4.3 { CAST('XXI' AS REAL) } real 0.0
|
||||
|
||||
# EVIDENCE-OF: R-21829-14563 When casting a BLOB value to INTEGER, the
|
||||
# value is first converted to TEXT.
|
||||
#
|
||||
do_expr_test e_expr-30.1.1 { CAST(X'313233' AS INTEGER) } integer 123
|
||||
do_expr_test e_expr-30.1.2 { CAST(X'2D363738' AS INTEGER) } integer -678
|
||||
do_expr_test e_expr-30.1.3 {
|
||||
CAST(X'31303030303030' AS INTEGER)
|
||||
} integer 1000000
|
||||
do_expr_test e_expr-30.1.4 {
|
||||
CAST(X'2D31313235383939393036383432363234' AS INTEGER)
|
||||
} integer -1125899906842624
|
||||
|
||||
rename db db2
|
||||
sqlite3 db :memory:
|
||||
execsql { PRAGMA encoding = 'utf-16be' }
|
||||
do_expr_test e_expr-30.1.5 { CAST(X'003100320033' AS INTEGER) } integer 123
|
||||
do_expr_test e_expr-30.1.6 { CAST(X'002D003600370038' AS INTEGER) } integer -678
|
||||
do_expr_test e_expr-30.1.7 {
|
||||
CAST(X'0031003000300030003000300030' AS INTEGER)
|
||||
} integer 1000000
|
||||
do_expr_test e_expr-30.1.8 {
|
||||
CAST(X'002D0031003100320035003800390039003900300036003800340032003600320034' AS INTEGER)
|
||||
} integer -1125899906842624
|
||||
db close
|
||||
rename db2 db
|
||||
|
||||
# EVIDENCE-OF: R-47612-45842 When casting a TEXT value to INTEGER, the
|
||||
# longest possible prefix of the value that can be interpreted as an
|
||||
# integer number is extracted from the TEXT value and the remainder
|
||||
# ignored.
|
||||
#
|
||||
do_expr_test e_expr-30.2.1 { CAST('123abcd' AS INT) } integer 123
|
||||
do_expr_test e_expr-30.2.2 { CAST('14523abcd' AS INT) } integer 14523
|
||||
do_expr_test e_expr-30.2.3 { CAST('-2.12e-01ABC' AS INT) } integer -2
|
||||
do_expr_test e_expr-30.2.4 { CAST('1 2 3 4' AS INT) } integer 1
|
||||
|
||||
# EVIDENCE-OF: R-34400-33772 Any leading spaces in the TEXT value when
|
||||
# converting from TEXT to INTEGER are ignored.
|
||||
#
|
||||
do_expr_test e_expr-30.3.1 { CAST(' 123abcd' AS INT) } integer 123
|
||||
do_expr_test e_expr-30.3.2 { CAST(' 14523abcd' AS INT) } integer 14523
|
||||
do_expr_test e_expr-30.3.3 { CAST(' -2.12e-01ABC' AS INT) } integer -2
|
||||
do_expr_test e_expr-30.3.4 { CAST(' 1 2 3 4' AS INT) } integer 1
|
||||
|
||||
# EVIDENCE-OF: R-43164-44276 If there is no prefix that can be
|
||||
# interpreted as an integer number, the result of the conversion is 0.
|
||||
#
|
||||
do_expr_test e_expr-30.4.1 { CAST('' AS INTEGER) } integer 0
|
||||
do_expr_test e_expr-30.4.2 { CAST('not a number' AS INTEGER) } integer 0
|
||||
do_expr_test e_expr-30.4.3 { CAST('XXI' AS INTEGER) } integer 0
|
||||
|
||||
# EVIDENCE-OF: R-00741-38776 A cast of a REAL value into an INTEGER will
|
||||
# truncate the fractional part of the REAL.
|
||||
#
|
||||
do_expr_test e_expr-31.1.1 { CAST(3.14159 AS INTEGER) } integer 3
|
||||
do_expr_test e_expr-31.1.2 { CAST(1.99999 AS INTEGER) } integer 1
|
||||
do_expr_test e_expr-31.1.3 { CAST(-1.99999 AS INTEGER) } integer -1
|
||||
do_expr_test e_expr-31.1.4 { CAST(-0.99999 AS INTEGER) } integer 0
|
||||
|
||||
# EVIDENCE-OF: R-06126-36021 If an REAL is too large to be represented
|
||||
# as an INTEGER then the result of the cast is the largest negative
|
||||
# integer: -9223372036854775808.
|
||||
#
|
||||
do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer -9223372036854775808
|
||||
do_expr_test e_expr-31.2.2 { CAST(-2e+50 AS INT) } integer -9223372036854775808
|
||||
do_expr_test e_expr-31.2.3 {
|
||||
CAST(-9223372036854775809.0 AS INT)
|
||||
} integer -9223372036854775808
|
||||
do_expr_test e_expr-31.2.4 {
|
||||
CAST(9223372036854775809.0 AS INT)
|
||||
} integer -9223372036854775808
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-09295-61337 Casting a TEXT or BLOB value into NUMERIC
|
||||
# first does a forced conversion into REAL but then further converts the
|
||||
# result into INTEGER if and only if the conversion from REAL to INTEGER
|
||||
# is lossless and reversible.
|
||||
#
|
||||
do_expr_test e_expr-32.1.1 { CAST('45' AS NUMERIC) } integer 45
|
||||
do_expr_test e_expr-32.1.2 { CAST('45.0' AS NUMERIC) } integer 45
|
||||
do_expr_test e_expr-32.1.3 { CAST('45.2' AS NUMERIC) } real 45.2
|
||||
do_expr_test e_expr-32.1.4 { CAST('11abc' AS NUMERIC) } integer 11
|
||||
do_expr_test e_expr-32.1.5 { CAST('11.1abc' AS NUMERIC) } real 11.1
|
||||
|
||||
# EVIDENCE-OF: R-30347-18702 Casting a REAL or INTEGER value to NUMERIC
|
||||
# is a no-op, even if a real value could be losslessly converted to an
|
||||
# integer.
|
||||
#
|
||||
do_expr_test e_expr-32.2.1 { CAST(13.0 AS NUMERIC) } real 13.0
|
||||
do_expr_test e_expr-32.2.2 { CAST(13.5 AS NUMERIC) } real 13.5
|
||||
|
||||
do_expr_test e_expr-32.2.3 {
|
||||
CAST(-9223372036854775808 AS NUMERIC)
|
||||
} integer -9223372036854775808
|
||||
do_expr_test e_expr-32.2.4 {
|
||||
CAST(9223372036854775807 AS NUMERIC)
|
||||
} integer 9223372036854775807
|
||||
|
||||
# EVIDENCE-OF: R-64550-29191 Note that the result from casting any
|
||||
# non-BLOB value into a BLOB and the result from casting any BLOB value
|
||||
# into a non-BLOB value may be different depending on whether the
|
||||
# database encoding is UTF-8, UTF-16be, or UTF-16le.
|
||||
#
|
||||
sqlite3 db1 :memory: ; db1 eval { PRAGMA encoding = 'utf-8' }
|
||||
sqlite3 db2 :memory: ; db2 eval { PRAGMA encoding = 'utf-16le' }
|
||||
sqlite3 db3 :memory: ; db3 eval { PRAGMA encoding = 'utf-16be' }
|
||||
foreach {tn castexpr differs} {
|
||||
1 { CAST(123 AS BLOB) } 1
|
||||
2 { CAST('' AS BLOB) } 0
|
||||
3 { CAST('abcd' AS BLOB) } 1
|
||||
|
||||
4 { CAST(X'abcd' AS TEXT) } 1
|
||||
5 { CAST(X'' AS TEXT) } 0
|
||||
} {
|
||||
set r1 [db1 eval "SELECT typeof($castexpr), quote($castexpr)"]
|
||||
set r2 [db2 eval "SELECT typeof($castexpr), quote($castexpr)"]
|
||||
set r3 [db3 eval "SELECT typeof($castexpr), quote($castexpr)"]
|
||||
|
||||
if {$differs} {
|
||||
set res [expr {$r1!=$r2 && $r2!=$r3}]
|
||||
} else {
|
||||
set res [expr {$r1==$r2 && $r2==$r3}]
|
||||
}
|
||||
|
||||
do_test e_expr-33.1.$tn {set res} 1
|
||||
}
|
||||
db1 close
|
||||
db2 close
|
||||
db3 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test statements related to the EXISTS and NOT EXISTS operators.
|
||||
#
|
||||
catch { db close }
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test e_expr-34.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(NULL, 2);
|
||||
INSERT INTO t1 VALUES(1, NULL);
|
||||
INSERT INTO t1 VALUES(NULL, NULL);
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-25588-27181 The EXISTS operator always evaluates to one
|
||||
# of the integer values 0 and 1.
|
||||
#
|
||||
# This statement is not tested by itself. Instead, all e_expr-34.* tests
|
||||
# following this point explicitly test that specific invocations of EXISTS
|
||||
# return either integer 0 or integer 1.
|
||||
#
|
||||
|
||||
# EVIDENCE-OF: R-58553-63740 If executing the SELECT statement specified
|
||||
# as the right-hand operand of the EXISTS operator would return one or
|
||||
# more rows, then the EXISTS operator evaluates to 1.
|
||||
#
|
||||
foreach {tn expr} {
|
||||
1 { EXISTS ( SELECT a FROM t1 ) }
|
||||
2 { EXISTS ( SELECT b FROM t1 ) }
|
||||
3 { EXISTS ( SELECT 24 ) }
|
||||
4 { EXISTS ( SELECT NULL ) }
|
||||
5 { EXISTS ( SELECT a FROM t1 WHERE a IS NULL ) }
|
||||
} {
|
||||
do_expr_test e_expr-34.2.$tn $expr integer 1
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-19673-40972 If executing the SELECT would return no
|
||||
# rows at all, then the EXISTS operator evaluates to 0.
|
||||
#
|
||||
foreach {tn expr} {
|
||||
1 { EXISTS ( SELECT a FROM t1 WHERE 0) }
|
||||
2 { EXISTS ( SELECT b FROM t1 WHERE a = 5) }
|
||||
3 { EXISTS ( SELECT 24 WHERE 0) }
|
||||
4 { EXISTS ( SELECT NULL WHERE 1=2) }
|
||||
} {
|
||||
do_expr_test e_expr-34.3.$tn $expr integer 0
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-35109-49139 The number of columns in each row returned
|
||||
# by the SELECT statement (if any) and the specific values returned have
|
||||
# no effect on the results of the EXISTS operator.
|
||||
#
|
||||
foreach {tn expr res} {
|
||||
1 { EXISTS ( SELECT * FROM t1 ) } 1
|
||||
2 { EXISTS ( SELECT *, *, * FROM t1 ) } 1
|
||||
3 { EXISTS ( SELECT 24, 25 ) } 1
|
||||
4 { EXISTS ( SELECT NULL, NULL, NULL ) } 1
|
||||
5 { EXISTS ( SELECT a,b,a||b FROM t1 WHERE a IS NULL ) } 1
|
||||
|
||||
6 { EXISTS ( SELECT a, a FROM t1 WHERE 0) } 0
|
||||
7 { EXISTS ( SELECT b, b, a FROM t1 WHERE a = 5) } 0
|
||||
8 { EXISTS ( SELECT 24, 46, 89 WHERE 0) } 0
|
||||
9 { EXISTS ( SELECT NULL, NULL WHERE 1=2) } 0
|
||||
} {
|
||||
do_expr_test e_expr-34.4.$tn $expr integer $res
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-10645-12439 In particular, rows containing NULL values
|
||||
# are not handled any differently from rows without NULL values.
|
||||
#
|
||||
foreach {tn e1 e2} {
|
||||
1 { EXISTS (SELECT 'not null') } { EXISTS (SELECT NULL) }
|
||||
2 { EXISTS (SELECT NULL FROM t1) } { EXISTS (SELECT 'bread' FROM t1) }
|
||||
} {
|
||||
set res [db one "SELECT $e1"]
|
||||
do_expr_test e_expr-34.5.${tn}a $e1 integer $res
|
||||
do_expr_test e_expr-34.5.${tn}b $e2 integer $res
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test statements related to scalar sub-queries.
|
||||
#
|
||||
|
||||
catch { db close }
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
do_test e_expr-35.0 {
|
||||
execsql {
|
||||
CREATE TABLE t2(a, b);
|
||||
INSERT INTO t2 VALUES('one', 'two');
|
||||
INSERT INTO t2 VALUES('three', NULL);
|
||||
INSERT INTO t2 VALUES(4, 5.0);
|
||||
}
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-00980-39256 A SELECT statement enclosed in parentheses
|
||||
# may appear as a scalar quantity.
|
||||
#
|
||||
# EVIDENCE-OF: R-56294-03966 All types of SELECT statement, including
|
||||
# aggregate and compound SELECT queries (queries with keywords like
|
||||
# UNION or EXCEPT) are allowed as scalar subqueries.
|
||||
#
|
||||
do_expr_test e_expr-35.1.1 { (SELECT 35) } integer 35
|
||||
do_expr_test e_expr-35.1.2 { (SELECT NULL) } null {}
|
||||
|
||||
do_expr_test e_expr-35.1.3 { (SELECT count(*) FROM t2) } integer 3
|
||||
do_expr_test e_expr-35.1.4 { (SELECT 4 FROM t2) } integer 4
|
||||
|
||||
do_expr_test e_expr-35.1.5 {
|
||||
(SELECT b FROM t2 UNION SELECT a+1 FROM t2)
|
||||
} null {}
|
||||
do_expr_test e_expr-35.1.6 {
|
||||
(SELECT a FROM t2 UNION SELECT COALESCE(b, 55) FROM t2 ORDER BY 1)
|
||||
} integer 4
|
||||
|
||||
# EVIDENCE-OF: R-46899-53765 A SELECT used as a scalar quantity must
|
||||
# return a result set with a single column.
|
||||
#
|
||||
# The following block tests that errors are returned in a bunch of cases
|
||||
# where a subquery returns more than one column.
|
||||
#
|
||||
set M {only a single result allowed for a SELECT that is part of an expression}
|
||||
foreach {tn sql} {
|
||||
1 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) }
|
||||
2 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) }
|
||||
3 { SELECT (SELECT 1, 2) }
|
||||
4 { SELECT (SELECT NULL, NULL, NULL) }
|
||||
5 { SELECT (SELECT * FROM t2) }
|
||||
6 { SELECT (SELECT * FROM (SELECT 1, 2, 3)) }
|
||||
} {
|
||||
do_catchsql_test e_expr-35.2.$tn $sql [list 1 $M]
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-35764-28041 The result of the expression is the value
|
||||
# of the only column in the first row returned by the SELECT statement.
|
||||
#
|
||||
# EVIDENCE-OF: R-41898-06686 If the SELECT yields more than one result
|
||||
# row, all rows after the first are ignored.
|
||||
#
|
||||
do_execsql_test e_expr-36.3.1 {
|
||||
CREATE TABLE t4(x, y);
|
||||
INSERT INTO t4 VALUES(1, 'one');
|
||||
INSERT INTO t4 VALUES(2, 'two');
|
||||
INSERT INTO t4 VALUES(3, 'three');
|
||||
} {}
|
||||
|
||||
foreach {tn expr restype resval} {
|
||||
2 { ( SELECT x FROM t4 ORDER BY x ) } integer 1
|
||||
3 { ( SELECT x FROM t4 ORDER BY y ) } integer 1
|
||||
4 { ( SELECT x FROM t4 ORDER BY x DESC ) } integer 3
|
||||
5 { ( SELECT x FROM t4 ORDER BY y DESC ) } integer 2
|
||||
6 { ( SELECT y FROM t4 ORDER BY y DESC ) } text two
|
||||
|
||||
7 { ( SELECT sum(x) FROM t4 ) } integer 6
|
||||
8 { ( SELECT group_concat(y,'') FROM t4 ) } text onetwothree
|
||||
9 { ( SELECT max(x) FROM t4 WHERE y LIKE '___') } integer 2
|
||||
|
||||
} {
|
||||
do_expr_test e_expr-36.3.$tn $expr $restype $resval
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-25492-41572 If the SELECT yields no rows, then the
|
||||
# value of the expression is NULL.
|
||||
#
|
||||
foreach {tn expr} {
|
||||
1 { ( SELECT x FROM t4 WHERE x>3 ORDER BY x ) }
|
||||
2 { ( SELECT x FROM t4 WHERE y<'one' ORDER BY y ) }
|
||||
} {
|
||||
do_expr_test e_expr-36.4.$tn $expr null {}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+28
-26
@@ -968,19 +968,21 @@ do_test e_fkey-25.1 {
|
||||
);
|
||||
}
|
||||
} {}
|
||||
do_test e_fkey-25.2 {
|
||||
execsql {
|
||||
PRAGMA foreign_keys = OFF;
|
||||
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
|
||||
EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?;
|
||||
}
|
||||
} {0 0 {TABLE artist} 0 0 {TABLE track}}
|
||||
do_test e_fkey-25.3 {
|
||||
execsql {
|
||||
PRAGMA foreign_keys = ON;
|
||||
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
|
||||
}
|
||||
} {0 0 {TABLE artist} 0 0 {TABLE track}}
|
||||
do_execsql_test e_fkey-25.2 {
|
||||
PRAGMA foreign_keys = OFF;
|
||||
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
|
||||
EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE artist (~1000000 rows)}
|
||||
0 0 0 {SCAN TABLE track (~100000 rows)}
|
||||
}
|
||||
do_execsql_test e_fkey-25.3 {
|
||||
PRAGMA foreign_keys = ON;
|
||||
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE artist (~1000000 rows)}
|
||||
0 0 0 {SCAN TABLE track (~100000 rows)}
|
||||
}
|
||||
do_test e_fkey-25.4 {
|
||||
execsql {
|
||||
INSERT INTO artist VALUES(5, 'artist 5');
|
||||
@@ -1093,19 +1095,19 @@ do_test e_fkey-27.1 {
|
||||
do_test e_fkey-27.2 {
|
||||
eqp { INSERT INTO artist VALUES(?, ?) }
|
||||
} {}
|
||||
do_test e_fkey-27.3 {
|
||||
eqp { UPDATE artist SET artistid = ?, artistname = ? }
|
||||
} [list \
|
||||
0 0 {TABLE artist} \
|
||||
0 0 {TABLE track WITH INDEX trackindex} \
|
||||
0 0 {TABLE track WITH INDEX trackindex}
|
||||
]
|
||||
do_test e_fkey-27.4 {
|
||||
eqp { DELETE FROM artist }
|
||||
} [list \
|
||||
0 0 {TABLE artist} \
|
||||
0 0 {TABLE track WITH INDEX trackindex}
|
||||
]
|
||||
do_execsql_test e_fkey-27.3 {
|
||||
EXPLAIN QUERY PLAN UPDATE artist SET artistid = ?, artistname = ?
|
||||
} {
|
||||
0 0 0 {SCAN TABLE artist (~1000000 rows)}
|
||||
0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)}
|
||||
0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)}
|
||||
}
|
||||
do_execsql_test e_fkey-27.4 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM artist
|
||||
} {
|
||||
0 0 0 {SCAN TABLE artist (~1000000 rows)}
|
||||
0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)}
|
||||
}
|
||||
|
||||
|
||||
###########################################################################
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The majority of this file implements tests to verify that the "testable
|
||||
# statements" in the lang_insert.html document are correct.
|
||||
#
|
||||
# Also, it contains tests to verify the statements in (the very short)
|
||||
# lang_replace.html.
|
||||
#
|
||||
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".
|
||||
#
|
||||
# e_insert-4.*: Test statements regarding the conflict clause.
|
||||
#
|
||||
# e_insert-5.*: Test that the qualified table name and "DEFAULT VALUES"
|
||||
# syntaxes do not work in trigger bodies.
|
||||
#
|
||||
|
||||
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);
|
||||
CREATE TABLE a4(c UNIQUE, d);
|
||||
} {}
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-52173-30215 A new entry is inserted into the table for
|
||||
# each row of data returned by executing the SELECT statement.
|
||||
#
|
||||
delete_all_data
|
||||
do_insert_tests e_insert-2.1 {
|
||||
0 "SELECT count(*) FROM a1" {0}
|
||||
|
||||
1a "SELECT count(*) FROM (SELECT 1, 2)" {1}
|
||||
1b "INSERT INTO a1 SELECT 1, 2" {}
|
||||
1c "SELECT count(*) FROM a1" {1}
|
||||
|
||||
2a "SELECT count(*) FROM (SELECT b, a FROM a1)" {1}
|
||||
2b "INSERT INTO a1 SELECT b, a FROM a1" {}
|
||||
2c "SELECT count(*) FROM a1" {2}
|
||||
|
||||
3a "SELECT count(*) FROM (SELECT b, a FROM a1)" {2}
|
||||
3b "INSERT INTO a1 SELECT b, a FROM a1" {}
|
||||
3c "SELECT count(*) FROM a1" {4}
|
||||
|
||||
4a "SELECT count(*) FROM (SELECT b, a FROM a1)" {4}
|
||||
4b "INSERT INTO a1 SELECT b, a FROM a1" {}
|
||||
4c "SELECT count(*) FROM a1" {8}
|
||||
|
||||
4a "SELECT count(*) FROM (SELECT min(b), min(a) FROM a1)" {1}
|
||||
4b "INSERT INTO a1 SELECT min(b), min(a) FROM a1" {}
|
||||
4c "SELECT count(*) FROM a1" {9}
|
||||
}
|
||||
|
||||
|
||||
# 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-31074-37730 Any SELECT statement, including compound
|
||||
# SELECTs and SELECT statements with ORDER BY and/or LIMIT clauses, may
|
||||
# be used in an INSERT statement of this form.
|
||||
#
|
||||
delete_all_data
|
||||
do_execsql_test e_insert-2.3.0 {
|
||||
INSERT INTO a1 VALUES('x', 'y');
|
||||
} {}
|
||||
do_insert_tests e_insert-2.3 {
|
||||
1 "INSERT INTO a1 SELECT a,b FROM a1 UNION SELECT b,a FROM a1 ORDER BY 1" {}
|
||||
2 "INSERT INTO a1(b, a) SELECT * FROM a1 LIMIT 1" {}
|
||||
3 "INSERT INTO a1 SELECT 'a'||a, 'b'||b FROM a1 LIMIT 2 OFFSET 1" {}
|
||||
4 "INSERT INTO a1 SELECT * FROM a1 ORDER BY b, a" {}
|
||||
S "SELECT * FROM a1" {
|
||||
x y
|
||||
x y y x
|
||||
y x
|
||||
ax by ay bx
|
||||
ay bx ax by y x y x x y x y
|
||||
}
|
||||
}
|
||||
|
||||
# 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" {{} {} {} {}}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-46928-50290 The optional conflict-clause allows the
|
||||
# specification of an alternative constraint conflict resolution
|
||||
# algorithm to use during this one INSERT command.
|
||||
#
|
||||
# EVIDENCE-OF: R-23110-47146 the parser allows the use of the single
|
||||
# keyword REPLACE as an alias for "INSERT OR REPLACE".
|
||||
#
|
||||
# The two requirements above are tested by e_select-4.1.* and
|
||||
# e_select-4.2.*, respectively.
|
||||
#
|
||||
# EVIDENCE-OF: R-03421-22330 The REPLACE command is an alias for the
|
||||
# "INSERT OR REPLACE" variant of the INSERT command.
|
||||
#
|
||||
# This is a dup of R-23110-47146. Therefore it is also verified
|
||||
# by e_select-4.2.*. This requirement is the only one from
|
||||
# lang_replace.html.
|
||||
#
|
||||
do_execsql_test e_insert-4.1.0 {
|
||||
INSERT INTO a4 VALUES(1, 'a');
|
||||
INSERT INTO a4 VALUES(2, 'a');
|
||||
INSERT INTO a4 VALUES(3, 'a');
|
||||
} {}
|
||||
foreach {tn sql error ac data } {
|
||||
1.1 "INSERT INTO a4 VALUES(2,'b')" {column c is not unique} 1 {1 a 2 a 3 a}
|
||||
1.2 "INSERT OR REPLACE INTO a4 VALUES(2, 'b')" {} 1 {1 a 3 a 2 b}
|
||||
1.3 "INSERT OR IGNORE INTO a4 VALUES(3, 'c')" {} 1 {1 a 3 a 2 b}
|
||||
1.4 "BEGIN" {} 0 {1 a 3 a 2 b}
|
||||
1.5 "INSERT INTO a4 VALUES(1, 'd')" {column c is not unique} 0 {1 a 3 a 2 b}
|
||||
1.6 "INSERT OR ABORT INTO a4 VALUES(1, 'd')"
|
||||
{column c is not unique} 0 {1 a 3 a 2 b}
|
||||
1.7 "INSERT OR ROLLBACK INTO a4 VALUES(1, 'd')"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b}
|
||||
1.8 "INSERT INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b}
|
||||
1.9 "INSERT OR FAIL INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b 4 e}
|
||||
|
||||
2.1 "INSERT INTO a4 VALUES(2,'f')"
|
||||
{column c is not unique} 1 {1 a 3 a 2 b 4 e}
|
||||
2.2 "REPLACE INTO a4 VALUES(2, 'f')" {} 1 {1 a 3 a 4 e 2 f}
|
||||
} {
|
||||
do_catchsql_test e_insert-4.1.$tn.1 $sql [list [expr {$error!=""}] $error]
|
||||
do_execsql_test e_insert-4.1.$tn.2 {SELECT * FROM a4} [list {*}$data]
|
||||
do_test e_insert-4.1.$tn.3 {sqlite3_get_autocommit db} $ac
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-64196-02418 The optional "database-name." prefix on the
|
||||
# table-name is support for top-level INSERT statements only.
|
||||
#
|
||||
# EVIDENCE-OF: R-05731-00924 The table name must be unqualified for
|
||||
# INSERT statements that occur within CREATE TRIGGER statements.
|
||||
#
|
||||
set err {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}}
|
||||
|
||||
do_catchsql_test e_insert-5.1.1 {
|
||||
CREATE TRIGGER AFTER UPDATE ON a1 BEGIN
|
||||
INSERT INTO main.a4 VALUES(new.a, new.b);
|
||||
END;
|
||||
} $err
|
||||
do_catchsql_test e_insert-5.1.2 {
|
||||
CREATE TEMP TABLE IF NOT EXISTS tmptable(a, b);
|
||||
CREATE TRIGGER AFTER DELETE ON a3 BEGIN
|
||||
INSERT INTO temp.tmptable VALUES(1, 2);
|
||||
END;
|
||||
} $err
|
||||
|
||||
# EVIDENCE-OF: R-15888-36326 Similarly, the "DEFAULT VALUES" form of the
|
||||
# INSERT statement is supported for top-level INSERT statements only and
|
||||
# not for INSERT statements within triggers.
|
||||
#
|
||||
do_catchsql_test e_insert-5.2.1 {
|
||||
CREATE TRIGGER AFTER UPDATE ON a1 BEGIN
|
||||
INSERT INTO a4 DEFAULT VALUES;
|
||||
END;
|
||||
} {1 {near "DEFAULT": syntax error}}
|
||||
|
||||
|
||||
delete_all_data
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,294 @@
|
||||
# 2010 September 24
|
||||
#
|
||||
# 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_reindex.html document are correct.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
proc do_reindex_tests {args} {
|
||||
uplevel do_select_tests $args
|
||||
}
|
||||
|
||||
do_execsql_test e_reindex-0.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
CREATE INDEX i2 ON t1(b, a);
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-57021-15304 -- syntax diagram reindex-stmt
|
||||
#
|
||||
do_reindex_tests e_reindex-0.1 {
|
||||
1 "REINDEX" {}
|
||||
2 "REINDEX nocase" {}
|
||||
3 "REINDEX binary" {}
|
||||
4 "REINDEX t1" {}
|
||||
5 "REINDEX main.t1" {}
|
||||
4 "REINDEX i1" {}
|
||||
5 "REINDEX main.i1" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-52173-44778 The REINDEX command is used to delete and
|
||||
# recreate indices from scratch.
|
||||
#
|
||||
# Test this by corrupting some database indexes, running REINDEX, and
|
||||
# observing that the corruption is gone.
|
||||
#
|
||||
do_execsql_test e_reindex-1.1 {
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master SET sql = '-- ' || sql WHERE type = 'index';
|
||||
} {}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test e_reindex-1.2 {
|
||||
DELETE FROM t1 WHERE a = 3;
|
||||
INSERT INTO t1 VALUES(7, 8);
|
||||
INSERT INTO t1 VALUES(9, 10);
|
||||
PRAGMA writable_schema = 1;
|
||||
UPDATE sqlite_master SET sql = substr(sql, 4) WHERE type = 'index';
|
||||
} {}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
do_execsql_test e_reindex-1.3 {
|
||||
PRAGMA integrity_check;
|
||||
} [list \
|
||||
{rowid 4 missing from index i2} \
|
||||
{rowid 4 missing from index i1} \
|
||||
{rowid 5 missing from index i2} \
|
||||
{rowid 5 missing from index i1} \
|
||||
{wrong # of entries in index i2} \
|
||||
{wrong # of entries in index i1}
|
||||
]
|
||||
|
||||
do_execsql_test e_reindex-1.4 {
|
||||
REINDEX;
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The remaining tests in this file focus on testing that the REINDEX
|
||||
# command reindexes the correct subset of the indexes in the database.
|
||||
# They all use the following dataset.
|
||||
#
|
||||
db close
|
||||
forcedelete test.db2
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
proc sort_by_length {lhs rhs} {
|
||||
set res [expr {[string length $lhs] - [string length $rhs]}]
|
||||
if {$res!=0} {return $res}
|
||||
return [string compare $lhs $rhs]
|
||||
}
|
||||
array set V {one 1 two 2 three 3 four 4 five 5 six 6 seven 7 eight 8}
|
||||
proc sort_by_value {lhs rhs} {
|
||||
global V
|
||||
set res [expr {$V($lhs) - $V(rhs)}]
|
||||
if {$res!=0} {return $res}
|
||||
return [string compare $lhs $rhs]
|
||||
}
|
||||
|
||||
db collate collA sort_by_length
|
||||
db collate collB sort_by_value
|
||||
|
||||
set BY(length) {one six two five four eight seven three}
|
||||
set BY(value) {one two three four five six seven eight}
|
||||
|
||||
do_execsql_test e_reindex-2.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
|
||||
CREATE TABLE t1(x);
|
||||
CREATE INDEX i1_a ON t1(x COLLATE collA);
|
||||
CREATE INDEX i1_b ON t1(x COLLATE collB);
|
||||
INSERT INTO t1 VALUES('one');
|
||||
INSERT INTO t1 VALUES('two');
|
||||
INSERT INTO t1 VALUES('three');
|
||||
INSERT INTO t1 VALUES('four');
|
||||
INSERT INTO t1 VALUES('five');
|
||||
INSERT INTO t1 VALUES('six');
|
||||
INSERT INTO t1 VALUES('seven');
|
||||
INSERT INTO t1 VALUES('eight');
|
||||
|
||||
CREATE TABLE t2(x);
|
||||
CREATE INDEX i2_a ON t2(x COLLATE collA);
|
||||
CREATE INDEX i2_b ON t2(x COLLATE collB);
|
||||
INSERT INTO t2 SELECT x FROM t1;
|
||||
|
||||
CREATE TABLE aux.t1(x);
|
||||
CREATE INDEX aux.i1_a ON t1(x COLLATE collA);
|
||||
CREATE INDEX aux.i1_b ON t1(x COLLATE collB);
|
||||
INSERT INTO aux.t1 SELECT x FROM main.t1;
|
||||
|
||||
} {}
|
||||
|
||||
proc test_index {tn tbl collation expected} {
|
||||
set sql "SELECT x FROM $tbl ORDER BY x COLLATE $collation"
|
||||
uplevel do_execsql_test e_reindex-2.$tn [list $sql] [list $::BY($expected)]
|
||||
}
|
||||
|
||||
proc set_collations {a b} {
|
||||
db collate collA "sort_by_$a"
|
||||
db collate collB "sort_by_$b"
|
||||
}
|
||||
|
||||
test_index 1.1 t1 collA length
|
||||
test_index 1.2 t1 collB value
|
||||
test_index 1.3 t2 collA length
|
||||
test_index 1.4 t2 collB value
|
||||
test_index 1.5 aux.t1 collA length
|
||||
test_index 1.6 aux.t1 collB value
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-47362-07898 If the REINDEX keyword is not followed by a
|
||||
# collation-sequence or database object identifier, then all indices in
|
||||
# all attached databases are rebuilt.
|
||||
#
|
||||
set_collations value length
|
||||
do_execsql_test e_reindex-2.2.1 "REINDEX" {}
|
||||
test_index 2.2 t1 collA value
|
||||
test_index 2.3 t1 collB length
|
||||
test_index 2.4 t2 collA value
|
||||
test_index 2.5 t2 collB length
|
||||
test_index 2.6 aux.t1 collA value
|
||||
test_index 2.7 aux.t1 collB length
|
||||
|
||||
# EVIDENCE-OF: R-45878-07697 If the REINDEX keyword is followed by a
|
||||
# collation-sequence name, then all indices in all attached databases
|
||||
# that use the named collation sequences are recreated.
|
||||
#
|
||||
set_collations length value
|
||||
do_execsql_test e_reindex-2.3.1 "REINDEX collA" {}
|
||||
test_index 3.2 t1 collA length
|
||||
test_index 3.3 t1 collB length
|
||||
test_index 3.4 t2 collA length
|
||||
test_index 3.5 t2 collB length
|
||||
test_index 3.6 aux.t1 collA length
|
||||
test_index 3.7 aux.t1 collB length
|
||||
do_execsql_test e_reindex-2.3.8 "REINDEX collB" {}
|
||||
test_index 3.9 t1 collA length
|
||||
test_index 3.10 t1 collB value
|
||||
test_index 3.11 t2 collA length
|
||||
test_index 3.12 t2 collB value
|
||||
test_index 3.13 aux.t1 collA length
|
||||
test_index 3.14 aux.t1 collB value
|
||||
|
||||
# EVIDENCE-OF: R-49616-30196 Or, if the argument attached to the REINDEX
|
||||
# identifies a specific database table, then all indices attached to the
|
||||
# database table are rebuilt.
|
||||
#
|
||||
set_collations value length
|
||||
do_execsql_test e_reindex-2.4.1 "REINDEX t1" {}
|
||||
test_index 4.2 t1 collA value
|
||||
test_index 4.3 t1 collB length
|
||||
test_index 4.4 t2 collA length
|
||||
test_index 4.5 t2 collB value
|
||||
test_index 4.6 aux.t1 collA length
|
||||
test_index 4.7 aux.t1 collB value
|
||||
do_execsql_test e_reindex-2.4.8 "REINDEX aux.t1" {}
|
||||
test_index 4.9 t1 collA value
|
||||
test_index 4.10 t1 collB length
|
||||
test_index 4.11 t2 collA length
|
||||
test_index 4.12 t2 collB value
|
||||
test_index 4.13 aux.t1 collA value
|
||||
test_index 4.14 aux.t1 collB length
|
||||
do_execsql_test e_reindex-2.4.15 "REINDEX t2" {}
|
||||
test_index 4.16 t1 collA value
|
||||
test_index 4.17 t1 collB length
|
||||
test_index 4.18 t2 collA value
|
||||
test_index 4.19 t2 collB length
|
||||
test_index 4.20 aux.t1 collA value
|
||||
test_index 4.21 aux.t1 collB length
|
||||
|
||||
# EVIDENCE-OF: R-58823-28748 If it identifies a specific database index,
|
||||
# then just that index is recreated.
|
||||
#
|
||||
set_collations length value
|
||||
do_execsql_test e_reindex-2.5.1 "REINDEX i1_a" {}
|
||||
test_index 5.2 t1 collA length
|
||||
test_index 5.3 t1 collB length
|
||||
test_index 5.4 t2 collA value
|
||||
test_index 5.5 t2 collB length
|
||||
test_index 5.6 aux.t1 collA value
|
||||
test_index 5.7 aux.t1 collB length
|
||||
do_execsql_test e_reindex-2.5.8 "REINDEX i2_b" {}
|
||||
test_index 5.9 t1 collA length
|
||||
test_index 5.10 t1 collB length
|
||||
test_index 5.11 t2 collA value
|
||||
test_index 5.12 t2 collB value
|
||||
test_index 5.13 aux.t1 collA value
|
||||
test_index 5.14 aux.t1 collB length
|
||||
do_execsql_test e_reindex-2.5.15 "REINDEX aux.i1_b" {}
|
||||
test_index 5.16 t1 collA length
|
||||
test_index 5.17 t1 collB length
|
||||
test_index 5.18 t2 collA value
|
||||
test_index 5.19 t2 collB value
|
||||
test_index 5.20 aux.t1 collA value
|
||||
test_index 5.21 aux.t1 collB value
|
||||
do_execsql_test e_reindex-2.5.22 "REINDEX i1_b" {}
|
||||
test_index 5.23 t1 collA length
|
||||
test_index 5.24 t1 collB value
|
||||
test_index 5.25 t2 collA value
|
||||
test_index 5.26 t2 collB value
|
||||
test_index 5.27 aux.t1 collA value
|
||||
test_index 5.28 aux.t1 collB value
|
||||
do_execsql_test e_reindex-2.5.29 "REINDEX i2_a" {}
|
||||
test_index 5.30 t1 collA length
|
||||
test_index 5.31 t1 collB value
|
||||
test_index 5.32 t2 collA length
|
||||
test_index 5.33 t2 collB value
|
||||
test_index 5.34 aux.t1 collA value
|
||||
test_index 5.35 aux.t1 collB value
|
||||
do_execsql_test e_reindex-2.5.36 "REINDEX aux.i1_a" {}
|
||||
test_index 5.37 t1 collA length
|
||||
test_index 5.38 t1 collB value
|
||||
test_index 5.39 t2 collA length
|
||||
test_index 5.40 t2 collB value
|
||||
test_index 5.41 aux.t1 collA length
|
||||
test_index 5.42 aux.t1 collB value
|
||||
|
||||
# EVIDENCE-OF: R-15639-02023 If no database-name is specified and there
|
||||
# exists both a table or index and a collation sequence of the specified
|
||||
# name, SQLite interprets this as a request to rebuild the indices that
|
||||
# use the named collation sequence.
|
||||
#
|
||||
set_collations value length
|
||||
do_execsql_test e_reindex-2.6.0 {
|
||||
CREATE TABLE collA(x);
|
||||
CREATE INDEX icolla_a ON collA(x COLLATE collA);
|
||||
CREATE INDEX icolla_b ON collA(x COLLATE collB);
|
||||
|
||||
INSERT INTO collA SELECT x FROM t1;
|
||||
} {}
|
||||
|
||||
test_index 6.1 collA collA value
|
||||
test_index 6.2 collA collB length
|
||||
|
||||
set_collations length value
|
||||
do_execsql_test e_reindex-2.6.3 "REINDEX collA" {}
|
||||
test_index 6.4 collA collA length
|
||||
test_index 6.5 collA collB length
|
||||
do_execsql_test e_reindex-2.6.3 "REINDEX main.collA" {}
|
||||
test_index 6.4 collA collA length
|
||||
test_index 6.5 collA collB value
|
||||
|
||||
set_collations value length
|
||||
do_execsql_test e_reindex-2.6.6 "REINDEX main.collA" {}
|
||||
test_index 6.7 collA collA value
|
||||
test_index 6.8 collA collB length
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,135 @@
|
||||
# 2010 November 30
|
||||
#
|
||||
# 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_naming.html document are correct.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix e_resolve
|
||||
|
||||
# An example database schema for testing name resolution:
|
||||
#
|
||||
set schema {
|
||||
ATTACH 'test.db2' AS at1;
|
||||
ATTACH 'test.db3' AS at2;
|
||||
|
||||
CREATE TABLE temp.n1(x, y); INSERT INTO temp.n1 VALUES('temp', 'n1');
|
||||
CREATE TRIGGER temp.n3 AFTER INSERT ON n1 BEGIN SELECT 1; END;
|
||||
CREATE INDEX temp.n4 ON n1(x, y);
|
||||
|
||||
CREATE TABLE main.n1(x, y); INSERT INTO main.n1 VALUES('main', 'n1');
|
||||
CREATE TABLE main.n2(x, y); INSERT INTO main.n2 VALUES('main', 'n2');
|
||||
CREATE INDEX main.n3 ON n2(y, x);
|
||||
CREATE TRIGGER main.n4 BEFORE INSERT ON n2 BEGIN SELECT 1; END;
|
||||
|
||||
CREATE TABLE at1.n1(x, y); INSERT INTO at1.n1 VALUES('at1', 'n1');
|
||||
CREATE TABLE at1.n2(x, y); INSERT INTO at1.n2 VALUES('at1', 'n2');
|
||||
CREATE TABLE at1.n3(x, y); INSERT INTO at1.n3 VALUES('at1', 'n3');
|
||||
|
||||
CREATE TABLE at2.n1(x, y); INSERT INTO at2.n1 VALUES('at2', 'n1');
|
||||
CREATE TABLE at2.n2(x, y); INSERT INTO at2.n2 VALUES('at2', 'n2');
|
||||
CREATE TABLE at2.n3(x, y); INSERT INTO at2.n3 VALUES('at2', 'n3');
|
||||
CREATE TABLE at2.n4(x, y); INSERT INTO at2.n4 VALUES('at2', 'n4');
|
||||
CREATE TRIGGER at2.n4 BEFORE INSERT ON n4 BEGIN SELECT 1; END;
|
||||
}
|
||||
|
||||
proc resolve_reopen_db {} {
|
||||
db close
|
||||
forcedelete test.db test.db2 test.db3
|
||||
sqlite3 db test.db
|
||||
db eval $::schema
|
||||
}
|
||||
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-33528-20612 If no database is specified as part of the
|
||||
# object reference, then SQLite searches the main, temp and all attached
|
||||
# databases for an object with a matching name. The temp database is
|
||||
# searched first, followed by the main database, followed all attached
|
||||
# databases in the order that they were attached. The reference resolves
|
||||
# to the first match found.
|
||||
#
|
||||
resolve_reopen_db
|
||||
do_execsql_test 1.1 { SELECT * FROM n1 } {temp n1}
|
||||
do_execsql_test 1.2 { SELECT * FROM n2 } {main n2}
|
||||
do_execsql_test 1.3 { SELECT * FROM n3 } {at1 n3}
|
||||
do_execsql_test 1.4 { SELECT * FROM n4 } {at2 n4}
|
||||
|
||||
# EVIDENCE-OF: R-54577-28142 If a database name is specified as part of
|
||||
# an object reference, it must be either "main", or "temp" or the name
|
||||
# of an attached database.
|
||||
#
|
||||
# Or else it is a "no such table: xxx" error.
|
||||
#
|
||||
resolve_reopen_db
|
||||
do_execsql_test 2.1.1 { SELECT * FROM main.n1 } {main n1}
|
||||
do_execsql_test 2.1.2 { SELECT * FROM temp.n1 } {temp n1}
|
||||
do_execsql_test 2.1.3 { SELECT * FROM at1.n1 } {at1 n1}
|
||||
do_execsql_test 2.1.4 { SELECT * FROM at2.n1 } {at2 n1}
|
||||
|
||||
do_catchsql_test 2.2 { SELECT * FROM xxx.n1 } {1 {no such table: xxx.n1}}
|
||||
|
||||
# EVIDENCE-OF: R-26223-47623 Like other SQL identifiers, database names
|
||||
# are case-insensitive.
|
||||
#
|
||||
resolve_reopen_db
|
||||
do_execsql_test 3.1 { SELECT * FROM MAIN.n1 } {main n1}
|
||||
do_execsql_test 3.2 { SELECT * FROM tEmP.n1 } {temp n1}
|
||||
do_execsql_test 3.3 { SELECT * FROM aT1.n1 } {at1 n1}
|
||||
do_execsql_test 3.4 { SELECT * FROM At2.n1 } {at2 n1}
|
||||
|
||||
# EVIDENCE-OF: R-15639-28392 If a database name is specified, then only
|
||||
# the named database is searched for the named object.
|
||||
#
|
||||
do_catchsql_test 4.1 { SELECT * FROM temp.n2 } {1 {no such table: temp.n2}}
|
||||
do_catchsql_test 4.2 { SELECT * FROM main.n2 } {0 {main n2}}
|
||||
do_catchsql_test 4.3 { SELECT * FROM at1.n2 } {0 {at1 n2}}
|
||||
do_catchsql_test 4.4 { SELECT * FROM at2.n2 } {0 {at2 n2}}
|
||||
|
||||
# EVIDENCE-OF: R-08951-19801 When searching database schemas for a named
|
||||
# object, objects of types that cannot be used in the context of the
|
||||
# reference are always ignored.
|
||||
#
|
||||
# In this case, "types that cannot be used" are triggers and indexes.
|
||||
# The temp and main databases both contain triggers and indexes named
|
||||
# "n3" and "n4". Database "at2" contains a trigger called "n4". And yet:
|
||||
#
|
||||
do_execsql_test 5.1 { SELECT * FROM n3 } {at1 n3}
|
||||
do_execsql_test 5.2 { SELECT * FROM n4 } {at2 n4}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# EVIDENCE-OF: R-37286-42536
|
||||
#
|
||||
db close
|
||||
forcedelete test.db file.db
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 6.1 {
|
||||
ATTACH 'file.db' AS aux;
|
||||
CREATE TABLE t1(x, y);
|
||||
CREATE TEMP TABLE t1(x, y);
|
||||
CREATE TABLE aux.t1(x, y);
|
||||
}
|
||||
|
||||
do_execsql_test 6.2.0 { DROP TABLE t1 }
|
||||
do_catchsql_test 6.2.1 { SELECT * FROM temp.t1 } {1 {no such table: temp.t1}}
|
||||
do_catchsql_test 6.2.2 { SELECT * FROM main.t1 } {0 {}}
|
||||
do_catchsql_test 6.2.3 { SELECT * FROM aux.t1 } {0 {}}
|
||||
|
||||
do_execsql_test 6.3.0 { DROP TABLE t1 }
|
||||
do_catchsql_test 6.3.1 { SELECT * FROM main.t1 } {1 {no such table: main.t1}}
|
||||
do_catchsql_test 6.3.3 { SELECT * FROM aux.t1 } {0 {}}
|
||||
|
||||
do_execsql_test 6.4.0 { DROP TABLE t1 }
|
||||
do_catchsql_test 6.4.1 { SELECT * FROM aux.t1 } {1 {no such table: aux.t1}}
|
||||
|
||||
finish_test
|
||||
+2162
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,580 @@
|
||||
# 2010 September 24
|
||||
#
|
||||
# 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_select.html document are correct.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# te_* commands:
|
||||
#
|
||||
#
|
||||
# te_read_sql DB SELECT-STATEMENT
|
||||
# te_read_tbl DB TABLENAME
|
||||
#
|
||||
# These two commands are used to read a dataset from the database. A dataset
|
||||
# consists of N rows of M named columns of values each, where each value has a
|
||||
# type (null, integer, real, text or blob) and a value within the types domain.
|
||||
# The tcl format for a "dataset" is a list of two elements:
|
||||
#
|
||||
# * A list of the column names.
|
||||
# * A list of data rows. Each row is itself a list, where each element is
|
||||
# the contents of a column of the row. Each of these is a list of two
|
||||
# elements, the type name and the actual value.
|
||||
#
|
||||
# For example, the contents of table [t1] as a dataset is:
|
||||
#
|
||||
# CREATE TABLE t1(a, b);
|
||||
# INSERT INTO t1 VALUES('abc', NULL);
|
||||
# INSERT INTO t1 VALUES(43.1, 22);
|
||||
#
|
||||
# {a b} {{{TEXT abc} {NULL {}}} {{REAL 43.1} {INTEGER 22}}}
|
||||
#
|
||||
# The [te_read_tbl] command returns a dataset read from a table. The
|
||||
# [te_read_sql] returns the dataset that results from executing a SELECT
|
||||
# command.
|
||||
#
|
||||
#
|
||||
# te_tbljoin ?SWITCHES? LHS-TABLE RHS-TABLE
|
||||
# te_join ?SWITCHES? LHS-DATASET RHS-DATASET
|
||||
#
|
||||
# This command joins the two datasets and returns the resulting dataset. If
|
||||
# there are no switches specified, then the results is the cartesian product
|
||||
# of the two inputs. The [te_tbljoin] command reads the left and right-hand
|
||||
# datasets from the specified tables. The [te_join] command is passed the
|
||||
# datasets directly.
|
||||
#
|
||||
# Optional switches are as follows:
|
||||
#
|
||||
# -on SCRIPT
|
||||
# -using COLUMN-LIST
|
||||
# -left
|
||||
#
|
||||
# The -on option specifies a tcl script that is executed for each row in the
|
||||
# cartesian product of the two datasets. The script has 4 arguments appended
|
||||
# to it, in the following order:
|
||||
#
|
||||
# * The list of column-names from the left-hand dataset.
|
||||
# * A single row from the left-hand dataset (one "data row" list as
|
||||
# described above.
|
||||
# * The list of column-names from the right-hand dataset.
|
||||
# * A single row from the right-hand dataset.
|
||||
#
|
||||
# The script must return a boolean value - true if the combination of rows
|
||||
# should be included in the output dataset, or false otherwise.
|
||||
#
|
||||
# The -using option specifies a list of the columns from the right-hand
|
||||
# dataset that should be omitted from the output dataset.
|
||||
#
|
||||
# If the -left option is present, the join is done LEFT JOIN style.
|
||||
# Specifically, an extra row is inserted if after the -on script is run there
|
||||
# exist rows in the left-hand dataset that have no corresponding rows in
|
||||
# the output. See the implementation for more specific comments.
|
||||
#
|
||||
#
|
||||
# te_equals ?SWITCHES? COLNAME1 COLNAME2 <-on script args>
|
||||
#
|
||||
# The only supported switch is "-nocase". If it is present, then text values
|
||||
# are compared in a case-independent fashion. Otherwise, they are compared
|
||||
# as if using the SQLite BINARY collation sequence.
|
||||
#
|
||||
#
|
||||
# te_and ONSCRIPT1 ONSCRIPT2...
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# te_read_tbl DB TABLENAME
|
||||
# te_read_sql DB SELECT-STATEMENT
|
||||
#
|
||||
# These two procs are used to extract datasets from the database, either
|
||||
# by reading the contents of a named table (te_read_tbl), or by executing
|
||||
# a SELECT statement (t3_read_sql).
|
||||
#
|
||||
# See the comment above, describing "te_* commands", for details of the
|
||||
# return values.
|
||||
#
|
||||
proc te_read_tbl {db tbl} {
|
||||
te_read_sql $db "SELECT * FROM '$tbl'"
|
||||
}
|
||||
proc te_read_sql {db sql} {
|
||||
set S [sqlite3_prepare_v2 $db $sql -1 DUMMY]
|
||||
|
||||
set cols [list]
|
||||
for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} {
|
||||
lappend cols [sqlite3_column_name $S $i]
|
||||
}
|
||||
|
||||
set rows [list]
|
||||
while {[sqlite3_step $S] == "SQLITE_ROW"} {
|
||||
set r [list]
|
||||
for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} {
|
||||
lappend r [list [sqlite3_column_type $S $i] [sqlite3_column_text $S $i]]
|
||||
}
|
||||
lappend rows $r
|
||||
}
|
||||
sqlite3_finalize $S
|
||||
|
||||
return [list $cols $rows]
|
||||
}
|
||||
|
||||
#-------
|
||||
# Usage: te_join <table-data1> <table-data2> <join spec>...
|
||||
#
|
||||
# Where a join-spec is an optional list of arguments as follows:
|
||||
#
|
||||
# ?-left?
|
||||
# ?-using colname-list?
|
||||
# ?-on on-expr-proc?
|
||||
#
|
||||
proc te_join {data1 data2 args} {
|
||||
|
||||
set testproc ""
|
||||
set usinglist [list]
|
||||
set isleft 0
|
||||
for {set i 0} {$i < [llength $args]} {incr i} {
|
||||
set a [lindex $args $i]
|
||||
switch -- $a {
|
||||
-on { set testproc [lindex $args [incr i]] }
|
||||
-using { set usinglist [lindex $args [incr i]] }
|
||||
-left { set isleft 1 }
|
||||
default {
|
||||
error "Unknown argument: $a"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set c1 [lindex $data1 0]
|
||||
set c2 [lindex $data2 0]
|
||||
set omitlist [list]
|
||||
set nullrowlist [list]
|
||||
set cret $c1
|
||||
|
||||
set cidx 0
|
||||
foreach col $c2 {
|
||||
set idx [lsearch $usinglist $col]
|
||||
if {$idx>=0} {lappend omitlist $cidx}
|
||||
if {$idx<0} {
|
||||
lappend nullrowlist {NULL {}}
|
||||
lappend cret $col
|
||||
}
|
||||
incr cidx
|
||||
}
|
||||
set omitlist [lsort -integer -decreasing $omitlist]
|
||||
|
||||
|
||||
set rret [list]
|
||||
foreach r1 [lindex $data1 1] {
|
||||
set one 0
|
||||
foreach r2 [lindex $data2 1] {
|
||||
set ok 1
|
||||
if {$testproc != ""} {
|
||||
set ok [eval $testproc [list $c1 $r1 $c2 $r2]]
|
||||
}
|
||||
if {$ok} {
|
||||
set one 1
|
||||
foreach idx $omitlist {set r2 [lreplace $r2 $idx $idx]}
|
||||
lappend rret [concat $r1 $r2]
|
||||
}
|
||||
}
|
||||
|
||||
if {$isleft && $one==0} {
|
||||
lappend rret [concat $r1 $nullrowlist]
|
||||
}
|
||||
}
|
||||
|
||||
list $cret $rret
|
||||
}
|
||||
|
||||
proc te_tbljoin {db t1 t2 args} {
|
||||
te_join [te_read_tbl $db $t1] [te_read_tbl $db $t2] {*}$args
|
||||
}
|
||||
|
||||
proc te_apply_affinity {affinity typevar valvar} {
|
||||
upvar $typevar type
|
||||
upvar $valvar val
|
||||
|
||||
switch -- $affinity {
|
||||
integer {
|
||||
if {[string is double $val]} { set type REAL }
|
||||
if {[string is wideinteger $val]} { set type INTEGER }
|
||||
if {$type == "REAL" && int($val)==$val} {
|
||||
set type INTEGER
|
||||
set val [expr {int($val)}]
|
||||
}
|
||||
}
|
||||
text {
|
||||
set type TEXT
|
||||
}
|
||||
none { }
|
||||
|
||||
default { error "invalid affinity: $affinity" }
|
||||
}
|
||||
}
|
||||
|
||||
#----------
|
||||
# te_equals ?SWITCHES? c1 c2 cols1 row1 cols2 row2
|
||||
#
|
||||
proc te_equals {args} {
|
||||
|
||||
if {[llength $args]<6} {error "invalid arguments to te_equals"}
|
||||
foreach {c1 c2 cols1 row1 cols2 row2} [lrange $args end-5 end] break
|
||||
|
||||
set nocase 0
|
||||
set affinity none
|
||||
|
||||
for {set i 0} {$i < ([llength $args]-6)} {incr i} {
|
||||
set a [lindex $args $i]
|
||||
switch -- $a {
|
||||
-nocase {
|
||||
set nocase 1
|
||||
}
|
||||
-affinity {
|
||||
set affinity [string tolower [lindex $args [incr i]]]
|
||||
}
|
||||
default {
|
||||
error "invalid arguments to te_equals"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set idx2 [if {[string is integer $c2]} { set c2 } else { lsearch $cols2 $c2 }]
|
||||
set idx1 [if {[string is integer $c1]} { set c1 } else { lsearch $cols1 $c1 }]
|
||||
|
||||
set t1 [lindex $row1 $idx1 0]
|
||||
set t2 [lindex $row2 $idx2 0]
|
||||
set v1 [lindex $row1 $idx1 1]
|
||||
set v2 [lindex $row2 $idx2 1]
|
||||
|
||||
te_apply_affinity $affinity t1 v1
|
||||
te_apply_affinity $affinity t2 v2
|
||||
|
||||
if {$t1 == "NULL" || $t2 == "NULL"} { return 0 }
|
||||
if {$nocase && $t1 == "TEXT"} { set v1 [string tolower $v1] }
|
||||
if {$nocase && $t2 == "TEXT"} { set v2 [string tolower $v2] }
|
||||
|
||||
|
||||
set res [expr {$t1 == $t2 && [string equal $v1 $v2]}]
|
||||
return $res
|
||||
}
|
||||
|
||||
proc te_false {args} { return 0 }
|
||||
proc te_true {args} { return 1 }
|
||||
|
||||
proc te_and {args} {
|
||||
foreach a [lrange $args 0 end-4] {
|
||||
set res [eval $a [lrange $args end-3 end]]
|
||||
if {$res == 0} {return 0}
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
proc te_dataset_eq {testname got expected} {
|
||||
uplevel #0 [list do_test $testname [list set {} $got] $expected]
|
||||
}
|
||||
proc te_dataset_eq_unordered {testname got expected} {
|
||||
lset got 1 [lsort [lindex $got 1]]
|
||||
lset expected 1 [lsort [lindex $expected 1]]
|
||||
te_dataset_eq $testname $got $expected
|
||||
}
|
||||
|
||||
proc te_dataset_ne {testname got unexpected} {
|
||||
uplevel #0 [list do_test $testname [list string equal $got $unexpected] 0]
|
||||
}
|
||||
proc te_dataset_ne_unordered {testname got unexpected} {
|
||||
lset got 1 [lsort [lindex $got 1]]
|
||||
lset unexpected 1 [lsort [lindex $unexpected 1]]
|
||||
te_dataset_ne $testname $got $unexpected
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
proc test_join {tn sqljoin tbljoinargs} {
|
||||
set sql [te_read_sql db "SELECT * FROM $sqljoin"]
|
||||
set te [te_tbljoin db {*}$tbljoinargs]
|
||||
te_dataset_eq_unordered $tn $sql $te
|
||||
}
|
||||
|
||||
drop_all_tables
|
||||
do_execsql_test e_select-2.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE t3(b COLLATE nocase);
|
||||
|
||||
INSERT INTO t1 VALUES(2, 'B');
|
||||
INSERT INTO t1 VALUES(1, 'A');
|
||||
INSERT INTO t1 VALUES(4, 'D');
|
||||
INSERT INTO t1 VALUES(NULL, NULL);
|
||||
INSERT INTO t1 VALUES(3, NULL);
|
||||
|
||||
INSERT INTO t2 VALUES(1, 'A');
|
||||
INSERT INTO t2 VALUES(2, NULL);
|
||||
INSERT INTO t2 VALUES(5, 'E');
|
||||
INSERT INTO t2 VALUES(NULL, NULL);
|
||||
INSERT INTO t2 VALUES(3, 'C');
|
||||
|
||||
INSERT INTO t3 VALUES('a');
|
||||
INSERT INTO t3 VALUES('c');
|
||||
INSERT INTO t3 VALUES('b');
|
||||
} {}
|
||||
|
||||
foreach {tn indexes} {
|
||||
e_select-2.1.1 { }
|
||||
e_select-2.1.2 { CREATE INDEX i1 ON t1(a) }
|
||||
e_select-2.1.3 { CREATE INDEX i1 ON t2(a) }
|
||||
e_select-2.1.4 { CREATE INDEX i1 ON t3(b) }
|
||||
} {
|
||||
|
||||
catchsql { DROP INDEX i1 }
|
||||
catchsql { DROP INDEX i2 }
|
||||
catchsql { DROP INDEX i3 }
|
||||
execsql $indexes
|
||||
|
||||
# EVIDENCE-OF: R-46122-14930 If the join-op is "CROSS JOIN", "INNER
|
||||
# JOIN", "JOIN" or a comma (",") and there is no ON or USING clause,
|
||||
# then the result of the join is simply the cartesian product of the
|
||||
# left and right-hand datasets.
|
||||
#
|
||||
# EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER
|
||||
# JOIN", "JOIN" and "," join operators.
|
||||
#
|
||||
# EVIDENCE-OF: R-07544-24155 The "CROSS JOIN" join operator produces the
|
||||
# same data as the "INNER JOIN", "JOIN" and "," operators
|
||||
#
|
||||
test_join $tn.1.1 "t1, t2" {t1 t2}
|
||||
test_join $tn.1.2 "t1 INNER JOIN t2" {t1 t2}
|
||||
test_join $tn.1.3 "t1 CROSS JOIN t2" {t1 t2}
|
||||
test_join $tn.1.4 "t1 JOIN t2" {t1 t2}
|
||||
test_join $tn.1.5 "t2, t3" {t2 t3}
|
||||
test_join $tn.1.6 "t2 INNER JOIN t3" {t2 t3}
|
||||
test_join $tn.1.7 "t2 CROSS JOIN t3" {t2 t3}
|
||||
test_join $tn.1.8 "t2 JOIN t3" {t2 t3}
|
||||
test_join $tn.1.9 "t2, t2 AS x" {t2 t2}
|
||||
test_join $tn.1.10 "t2 INNER JOIN t2 AS x" {t2 t2}
|
||||
test_join $tn.1.11 "t2 CROSS JOIN t2 AS x" {t2 t2}
|
||||
test_join $tn.1.12 "t2 JOIN t2 AS x" {t2 t2}
|
||||
|
||||
# EVIDENCE-OF: R-22775-56496 If there is an ON clause specified, then
|
||||
# the ON expression is evaluated for each row of the cartesian product
|
||||
# as a boolean expression. All rows for which the expression evaluates
|
||||
# to false are excluded from the dataset.
|
||||
#
|
||||
test_join $tn.2.1 "t1, t2 ON (t1.a=t2.a)" {t1 t2 -on {te_equals a a}}
|
||||
test_join $tn.2.2 "t2, t1 ON (t1.a=t2.a)" {t2 t1 -on {te_equals a a}}
|
||||
test_join $tn.2.3 "t2, t1 ON (1)" {t2 t1 -on te_true}
|
||||
test_join $tn.2.4 "t2, t1 ON (NULL)" {t2 t1 -on te_false}
|
||||
test_join $tn.2.5 "t2, t1 ON (1.1-1.1)" {t2 t1 -on te_false}
|
||||
test_join $tn.2.6 "t1, t2 ON (1.1-1.0)" {t1 t2 -on te_true}
|
||||
|
||||
|
||||
test_join $tn.3 "t1 LEFT JOIN t2 ON (t1.a=t2.a)" {t1 t2 -left -on {te_equals a a}}
|
||||
test_join $tn.4 "t1 LEFT JOIN t2 USING (a)" {
|
||||
t1 t2 -left -using a -on {te_equals a a}
|
||||
}
|
||||
test_join $tn.5 "t1 CROSS JOIN t2 USING(b, a)" {
|
||||
t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.6 "t1 NATURAL JOIN t2" {
|
||||
t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.7 "t1 NATURAL INNER JOIN t2" {
|
||||
t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.8 "t1 NATURAL CROSS JOIN t2" {
|
||||
t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.9 "t1 NATURAL INNER JOIN t2" {
|
||||
t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.10 "t1 NATURAL LEFT JOIN t2" {
|
||||
t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.11 "t1 NATURAL LEFT OUTER JOIN t2" {
|
||||
t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.12 "t2 NATURAL JOIN t1" {
|
||||
t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.13 "t2 NATURAL INNER JOIN t1" {
|
||||
t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.14 "t2 NATURAL CROSS JOIN t1" {
|
||||
t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.15 "t2 NATURAL INNER JOIN t1" {
|
||||
t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.16 "t2 NATURAL LEFT JOIN t1" {
|
||||
t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.17 "t2 NATURAL LEFT OUTER JOIN t1" {
|
||||
t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}}
|
||||
}
|
||||
test_join $tn.18 "t1 LEFT JOIN t2 USING (b)" {
|
||||
t1 t2 -left -using b -on {te_equals b b}
|
||||
}
|
||||
test_join $tn.19 "t1 JOIN t3 USING(b)" {t1 t3 -using b -on {te_equals b b}}
|
||||
test_join $tn.20 "t3 JOIN t1 USING(b)" {
|
||||
t3 t1 -using b -on {te_equals -nocase b b}
|
||||
}
|
||||
test_join $tn.21 "t1 NATURAL JOIN t3" {
|
||||
t1 t3 -using b -on {te_equals b b}
|
||||
}
|
||||
test_join $tn.22 "t3 NATURAL JOIN t1" {
|
||||
t3 t1 -using b -on {te_equals -nocase b b}
|
||||
}
|
||||
test_join $tn.23 "t1 NATURAL LEFT JOIN t3" {
|
||||
t1 t3 -left -using b -on {te_equals b b}
|
||||
}
|
||||
test_join $tn.24 "t3 NATURAL LEFT JOIN t1" {
|
||||
t3 t1 -left -using b -on {te_equals -nocase b b}
|
||||
}
|
||||
test_join $tn.25 "t1 LEFT JOIN t3 ON (t3.b=t1.b)" {
|
||||
t1 t3 -left -on {te_equals -nocase b b}
|
||||
}
|
||||
test_join $tn.26 "t1 LEFT JOIN t3 ON (t1.b=t3.b)" {
|
||||
t1 t3 -left -on {te_equals b b}
|
||||
}
|
||||
test_join $tn.27 "t1 JOIN t3 ON (t1.b=t3.b)" { t1 t3 -on {te_equals b b} }
|
||||
|
||||
# EVIDENCE-OF: R-28760-53843 When more than two tables are joined
|
||||
# together as part of a FROM clause, the join operations are processed
|
||||
# in order from left to right. In other words, the FROM clause (A
|
||||
# join-op-1 B join-op-2 C) is computed as ((A join-op-1 B) join-op-2 C).
|
||||
#
|
||||
# Tests 28a and 28b show that the statement above is true for this case.
|
||||
# Test 28c shows that if the parenthesis force a different order of
|
||||
# evaluation the result is different. Test 28d verifies that the result
|
||||
# of the query with the parenthesis forcing a different order of evaluation
|
||||
# is as calculated by the [te_*] procs.
|
||||
#
|
||||
set t3_natural_left_join_t2 [
|
||||
te_tbljoin db t3 t2 -left -using {b} -on {te_equals -nocase b b}
|
||||
]
|
||||
set t1 [te_read_tbl db t1]
|
||||
te_dataset_eq_unordered $tn.28a [
|
||||
te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1"
|
||||
] [te_join $t3_natural_left_join_t2 $t1 \
|
||||
-using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \
|
||||
]
|
||||
|
||||
te_dataset_eq_unordered $tn.28b [
|
||||
te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1"
|
||||
] [te_join $t3_natural_left_join_t2 $t1 \
|
||||
-using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \
|
||||
]
|
||||
|
||||
te_dataset_ne_unordered $tn.28c [
|
||||
te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1"
|
||||
] [
|
||||
te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)"
|
||||
]
|
||||
|
||||
set t2_natural_join_t1 [te_tbljoin db t2 t1 -using {a b} \
|
||||
-using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \
|
||||
]
|
||||
set t3 [te_read_tbl db t3]
|
||||
te_dataset_eq_unordered $tn.28d [
|
||||
te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)"
|
||||
] [te_join $t3 $t2_natural_join_t1 \
|
||||
-left -using {b} -on {te_equals -nocase b b} \
|
||||
]
|
||||
}
|
||||
|
||||
do_execsql_test e_select-2.2.0 {
|
||||
CREATE TABLE t4(x TEXT COLLATE nocase);
|
||||
CREATE TABLE t5(y INTEGER, z TEXT COLLATE binary);
|
||||
|
||||
INSERT INTO t4 VALUES('2.0');
|
||||
INSERT INTO t4 VALUES('TWO');
|
||||
INSERT INTO t5 VALUES(2, 'two');
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-55824-40976 A sub-select specified in the join-source
|
||||
# following the FROM clause in a simple SELECT statement is handled as
|
||||
# if it was a table containing the data returned by executing the
|
||||
# sub-select statement.
|
||||
#
|
||||
# EVIDENCE-OF: R-42612-06757 Each column of the sub-select dataset
|
||||
# inherits the collation sequence and affinity of the corresponding
|
||||
# expression in the sub-select statement.
|
||||
#
|
||||
foreach {tn subselect select spec} {
|
||||
1 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss%"
|
||||
{t1 %ss%}
|
||||
|
||||
2 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss% AS x ON (t1.a=x.a)"
|
||||
{t1 %ss% -on {te_equals 0 0}}
|
||||
|
||||
3 "SELECT * FROM t2" "SELECT * FROM %ss% AS x JOIN t1 ON (t1.a=x.a)"
|
||||
{%ss% t1 -on {te_equals 0 0}}
|
||||
|
||||
4 "SELECT * FROM t1, t2" "SELECT * FROM %ss% AS x JOIN t3"
|
||||
{%ss% t3}
|
||||
|
||||
5 "SELECT * FROM t1, t2" "SELECT * FROM %ss% NATURAL JOIN t3"
|
||||
{%ss% t3 -using b -on {te_equals 1 0}}
|
||||
|
||||
6 "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL JOIN %ss%"
|
||||
{t3 %ss% -using b -on {te_equals -nocase 0 1}}
|
||||
|
||||
7 "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL LEFT JOIN %ss%"
|
||||
{t3 %ss% -left -using b -on {te_equals -nocase 0 1}}
|
||||
|
||||
8 "SELECT count(*) AS y FROM t4" "SELECT * FROM t5, %ss% USING (y)"
|
||||
{t5 %ss% -using y -on {te_equals -affinity text 0 0}}
|
||||
|
||||
9 "SELECT count(*) AS y FROM t4" "SELECT * FROM %ss%, t5 USING (y)"
|
||||
{%ss% t5 -using y -on {te_equals -affinity text 0 0}}
|
||||
|
||||
10 "SELECT x AS y FROM t4" "SELECT * FROM %ss% JOIN t5 USING (y)"
|
||||
{%ss% t5 -using y -on {te_equals -nocase -affinity integer 0 0}}
|
||||
|
||||
11 "SELECT x AS y FROM t4" "SELECT * FROM t5 JOIN %ss% USING (y)"
|
||||
{t5 %ss% -using y -on {te_equals -nocase -affinity integer 0 0}}
|
||||
|
||||
12 "SELECT y AS x FROM t5" "SELECT * FROM %ss% JOIN t4 USING (x)"
|
||||
{%ss% t4 -using x -on {te_equals -nocase -affinity integer 0 0}}
|
||||
|
||||
13 "SELECT y AS x FROM t5" "SELECT * FROM t4 JOIN %ss% USING (x)"
|
||||
{t4 %ss% -using x -on {te_equals -nocase -affinity integer 0 0}}
|
||||
|
||||
14 "SELECT +y AS x FROM t5" "SELECT * FROM %ss% JOIN t4 USING (x)"
|
||||
{%ss% t4 -using x -on {te_equals -nocase -affinity text 0 0}}
|
||||
|
||||
15 "SELECT +y AS x FROM t5" "SELECT * FROM t4 JOIN %ss% USING (x)"
|
||||
{t4 %ss% -using x -on {te_equals -nocase -affinity text 0 0}}
|
||||
} {
|
||||
|
||||
# Create a temporary table named %ss% containing the data returned by
|
||||
# the sub-select. Then have the [te_tbljoin] proc use this table to
|
||||
# compute the expected results of the $select query. Drop the temporary
|
||||
# table before continuing.
|
||||
#
|
||||
execsql "CREATE TEMP TABLE '%ss%' AS $subselect"
|
||||
set te [eval te_tbljoin db $spec]
|
||||
execsql "DROP TABLE '%ss%'"
|
||||
|
||||
# Check that the actual data returned by the $select query is the same
|
||||
# as the expected data calculated using [te_tbljoin] above.
|
||||
#
|
||||
te_dataset_eq_unordered e_select-2.2.1.$tn [
|
||||
te_read_sql db [string map [list %ss% "($subselect)"] $select]
|
||||
] $te
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,608 @@
|
||||
# 2010 September 20
|
||||
#
|
||||
# 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_update.html document are correct.
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
#--------------------
|
||||
# Test organization:
|
||||
#
|
||||
# e_update-1.*: Test statements describing the workings of UPDATE statements.
|
||||
#
|
||||
# e_update-2.*: Test the restrictions on the UPDATE statement syntax that
|
||||
# can be used within triggers.
|
||||
#
|
||||
# e_update-3.*: Test the special LIMIT/OFFSET and ORDER BY clauses that can
|
||||
# be used with UPDATE when SQLite is compiled with
|
||||
# SQLITE_ENABLE_UPDATE_DELETE_LIMIT.
|
||||
#
|
||||
|
||||
forcedelete test.db2
|
||||
|
||||
do_execsql_test e_update-0.0 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE TABLE t2(a, b, c);
|
||||
CREATE TABLE t3(a, b UNIQUE);
|
||||
CREATE TABLE t6(x, y);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
|
||||
CREATE TEMP TABLE t4(x, y);
|
||||
CREATE TEMP TABLE t6(x, y);
|
||||
|
||||
CREATE TABLE aux.t1(a, b);
|
||||
CREATE TABLE aux.t5(a, b);
|
||||
} {}
|
||||
|
||||
proc do_update_tests {args} {
|
||||
uplevel do_select_tests $args
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-05685-44205 -- syntax diagram update-stmt
|
||||
#
|
||||
do_update_tests e_update-0 {
|
||||
1 "UPDATE t1 SET a=10" {}
|
||||
2 "UPDATE t1 SET a=10, b=5" {}
|
||||
3 "UPDATE t1 SET a=10 WHERE b=5" {}
|
||||
4 "UPDATE t1 SET b=5,a=10 WHERE 1" {}
|
||||
5 "UPDATE main.t1 SET a=10" {}
|
||||
6 "UPDATE main.t1 SET a=10, b=5" {}
|
||||
7 "UPDATE main.t1 SET a=10 WHERE b=5" {}
|
||||
9 "UPDATE OR ROLLBACK t1 SET a=10" {}
|
||||
10 "UPDATE OR ROLLBACK t1 SET a=10, b=5" {}
|
||||
11 "UPDATE OR ROLLBACK t1 SET a=10 WHERE b=5" {}
|
||||
12 "UPDATE OR ROLLBACK t1 SET b=5,a=10 WHERE 1" {}
|
||||
13 "UPDATE OR ROLLBACK main.t1 SET a=10" {}
|
||||
14 "UPDATE OR ROLLBACK main.t1 SET a=10, b=5" {}
|
||||
15 "UPDATE OR ROLLBACK main.t1 SET a=10 WHERE b=5" {}
|
||||
16 "UPDATE OR ROLLBACK main.t1 SET b=5,a=10 WHERE 1" {}
|
||||
17 "UPDATE OR ABORT t1 SET a=10" {}
|
||||
18 "UPDATE OR ABORT t1 SET a=10, b=5" {}
|
||||
19 "UPDATE OR ABORT t1 SET a=10 WHERE b=5" {}
|
||||
20 "UPDATE OR ABORT t1 SET b=5,a=10 WHERE 1" {}
|
||||
21 "UPDATE OR ABORT main.t1 SET a=10" {}
|
||||
22 "UPDATE OR ABORT main.t1 SET a=10, b=5" {}
|
||||
23 "UPDATE OR ABORT main.t1 SET a=10 WHERE b=5" {}
|
||||
24 "UPDATE OR ABORT main.t1 SET b=5,a=10 WHERE 1" {}
|
||||
25 "UPDATE OR REPLACE t1 SET a=10" {}
|
||||
26 "UPDATE OR REPLACE t1 SET a=10, b=5" {}
|
||||
27 "UPDATE OR REPLACE t1 SET a=10 WHERE b=5" {}
|
||||
28 "UPDATE OR REPLACE t1 SET b=5,a=10 WHERE 1" {}
|
||||
29 "UPDATE OR REPLACE main.t1 SET a=10" {}
|
||||
30 "UPDATE OR REPLACE main.t1 SET a=10, b=5" {}
|
||||
31 "UPDATE OR REPLACE main.t1 SET a=10 WHERE b=5" {}
|
||||
32 "UPDATE OR REPLACE main.t1 SET b=5,a=10 WHERE 1" {}
|
||||
33 "UPDATE OR FAIL t1 SET a=10" {}
|
||||
34 "UPDATE OR FAIL t1 SET a=10, b=5" {}
|
||||
35 "UPDATE OR FAIL t1 SET a=10 WHERE b=5" {}
|
||||
36 "UPDATE OR FAIL t1 SET b=5,a=10 WHERE 1" {}
|
||||
37 "UPDATE OR FAIL main.t1 SET a=10" {}
|
||||
38 "UPDATE OR FAIL main.t1 SET a=10, b=5" {}
|
||||
39 "UPDATE OR FAIL main.t1 SET a=10 WHERE b=5" {}
|
||||
40 "UPDATE OR FAIL main.t1 SET b=5,a=10 WHERE 1" {}
|
||||
41 "UPDATE OR IGNORE t1 SET a=10" {}
|
||||
42 "UPDATE OR IGNORE t1 SET a=10, b=5" {}
|
||||
43 "UPDATE OR IGNORE t1 SET a=10 WHERE b=5" {}
|
||||
44 "UPDATE OR IGNORE t1 SET b=5,a=10 WHERE 1" {}
|
||||
45 "UPDATE OR IGNORE main.t1 SET a=10" {}
|
||||
46 "UPDATE OR IGNORE main.t1 SET a=10, b=5" {}
|
||||
47 "UPDATE OR IGNORE main.t1 SET a=10 WHERE b=5" {}
|
||||
48 "UPDATE OR IGNORE main.t1 SET b=5,a=10 WHERE 1" {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-38515-45264 An UPDATE statement is used to modify a
|
||||
# subset of the values stored in zero or more rows of the database table
|
||||
# identified by the qualified-table-name specified as part of the UPDATE
|
||||
# statement.
|
||||
#
|
||||
# Test cases e_update-1.1.1.* test the "identified by the
|
||||
# qualified-table-name" part of the statement above. Tests
|
||||
# e_update-1.1.2.* show that the "zero or more rows" part is
|
||||
# accurate.
|
||||
#
|
||||
do_execsql_test e_update-1.1.0 {
|
||||
INSERT INTO main.t1 VALUES(1, 'i');
|
||||
INSERT INTO main.t1 VALUES(2, 'ii');
|
||||
INSERT INTO main.t1 VALUES(3, 'iii');
|
||||
|
||||
INSERT INTO aux.t1 VALUES(1, 'I');
|
||||
INSERT INTO aux.t1 VALUES(2, 'II');
|
||||
INSERT INTO aux.t1 VALUES(3, 'III');
|
||||
} {}
|
||||
do_update_tests e_update-1.1 {
|
||||
1.1 "UPDATE t1 SET a = a+1; SELECT * FROM t1" {2 i 3 ii 4 iii}
|
||||
1.2 "UPDATE main.t1 SET a = a+1; SELECT * FROM main.t1" {3 i 4 ii 5 iii}
|
||||
1.3 "UPDATE aux.t1 SET a = a+1; SELECT * FROM aux.t1" {2 I 3 II 4 III}
|
||||
|
||||
2.1 "UPDATE t1 SET a = a+1 WHERE a = 1; SELECT * FROM t1" {3 i 4 ii 5 iii}
|
||||
2.2 "UPDATE t1 SET a = a+1 WHERE a = 4; SELECT * FROM t1" {3 i 5 ii 5 iii}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-55869-30521 If the UPDATE statement does not have a
|
||||
# WHERE clause, all rows in the table are modified by the UPDATE.
|
||||
#
|
||||
do_execsql_test e_update-1.2.0 {
|
||||
DELETE FROM main.t1;
|
||||
INSERT INTO main.t1 VALUES(1, 'i');
|
||||
INSERT INTO main.t1 VALUES(2, 'ii');
|
||||
INSERT INTO main.t1 VALUES(3, 'iii');
|
||||
} {}
|
||||
do_update_tests e_update-1.2 {
|
||||
1 "UPDATE t1 SET b = 'roman' ; SELECT * FROM t1"
|
||||
{1 roman 2 roman 3 roman}
|
||||
|
||||
2 "UPDATE t1 SET a = 'greek' ; SELECT * FROM t1"
|
||||
{greek roman greek roman greek roman}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-42117-40023 Otherwise, the UPDATE affects only those
|
||||
# rows for which the result of evaluating the WHERE clause expression as
|
||||
# a boolean expression is true.
|
||||
#
|
||||
do_execsql_test e_update-1.3.0 {
|
||||
DELETE FROM main.t1;
|
||||
INSERT INTO main.t1 VALUES(NULL, '');
|
||||
INSERT INTO main.t1 VALUES(1, 'i');
|
||||
INSERT INTO main.t1 VALUES(2, 'ii');
|
||||
INSERT INTO main.t1 VALUES(3, 'iii');
|
||||
} {}
|
||||
do_update_tests e_update-1.3 {
|
||||
1 "UPDATE t1 SET b = 'roman' WHERE a<2 ; SELECT * FROM t1"
|
||||
{{} {} 1 roman 2 ii 3 iii}
|
||||
|
||||
2 "UPDATE t1 SET b = 'egyptian' WHERE (a-3)/10.0 ; SELECT * FROM t1"
|
||||
{{} {} 1 egyptian 2 egyptian 3 iii}
|
||||
|
||||
3 "UPDATE t1 SET b = 'macedonian' WHERE a; SELECT * FROM t1"
|
||||
{{} {} 1 macedonian 2 macedonian 3 macedonian}
|
||||
|
||||
4 "UPDATE t1 SET b = 'lithuanian' WHERE a IS NULL; SELECT * FROM t1"
|
||||
{{} lithuanian 1 macedonian 2 macedonian 3 macedonian}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-58129-20729 It is not an error if the WHERE clause does
|
||||
# not evaluate to true for any row in the table - this just means that
|
||||
# the UPDATE statement affects zero rows.
|
||||
#
|
||||
do_execsql_test e_update-1.4.0 {
|
||||
DELETE FROM main.t1;
|
||||
INSERT INTO main.t1 VALUES(NULL, '');
|
||||
INSERT INTO main.t1 VALUES(1, 'i');
|
||||
INSERT INTO main.t1 VALUES(2, 'ii');
|
||||
INSERT INTO main.t1 VALUES(3, 'iii');
|
||||
} {}
|
||||
do_update_tests e_update-1.4 -query {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 "UPDATE t1 SET b = 'burmese' WHERE a=5" {{} {} 1 i 2 ii 3 iii}
|
||||
|
||||
2 "UPDATE t1 SET b = 'burmese' WHERE length(b)<1 AND a IS NOT NULL"
|
||||
{{} {} 1 i 2 ii 3 iii}
|
||||
|
||||
3 "UPDATE t1 SET b = 'burmese' WHERE 0" {{} {} 1 i 2 ii 3 iii}
|
||||
|
||||
4 "UPDATE t1 SET b = 'burmese' WHERE (SELECT a FROM t1 WHERE rowid=1)"
|
||||
{{} {} 1 i 2 ii 3 iii}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-40598-36595 For each affected row, the named columns
|
||||
# are set to the values found by evaluating the corresponding scalar
|
||||
# expressions.
|
||||
#
|
||||
# EVIDENCE-OF: R-40472-60438 Columns that do not appear in the list of
|
||||
# assignments are left unmodified.
|
||||
#
|
||||
do_execsql_test e_update-1.5.0 {
|
||||
INSERT INTO t2(rowid, a, b, c) VALUES(1, 3, 1, 4);
|
||||
INSERT INTO t2(rowid, a, b, c) VALUES(2, 1, 5, 9);
|
||||
INSERT INTO t2(rowid, a, b, c) VALUES(3, 2, 6, 5);
|
||||
} {}
|
||||
do_update_tests e_update-1.5 -query {
|
||||
SELECT * FROM t2
|
||||
} {
|
||||
1 "UPDATE t2 SET c = 1+1 WHERE a=2"
|
||||
{3 1 4 1 5 9 2 6 2}
|
||||
|
||||
2 "UPDATE t2 SET b = 4/2, c=CAST((0.4*5) AS INTEGER) WHERE a<3"
|
||||
{3 1 4 1 2 2 2 2 2}
|
||||
|
||||
3 "UPDATE t2 SET a = 1"
|
||||
{1 1 4 1 2 2 1 2 2}
|
||||
|
||||
4 "UPDATE t2 SET b = (SELECT count(*)+2 FROM t2), c = 24/3+1 WHERE rowid=2"
|
||||
{1 1 4 1 5 9 1 2 2}
|
||||
|
||||
5 "UPDATE t2 SET a = 3 WHERE c = 4"
|
||||
{3 1 4 1 5 9 1 2 2}
|
||||
|
||||
6 "UPDATE t2 SET a = b WHERE rowid>2"
|
||||
{3 1 4 1 5 9 2 2 2}
|
||||
|
||||
6 "UPDATE t2 SET b=6, c=5 WHERE a=b AND b=c"
|
||||
{3 1 4 1 5 9 2 6 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-09060-20018 If a single column-name appears more than
|
||||
# once in the list of assignment expressions, all but the rightmost
|
||||
# occurence is ignored.
|
||||
#
|
||||
do_update_tests e_update-1.6 -query {
|
||||
SELECT * FROM t2
|
||||
} {
|
||||
1 "UPDATE t2 SET c=5, c=6, c=7 WHERE rowid=1" {3 1 7 1 5 9 2 6 5}
|
||||
2 "UPDATE t2 SET c=7, c=6, c=5 WHERE rowid=1" {3 1 5 1 5 9 2 6 5}
|
||||
3 "UPDATE t2 SET c=5, b=6, c=7 WHERE rowid=1" {3 6 7 1 5 9 2 6 5}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-36239-04077 The scalar expressions may refer to columns
|
||||
# of the row being updated.
|
||||
#
|
||||
# EVIDENCE-OF: R-04558-24451 In this case all scalar expressions are
|
||||
# evaluated before any assignments are made.
|
||||
#
|
||||
do_execsql_test e_update-1.7.0 {
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2(rowid, a, b, c) VALUES(1, 3, 1, 4);
|
||||
INSERT INTO t2(rowid, a, b, c) VALUES(2, 1, 5, 9);
|
||||
INSERT INTO t2(rowid, a, b, c) VALUES(3, 2, 6, 5);
|
||||
} {}
|
||||
do_update_tests e_update-1.7 -query {
|
||||
SELECT * FROM t2
|
||||
} {
|
||||
1 "UPDATE t2 SET a=b+c" {5 1 4 14 5 9 11 6 5}
|
||||
2 "UPDATE t2 SET a=b, b=a" {1 5 4 5 14 9 6 11 5}
|
||||
3 "UPDATE t2 SET a=c||c, c=NULL" {44 5 {} 99 14 {} 55 11 {}}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-12619-24112 The optional conflict-clause allows the
|
||||
# user to nominate a specific constraint conflict resolution algorithm
|
||||
# to use during this one UPDATE command.
|
||||
#
|
||||
do_execsql_test e_update-1.8.0 {
|
||||
DELETE FROM t3;
|
||||
INSERT INTO t3 VALUES(1, 'one');
|
||||
INSERT INTO t3 VALUES(2, 'two');
|
||||
INSERT INTO t3 VALUES(3, 'three');
|
||||
INSERT INTO t3 VALUES(4, 'four');
|
||||
} {}
|
||||
foreach {tn sql error ac data } {
|
||||
1 "UPDATE t3 SET b='one' WHERE a=3"
|
||||
{column b is not unique} 1 {1 one 2 two 3 three 4 four}
|
||||
|
||||
2 "UPDATE OR REPLACE t3 SET b='one' WHERE a=3"
|
||||
{} 1 {2 two 3 one 4 four}
|
||||
|
||||
3 "UPDATE OR FAIL t3 SET b='three'"
|
||||
{column b is not unique} 1 {2 three 3 one 4 four}
|
||||
|
||||
4 "UPDATE OR IGNORE t3 SET b='three' WHERE a=3"
|
||||
{} 1 {2 three 3 one 4 four}
|
||||
|
||||
5 "UPDATE OR ABORT t3 SET b='three' WHERE a=3"
|
||||
{column b is not unique} 1 {2 three 3 one 4 four}
|
||||
|
||||
6 "BEGIN" {} 0 {2 three 3 one 4 four}
|
||||
|
||||
7 "UPDATE t3 SET b='three' WHERE a=3"
|
||||
{column b is not unique} 0 {2 three 3 one 4 four}
|
||||
|
||||
8 "UPDATE OR ABORT t3 SET b='three' WHERE a=3"
|
||||
{column b is not unique} 0 {2 three 3 one 4 four}
|
||||
|
||||
9 "UPDATE OR FAIL t3 SET b='two'"
|
||||
{column b is not unique} 0 {2 two 3 one 4 four}
|
||||
|
||||
10 "UPDATE OR IGNORE t3 SET b='four' WHERE a=3"
|
||||
{} 0 {2 two 3 one 4 four}
|
||||
|
||||
11 "UPDATE OR REPLACE t3 SET b='four' WHERE a=3"
|
||||
{} 0 {2 two 3 four}
|
||||
|
||||
12 "UPDATE OR ROLLBACK t3 SET b='four'"
|
||||
{column b is not unique} 1 {2 three 3 one 4 four}
|
||||
} {
|
||||
do_catchsql_test e_update-1.8.$tn.1 $sql [list [expr {$error!=""}] $error]
|
||||
do_execsql_test e_update-1.8.$tn.2 {SELECT * FROM t3} [list {*}$data]
|
||||
do_test e_update-1.8.$tn.3 {sqlite3_get_autocommit db} $ac
|
||||
}
|
||||
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-12123-54095 The table-name specified as part of an
|
||||
# UPDATE statement within a trigger body must be unqualified.
|
||||
#
|
||||
# EVIDENCE-OF: R-09690-36749 In other words, the database-name. prefix
|
||||
# on the table name of the UPDATE is not allowed within triggers.
|
||||
#
|
||||
do_update_tests e_update-2.1 -error {
|
||||
qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers
|
||||
} {
|
||||
1 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
UPDATE main.t2 SET a=1, b=2, c=3;
|
||||
END;
|
||||
} {}
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER tr1 BEFORE UPDATE ON t2 BEGIN
|
||||
UPDATE aux.t1 SET a=1, b=2;
|
||||
END;
|
||||
} {}
|
||||
|
||||
3 {
|
||||
CREATE TRIGGER tr1 AFTER DELETE ON t4 BEGIN
|
||||
UPDATE main.t1 SET a=1, b=2;
|
||||
END;
|
||||
} {}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-06085-13761 Unless the table to which the trigger is
|
||||
# attached is in the TEMP database, the table being updated by the
|
||||
# trigger program must reside in the same database as it.
|
||||
#
|
||||
do_update_tests e_update-2.2 -error {
|
||||
no such table: %s
|
||||
} {
|
||||
1 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
|
||||
UPDATE t4 SET x=x+1;
|
||||
END;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
} "main.t4"
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER aux.tr1 AFTER INSERT ON t5 BEGIN
|
||||
UPDATE t4 SET x=x+1;
|
||||
END;
|
||||
INSERT INTO t5 VALUES(1, 2);
|
||||
} "aux.t4"
|
||||
}
|
||||
do_execsql_test e_update-2.2.X {
|
||||
DROP TRIGGER tr1;
|
||||
DROP TRIGGER aux.tr1;
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-29512-54644 If the table to which the trigger is
|
||||
# attached is in the TEMP database, then the unqualified name of the
|
||||
# table being updated is resolved in the same way as it is for a
|
||||
# top-level statement (by searching first the TEMP database, then the
|
||||
# main database, then any other databases in the order they were
|
||||
# attached).
|
||||
#
|
||||
do_execsql_test e_update-2.3.0 {
|
||||
SELECT 'main', tbl_name FROM main.sqlite_master WHERE type = 'table'
|
||||
UNION ALL
|
||||
SELECT 'temp', tbl_name FROM sqlite_temp_master WHERE type = 'table'
|
||||
UNION ALL
|
||||
SELECT 'aux', tbl_name FROM aux.sqlite_master WHERE type = 'table'
|
||||
} [list {*}{
|
||||
main t1
|
||||
main t2
|
||||
main t3
|
||||
main t6
|
||||
temp t4
|
||||
temp t6
|
||||
aux t1
|
||||
aux t5
|
||||
}]
|
||||
do_execsql_test e_update-2.3.1 {
|
||||
DELETE FROM main.t6;
|
||||
DELETE FROM temp.t6;
|
||||
INSERT INTO main.t6 VALUES(1, 2);
|
||||
INSERT INTO temp.t6 VALUES(1, 2);
|
||||
|
||||
CREATE TRIGGER temp.tr1 AFTER INSERT ON t4 BEGIN
|
||||
UPDATE t6 SET x=x+1;
|
||||
END;
|
||||
|
||||
INSERT INTO t4 VALUES(1, 2);
|
||||
SELECT * FROM main.t6;
|
||||
SELECT * FROM temp.t6;
|
||||
} {1 2 2 2}
|
||||
do_execsql_test e_update-2.3.2 {
|
||||
DELETE FROM main.t1;
|
||||
DELETE FROM aux.t1;
|
||||
INSERT INTO main.t1 VALUES(1, 2);
|
||||
INSERT INTO aux.t1 VALUES(1, 2);
|
||||
|
||||
CREATE TRIGGER temp.tr2 AFTER DELETE ON t4 BEGIN
|
||||
UPDATE t1 SET a=a+1;
|
||||
END;
|
||||
|
||||
DELETE FROM t4;
|
||||
SELECT * FROM main.t1;
|
||||
SELECT * FROM aux.t1;
|
||||
} {2 2 1 2}
|
||||
do_execsql_test e_update-2.3.3 {
|
||||
DELETE FROM aux.t5;
|
||||
INSERT INTO aux.t5 VALUES(1, 2);
|
||||
|
||||
INSERT INTO t4 VALUES('x', 'y');
|
||||
CREATE TRIGGER temp.tr3 AFTER UPDATE ON t4 BEGIN
|
||||
UPDATE t5 SET a=a+1;
|
||||
END;
|
||||
|
||||
UPDATE t4 SET x=10;
|
||||
SELECT * FROM aux.t5;
|
||||
} {2 2}
|
||||
|
||||
# EVIDENCE-OF: R-19619-42762 The INDEXED BY and NOT INDEXED clauses are
|
||||
# not allowed on UPDATE statements within triggers.
|
||||
#
|
||||
do_update_tests e_update-2.4 -error {
|
||||
the %s %s clause is not allowed on UPDATE or DELETE statements within triggers
|
||||
} {
|
||||
1 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t1 INDEXED BY i1 SET a=a+1;
|
||||
END;
|
||||
} {INDEXED BY}
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t1 NOT INDEXED SET a=a+1;
|
||||
END;
|
||||
} {NOT INDEXED}
|
||||
}
|
||||
|
||||
ifcapable update_delete_limit {
|
||||
|
||||
# EVIDENCE-OF: R-57359-59558 The LIMIT and ORDER BY clauses for UPDATE
|
||||
# are unsupported within triggers, regardless of the compilation options
|
||||
# used to build SQLite.
|
||||
#
|
||||
do_update_tests e_update-2.5 -error {
|
||||
near "%s": syntax error
|
||||
} {
|
||||
1 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t1 SET a=a+1 LIMIT 10;
|
||||
END;
|
||||
} {LIMIT}
|
||||
|
||||
2 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t1 SET a=a+1 ORDER BY a LIMIT 10;
|
||||
END;
|
||||
} {ORDER}
|
||||
|
||||
3 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t1 SET a=a+1 ORDER BY a LIMIT 10 OFFSET 2;
|
||||
END;
|
||||
} {ORDER}
|
||||
|
||||
4 {
|
||||
CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN
|
||||
UPDATE t1 SET a=a+1 LIMIT 10 OFFSET 2;
|
||||
END;
|
||||
} {LIMIT}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-59581-44104 If SQLite is built with the
|
||||
# SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile-time option then the syntax
|
||||
# of the UPDATE statement is extended with optional ORDER BY and LIMIT
|
||||
# clauses
|
||||
#
|
||||
# EVIDENCE-OF: R-08948-01887 -- syntax diagram update-stmt-limited
|
||||
#
|
||||
do_update_tests e_update-3.0 {
|
||||
1 "UPDATE t1 SET a=b LIMIT 5" {}
|
||||
2 "UPDATE t1 SET a=b LIMIT 5-1 OFFSET 2+2" {}
|
||||
3 "UPDATE t1 SET a=b LIMIT 2+2, 16/4" {}
|
||||
4 "UPDATE t1 SET a=b ORDER BY a LIMIT 5" {}
|
||||
5 "UPDATE t1 SET a=b ORDER BY a LIMIT 5-1 OFFSET 2+2" {}
|
||||
6 "UPDATE t1 SET a=b ORDER BY a LIMIT 2+2, 16/4" {}
|
||||
7 "UPDATE t1 SET a=b WHERE a>2 LIMIT 5" {}
|
||||
8 "UPDATE t1 SET a=b WHERE a>2 LIMIT 5-1 OFFSET 2+2" {}
|
||||
9 "UPDATE t1 SET a=b WHERE a>2 LIMIT 2+2, 16/4" {}
|
||||
10 "UPDATE t1 SET a=b WHERE a>2 ORDER BY a LIMIT 5" {}
|
||||
11 "UPDATE t1 SET a=b WHERE a>2 ORDER BY a LIMIT 5-1 OFFSET 2+2" {}
|
||||
12 "UPDATE t1 SET a=b WHERE a>2 ORDER BY a LIMIT 2+2, 16/4" {}
|
||||
}
|
||||
|
||||
do_execsql_test e_update-3.1.0 {
|
||||
CREATE TABLE t7(q, r, s);
|
||||
INSERT INTO t7 VALUES(1, 'one', 'X');
|
||||
INSERT INTO t7 VALUES(2, 'two', 'X');
|
||||
INSERT INTO t7 VALUES(3, 'three', 'X');
|
||||
INSERT INTO t7 VALUES(4, 'four', 'X');
|
||||
INSERT INTO t7 VALUES(5, 'five', 'X');
|
||||
INSERT INTO t7 VALUES(6, 'six', 'X');
|
||||
INSERT INTO t7 VALUES(7, 'seven', 'X');
|
||||
INSERT INTO t7 VALUES(8, 'eight', 'X');
|
||||
INSERT INTO t7 VALUES(9, 'nine', 'X');
|
||||
INSERT INTO t7 VALUES(10, 'ten', 'X');
|
||||
} {}
|
||||
|
||||
# EVIDENCE-OF: R-58862-44169 If an UPDATE statement has a LIMIT clause,
|
||||
# the maximum number of rows that will be updated is found by evaluating
|
||||
# the accompanying expression and casting it to an integer value.
|
||||
#
|
||||
do_update_tests e_update-3.1 -query { SELECT s FROM t7 } {
|
||||
1 "UPDATE t7 SET s = q LIMIT 5" {1 2 3 4 5 X X X X X}
|
||||
2 "UPDATE t7 SET s = r WHERE q>2 LIMIT 4" {1 2 three four five six X X X X}
|
||||
3 "UPDATE t7 SET s = q LIMIT 0" {1 2 three four five six X X X X}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-63582-45120 A negative value is interpreted as "no limit".
|
||||
#
|
||||
do_update_tests e_update-3.2 -query { SELECT s FROM t7 } {
|
||||
1 "UPDATE t7 SET s = q LIMIT -1" {1 2 3 4 5 6 7 8 9 10}
|
||||
2 "UPDATE t7 SET s = r WHERE q>4 LIMIT -1"
|
||||
{1 2 3 4 five six seven eight nine ten}
|
||||
3 "UPDATE t7 SET s = 'X' LIMIT -1" {X X X X X X X X X X}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-18628-11938 If the LIMIT expression evaluates to
|
||||
# non-negative value N and the UPDATE statement has an ORDER BY clause,
|
||||
# then all rows that would be updated in the absence of the LIMIT clause
|
||||
# are sorted according to the ORDER BY and the first N updated.
|
||||
#
|
||||
do_update_tests e_update-3.3 -query { SELECT s FROM t7 } {
|
||||
1 "UPDATE t7 SET s = q ORDER BY r LIMIT 3" {X X X 4 5 X X 8 X X}
|
||||
2 "UPDATE t7 SET s = r ORDER BY r DESC LIMIT 2" {X two three 4 5 X X 8 X X}
|
||||
3 "UPDATE t7 SET s = q ORDER BY q DESC LIMIT 5" {X two three 4 5 6 7 8 9 10}
|
||||
|
||||
X "UPDATE t7 SET s = 'X'" {X X X X X X X X X X}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-30955-38324 If the UPDATE statement also has an OFFSET
|
||||
# clause, then it is similarly evaluated and cast to an integer value.
|
||||
# If the OFFSET expression evaluates to a non-negative value M, then the
|
||||
# first M rows are skipped and the following N rows updated instead.
|
||||
#
|
||||
do_update_tests e_update-3.3 -query { SELECT s FROM t7 } {
|
||||
1 "UPDATE t7 SET s = q ORDER BY q LIMIT 3 OFFSET 2" {X X 3 4 5 X X X X X}
|
||||
2 "UPDATE t7 SET s = q ORDER BY q DESC LIMIT 2, 3 " {X X 3 4 5 6 7 8 X X}
|
||||
|
||||
X "UPDATE t7 SET s = 'X'" {X X X X X X X X X X}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-19486-35828 If the UPDATE statement has no ORDER BY
|
||||
# clause, then all rows that would be updated in the absence of the
|
||||
# LIMIT clause are assembled in an arbitrary order before applying the
|
||||
# LIMIT and OFFSET clauses to determine which are actually updated.
|
||||
#
|
||||
# In practice, "arbitrary order" is rowid order. This is also tested
|
||||
# by e_update-3.2.* above.
|
||||
#
|
||||
do_update_tests e_update-3.4 -query { SELECT s FROM t7 } {
|
||||
1 "UPDATE t7 SET s = q LIMIT 4, 2" {X X X X 5 6 X X X X}
|
||||
2 "UPDATE t7 SET s = q LIMIT 2 OFFSET 7" {X X X X 5 6 X 8 9 X}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-10927-26133 The ORDER BY clause on an UPDATE statement
|
||||
# is used only to determine which rows fall within the LIMIT. The order
|
||||
# in which rows are modified is arbitrary and is not influenced by the
|
||||
# ORDER BY clause.
|
||||
#
|
||||
do_execsql_test e_update-3.5.0 {
|
||||
CREATE TABLE t8(x);
|
||||
CREATE TRIGGER tr7 BEFORE UPDATE ON t7 BEGIN
|
||||
INSERT INTO t8 VALUES(old.q);
|
||||
END;
|
||||
} {}
|
||||
do_update_tests e_update-3.5 -query { SELECT x FROM t8 ; DELETE FROM t8 } {
|
||||
1 "UPDATE t7 SET s = q ORDER BY r LIMIT -1" {1 2 3 4 5 6 7 8 9 10}
|
||||
2 "UPDATE t7 SET s = q ORDER BY r ASC LIMIT -1" {1 2 3 4 5 6 7 8 9 10}
|
||||
3 "UPDATE t7 SET s = q ORDER BY r DESC LIMIT -1" {1 2 3 4 5 6 7 8 9 10}
|
||||
4 "UPDATE t7 SET s = q ORDER BY q DESC LIMIT 5" {6 7 8 9 10}
|
||||
}
|
||||
|
||||
|
||||
} ;# ifcapable update_delete_limit
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
# 2010 September 24
|
||||
#
|
||||
# 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_vacuum.html document are correct.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
sqlite3_test_control_pending_byte 0x1000000
|
||||
|
||||
proc create_db {{sql ""}} {
|
||||
catch { db close }
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
db transaction {
|
||||
execsql { PRAGMA page_size = 1024; }
|
||||
execsql $sql
|
||||
execsql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, randomblob(400));
|
||||
INSERT INTO t1 SELECT a+1, randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT a+2, randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT a+4, randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT a+8, randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT a+16, randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT a+32, randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT a+64, randomblob(400) FROM t1;
|
||||
|
||||
CREATE TABLE t2(a PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
}
|
||||
}
|
||||
|
||||
return [expr {[file size test.db] / 1024}]
|
||||
}
|
||||
|
||||
# This proc returns the number of contiguous blocks of pages that make up
|
||||
# the table or index named by the only argument. For example, if the table
|
||||
# occupies database pages 3, 4, 8 and 9, then this command returns 2 (there
|
||||
# are 2 fragments - one consisting of pages 3 and 4, the other of fragments
|
||||
# 8 and 9).
|
||||
#
|
||||
proc fragment_count {name} {
|
||||
execsql { CREATE VIRTUAL TABLE temp.stat USING dbstat }
|
||||
set nFrag 1
|
||||
db eval {SELECT pageno FROM stat WHERE name = 't1' ORDER BY pageno} {
|
||||
if {[info exists prevpageno] && $prevpageno != $pageno-1} {
|
||||
incr nFrag
|
||||
}
|
||||
set prevpageno $pageno
|
||||
}
|
||||
execsql { DROP TABLE temp.stat }
|
||||
set nFrag
|
||||
}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-63707-33375 -- syntax diagram vacuum-stmt
|
||||
#
|
||||
do_execsql_test e_vacuum-0.1 { VACUUM } {}
|
||||
|
||||
# EVIDENCE-OF: R-51469-36013 Unless SQLite is running in
|
||||
# "auto_vacuum=FULL" mode, when a large amount of data is deleted from
|
||||
# the database file it leaves behind empty space, or "free" database
|
||||
# pages.
|
||||
#
|
||||
# EVIDENCE-OF: R-60541-63059 Running VACUUM to rebuild the database
|
||||
# reclaims this space and reduces the size of the database file.
|
||||
#
|
||||
foreach {tn avmode sz} {
|
||||
1 none 7
|
||||
2 full 8
|
||||
3 incremental 8
|
||||
} {
|
||||
set nPage [create_db "PRAGMA auto_vacuum = $avmode"]
|
||||
|
||||
do_execsql_test e_vacuum-1.1.$tn.1 {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
} {}
|
||||
|
||||
if {$avmode == "full"} {
|
||||
# This branch tests the "unless ... auto_vacuum=FULL" in the requirement
|
||||
# above. If auto_vacuum is set to FULL, then no empty space is left in
|
||||
# the database file.
|
||||
do_execsql_test e_vacuum-1.1.$tn.2 {PRAGMA freelist_count} 0
|
||||
} else {
|
||||
set freelist [expr {$nPage - $sz}]
|
||||
if {$avmode == "incremental"} {
|
||||
# The page size is 1024 bytes. Therefore, assuming the database contains
|
||||
# somewhere between 207 and 411 pages (it does), there are 2 pointer-map
|
||||
# pages.
|
||||
incr freelist -2
|
||||
}
|
||||
do_execsql_test e_vacuum-1.1.$tn.3 {PRAGMA freelist_count} $freelist
|
||||
do_execsql_test e_vacuum-1.1.$tn.4 {VACUUM} {}
|
||||
}
|
||||
|
||||
do_test e_vacuum-1.1.$tn.5 { expr {[file size test.db] / 1024} } $sz
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-50943-18433 Frequent inserts, updates, and deletes can
|
||||
# cause the database file to become fragmented - where data for a single
|
||||
# table or index is scattered around the database file.
|
||||
#
|
||||
# EVIDENCE-OF: R-05791-54928 Running VACUUM ensures that each table and
|
||||
# index is largely stored contiguously within the database file.
|
||||
#
|
||||
# e_vacuum-1.2.1 - Perform many INSERT, UPDATE and DELETE ops on table t1.
|
||||
# e_vacuum-1.2.2 - Verify that t1 and its indexes are now quite fragmented.
|
||||
# e_vacuum-1.2.3 - Run VACUUM.
|
||||
# e_vacuum-1.2.4 - Verify that t1 and its indexes are now much
|
||||
# less fragmented.
|
||||
#
|
||||
ifcapable vtab {
|
||||
create_db
|
||||
register_dbstat_vtab db
|
||||
do_execsql_test e_vacuum-1.2.1 {
|
||||
DELETE FROM t1 WHERE a%2;
|
||||
INSERT INTO t1 SELECT b, a FROM t2 WHERE a%2;
|
||||
UPDATE t1 SET b=randomblob(600) WHERE (a%2)==0;
|
||||
} {}
|
||||
|
||||
do_test e_vacuum-1.2.2.1 { expr [fragment_count t1]>100 } 1
|
||||
do_test e_vacuum-1.2.2.2 { expr [fragment_count sqlite_autoindex_t1_1]>100 } 1
|
||||
do_test e_vacuum-1.2.2.3 { expr [fragment_count sqlite_autoindex_t1_2]>100 } 1
|
||||
|
||||
do_execsql_test e_vacuum-1.2.3 { VACUUM } {}
|
||||
|
||||
# In practice, the tables and indexes each end up stored as two fragments -
|
||||
# one containing the root page and another containing all other pages.
|
||||
#
|
||||
do_test e_vacuum-1.2.4.1 { fragment_count t1 } 2
|
||||
do_test e_vacuum-1.2.4.2 { fragment_count sqlite_autoindex_t1_1 } 2
|
||||
do_test e_vacuum-1.2.4.3 { fragment_count sqlite_autoindex_t1_2 } 2
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-20474-44465 Normally, the database page_size and
|
||||
# whether or not the database supports auto_vacuum must be configured
|
||||
# before the database file is actually created.
|
||||
#
|
||||
do_test e_vacuum-1.3.1.1 {
|
||||
create_db "PRAGMA page_size = 1024 ; PRAGMA auto_vacuum = FULL"
|
||||
execsql { PRAGMA page_size ; PRAGMA auto_vacuum }
|
||||
} {1024 1}
|
||||
do_test e_vacuum-1.3.1.2 {
|
||||
execsql { PRAGMA page_size = 2048 }
|
||||
execsql { PRAGMA auto_vacuum = NONE }
|
||||
execsql { PRAGMA page_size ; PRAGMA auto_vacuum }
|
||||
} {1024 1}
|
||||
|
||||
# EVIDENCE-OF: R-08570-19916 However, when not in write-ahead log mode,
|
||||
# the page_size and/or auto_vacuum properties of an existing database
|
||||
# may be changed by using the page_size and/or pragma auto_vacuum
|
||||
# pragmas and then immediately VACUUMing the database.
|
||||
#
|
||||
do_test e_vacuum-1.3.2.1 {
|
||||
execsql { PRAGMA journal_mode = delete }
|
||||
execsql { PRAGMA page_size = 2048 }
|
||||
execsql { PRAGMA auto_vacuum = NONE }
|
||||
execsql VACUUM
|
||||
execsql { PRAGMA page_size ; PRAGMA auto_vacuum }
|
||||
} {2048 0}
|
||||
|
||||
# EVIDENCE-OF: R-48521-51450 When in write-ahead log mode, only the
|
||||
# auto_vacuum support property can be changed using VACUUM.
|
||||
#
|
||||
do_test e_vacuum-1.3.3.1 {
|
||||
execsql { PRAGMA journal_mode = wal }
|
||||
execsql { PRAGMA page_size ; PRAGMA auto_vacuum }
|
||||
} {2048 0}
|
||||
do_test e_vacuum-1.3.3.2 {
|
||||
execsql { PRAGMA page_size = 1024 }
|
||||
execsql { PRAGMA auto_vacuum = FULL }
|
||||
execsql VACUUM
|
||||
execsql { PRAGMA page_size ; PRAGMA auto_vacuum }
|
||||
} {2048 1}
|
||||
|
||||
# EVIDENCE-OF: R-38001-03952 VACUUM only works on the main database. It
|
||||
# is not possible to VACUUM an attached database file.
|
||||
forcedelete test.db2
|
||||
create_db { PRAGMA auto_vacuum = NONE }
|
||||
do_execsql_test e_vacuum-2.1.1 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
PRAGMA aux.page_size = 1024;
|
||||
CREATE TABLE aux.t3 AS SELECT * FROM t1;
|
||||
DELETE FROM t3;
|
||||
} {}
|
||||
set original_size [file size test.db2]
|
||||
|
||||
# Try everything we can think of to get the aux database vacuumed:
|
||||
do_execsql_test e_vacuum-2.1.3 { VACUUM } {}
|
||||
do_execsql_test e_vacuum-2.1.4 { VACUUM aux } {}
|
||||
do_execsql_test e_vacuum-2.1.5 { VACUUM 'test.db2' } {}
|
||||
|
||||
# Despite our efforts, space in the aux database has not been reclaimed:
|
||||
do_test e_vacuum-2.1.6 { expr {[file size test.db2]==$::original_size} } 1
|
||||
|
||||
# EVIDENCE-OF: R-17495-17419 The VACUUM command may change the ROWIDs of
|
||||
# entries in any tables that do not have an explicit INTEGER PRIMARY
|
||||
# KEY.
|
||||
#
|
||||
# Tests e_vacuum-3.1.1 - 3.1.2 demonstrate that rowids can change when
|
||||
# a database is VACUUMed. Tests e_vacuum-3.1.3 - 3.1.4 show that adding
|
||||
# an INTEGER PRIMARY KEY column to a table stops this from happening.
|
||||
#
|
||||
do_execsql_test e_vacuum-3.1.1 {
|
||||
CREATE TABLE t4(x);
|
||||
INSERT INTO t4(x) VALUES('x');
|
||||
INSERT INTO t4(x) VALUES('y');
|
||||
INSERT INTO t4(x) VALUES('z');
|
||||
DELETE FROM t4 WHERE x = 'y';
|
||||
SELECT rowid, x FROM t4;
|
||||
} {1 x 3 z}
|
||||
do_execsql_test e_vacuum-3.1.2 {
|
||||
VACUUM;
|
||||
SELECT rowid, x FROM t4;
|
||||
} {1 x 2 z}
|
||||
|
||||
do_execsql_test e_vacuum-3.1.3 {
|
||||
CREATE TABLE t5(x, y INTEGER PRIMARY KEY);
|
||||
INSERT INTO t5(x) VALUES('x');
|
||||
INSERT INTO t5(x) VALUES('y');
|
||||
INSERT INTO t5(x) VALUES('z');
|
||||
DELETE FROM t5 WHERE x = 'y';
|
||||
SELECT rowid, x FROM t5;
|
||||
} {1 x 3 z}
|
||||
do_execsql_test e_vacuum-3.1.4 {
|
||||
VACUUM;
|
||||
SELECT rowid, x FROM t5;
|
||||
} {1 x 3 z}
|
||||
|
||||
# EVIDENCE-OF: R-49563-33883 A VACUUM will fail if there is an open
|
||||
# transaction, or if there are one or more active SQL statements when it
|
||||
# is run.
|
||||
#
|
||||
do_execsql_test e_vacuum-3.2.1.1 { BEGIN } {}
|
||||
do_catchsql_test e_vacuum-3.2.1.2 {
|
||||
VACUUM
|
||||
} {1 {cannot VACUUM from within a transaction}}
|
||||
do_execsql_test e_vacuum-3.2.1.3 { COMMIT } {}
|
||||
do_execsql_test e_vacuum-3.2.1.4 { VACUUM } {}
|
||||
do_execsql_test e_vacuum-3.2.1.5 { SAVEPOINT x } {}
|
||||
do_catchsql_test e_vacuum-3.2.1.6 {
|
||||
VACUUM
|
||||
} {1 {cannot VACUUM from within a transaction}}
|
||||
do_execsql_test e_vacuum-3.2.1.7 { COMMIT } {}
|
||||
do_execsql_test e_vacuum-3.2.1.8 { VACUUM } {}
|
||||
|
||||
create_db
|
||||
do_test e_vacuum-3.2.2.1 {
|
||||
set res ""
|
||||
db eval { SELECT a FROM t1 } {
|
||||
if {$a == 10} { set res [catchsql VACUUM] }
|
||||
}
|
||||
set res
|
||||
} {1 {cannot VACUUM - SQL statements in progress}}
|
||||
|
||||
|
||||
# EVIDENCE-OF: R-38735-12540 As of SQLite version 3.1, an alternative to
|
||||
# using the VACUUM command to reclaim space after data has been deleted
|
||||
# is auto-vacuum mode, enabled using the auto_vacuum pragma.
|
||||
#
|
||||
do_test e_vacuum-3.3.1 {
|
||||
create_db { PRAGMA auto_vacuum = FULL }
|
||||
execsql { PRAGMA auto_vacuum }
|
||||
} {1}
|
||||
|
||||
# EVIDENCE-OF: R-64844-34873 When auto_vacuum is enabled for a database
|
||||
# free pages may be reclaimed after deleting data, causing the file to
|
||||
# shrink, without rebuilding the entire database using VACUUM.
|
||||
#
|
||||
do_test e_vacuum-3.3.2.1 {
|
||||
create_db { PRAGMA auto_vacuum = FULL }
|
||||
execsql {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
}
|
||||
expr {[file size test.db] / 1024}
|
||||
} {8}
|
||||
do_test e_vacuum-3.3.2.2 {
|
||||
create_db { PRAGMA auto_vacuum = INCREMENTAL }
|
||||
execsql {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
PRAGMA incremental_vacuum;
|
||||
}
|
||||
expr {[file size test.db] / 1024}
|
||||
} {8}
|
||||
|
||||
finish_test
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
# 2010 Sept 29
|
||||
#
|
||||
# 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 regression tests for SQLite library. The focus of
|
||||
# this file is testing the SQLite routines used for converting between the
|
||||
# various suported unicode encodings (UTF-8, UTF-16, UTF-16le and
|
||||
# UTF-16be).
|
||||
#
|
||||
# $Id: enc4.test,v 1.0 2010/09/29 08:29:32 shaneh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If UTF16 support is disabled, ignore the tests in this file
|
||||
#
|
||||
ifcapable {!utf16} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
# The three unicode encodings understood by SQLite.
|
||||
set encodings [list UTF-8 UTF-16le UTF-16be]
|
||||
|
||||
# initial value to use in SELECT
|
||||
set inits [list 1 1.0 1. 1e0]
|
||||
|
||||
# vals
|
||||
set vals [list\
|
||||
"922337203685477580792233720368547758079223372036854775807"\
|
||||
"100000000000000000000000000000000000000000000000000000000"\
|
||||
"1.0000000000000000000000000000000000000000000000000000000"\
|
||||
]
|
||||
|
||||
set i 1
|
||||
foreach enc $encodings {
|
||||
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
db eval "PRAGMA encoding = \"$enc\""
|
||||
|
||||
do_test enc4-$i.1 {
|
||||
db eval {PRAGMA encoding}
|
||||
} $enc
|
||||
|
||||
set j 1
|
||||
foreach init $inits {
|
||||
|
||||
do_test enc4-$i.$j.2 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT $init+?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
} {0}
|
||||
|
||||
set k 1
|
||||
foreach val $vals {
|
||||
for {set x 1} {$x<18} {incr x} {
|
||||
set part [expr $init + [string range $val 0 [expr $x-1]]]
|
||||
regsub {e\+0} $part {e+} part
|
||||
regsub {^1e} $part {1.0e} part
|
||||
|
||||
do_test enc4-$i.$j.$k.3.$x {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text $S 1 $val $x
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} [list $part]
|
||||
|
||||
do_test enc4-$i.$j.$k.4.$x {
|
||||
sqlite3_reset $S
|
||||
sqlite3_bind_text16 $S 1 [encoding convertto unicode $val] [expr $x*2]
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} [list $part]
|
||||
}
|
||||
|
||||
incr k
|
||||
}
|
||||
|
||||
do_test enc4-$i.$j.5 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
incr j
|
||||
}
|
||||
|
||||
db close
|
||||
incr i
|
||||
}
|
||||
|
||||
file delete -force test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
do_test enc4-4.1 {
|
||||
db eval "select 1+1."
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.2.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+1." -1 dummy]
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.2.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test enc4-4.3.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy]
|
||||
sqlite3_bind_text $S 1 "1." 2
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.3.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test enc4-4.4.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy]
|
||||
sqlite3_bind_text $S 1 "1.0" 2
|
||||
sqlite3_step $S
|
||||
sqlite3_column_text $S 0
|
||||
} {2.0}
|
||||
|
||||
do_test enc4-4.4.2 {
|
||||
sqlite3_finalize $S
|
||||
} {SQLITE_OK}
|
||||
|
||||
db close
|
||||
|
||||
finish_test
|
||||
+535
@@ -0,0 +1,535 @@
|
||||
# 2010 November 6
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set testprefix eqp
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# eqp-1.*: Assorted tests.
|
||||
# eqp-2.*: Tests for single select statements.
|
||||
# eqp-3.*: Select statements that execute sub-selects.
|
||||
# eqp-4.*: Compound select statements.
|
||||
#
|
||||
|
||||
proc det {args} { uplevel do_eqp_test $args }
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b);
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE t3(a, b);
|
||||
}
|
||||
|
||||
do_eqp_test 1.2 {
|
||||
SELECT * FROM t2, t1 WHERE t1.a=1 OR t1.b=2;
|
||||
} {
|
||||
0 0 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
|
||||
0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}
|
||||
0 1 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
}
|
||||
do_eqp_test 1.3 {
|
||||
SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a=1 OR t1.b=2;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
|
||||
0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}
|
||||
}
|
||||
do_eqp_test 1.3 {
|
||||
SELECT a FROM t1 ORDER BY a
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
|
||||
}
|
||||
do_eqp_test 1.4 {
|
||||
SELECT a FROM t1 ORDER BY +a
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
do_eqp_test 1.5 {
|
||||
SELECT a FROM t1 WHERE a=4
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}
|
||||
}
|
||||
do_eqp_test 1.6 {
|
||||
SELECT DISTINCT count(*) FROM t3 GROUP BY a;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR GROUP BY}
|
||||
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
|
||||
}
|
||||
|
||||
do_eqp_test 1.7 {
|
||||
SELECT * FROM t3 JOIN (SELECT 1)
|
||||
} {
|
||||
0 0 1 {SCAN SUBQUERY 1 (~1 rows)}
|
||||
0 1 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
}
|
||||
do_eqp_test 1.8 {
|
||||
SELECT * FROM t3 JOIN (SELECT 1 UNION SELECT 2)
|
||||
} {
|
||||
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)}
|
||||
0 0 1 {SCAN SUBQUERY 1 (~2 rows)}
|
||||
0 1 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
}
|
||||
do_eqp_test 1.9 {
|
||||
SELECT * FROM t3 JOIN (SELECT 1 EXCEPT SELECT a FROM t3 LIMIT 17)
|
||||
} {
|
||||
3 0 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (EXCEPT)}
|
||||
0 0 1 {SCAN SUBQUERY 1 (~17 rows)}
|
||||
0 1 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
}
|
||||
do_eqp_test 1.10 {
|
||||
SELECT * FROM t3 JOIN (SELECT 1 INTERSECT SELECT a FROM t3 LIMIT 17)
|
||||
} {
|
||||
3 0 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (INTERSECT)}
|
||||
0 0 1 {SCAN SUBQUERY 1 (~1 rows)}
|
||||
0 1 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
}
|
||||
|
||||
do_eqp_test 1.11 {
|
||||
SELECT * FROM t3 JOIN (SELECT 1 UNION ALL SELECT a FROM t3 LIMIT 17)
|
||||
} {
|
||||
3 0 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)}
|
||||
0 0 1 {SCAN SUBQUERY 1 (~17 rows)}
|
||||
0 1 0 {SCAN TABLE t3 (~1000000 rows)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases eqp-2.* - tests for single select statements.
|
||||
#
|
||||
drop_all_tables
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(x, y);
|
||||
|
||||
CREATE TABLE t2(x, y);
|
||||
CREATE INDEX t2i1 ON t2(x);
|
||||
}
|
||||
|
||||
det 2.2.1 "SELECT DISTINCT min(x), max(x) FROM t1 GROUP BY x ORDER BY 1" {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR GROUP BY}
|
||||
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
det 2.2.2 "SELECT DISTINCT min(x), max(x) FROM t2 GROUP BY x ORDER BY 1" {
|
||||
0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
det 2.2.3 "SELECT DISTINCT * FROM t1" {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
|
||||
}
|
||||
det 2.2.4 "SELECT DISTINCT * FROM t1, t2" {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 1 1 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
|
||||
}
|
||||
det 2.2.5 "SELECT DISTINCT * FROM t1, t2 ORDER BY t1.x" {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 1 1 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
det 2.2.6 "SELECT DISTINCT t2.x FROM t1, t2 ORDER BY t2.x" {
|
||||
0 0 1 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
|
||||
0 1 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
}
|
||||
|
||||
det 2.3.1 "SELECT max(x) FROM t2" {
|
||||
0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)}
|
||||
}
|
||||
det 2.3.2 "SELECT min(x) FROM t2" {
|
||||
0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)}
|
||||
}
|
||||
det 2.3.3 "SELECT min(x), max(x) FROM t2" {
|
||||
0 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
}
|
||||
|
||||
det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" {
|
||||
0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases eqp-3.* - tests for select statements that use sub-selects.
|
||||
#
|
||||
do_eqp_test 3.1.1 {
|
||||
SELECT (SELECT x FROM t1 AS sub) FROM t1;
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)}
|
||||
}
|
||||
do_eqp_test 3.1.2 {
|
||||
SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub);
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)}
|
||||
}
|
||||
do_eqp_test 3.1.3 {
|
||||
SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub ORDER BY y);
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
do_eqp_test 3.1.4 {
|
||||
SELECT * FROM t1 WHERE (SELECT x FROM t2 ORDER BY x);
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
|
||||
}
|
||||
|
||||
det 3.2.1 {
|
||||
SELECT * FROM (SELECT * FROM t1 ORDER BY x LIMIT 10) ORDER BY y LIMIT 5
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {SCAN SUBQUERY 1 (~10 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
det 3.2.2 {
|
||||
SELECT * FROM
|
||||
(SELECT * FROM t1 ORDER BY x LIMIT 10) AS x1,
|
||||
(SELECT * FROM t2 ORDER BY x LIMIT 10) AS x2
|
||||
ORDER BY x2.y LIMIT 5
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)}
|
||||
0 0 0 {SCAN SUBQUERY 1 AS x1 (~10 rows)}
|
||||
0 1 1 {SCAN SUBQUERY 2 AS x2 (~10 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
|
||||
det 3.3.1 {
|
||||
SELECT * FROM t1 WHERE y IN (SELECT y FROM t2)
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~100000 rows)}
|
||||
0 0 0 {EXECUTE LIST SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
}
|
||||
det 3.3.2 {
|
||||
SELECT * FROM t1 WHERE y IN (SELECT y FROM t2 WHERE t1.x!=t2.x)
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~500000 rows)}
|
||||
0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t2 (~500000 rows)}
|
||||
}
|
||||
det 3.3.3 {
|
||||
SELECT * FROM t1 WHERE EXISTS (SELECT y FROM t2 WHERE t1.x!=t2.x)
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~500000 rows)}
|
||||
0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1}
|
||||
1 0 0 {SCAN TABLE t2 (~500000 rows)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test cases eqp-4.* - tests for composite select statements.
|
||||
#
|
||||
do_eqp_test 4.1.1 {
|
||||
SELECT * FROM t1 UNION ALL SELECT * FROM t2
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
|
||||
}
|
||||
do_eqp_test 4.1.2 {
|
||||
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 2
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
|
||||
}
|
||||
do_eqp_test 4.1.3 {
|
||||
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 2
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)}
|
||||
}
|
||||
do_eqp_test 4.1.4 {
|
||||
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 2
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)}
|
||||
}
|
||||
do_eqp_test 4.1.5 {
|
||||
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 2
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
|
||||
}
|
||||
|
||||
do_eqp_test 4.2.2 {
|
||||
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 1
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
|
||||
}
|
||||
do_eqp_test 4.2.3 {
|
||||
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 1
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)}
|
||||
}
|
||||
do_eqp_test 4.2.4 {
|
||||
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 1
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)}
|
||||
}
|
||||
do_eqp_test 4.2.5 {
|
||||
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 1
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
|
||||
}
|
||||
|
||||
do_eqp_test 4.3.1 {
|
||||
SELECT x FROM t1 UNION SELECT x FROM t2
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)}
|
||||
}
|
||||
|
||||
do_eqp_test 4.3.2 {
|
||||
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1
|
||||
} {
|
||||
2 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
3 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)}
|
||||
4 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)}
|
||||
}
|
||||
do_eqp_test 4.3.3 {
|
||||
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 ORDER BY 1
|
||||
} {
|
||||
2 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
|
||||
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION)}
|
||||
4 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
4 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 4 (UNION)}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This next block of tests verifies that the examples on the
|
||||
# lang_explain.html page are correct.
|
||||
#
|
||||
drop_all_tables
|
||||
|
||||
# EVIDENCE-OF: R-64208-08323 sqlite> EXPLAIN QUERY PLAN SELECT a, b
|
||||
# FROM t1 WHERE a=1; 0|0|0|SCAN TABLE t1 (~100000 rows)
|
||||
do_execsql_test 5.1.0 { CREATE TABLE t1(a, b) }
|
||||
det 5.1.1 "SELECT a, b FROM t1 WHERE a=1" {
|
||||
0 0 0 {SCAN TABLE t1 (~100000 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-09022-44606 sqlite> CREATE INDEX i1 ON t1(a);
|
||||
# sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1;
|
||||
# 0|0|0|SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)
|
||||
do_execsql_test 5.2.0 { CREATE INDEX i1 ON t1(a) }
|
||||
det 5.2.1 "SELECT a, b FROM t1 WHERE a=1" {
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-62228-34103 sqlite> CREATE INDEX i2 ON t1(a, b);
|
||||
# sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1;
|
||||
# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)
|
||||
do_execsql_test 5.3.0 { CREATE INDEX i2 ON t1(a, b) }
|
||||
det 5.3.1 "SELECT a, b FROM t1 WHERE a=1" {
|
||||
0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-22253-05302 sqlite> EXPLAIN QUERY PLAN SELECT t1.*,
|
||||
# t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2; 0|0|0|SEARCH TABLE t1
|
||||
# USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|1|SCAN TABLE t2
|
||||
# (~1000000 rows)
|
||||
do_execsql_test 5.4.0 {CREATE TABLE t2(c, d)}
|
||||
det 5.4.1 "SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2" {
|
||||
0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~3 rows)}
|
||||
0 1 1 {SCAN TABLE t2 (~1000000 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-21040-07025 sqlite> EXPLAIN QUERY PLAN SELECT t1.*,
|
||||
# t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2; 0|0|1|SEARCH TABLE t1
|
||||
# USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|0|SCAN TABLE t2
|
||||
# (~1000000 rows)
|
||||
det 5.5 "SELECT t1.*, t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2" {
|
||||
0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~3 rows)}
|
||||
0 1 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-39007-61103 sqlite> CREATE INDEX i3 ON t1(b);
|
||||
# sqlite> EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=1 OR b=2;
|
||||
# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)
|
||||
# 0|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)
|
||||
do_execsql_test 5.5.0 {CREATE INDEX i3 ON t1(b)}
|
||||
det 5.6.1 "SELECT * FROM t1 WHERE a=1 OR b=2" {
|
||||
0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)}
|
||||
0 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-33025-54904 sqlite> EXPLAIN QUERY PLAN SELECT c, d
|
||||
# FROM t2 ORDER BY c; 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|USE TEMP
|
||||
# B-TREE FOR ORDER BY
|
||||
det 5.7 "SELECT c, d FROM t2 ORDER BY c" {
|
||||
0 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-38854-22809 sqlite> CREATE INDEX i4 ON t2(c);
|
||||
# sqlite> EXPLAIN QUERY PLAN SELECT c, d FROM t2 ORDER BY c;
|
||||
# 0|0|0|SCAN TABLE t2 USING INDEX i4 (~1000000 rows)
|
||||
do_execsql_test 5.8.0 {CREATE INDEX i4 ON t2(c)}
|
||||
det 5.8.1 "SELECT c, d FROM t2 ORDER BY c" {
|
||||
0 0 0 {SCAN TABLE t2 USING INDEX i4 (~1000000 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-29884-43993 sqlite> EXPLAIN QUERY PLAN SELECT
|
||||
# (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2;
|
||||
# 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|EXECUTE SCALAR SUBQUERY 1
|
||||
# 1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)
|
||||
# 0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2 2|0|0|SEARCH TABLE t1 USING
|
||||
# INDEX i3 (b=?) (~10 rows)
|
||||
det 5.9 {
|
||||
SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
|
||||
1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)}
|
||||
0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2}
|
||||
2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-17911-16445 sqlite> EXPLAIN QUERY PLAN SELECT
|
||||
# count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x;
|
||||
# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows) 0|0|0|SCAN
|
||||
# SUBQUERY 1 (~1000000 rows) 0|0|0|USE TEMP B-TREE FOR GROUP BY
|
||||
det 5.10 {
|
||||
SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x
|
||||
} {
|
||||
1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)}
|
||||
0 0 0 {SCAN SUBQUERY 1 (~100 rows)}
|
||||
0 0 0 {USE TEMP B-TREE FOR GROUP BY}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-18544-33103 sqlite> EXPLAIN QUERY PLAN SELECT * FROM
|
||||
# (SELECT * FROM t2 WHERE c=1), t1; 0|0|0|SEARCH TABLE t2 USING INDEX i4
|
||||
# (c=?) (~10 rows) 0|1|1|SCAN TABLE t1 (~1000000 rows)
|
||||
det 5.11 "SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1" {
|
||||
0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?) (~10 rows)}
|
||||
0 1 1 {SCAN TABLE t1 (~1000000 rows)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-40701-42164 sqlite> EXPLAIN QUERY PLAN SELECT a FROM
|
||||
# t1 UNION SELECT c FROM t2; 1|0|0|SCAN TABLE t1 (~1000000 rows)
|
||||
# 2|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|COMPOUND SUBQUERIES 1 AND 2
|
||||
# USING TEMP B-TREE (UNION)
|
||||
det 5.12 "SELECT a FROM t1 UNION SELECT c FROM t2" {
|
||||
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)}
|
||||
}
|
||||
|
||||
# EVIDENCE-OF: R-61538-24748 sqlite> EXPLAIN QUERY PLAN SELECT a FROM
|
||||
# t1 EXCEPT SELECT d FROM t2 ORDER BY 1; 1|0|0|SCAN TABLE t1 USING
|
||||
# COVERING INDEX i2 (~1000000 rows) 2|0|0|SCAN TABLE t2 (~1000000 rows)
|
||||
# 2|0|0|USE TEMP B-TREE FOR ORDER BY 0|0|0|COMPOUND SUBQUERIES 1 AND 2
|
||||
# (EXCEPT)
|
||||
det 5.13 "SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1" {
|
||||
1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)}
|
||||
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
|
||||
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
|
||||
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests - eqp-6.* - test that the example C code on
|
||||
# documentation page eqp.html works. The C code is duplicated in test1.c
|
||||
# and wrapped in Tcl command [print_explain_query_plan]
|
||||
#
|
||||
set boilerplate {
|
||||
proc explain_query_plan {db sql} {
|
||||
set stmt [sqlite3_prepare_v2 db $sql -1 DUMMY]
|
||||
print_explain_query_plan $stmt
|
||||
sqlite3_finalize $stmt
|
||||
}
|
||||
sqlite3 db test.db
|
||||
explain_query_plan db {%SQL%}
|
||||
db close
|
||||
exit
|
||||
}
|
||||
|
||||
proc do_peqp_test {tn sql res} {
|
||||
set fd [open script.tcl w]
|
||||
puts $fd [string map [list %SQL% $sql] $::boilerplate]
|
||||
close $fd
|
||||
|
||||
uplevel do_test $tn [list {
|
||||
set fd [open "|[info nameofexec] script.tcl"]
|
||||
set data [read $fd]
|
||||
close $fd
|
||||
set data
|
||||
}] [list $res]
|
||||
}
|
||||
|
||||
do_peqp_test 6.1 {
|
||||
SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1
|
||||
} [string trimleft {
|
||||
1 0 0 SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)
|
||||
2 0 0 SCAN TABLE t2 (~1000000 rows)
|
||||
2 0 0 USE TEMP B-TREE FOR ORDER BY
|
||||
0 0 0 COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)
|
||||
}]
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
+38
-1
@@ -396,7 +396,7 @@ do_test exclusive-4.5 {
|
||||
# longer active.
|
||||
#
|
||||
db close
|
||||
sqlite db test.db
|
||||
sqlite3 db test.db
|
||||
|
||||
# if we're using proxy locks, we use 3 filedescriptors for a db
|
||||
# that is open but NOT writing changes, normally
|
||||
@@ -469,4 +469,41 @@ do_test exclusive-5.7 {
|
||||
expr $sqlite_open_file_count-$extrafds
|
||||
} {1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
do_execsql_test exclusive-6.1 {
|
||||
CREATE TABLE t4(a, b);
|
||||
INSERT INTO t4 VALUES('Eden', 1955);
|
||||
BEGIN;
|
||||
INSERT INTO t4 VALUES('Macmillan', 1957);
|
||||
INSERT INTO t4 VALUES('Douglas-Home', 1963);
|
||||
INSERT INTO t4 VALUES('Wilson', 1964);
|
||||
}
|
||||
do_test exclusive-6.2 {
|
||||
forcedelete test2.db test2.db-journal
|
||||
file copy test.db test2.db
|
||||
file copy test.db-journal test2.db-journal
|
||||
sqlite3 db test2.db
|
||||
} {}
|
||||
|
||||
do_execsql_test exclusive-6.3 {
|
||||
PRAGMA locking_mode = EXCLUSIVE;
|
||||
SELECT * FROM t4;
|
||||
} {exclusive Eden 1955}
|
||||
|
||||
do_test exclusive-6.4 {
|
||||
db close
|
||||
forcedelete test.db test.db-journal
|
||||
set fd [open test.db-journal w]
|
||||
puts $fd x
|
||||
close $fd
|
||||
sqlite3 db test.db
|
||||
} {}
|
||||
|
||||
do_execsql_test exclusive-6.5 {
|
||||
PRAGMA locking_mode = EXCLUSIVE;
|
||||
SELECT * FROM sqlite_master;
|
||||
} {exclusive}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+22
-14
@@ -83,7 +83,7 @@ do_test exclusive2-1.0 {
|
||||
# The following tests - exclusive2-1.X - check that:
|
||||
#
|
||||
# 1-3: Build a database with connection 1, calculate a signature.
|
||||
# 4-9: Modify the database using a second connection in a way that
|
||||
# 4-7: Modify the database using a second connection in a way that
|
||||
# does not modify the freelist, then reset the pager change-counter
|
||||
# to the value it had before the modifications.
|
||||
# 8: Check that using the first connection, the database signature
|
||||
@@ -99,6 +99,11 @@ do_test exclusive2-1.0 {
|
||||
# the cache size must be at least 17. Otherwise, some pages will be
|
||||
# loaded from the database file in step 8.
|
||||
#
|
||||
# For similar reasons, this test does not work with the memsubsys1 permutation.
|
||||
# Permutation memsubsys1 configures the pcache subsystem to use a static
|
||||
# allocation of 24 pages (shared between all pagers). This is not enough for
|
||||
# this test.
|
||||
#
|
||||
do_test exclusive2-1.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
@@ -146,16 +151,19 @@ do_test exclusive2-1.6 {
|
||||
do_test exclusive2-1.7 {
|
||||
pagerChangeCounter test.db 1
|
||||
} {1}
|
||||
do_test exclusive2-1.9 {
|
||||
t1sig
|
||||
expr {[t1sig] eq $::sig}
|
||||
} {1}
|
||||
if {[permutation] != "memsubsys1"} {
|
||||
do_test exclusive2-1.9 {
|
||||
t1sig
|
||||
expr {[t1sig] eq $::sig}
|
||||
} {1}
|
||||
}
|
||||
do_test exclusive2-1.10 {
|
||||
pagerChangeCounter test.db 2
|
||||
} {2}
|
||||
do_test exclusive2-1.11 {
|
||||
expr {[t1sig] eq $::sig}
|
||||
} {0}
|
||||
db2 close
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# These tests - exclusive2-2.X - are similar to exclusive2-1.X,
|
||||
@@ -245,7 +253,6 @@ do_test exclusive2-2.8 {
|
||||
#
|
||||
|
||||
db close
|
||||
db2 close
|
||||
catch {close $::fd}
|
||||
file delete -force test.db
|
||||
file delete -force test.db-journal
|
||||
@@ -255,47 +262,48 @@ do_test exclusive2-3.0 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(a UNIQUE);
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
COMMIT;
|
||||
}
|
||||
readPagerChangeCounter test.db
|
||||
} {1}
|
||||
do_test exclusive2-3.1 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
}
|
||||
readPagerChangeCounter test.db
|
||||
} {2}
|
||||
do_test exclusive2-3.2 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
}
|
||||
readPagerChangeCounter test.db
|
||||
} {3}
|
||||
do_test exclusive2-3.3 {
|
||||
execsql {
|
||||
PRAGMA locking_mode = exclusive;
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
}
|
||||
readPagerChangeCounter test.db
|
||||
} {4}
|
||||
do_test exclusive2-3.4 {
|
||||
breakpoint
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
}
|
||||
readPagerChangeCounter test.db
|
||||
} {4}
|
||||
do_test exclusive2-3.5 {
|
||||
execsql {
|
||||
PRAGMA locking_mode = normal;
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
}
|
||||
readPagerChangeCounter test.db
|
||||
} {4}
|
||||
do_test exclusive2-3.6 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(randstr(10, 400));
|
||||
INSERT INTO t1 VALUES(randstr(200, 200));
|
||||
}
|
||||
readPagerChangeCounter test.db
|
||||
} {5}
|
||||
|
||||
+1
-1
@@ -290,7 +290,7 @@ ifcapable floatingpoint {
|
||||
test_expr expr-4.14 {r1='abc', r2='Bbc'} {r1>r2} 1
|
||||
test_expr expr-4.15 {r1='0', r2='0.0'} {r1==r2} 1
|
||||
test_expr expr-4.16 {r1='0.000', r2='0.0'} {r1==r2} 1
|
||||
test_expr expr-4.17 {r1=' 0.000', r2=' 0.0'} {r1==r2} 0
|
||||
test_expr expr-4.17 {r1=' 0.000', r2=' 0.0'} {r1==r2} 1
|
||||
test_expr expr-4.18 {r1='0.0', r2='abc'} {r1<r2} 1
|
||||
test_expr expr-4.19 {r1='0.0', r2='abc'} {r1==r2} 0
|
||||
test_expr expr-4.20 {r1='0.0', r2='abc'} {r1>r2} 0
|
||||
|
||||
+9
-3
@@ -1414,9 +1414,15 @@ do_test fkey2-17.1.2 {
|
||||
set STMT [sqlite3_prepare_v2 db "INSERT INTO two VALUES(4, 5, 6)" -1 dummy]
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_CONSTRAINT}
|
||||
do_test fkey2-17.1.3 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_CONSTRAINT}
|
||||
ifcapable autoreset {
|
||||
do_test fkey2-17.1.3 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_CONSTRAINT}
|
||||
} else {
|
||||
do_test fkey2-17.1.3 {
|
||||
sqlite3_step $STMT
|
||||
} {SQLITE_MISUSE}
|
||||
}
|
||||
do_test fkey2-17.1.4 {
|
||||
sqlite3_finalize $STMT
|
||||
} {SQLITE_CONSTRAINT}
|
||||
|
||||
+24
-1
@@ -196,9 +196,32 @@ do_test fts3aa-6.2 {
|
||||
do_test fts3aa-6.3 {
|
||||
execsql {SELECT content FROM t1 WHERE rowid = -1}
|
||||
} {{three four}}
|
||||
breakpoint
|
||||
do_test fts3aa-6.4 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'four'}
|
||||
} {-1 0 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31}
|
||||
|
||||
# Test creation of FTS3 and FTS4 tables with columns that contain
|
||||
# an "=" character.
|
||||
#
|
||||
do_execsql_test fts3aa-7.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts3(xyz=abc);
|
||||
SELECT xyz FROM t2;
|
||||
} {}
|
||||
do_catchsql_test fts3aa-7.2 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(xyz=abc);
|
||||
} {1 {unrecognized parameter: xyz=abc}}
|
||||
do_catchsql_test fts3aa-7.3 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts4(xyz = abc);
|
||||
} {1 {unrecognized parameter: xyz = abc}}
|
||||
|
||||
do_execsql_test fts3aa-7.4 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts3(tokenize=simple, tokenize=simple);
|
||||
SELECT tokenize FROM t3;
|
||||
} {}
|
||||
do_catchsql_test fts3aa-7.5 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts4(tokenize=simple, tokenize=simple);
|
||||
} {1 {unrecognized parameter: tokenize=simple}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+16
-21
@@ -1,37 +1,32 @@
|
||||
# 2006 October 31 (scaaarey)
|
||||
# 2006 October 31
|
||||
#
|
||||
# The author disclaims copyright to this source code.
|
||||
# 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 regression tests for SQLite library. The focus
|
||||
# here is testing correct handling of excessively long terms.
|
||||
#
|
||||
# $Id: fts3ah.test,v 1.1 2007/08/20 17:38:42 shess Exp $
|
||||
# here is testing correct handling of very long terms.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is defined, omit this file.
|
||||
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Generate a term of len copies of char.
|
||||
proc bigterm {char len} {
|
||||
for {set term ""} {$len>0} {incr len -1} {
|
||||
append term $char
|
||||
}
|
||||
return $term
|
||||
}
|
||||
|
||||
# Generate a document of bigterms based on characters from the list
|
||||
# chars.
|
||||
proc bigtermdoc {chars len} {
|
||||
set doc ""
|
||||
foreach char $chars {
|
||||
append doc " " [bigterm $char $len]
|
||||
append doc " " [string repeat $char $len]
|
||||
}
|
||||
return $doc
|
||||
}
|
||||
@@ -41,9 +36,9 @@ set doc1 [bigtermdoc {a b c d} $len]
|
||||
set doc2 [bigtermdoc {b d e f} $len]
|
||||
set doc3 [bigtermdoc {a c e} $len]
|
||||
|
||||
set aterm [bigterm a $len]
|
||||
set bterm [bigterm b $len]
|
||||
set xterm [bigterm x $len]
|
||||
set aterm [string repeat a $len]
|
||||
set bterm [string repeat b $len]
|
||||
set xterm [string repeat x $len]
|
||||
|
||||
db eval {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3(content);
|
||||
@@ -61,15 +56,15 @@ do_test fts3ah-1.2 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH $aterm}
|
||||
} {1 3}
|
||||
|
||||
do_test fts3ah-1.2 {
|
||||
do_test fts3ah-1.3 {
|
||||
execsql {SELECT rowid FROM t1 WHERE t1 MATCH $xterm}
|
||||
} {}
|
||||
|
||||
do_test fts3ah-1.3 {
|
||||
do_test fts3ah-1.4 {
|
||||
execsql "SELECT rowid FROM t1 WHERE t1 MATCH '$aterm -$xterm'"
|
||||
} {1 3}
|
||||
|
||||
do_test fts3ah-1.4 {
|
||||
do_test fts3ah-1.5 {
|
||||
execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"$aterm $bterm\"'"
|
||||
} {1}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ ifcapable !fts3 {
|
||||
return
|
||||
}
|
||||
|
||||
set ::testprefix fts3ao
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# These tests, fts3ao-1.*, test that ticket #2429 is fixed.
|
||||
#
|
||||
@@ -166,4 +168,53 @@ do_test fts3ao-3.3 {
|
||||
execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; }
|
||||
} {{one three four} {one four} {one two}}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Test that it is possible to rename an fts3 table within a
|
||||
# transaction.
|
||||
#
|
||||
do_test fts3ao-4.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t4 USING fts3;
|
||||
INSERT INTO t4 VALUES('the quick brown fox');
|
||||
}
|
||||
} {}
|
||||
do_test fts3ao-4.2 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t4 VALUES('jumped over the');
|
||||
}
|
||||
} {}
|
||||
do_test fts3ao-4.3 { execsql { ALTER TABLE t4 RENAME TO t5; } } {}
|
||||
do_test fts3ao-4.4 { execsql { INSERT INTO t5 VALUES('lazy dog'); } } {}
|
||||
do_test fts3ao-4.5 { execsql COMMIT } {}
|
||||
do_test fts3ao-4.6 {
|
||||
execsql { SELECT * FROM t5 }
|
||||
} {{the quick brown fox} {jumped over the} {lazy dog}}
|
||||
do_test fts3ao-4.7 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t5 VALUES('Down came a jumbuck to drink at that billabong');
|
||||
ALTER TABLE t5 RENAME TO t6;
|
||||
INSERT INTO t6 VALUES('Down came the troopers, one, two, three');
|
||||
ROLLBACK;
|
||||
SELECT * FROM t5;
|
||||
}
|
||||
} {{the quick brown fox} {jumped over the} {lazy dog}}
|
||||
|
||||
# Test that it is possible to rename an FTS4 table. Renaming an FTS4 table
|
||||
# involves renaming the extra %_docsize and %_stat tables.
|
||||
#
|
||||
do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE t7 USING FTS4;
|
||||
INSERT INTO t7 VALUES('coined by a German clinician');
|
||||
SELECT count(*) FROM sqlite_master WHERE name LIKE 't7%';
|
||||
SELECT count(*) FROM sqlite_master WHERE name LIKE 't8%';
|
||||
} {6 0}
|
||||
do_execsql_test 5.2 {
|
||||
ALTER TABLE t7 RENAME TO t8;
|
||||
SELECT count(*) FROM sqlite_master WHERE name LIKE 't7%';
|
||||
SELECT count(*) FROM sqlite_master WHERE name LIKE 't8%';
|
||||
} {0 6}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
# 2010 October 27
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Test that the FTS3 extension does not crash when it encounters a
|
||||
# corrupt data structure on disk.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
|
||||
set ::testprefix fts3corrupt
|
||||
|
||||
|
||||
# Test that a doclist with a length field that indicates that the doclist
|
||||
# extends past the end of the node on which it resides is correctly identified
|
||||
# as database corruption.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3;
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
} {}
|
||||
do_test fts3corrupt-1.1 {
|
||||
set blob [db one {SELECT root from t1_segdir}]
|
||||
set blob [binary format a7ca* $blob 24 [string range $blob 8 end]]
|
||||
execsql { UPDATE t1_segdir SET root = $blob }
|
||||
} {}
|
||||
do_test fts3corrupt-1.2 {
|
||||
foreach w {a b c d e f g h i j k l m n o} {
|
||||
execsql { INSERT INTO t1 VALUES($w) }
|
||||
}
|
||||
} {}
|
||||
do_catchsql_test 1.3 {
|
||||
INSERT INTO t1 VALUES('world');
|
||||
} {1 {database disk image is malformed}}
|
||||
do_execsql_test 1.4 {
|
||||
DROP TABLE t1;
|
||||
}
|
||||
|
||||
# This block of tests checks that corruption is correctly detected if the
|
||||
# length field of a term on a leaf node indicates that the term extends past
|
||||
# the end of the node on which it resides. There are two cases:
|
||||
#
|
||||
# 1. The first term on the node.
|
||||
# 2. The second or subsequent term on the node (prefix compressed term).
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
COMMIT;
|
||||
} {}
|
||||
do_test fts3corrupt-2.1 {
|
||||
set blob [db one {SELECT root from t1_segdir}]
|
||||
set blob [binary format a*a* "\x00\x7F" [string range $blob 2 end]]
|
||||
execsql { UPDATE t1_segdir SET root = $blob }
|
||||
} {}
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'hello'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts3;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('hello');
|
||||
INSERT INTO t1 VALUES('world');
|
||||
COMMIT;
|
||||
} {}
|
||||
do_test fts3corrupt-3.1 {
|
||||
set blob [db one {SELECT quote(root) from t1_segdir}]
|
||||
set blob [binary format a11a*a* $blob "\x7F" [string range $blob 12 end]]
|
||||
execsql { UPDATE t1_segdir SET root = $blob }
|
||||
} {}
|
||||
do_catchsql_test 3.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'world'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts3;
|
||||
INSERT INTO t1(t1) VALUES('nodesize=24');
|
||||
}
|
||||
do_test fts3corrupt-4.1 {
|
||||
execsql BEGIN
|
||||
foreach s {
|
||||
"amxtvoo adqwroyhz auq aithtir avniqnuynvf axp ahibayfynig agbicpm"
|
||||
"ajdtebs anteaxr aieynenwmd awpl alo akxcrwow aoxftge aoqvgul"
|
||||
"amcfvdr auz apu aebelm ahuxyz aqc asyafdb agulvhvqu"
|
||||
"apepwfyz azkhdvkw aenyelxzbk aslnitbyet aycdsdcpgr aqzzdbc agfi axnypydou"
|
||||
"aaqrzzcm apcxdxo atumltzj aevvivo aodknoft aqoyytoz alobx apldt"
|
||||
} {
|
||||
execsql { INSERT INTO t1 VALUES($s) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_catchsql_test 4.2 {
|
||||
UPDATE t1_segdir SET root = X'FFFFFFFFFFFFFFFF';
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'world';
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
set blob [binary format cca*cca*cca*cca*cca*cca*cca*cca*cca*cca*a* \
|
||||
22 120 [string repeat a 120] \
|
||||
22 120 [string repeat b 120] \
|
||||
22 120 [string repeat c 120] \
|
||||
22 120 [string repeat d 120] \
|
||||
22 120 [string repeat e 120] \
|
||||
22 120 [string repeat f 120] \
|
||||
22 120 [string repeat g 120] \
|
||||
22 120 [string repeat h 120] \
|
||||
22 120 [string repeat i 120] \
|
||||
22 120 [string repeat j 120] \
|
||||
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
|
||||
]
|
||||
|
||||
do_catchsql_test 4.3 {
|
||||
UPDATE t1_segdir SET root = $blob;
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'world';
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# 2010 October 30
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# Test that the FTS3 extension does not crash when it encounters a
|
||||
# corrupt data structure on disk.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
|
||||
set ::testprefix fts3corrupt2
|
||||
|
||||
set data [list]
|
||||
lappend data {*}{
|
||||
"amxtvoo adqwroyhz auq aithtir avniqnuynvf axp ahibayfynig agbicpm"
|
||||
"ajdtebs anteaxr aieynenwmd awpl alo akxcrwow aoxftge aoqvgul"
|
||||
"amcfvdr auz apu aebelm ahuxyz aqc asyafdb agulvhvqu"
|
||||
"apepwfyz azkhdvkw aenyelxzbk aslnitbyet aycdsdcpgr aqzzdbc agfi axnypydou"
|
||||
"aaqrzzcm apcxdxo atumltzj aevvivo aodknoft aqoyytoz alobx apldt"
|
||||
"adjllxlhnmj aiuhvuj adwppceuht atvj azrsam ahkjqdhny audlqxr aotgcd"
|
||||
"aira azflsceos awj auzbobfkc awmezplr aeh awec ahndxlmv"
|
||||
"aydwnied alk auoap agihyqeix aymqxzajnl aydwnied aojkarx agbo"
|
||||
"ahajsmcl anvx amdhjm aoptsj agugzjjm apkevm acnj acjg"
|
||||
"amwtkw aogttbykvt aubwrfqnbjf ajow agsj aerkqzjdqst anenlvbalkn arfajzzgckx"
|
||||
"adqqqofkmz amjpavjuhw aqgehgnb awvvxlbtqzn agstqko akmkzehyh atagzey agwja"
|
||||
"amag ahe autkllywhr avnk atmt akn anvdh aixfrv"
|
||||
"aqdyerbws avefykly awl azaduojgzo anxfsmw axpt abgbvk ati"
|
||||
"attyqkwz aiweypiczul afy asitaqbczhh aitxisizpv auhviq aibql ajfqc"
|
||||
"aylzprtmta aiuemihqrpi awluvgsw ampbuy axlifpzfqr aems aoaxwads apianfn"
|
||||
"aodrkijelq acdb aaserrdxm aqyasgofqu aevvivo afi apmwu aeoqysl"
|
||||
"amqnk ankaotm ayfy ajcupeeoc advcbukan aucahlwnyk adbfyo azqjpeant"
|
||||
"afczpp asqrs ahslvda akhlf aiqgdp atyd aznuglxqbrg awirndrh"
|
||||
"aqhiajp amxeazb asxuehg akod axvolvsp agcz asmovmohy acmqa"
|
||||
"avvomv aafms ashuaec arevx audtq alrwqhjvao avgsgpg ajbrctpsel"
|
||||
"atxoirr ayopboobqdu ajunntua arh aernimxid aipljda aglo aefk"
|
||||
"aonxf acmnnkna abgviaswe aulvcbv axp apemgakpzo aibql acioaid"
|
||||
"axo alrwqhjvao ayqounftdzl azmoakdyh apajze ajk artvy apxiamy"
|
||||
"ayjafsraz addjj agsj asejtziqws acatvhegu aoxdjqblsvv aekdmmbs aaobe"
|
||||
"abjjvzubkwt alczv ati awz auyxgcxeb aymjoym anqoukprtyt atwfhpmbooh"
|
||||
"ajfqz aethlgir aclcx aowlyvetby aproqm afjlqtkv anebfy akzrcpfrrvw"
|
||||
"aoledfotm aiwlfm aeejlaej anz abgbvk aktfn aayoh anpywgdvgz"
|
||||
"acvmldguld asdvz aqb aeomsyzyu aggylhprbdz asrfkwz auipybpsn agsnszzfb"
|
||||
}
|
||||
|
||||
do_test fts3corrupt2-1.0 {
|
||||
execsql BEGIN
|
||||
execsql { CREATE VIRTUAL TABLE t2 USING FTS3(a, b); }
|
||||
execsql { INSERT INTO t2(t2) VALUES('nodesize=32') }
|
||||
foreach d $data {
|
||||
execsql { INSERT INTO t2 VALUES($d, $d) }
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql { SELECT count(*) FROM t2_segments }
|
||||
} {163}
|
||||
|
||||
proc set_byte {blob byte val} {
|
||||
binary format a*ca* \
|
||||
[string range $blob 0 [expr $byte-1]] \
|
||||
$val \
|
||||
[string range $blob [expr $byte+1] end] \
|
||||
}
|
||||
|
||||
set tn 0
|
||||
set c 256
|
||||
foreach {rowid sz blob} [
|
||||
db eval {SELECT rowid, length(block), block FROM t2_segments}
|
||||
] {
|
||||
incr tn
|
||||
set c [expr (($c+255)%256)]
|
||||
for {set i 0} {$i < $sz} {incr i} {
|
||||
set b2 [set_byte $blob $i $c]
|
||||
execsql { UPDATE t2_segments SET block = $b2 WHERE rowid = $rowid }
|
||||
do_test fts3corrupt2-1.$tn.$i {
|
||||
catchsql { SELECT * FROM t2 WHERE t2 MATCH 'a*' }
|
||||
set {} {}
|
||||
} {}
|
||||
}
|
||||
execsql { UPDATE t2_segments SET block = $blob WHERE rowid = $rowid }
|
||||
}
|
||||
|
||||
foreach c {50 100 150 200 250} {
|
||||
foreach {rowid sz blob} [
|
||||
db eval {SELECT rowid, length(root), root FROM t2_segdir}
|
||||
] {
|
||||
incr tn
|
||||
for {set i 0} {$i < $sz} {incr i} {
|
||||
set b2 [set_byte $blob $i $c]
|
||||
execsql { UPDATE t2_segdir SET root = $b2 WHERE rowid = $rowid }
|
||||
do_test fts3corrupt2-2.$c.$tn.$i {
|
||||
catchsql { SELECT * FROM t2 WHERE t2 MATCH 'a*' }
|
||||
set {} {}
|
||||
} {}
|
||||
}
|
||||
execsql { UPDATE t2_segdir SET root = $blob WHERE rowid = $rowid }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
+69
-10
@@ -6,9 +6,7 @@
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The tests in this file are structural coverage tests. They are designed
|
||||
# to complement the tests in fts3rnd.test and fts3doc.test. Between them,
|
||||
# the three files should provide full coverage of the fts3 extension code.
|
||||
# The tests in this file are structural coverage tests for FTS3.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@@ -21,6 +19,7 @@ source $testdir/fts3_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
set DO_MALLOC_TEST 0
|
||||
set testprefix fts3cov
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# When it first needs to read a block from the %_segments table, the FTS3
|
||||
@@ -82,29 +81,30 @@ do_test fts3cov-2.1 {
|
||||
INSERT INTO t1 VALUES('And she in the midnight wood will pray');
|
||||
INSERT INTO t1 VALUES('For the weal of her lover that''s far away.');
|
||||
COMMIT;
|
||||
|
||||
}
|
||||
execsql {
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
SELECT substr(hex(root), 1, 2) FROM t1_segdir;
|
||||
}
|
||||
} {03}
|
||||
|
||||
# Test the "missing entry" case:
|
||||
do_test fts3cov-2.1 {
|
||||
do_test fts3cov-2.2 {
|
||||
set root [db one {SELECT root FROM t1_segdir}]
|
||||
read_fts3varint [string range $root 1 end] left_child
|
||||
execsql { DELETE FROM t1_segments WHERE blockid = $left_child }
|
||||
} {}
|
||||
do_error_test fts3cov-2.2 {
|
||||
do_error_test fts3cov-2.3 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'c*'
|
||||
} {database disk image is malformed}
|
||||
} {SQL logic error or missing database}
|
||||
|
||||
# Test the "replaced with NULL" case:
|
||||
do_test fts3cov-2.3 {
|
||||
do_test fts3cov-2.4 {
|
||||
execsql { INSERT INTO t1_segments VALUES($left_child, NULL) }
|
||||
} {}
|
||||
do_error_test fts3cov-2.4 {
|
||||
do_error_test fts3cov-2.5 {
|
||||
SELECT * FROM t1 WHERE t1 MATCH 'cloud'
|
||||
} {database disk image is malformed}
|
||||
} {SQL logic error or missing database}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# The following tests are to test the effects of OOM errors while storing
|
||||
@@ -370,4 +370,63 @@ do_malloc_test fts3cov-13 -sqlprep {
|
||||
SELECT snippet(t13, '%%') FROM t13 WHERE t13 MATCH 'two';
|
||||
}
|
||||
|
||||
do_execsql_test 14.0 {
|
||||
CREATE VIRTUAL TABLE t14 USING fts4(a, b);
|
||||
INSERT INTO t14 VALUES('one two three', 'one three four');
|
||||
INSERT INTO t14 VALUES('a b c', 'd e a');
|
||||
}
|
||||
do_execsql_test 14.1 {
|
||||
SELECT rowid FROM t14 WHERE t14 MATCH '"one two three"'
|
||||
} {1}
|
||||
do_execsql_test 14.2 {
|
||||
SELECT rowid FROM t14 WHERE t14 MATCH '"one four"'
|
||||
} {}
|
||||
do_execsql_test 14.3 {
|
||||
SELECT rowid FROM t14 WHERE t14 MATCH '"e a"'
|
||||
} {2}
|
||||
do_execsql_test 14.5 {
|
||||
SELECT rowid FROM t14 WHERE t14 MATCH '"e b"'
|
||||
} {}
|
||||
do_catchsql_test 14.6 {
|
||||
SELECT rowid FROM t14 WHERE rowid MATCH 'one'
|
||||
} {1 {unable to use function MATCH in the requested context}}
|
||||
do_catchsql_test 14.7 {
|
||||
SELECT rowid FROM t14 WHERE docid MATCH 'one'
|
||||
} {1 {unable to use function MATCH in the requested context}}
|
||||
|
||||
do_execsql_test 15.0 {
|
||||
CREATE VIRTUAL TABLE t15 USING fts4(a, b, c);
|
||||
INSERT INTO t15 VALUES('abc def ghi', 'abc2 def2 ghi2', 'abc3 def3 ghi3');
|
||||
INSERT INTO t15 VALUES('abc2 def2 ghi2', 'abc2 def2 ghi2', 'abc def3 ghi3');
|
||||
}
|
||||
do_execsql_test 15.1 {
|
||||
SELECT rowid FROM t15 WHERE t15 MATCH '"abc* def2"'
|
||||
} {1 2}
|
||||
|
||||
# Test a corruption case.
|
||||
#
|
||||
do_execsql_test 16.1 {
|
||||
CREATE VIRTUAL TABLE t16 USING fts4;
|
||||
INSERT INTO t16 VALUES('theoretical work to examine the relationship');
|
||||
INSERT INTO t16 VALUES('solution of our problems on the invisible');
|
||||
DELETE FROM t16_content WHERE rowid = 2;
|
||||
}
|
||||
do_catchsql_test 16.2 {
|
||||
SELECT * FROM t16 WHERE t16 MATCH 'invisible'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
# And another corruption test case.
|
||||
#
|
||||
do_execsql_test 17.1 {
|
||||
CREATE VIRTUAL TABLE t17 USING fts4;
|
||||
INSERT INTO t17(content) VALUES('one one one');
|
||||
UPDATE t17_segdir SET root = X'00036F6E65FFFFFFFFFFFFFFFFFFFFFF02030300'
|
||||
} {}
|
||||
do_catchsql_test 17.2 {
|
||||
SELECT * FROM t17 WHERE t17 MATCH 'one'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
# 2010 October 15
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
|
||||
set ::testprefix fts3defer
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test cases fts3defer-1.* are the "warm body" cases. The database contains
|
||||
# one row with 15000 instances of the token "a". This makes the doclist for
|
||||
# "a" so large that FTS3 will avoid loading it in most cases.
|
||||
#
|
||||
# To show this, test cases fts3defer-1.2.* execute a bunch of FTS3 queries
|
||||
# involving token "a". Then, fts3defer-1.3.* replaces the doclist for token
|
||||
# "a" with all zeroes and fts3defer-1.4.* repeats the tests from 1.2. If
|
||||
# the tests still work, we can conclude that the doclist for "a" was not
|
||||
# used.
|
||||
#
|
||||
|
||||
set aaa [string repeat "a " 15000]
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('this is a dog');
|
||||
INSERT INTO t1 VALUES('an instance of a phrase');
|
||||
INSERT INTO t1 VALUES('an instance of a longer phrase');
|
||||
INSERT INTO t1 VALUES($aaa);
|
||||
COMMIT;
|
||||
} {}
|
||||
|
||||
set tests {
|
||||
1 {SELECT rowid FROM t1 WHERE t1 MATCH '"a dog"'} {1}
|
||||
2 {SELECT rowid FROM t1 WHERE t1 MATCH '"is a dog"'} {1}
|
||||
3 {SELECT rowid FROM t1 WHERE t1 MATCH '"a longer phrase"'} {3}
|
||||
4 {SELECT snippet(t1) FROM t1 WHERE t1 MATCH '"a longer phrase"'}
|
||||
{"an instance of <b>a</b> <b>longer</b> <b>phrase</b>"}
|
||||
5 {SELECT rowid FROM t1 WHERE t1 MATCH 'a dog'} {1}
|
||||
}
|
||||
|
||||
do_select_tests 1.2 $tests
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
SELECT count(*) FROM t1_segments WHERE length(block)>10000;
|
||||
UPDATE t1_segments
|
||||
SET block = zeroblob(length(block))
|
||||
WHERE length(block)>10000;
|
||||
} {1}
|
||||
|
||||
do_select_tests 1.4 $tests
|
||||
|
||||
# Drop the table. It is corrupt now anyhow, so not useful for subsequent tests.
|
||||
#
|
||||
do_execsql_test 1.5 { DROP TABLE t1 }
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# These tests - fts3defer-2.* - are more rigorous. They test that for a
|
||||
# variety of queries, FTS3 and FTS4 return the same results. And that
|
||||
# zeroing the very large doclists that FTS4 does not load does not change
|
||||
# the results.
|
||||
#
|
||||
# They use the following pseudo-randomly generated document data. The
|
||||
# tokens "zm" and "jk" are especially common in this dataset. Additionally,
|
||||
# two documents are added to the pseudo-random data before it is loaded
|
||||
# into FTS4 containing 100,000 instances of the "zm" and "jk" tokens. This
|
||||
# makes the doclists for those tokens so large that FTS4 avoids loading them
|
||||
# into memory if possible.
|
||||
#
|
||||
set data [list]
|
||||
lappend data [string repeat "zm " 100000]
|
||||
lappend data [string repeat "jk " 100000]
|
||||
lappend data {*}{
|
||||
"zm zm agmckuiu uhzq nsab jk rrkx duszemmzl hyq jk"
|
||||
"jk uhzq zm zm rgpzmlnmd zm zk jk jk zm"
|
||||
"duszemmzl zm jk xldlpy zm jk sbptoa xh jk xldlpy"
|
||||
"zm xh zm xqf azavwm jk jk trqd rgpzmlnmd jk"
|
||||
"zm vwq urvysbnykk ubwrfqnbjf zk lsz jk doiwavhwwo jk jk"
|
||||
"jk xduvfhk orpfawpx zkhdvkw jk mjpavjuhw zm jk duszemmzl zm"
|
||||
"jk igju jk jk zm hmjf xh zm gwdfhwurx zk"
|
||||
"vgsld jk jk zm hrlipdm jn zm zsmhnf vgsld duszemmzl"
|
||||
"gtuiexzsu aayxpmve zm zm zm drir scpgna xh azavwm uhzq"
|
||||
"farlehdhq hkfoudzftq igju duszemmzl xnxhf ewle zm hrlipdm urvysbnykk kn"
|
||||
"xnxhf jk jk agmckuiu duszemmzl jk zm zm jk vgsld"
|
||||
"zm zm zm jk jk urvysbnykk ogttbykvt zm zm jk"
|
||||
"iasrqgqv zm azavwm zidhxhbtv jk jk mjpavjuhw zm zm ajmvcydy"
|
||||
"rgpzmlnmd tmt mjpavjuhw xh igju jk azavwm fibokdry vgsld ofm"
|
||||
"zm jk vgsld jk xh jk csjqxhgj drir jk pmrb"
|
||||
"xh jk jk zm rrkx duszemmzl mjpavjuhw xldlpy igju zm"
|
||||
"jk hkfoudzftq zf rrkx wdmy jupk jk zm urvysbnykk npywgdvgz"
|
||||
"zm jk zm zm zhbrzadb uenvbm aayxpmve urvysbnykk duszemmzl jk"
|
||||
"uenvbm jk zm fxw xh bdilwmjw mjpavjuhw uv jk zm"
|
||||
"nk jk bnhc pahlds jk igju dzadnqzprr jk jk jk"
|
||||
"uhzq uv zm duszemmzl tlqix jk jk xh jk zm"
|
||||
"jk zm agmckuiu urvysbnykk jk jk zm zm jk jk"
|
||||
"azavwm mjpavjuhw lsgshn trqd xldlpy ogyavjvv agmckuiu ryvwwhlbc jk jk"
|
||||
"tmt jk zk zm azavwm ofm acpgim bvgimjik iasrqgqv wuvajhwqz"
|
||||
"igju ogyavjvv xrbdak rrkx fibokdry zf ujfhmrllq jk zm hxgwvib"
|
||||
"zm pahlds jk uenvbm aayxpmve iaf hmjf xph vnlyvtkgx zm"
|
||||
"jk xnxhf igju jk xh jk nvfasfh zm js jk"
|
||||
"zm zm rwaj igju xr rrkx xnxhf nvfasfh skxbsqzvmt xatbxeqq"
|
||||
"vgsld zm ujfhmrllq uhzq ogyavjvv nsab azavwm zm vgsld jmfiqhwnjg"
|
||||
"ymjoym duszemmzl urvysbnykk azavwm jk jmfiqhwnjg bu qcdziqomqk vnlyvtkgx"
|
||||
"zm nbilqcnz dzadnqzprr xh bkfgzsxn urvysbnykk xrujfzxqf zm zf agmckuiu"
|
||||
"jk urvysbnykk nvfasfh zf xh zm zm qcdziqomqk qvxtclg wdmy"
|
||||
"fibokdry jk urvysbnykk jk xr osff zm cvnnsl zm vgsld"
|
||||
"jk mjpavjuhw hkfoudzftq jk zm xh xqf urvysbnykk jk iasrqgqv"
|
||||
"jk csjqxhgj duszemmzl iasrqgqv aayxpmve zm brsuoqww jk qpmhtvl wluvgsw"
|
||||
"jk mj azavwm jk zm jn dzadnqzprr zm jk uhzq"
|
||||
"zk xqf jupk fxw nbilqcnz zm jk jcpiwj tznlvbfcv nvfasfh"
|
||||
"jk jcpiwj zm xnxhf zm mjpavjuhw mj drir pa pvjrjlas"
|
||||
"duszemmzl dzadnqzprr jk swc duszemmzl tmt jk jk pahlds jk"
|
||||
"zk zm jk zm zm eczkjblu zm hi pmrb jk"
|
||||
"azavwm zm iz agmckuiu jk sntk jk duszemmzl duszemmzl zm"
|
||||
"jk zm jk eczkjblu urvysbnykk sk gnl jk ttvgf hmjf"
|
||||
"jk bnhc jjrxpjkb mjpavjuhw fibokdry igju jk zm zm xh"
|
||||
"wxe ogttbykvt uhzq xr iaf zf urvysbnykk aayxpmve oacaxgjoo mjpavjuhw"
|
||||
"gazrt jk ephknonq myjp uenvbm wuvajhwqz jk zm xnxhf nvfasfh"
|
||||
"zm aayxpmve csjqxhgj xnxhf xr jk aayxpmve xnxhf zm zm"
|
||||
"sokcyf zm ogyavjvv jk zm fibokdry zm jk igju igju"
|
||||
"vgsld bvgimjik xuprtlyle jk akmikrqyt jk aayxpmve hkfoudzftq ddjj ithtir"
|
||||
"zm uhzq ovkyevlgv zk uenvbm csjqxhgj jk vgsld pgybs jk"
|
||||
"zm agmckuiu zexh fibokdry jk uhzq bu tugflixoex xnxhf sk"
|
||||
"zm zf uenvbm jk azavwm zm zm agmckuiu zm jk"
|
||||
"rrkx jk zf jt zm oacaxgjoo fibokdry wdmy igju csjqxhgj"
|
||||
"hi igju zm jk zidhxhbtv dzadnqzprr jk jk trqd duszemmzl"
|
||||
"zm zm mjpavjuhw xrbdak qrvbjruc jk qzzqdxq guwq cvnnsl zm"
|
||||
"ithtir jk jk qcdziqomqk zm farlehdhq zm zm xrbdak jk"
|
||||
"ixfipk csjqxhgj azavwm sokcyf ttvgf vgsld jk sk xh zk"
|
||||
"nvfasfh azavwm zm zm zm fxw nvfasfh zk gnl trqd"
|
||||
"zm fibokdry csjqxhgj ofm dzadnqzprr jk akmikrqyt orpfawpx duszemmzl vwq"
|
||||
"csjqxhgj jk jk vgsld urvysbnykk jk nxum jk jk nxum"
|
||||
"zm hkfoudzftq jk ryvwwhlbc mjpavjuhw ephknonq jk zm ogyavjvv zm"
|
||||
"lwa hi xnxhf qdyerbws zk njtc jk uhzq zm jk"
|
||||
"trqd zm dzadnqzprr zm urvysbnykk jk lsz jk mjpavjuhw cmnnkna"
|
||||
"duszemmzl zk jk jk fibokdry jseuhjnzo zm aayxpmve zk jk"
|
||||
"fibokdry jk sviq qvxtclg wdmy jk doiwavhwwo zexh jk zm"
|
||||
"jupk zm xh jk mjpavjuhw zm jk nsab npywgdvgz duszemmzl"
|
||||
"zm igju zm zm nvfasfh eh hkfoudzftq fibokdry fxw xkblf"
|
||||
"jk zm jk jk zm xh zk abthnzcv zf csjqxhgj"
|
||||
"zm zm jk nkaotm urvysbnykk sbptoa bq jk ktxdty ubwrfqnbjf"
|
||||
"nvfasfh aayxpmve xdcuz zm tugflixoex jcpiwj zm mjpavjuhw fibokdry doiwavhwwo"
|
||||
"iaf jk mjpavjuhw zm duszemmzl jk jk uhzq pahlds fibokdry"
|
||||
"ddjj zk azavwm jk swc zm gjtexkv jk xh jk"
|
||||
"igju jk csjqxhgj zm jk dzadnqzprr duszemmzl ulvcbv jk jk"
|
||||
"jk fibokdry zm csjqxhgj jn zm zm zm zf uhzq"
|
||||
"duszemmzl jk xkblf zk hrlipdm aayxpmve uenvbm uhzq jk zf"
|
||||
"dzadnqzprr jk zm zdu nvfasfh zm jk urvysbnykk hmjf jk"
|
||||
"jk aayxpmve aserrdxm acpgim fibokdry jk drir wxe brsuoqww rrkx"
|
||||
"uhzq csjqxhgj nvfasfh jk rrkx qbamok trqd uenvbm sntk zm"
|
||||
"ps azavwm zkhdvkw jk zm jk jk zm csjqxhgj xedlrcfo"
|
||||
"jk jk ogyavjvv jk zm farlehdhq duszemmzl jk agitgxamxe jk"
|
||||
"qzzqdxq rwaj jk jk zm xqf jk uenvbm jk zk"
|
||||
"zm hxgwvib akmikrqyt zf agmckuiu uenvbm bq npywgdvgz azavwm jk"
|
||||
"zf jmfiqhwnjg js igju zm aayxpmve zm mbxnljomiv csjqxhgj nvfasfh"
|
||||
"zm jk jk gazrt jk jk lkc jk nvfasfh jk"
|
||||
"xldlpy orpfawpx zkhdvkw jk zm igju zm urvysbnykk dzadnqzprr mbxnljomiv"
|
||||
"urvysbnykk jk zk igju zm uenvbm jk zm ithtir jk"
|
||||
"zm zk zm zf ofm zm xdcuz dzadnqzprr zm vgsld"
|
||||
"sbptoa jk tugflixoex jk zm zm vgsld zm xh zm"
|
||||
"uhzq jk zk evvivo vgsld vniqnuynvf agmckuiu jk zm zm"
|
||||
"zm nvfasfh zm zm zm abthnzcv uenvbm jk zk dzadnqzprr"
|
||||
"zm azavwm igju qzzqdxq jk xnxhf abthnzcv jk nvfasfh zm"
|
||||
"qbamok fxw vgsld igju cmnnkna xnxhf vniqnuynvf zk xh zm"
|
||||
"nvfasfh zk zm mjpavjuhw dzadnqzprr jk jk duszemmzl xldlpy nvfasfh"
|
||||
"xnxhf sviq nsab npywgdvgz osff vgsld farlehdhq fibokdry wjbkhzsa hhac"
|
||||
"zm azavwm scpgna jk jk bq jk duszemmzl fibokdry ovkyevlgv"
|
||||
"csjqxhgj zm jk jk duszemmzl zk xh zm jk zf"
|
||||
"urvysbnykk dzadnqzprr csjqxhgj mjpavjuhw ubwrfqnbjf nkaotm jk jk zm drir"
|
||||
"nvfasfh xh igju zm wluvgsw jk zm srwwnezqk ewle ovnq"
|
||||
"jk nvfasfh eh ktxdty urvysbnykk vgsld zm jk eh uenvbm"
|
||||
"orpfawpx pahlds jk uhzq hi zm zm zf jk dzadnqzprr"
|
||||
"srwwnezqk csjqxhgj rbwzuf nvfasfh jcpiwj xldlpy nvfasfh jk vgsld wjybxmieki"
|
||||
}
|
||||
|
||||
proc add_empty_records {n} {
|
||||
execsql BEGIN
|
||||
for {set i 0} {$i < $n} {incr i} {
|
||||
execsql { INSERT INTO t1 VALUES('') }
|
||||
}
|
||||
execsql COMMIT
|
||||
}
|
||||
|
||||
|
||||
#set e [list]
|
||||
#foreach d $data {set e [concat $e $d]}
|
||||
#puts [lsort -unique $e]
|
||||
#exit
|
||||
|
||||
set zero_long_doclists {
|
||||
UPDATE t1_segments SET block=zeroblob(length(block)) WHERE length(block)>10000
|
||||
}
|
||||
|
||||
foreach {tn setup} {
|
||||
1 {
|
||||
set dmt_modes 0
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING FTS3 }
|
||||
foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } }
|
||||
}
|
||||
2 {
|
||||
set dmt_modes 0
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING FTS4 }
|
||||
foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } }
|
||||
}
|
||||
3 {
|
||||
set dmt_modes {0 1 2}
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING FTS4 }
|
||||
foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } }
|
||||
add_empty_records 1000
|
||||
execsql $zero_long_doclists
|
||||
}
|
||||
4 {
|
||||
set dmt_modes 0
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING FTS4 }
|
||||
foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } }
|
||||
add_empty_records 1000
|
||||
execsql "INSERT INTO t1(t1) VALUES('optimize')"
|
||||
execsql $zero_long_doclists
|
||||
}
|
||||
5 {
|
||||
set dmt_modes 0
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING FTS4(matchinfo=fts3) }
|
||||
foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } }
|
||||
add_empty_records 1000
|
||||
execsql $zero_long_doclists
|
||||
}
|
||||
} {
|
||||
|
||||
execsql { DROP TABLE IF EXISTS t1 }
|
||||
eval $setup
|
||||
set ::testprefix fts3defer-2.$tn
|
||||
set DO_MALLOC_TEST 0
|
||||
|
||||
do_execsql_test 0 {
|
||||
SELECT count(*) FROM t1_segments WHERE length(block)>10000
|
||||
} {2}
|
||||
|
||||
do_select_test 1.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'jk xnxhf'
|
||||
} {13 29 40 47 48 52 63 92}
|
||||
do_select_test 1.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'jk eh'
|
||||
} {100}
|
||||
if {$tn==3} breakpoint
|
||||
do_select_test 1.3 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'jk ubwrfqnbjf'
|
||||
} {7 70 98}
|
||||
do_select_test 1.4 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'duszemmzl jk'
|
||||
} {3 5 8 10 13 18 20 23 32 37 41 43 55 60 65 67 72 74 76 81 94 96 97}
|
||||
do_select_test 1.5 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'ubwrfqnbjf jk'
|
||||
} {7 70 98}
|
||||
do_select_test 1.6 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'jk ubwrfqnbjf jk jk jk jk'
|
||||
} {7 70 98}
|
||||
do_select_test 1.7 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'zm xnxhf'
|
||||
} {12 13 29 30 40 47 48 52 63 92 93}
|
||||
do_select_test 1.8 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'zm eh'
|
||||
} {68 100}
|
||||
do_select_test 1.9 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'zm ubwrfqnbjf'
|
||||
} {7 70 98}
|
||||
do_select_test 1.10 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'z* vgsld'
|
||||
} {10 13 17 31 35 51 58 88 89 90 93 100}
|
||||
do_select_test 1.11 {
|
||||
SELECT rowid FROM t1
|
||||
WHERE t1 MATCH '(
|
||||
zdu OR zexh OR zf OR zhbrzadb OR zidhxhbtv OR
|
||||
zk OR zkhdvkw OR zm OR zsmhnf
|
||||
) vgsld'
|
||||
} {10 13 17 31 35 51 58 88 89 90 93 100}
|
||||
|
||||
do_select_test 2.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm agmckuiu"'
|
||||
} {3 24 52 53}
|
||||
do_select_test 2.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm zf"'
|
||||
} {33 53 75 88 101}
|
||||
do_select_test 2.3 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm aayxpmve"'
|
||||
} {48 65 84}
|
||||
do_select_test 2.4 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"aayxpmve zm"'
|
||||
} {11 37 84}
|
||||
do_select_test 2.5 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"jk azavwm"'
|
||||
} {16 53}
|
||||
do_select_test 2.6 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"xh jk jk"'
|
||||
} {18}
|
||||
do_select_test 2.7 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm jk vgsld"'
|
||||
} {13 17}
|
||||
do_select_test 2.8 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm jk vgsld lkjlkjlkj"'
|
||||
} {}
|
||||
|
||||
do_select_test 3.1 {
|
||||
SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH '"zm agmckuiu"'
|
||||
} {
|
||||
{zm [zm] [agmckuiu] uhzq nsab jk rrkx duszemmzl hyq jk}
|
||||
{jk [zm] [agmckuiu] urvysbnykk jk jk zm zm jk jk}
|
||||
{[zm] [agmckuiu] zexh fibokdry jk uhzq bu tugflixoex xnxhf sk}
|
||||
{zm zf uenvbm jk azavwm zm [zm] [agmckuiu] zm jk}
|
||||
}
|
||||
|
||||
do_select_test 3.2 {
|
||||
SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH 'xnxhf jk'
|
||||
} {
|
||||
{[xnxhf] [jk] [jk] agmckuiu duszemmzl [jk] zm zm [jk] vgsld}
|
||||
{[jk] [xnxhf] igju [jk] xh [jk] nvfasfh zm js [jk]}
|
||||
{[jk] jcpiwj zm [xnxhf] zm mjpavjuhw mj drir pa pvjrjlas}
|
||||
{gazrt [jk] ephknonq myjp uenvbm wuvajhwqz [jk] zm [xnxhf] nvfasfh}
|
||||
{zm aayxpmve csjqxhgj [xnxhf] xr [jk] aayxpmve [xnxhf] zm zm}
|
||||
{zm agmckuiu zexh fibokdry [jk] uhzq bu tugflixoex [xnxhf] sk}
|
||||
{lwa hi [xnxhf] qdyerbws zk njtc [jk] uhzq zm [jk]}
|
||||
{zm azavwm igju qzzqdxq [jk] [xnxhf] abthnzcv [jk] nvfasfh zm}
|
||||
}
|
||||
|
||||
do_select_test 4.1 {
|
||||
SELECT offsets(t1) FROM t1 WHERE t1 MATCH '"jk uenvbm"'
|
||||
} {
|
||||
{0 0 10 2 0 1 13 6} {0 0 26 2 0 1 29 6}
|
||||
}
|
||||
|
||||
do_select_test 4.2 {
|
||||
SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'duszemmzl jk fibokdry'
|
||||
} {
|
||||
{0 2 3 8 0 1 36 2 0 0 58 9}
|
||||
{0 0 0 9 0 1 13 2 0 1 16 2 0 2 19 8 0 1 53 2}
|
||||
{0 1 4 2 0 0 20 9 0 1 30 2 0 1 33 2 0 2 48 8}
|
||||
{0 1 17 2 0 1 20 2 0 1 26 2 0 0 29 9 0 2 39 8}
|
||||
}
|
||||
|
||||
do_select_test 4.3 {
|
||||
SELECT offsets(t1) FROM t1
|
||||
WHERE t1 MATCH 'vgsld (hrlipdm OR (aapmve NEAR duszemmzl))'
|
||||
} {{0 0 0 5 0 1 15 7 0 0 36 5}}
|
||||
|
||||
# The following block of tests runs normally with FTS3 or FTS4 without the
|
||||
# long doclists zeroed. And with OOM-injection for FTS4 with long doclists
|
||||
# zeroed. Change this by messing with the [set dmt_modes] commands above.
|
||||
#
|
||||
foreach DO_MALLOC_TEST $dmt_modes {
|
||||
|
||||
# Phrase search.
|
||||
do_select_test 5.$DO_MALLOC_TEST.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"jk mjpavjuhw"'
|
||||
} {8 15 36 64 67 72}
|
||||
|
||||
# Multiple tokens search.
|
||||
do_select_test 5.$DO_MALLOC_TEST.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'duszemmzl zm'
|
||||
} {3 5 8 10 12 13 18 20 23 37 43 55 60 65 67 72 74 81 94 96 97}
|
||||
|
||||
# snippet() function with phrase.
|
||||
do_select_test 5.$DO_MALLOC_TEST.3 {
|
||||
SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH '"zm aayxpmve"'
|
||||
} {
|
||||
{[zm] [aayxpmve] csjqxhgj xnxhf xr jk aayxpmve xnxhf zm zm}
|
||||
{duszemmzl zk jk jk fibokdry jseuhjnzo [zm] [aayxpmve] zk jk}
|
||||
{zf jmfiqhwnjg js igju [zm] [aayxpmve] zm mbxnljomiv csjqxhgj nvfasfh}
|
||||
}
|
||||
|
||||
# snippet() function with multiple tokens.
|
||||
do_select_test 5.$DO_MALLOC_TEST.4 {
|
||||
SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH 'zm zhbrzadb'
|
||||
} {
|
||||
{[zm] jk [zm] [zm] [zhbrzadb] uenvbm aayxpmve urvysbnykk duszemmzl jk}
|
||||
}
|
||||
|
||||
# snippet() function with phrase.
|
||||
do_select_test 5.$DO_MALLOC_TEST.5 {
|
||||
SELECT offsets(t1) FROM t1 WHERE t1 MATCH '"zm aayxpmve"'
|
||||
} {
|
||||
{0 0 0 2 0 1 3 8} {0 0 38 2 0 1 41 8} {0 0 22 2 0 1 25 8}
|
||||
}
|
||||
|
||||
# snippet() function with multiple tokens.
|
||||
do_select_test 5.$DO_MALLOC_TEST.6 {
|
||||
SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'zm zhbrzadb'
|
||||
} {
|
||||
{0 0 0 2 0 0 6 2 0 0 9 2 0 1 12 8}
|
||||
}
|
||||
|
||||
set DO_MALLOC_TEST 0
|
||||
}
|
||||
|
||||
do_select_test 6.1 {
|
||||
SELECT rowid FROM t1
|
||||
WHERE t1 MATCH 'vgsld (hrlipdm OR (aayxpmve duszemmzl))'
|
||||
} {10}
|
||||
do_select_test 6.2.1 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"jk xduvfhk"'
|
||||
} {8}
|
||||
do_select_test 6.2.2 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"zm azavwm"'
|
||||
} {15 26 92 96}
|
||||
do_select_test 6.2.3 {
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH '"jk xduvfhk" OR "zm azavwm"'
|
||||
} {8 15 26 92 96}
|
||||
}
|
||||
|
||||
set testprefix fts3defer
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts4(a, b);
|
||||
INSERT INTO x1 VALUES('a b c', 'd e f');
|
||||
INSERT INTO x1 SELECT * FROM x1;
|
||||
INSERT INTO x1 SELECT * FROM x1;
|
||||
INSERT INTO x1 SELECT * FROM x1;
|
||||
INSERT INTO x1 SELECT * FROM x1;
|
||||
}
|
||||
do_execsql_test 3.2 "
|
||||
INSERT INTO x1 VALUES(
|
||||
'[string repeat {d } 3000]', '[string repeat {f } 30000]'
|
||||
);
|
||||
INSERT INTO x1(x1) VALUES('optimize');
|
||||
"
|
||||
|
||||
do_execsql_test 3.3 {
|
||||
SELECT count(*) FROM x1 WHERE x1 MATCH '"d e f"'
|
||||
} {16}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,134 @@
|
||||
# 2010 October 23
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
|
||||
set testprefix fts3defer2
|
||||
|
||||
proc mit {blob} {
|
||||
set scan(littleEndian) i*
|
||||
set scan(bigEndian) I*
|
||||
binary scan $blob $scan($::tcl_platform(byteOrder)) r
|
||||
return $r
|
||||
}
|
||||
db func mit mit
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# This block of tests - fts3defer2-1.* - test the interaction of deferred NEAR
|
||||
# expressions and the snippet, offsets and matchinfo functions.
|
||||
#
|
||||
do_execsql_test 1.1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4;
|
||||
}
|
||||
do_execsql_test 1.1.2 "INSERT INTO t1 VALUES('[string repeat {a } 20000]')"
|
||||
do_execsql_test 1.1.3 "INSERT INTO t1 VALUES('[string repeat {z } 20000]')"
|
||||
do_execsql_test 1.1.4 {
|
||||
INSERT INTO t1 VALUES('a b c d e f a x y');
|
||||
INSERT INTO t1 VALUES('');
|
||||
INSERT INTO t1 VALUES('');
|
||||
INSERT INTO t1 VALUES('');
|
||||
INSERT INTO t1 VALUES('');
|
||||
INSERT INTO t1 VALUES('');
|
||||
INSERT INTO t1(t1) VALUES('optimize');
|
||||
}
|
||||
do_execsql_test 1.1.4 {
|
||||
SELECT count(*) FROM t1_segments WHERE length(block)>10000;
|
||||
UPDATE t1_segments SET block = zeroblob(length(block)) WHERE length(block)>10000;
|
||||
} {2}
|
||||
|
||||
do_execsql_test 1.2.1 {
|
||||
SELECT content FROM t1 WHERE t1 MATCH 'f (e NEAR/2 a)';
|
||||
} {{a b c d e f a x y}}
|
||||
|
||||
do_execsql_test 1.2.2 {
|
||||
SELECT snippet(t1, '[', ']'), offsets(t1), mit(matchinfo(t1, 'pcxnal'))
|
||||
FROM t1 WHERE t1 MATCH 'f (e NEAR/2 a)';
|
||||
} [list \
|
||||
{a b c d [e] [f] [a] x y} \
|
||||
{0 1 8 1 0 0 10 1 0 2 12 1} \
|
||||
[list 3 1 1 1 1 1 8 8 1 8 8 8 5001 9]
|
||||
]
|
||||
|
||||
do_execsql_test 1.2.3 {
|
||||
SELECT snippet(t1, '[', ']'), offsets(t1), mit(matchinfo(t1, 'pcxnal'))
|
||||
FROM t1 WHERE t1 MATCH 'f (e NEAR/3 a)';
|
||||
} [list \
|
||||
{[a] b c d [e] [f] [a] x y} \
|
||||
{0 2 0 1 0 1 8 1 0 0 10 1 0 2 12 1} \
|
||||
[list 3 1 1 1 1 1 8 8 2 8 8 8 5001 9]
|
||||
]
|
||||
|
||||
do_execsql_test 1.3.1 { DROP TABLE t1 }
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Test cases fts3defer2-2.* focus specifically on the matchinfo function.
|
||||
#
|
||||
do_execsql_test 2.1.1 "CREATE VIRTUAL TABLE t2 USING fts4"
|
||||
do_execsql_test 2.1.2 "INSERT INTO t2 VALUES('[string repeat {a } 10000]')"
|
||||
do_execsql_test 2.1.3 "INSERT INTO t2 VALUES('b [string repeat {z } 10000]')"
|
||||
do_execsql_test 2.1.4 [string repeat "INSERT INTO t2 VALUES('x');" 50]
|
||||
do_execsql_test 2.1.5 {
|
||||
INSERT INTO t2 VALUES('a b c d e f g');
|
||||
INSERT INTO t2 VALUES('a b c d e f g');
|
||||
}
|
||||
foreach {tn sql} {
|
||||
1 {}
|
||||
2 { INSERT INTO t2(t2) VALUES('optimize') }
|
||||
3 { UPDATE t2_segments SET block = zeroblob(length(block))
|
||||
WHERE length(block)>10000;
|
||||
}
|
||||
} {
|
||||
execsql $sql
|
||||
|
||||
do_execsql_test 2.2.$tn {
|
||||
SELECT mit(matchinfo(t2, 'pcxnal')) FROM t2 WHERE t2 MATCH 'a b';
|
||||
} [list \
|
||||
[list 2 1 1 54 54 1 3 3 54 372 7] \
|
||||
[list 2 1 1 54 54 1 3 3 54 372 7] \
|
||||
]
|
||||
}
|
||||
|
||||
do_execsql_test 2.3.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts4;
|
||||
INSERT INTO t3 VALUES('a b c d e f');
|
||||
INSERT INTO t3 VALUES('x b c d e f');
|
||||
INSERT INTO t3 VALUES('d e f a b c');
|
||||
INSERT INTO t3 VALUES('b c d e f');
|
||||
INSERT INTO t3 VALUES('');
|
||||
INSERT INTO t3 VALUES('');
|
||||
INSERT INTO t3 VALUES('');
|
||||
INSERT INTO t3 VALUES('');
|
||||
INSERT INTO t3 VALUES('');
|
||||
INSERT INTO t3 VALUES('');
|
||||
}
|
||||
do_execsql_test 2.3.2 "
|
||||
INSERT INTO t3 VALUES('f e d c b [string repeat {a } 10000]')
|
||||
"
|
||||
foreach {tn sql} {
|
||||
1 {}
|
||||
2 { INSERT INTO t3(t3) VALUES('optimize') }
|
||||
3 { UPDATE t3_segments SET block = zeroblob(length(block))
|
||||
WHERE length(block)>10000;
|
||||
}
|
||||
} {
|
||||
execsql $sql
|
||||
do_execsql_test 2.4.$tn {
|
||||
SELECT docid, mit(matchinfo(t3, 'pcxnal')) FROM t3 WHERE t3 MATCH '"a b c"';
|
||||
} {1 {1 1 1 4 4 11 912 6} 3 {1 1 1 4 4 11 912 6}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
# 2010 June 15
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set ::testprefix fts3fault
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
|
||||
if 1 {
|
||||
|
||||
# Test error handling in the sqlite3Fts3Init() function. This is the
|
||||
# function that registers the FTS3 module and various support functions
|
||||
# with SQLite.
|
||||
#
|
||||
do_faultsim_test 1 -body {
|
||||
sqlite3 db test.db
|
||||
expr 0
|
||||
} -test {
|
||||
catch { db close }
|
||||
}
|
||||
|
||||
# Test error handling in an "ALTER TABLE ... RENAME TO" statement on an
|
||||
# FTS3 table. Specifically, test renaming the table within a transaction
|
||||
# after it has been written to.
|
||||
#
|
||||
faultsim_delete_and_reopen
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3;
|
||||
INSERT INTO t1 VALUES('test renaming the table');
|
||||
INSERT INTO t1 VALUES(' after it has been written');
|
||||
}
|
||||
do_faultsim_test 2 -prep {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('registers the FTS3 module');
|
||||
INSERT INTO t1 VALUES('various support functions');
|
||||
}
|
||||
} -body {
|
||||
execsql { ALTER TABLE t1 RENAME TO t2 }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
# Test error handling in the special case where a single prefix query
|
||||
# matches terms that reside on a large range of leaf nodes.
|
||||
#
|
||||
do_test fts3fault-3.0 {
|
||||
sqlite3 db test.db
|
||||
execsql { CREATE VIRTUAL TABLE t3 USING fts4; }
|
||||
execsql { INSERT INTO t3(t3) VALUES('nodesize=50') }
|
||||
execsql { BEGIN }
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
execsql { INSERT INTO t3 VALUES('aaa' || $i) }
|
||||
}
|
||||
execsql { COMMIT }
|
||||
} {}
|
||||
|
||||
do_faultsim_test 3 -faults oom-transient -prep {
|
||||
sqlite3 db test.db
|
||||
execsql { SELECT * FROM t3 WHERE t3 MATCH 'x' }
|
||||
} -body {
|
||||
execsql { SELECT count(rowid) FROM t3 WHERE t3 MATCH 'aa*' }
|
||||
} -test {
|
||||
faultsim_test_result {0 1000}
|
||||
}
|
||||
|
||||
do_test fts3fault-4.0 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t4 USING fts4;
|
||||
INSERT INTO t4 VALUES('The British Government called on');
|
||||
INSERT INTO t4 VALUES('as pesetas then became much');
|
||||
}
|
||||
} {}
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 4 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT content FROM t4 }
|
||||
} -body {
|
||||
execsql { SELECT optimize(t4) FROM t4 LIMIT 1 }
|
||||
} -test {
|
||||
faultsim_test_result {0 {{Index optimized}}}
|
||||
}
|
||||
|
||||
do_test fts3fault-5.0 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t5 USING fts4;
|
||||
INSERT INTO t5 VALUES('The British Government called on');
|
||||
INSERT INTO t5 VALUES('as pesetas then became much');
|
||||
}
|
||||
} {}
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 5 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t5 VALUES('influential in shaping his future outlook');
|
||||
INSERT INTO t5 VALUES('might be acceptable to the British electorate');
|
||||
}
|
||||
} -body {
|
||||
execsql { SELECT rowid FROM t5 WHERE t5 MATCH 'british' }
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 4}}
|
||||
}
|
||||
|
||||
do_test fts3fault-6.0 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql { CREATE VIRTUAL TABLE t6 USING fts4 }
|
||||
} {}
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 6 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT rowid FROM t6 }
|
||||
} -body {
|
||||
execsql { DROP TABLE t6 }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
# Test various malloc failures while processing FTS4 parameters.
|
||||
#
|
||||
do_faultsim_test 7.1 -prep {
|
||||
faultsim_delete_and_reopen
|
||||
} -body {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchinfo=fts3) }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
do_faultsim_test 7.2 -prep {
|
||||
faultsim_delete_and_reopen
|
||||
} -body {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchinfo=fs3) }
|
||||
} -test {
|
||||
faultsim_test_result {1 {unrecognized matchinfo: fs3}} \
|
||||
{1 {vtable constructor failed: t1}}
|
||||
}
|
||||
do_faultsim_test 7.3 -prep {
|
||||
faultsim_delete_and_reopen
|
||||
} -body {
|
||||
execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchnfo=fts3) }
|
||||
} -test {
|
||||
faultsim_test_result {1 {unrecognized parameter: matchnfo=fts3}} \
|
||||
{1 {vtable constructor failed: t1}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
proc mit {blob} {
|
||||
set scan(littleEndian) i*
|
||||
set scan(bigEndian) I*
|
||||
binary scan $blob $scan($::tcl_platform(byteOrder)) r
|
||||
return $r
|
||||
}
|
||||
|
||||
do_test 8.0 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql { CREATE VIRTUAL TABLE t8 USING fts4 }
|
||||
execsql "INSERT INTO t8 VALUES('a b c')"
|
||||
execsql "INSERT INTO t8 VALUES('b b b')"
|
||||
execsql "INSERT INTO t8 VALUES('[string repeat {c } 50000]')"
|
||||
execsql "INSERT INTO t8 VALUES('d d d')"
|
||||
execsql "INSERT INTO t8 VALUES('e e e')"
|
||||
execsql "INSERT INTO t8(t8) VALUES('optimize')"
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
|
||||
do_faultsim_test 8.1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db func mit mit
|
||||
} -body {
|
||||
execsql { SELECT mit(matchinfo(t8, 'x')) FROM t8 WHERE t8 MATCH 'a b c' }
|
||||
} -test {
|
||||
faultsim_test_result {0 {{1 1 1 1 4 2 1 5 5}}}
|
||||
}
|
||||
do_faultsim_test 8.2 -faults oom-t* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db func mit mit
|
||||
} -body {
|
||||
execsql { SELECT mit(matchinfo(t8, 's')) FROM t8 WHERE t8 MATCH 'a b c' }
|
||||
} -test {
|
||||
faultsim_test_result {0 3}
|
||||
}
|
||||
do_faultsim_test 8.3 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db func mit mit
|
||||
} -body {
|
||||
execsql { SELECT mit(matchinfo(t8, 'a')) FROM t8 WHERE t8 MATCH 'a b c' }
|
||||
} -test {
|
||||
faultsim_test_result {0 10002}
|
||||
}
|
||||
do_faultsim_test 8.4 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db func mit mit
|
||||
} -body {
|
||||
execsql { SELECT mit(matchinfo(t8, 'l')) FROM t8 WHERE t8 MATCH 'a b c' }
|
||||
} -test {
|
||||
faultsim_test_result {0 3}
|
||||
}
|
||||
|
||||
do_test 9.0 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t9 USING fts4(tokenize=porter);
|
||||
INSERT INTO t9 VALUES(
|
||||
'this record is used toooooooooooooooooooooooooooooooooooooo try to'
|
||||
);
|
||||
SELECT offsets(t9) FROM t9 WHERE t9 MATCH 'to*';
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test 9.1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { SELECT offsets(t9) FROM t9 WHERE t9 MATCH 'to*' }
|
||||
} -test {
|
||||
faultsim_test_result {0 {{0 0 20 39 0 0 64 2}}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -44,7 +44,6 @@ proc normal_list {l} {
|
||||
set ret
|
||||
}
|
||||
|
||||
|
||||
do_write_test fts3_malloc-1.1 sqlite_master {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts3(a, b)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
# 2010 November 02
|
||||
#
|
||||
# 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 regression tests for the FTS3 module. The focus
|
||||
# of this file is tables created with the "matchinfo=fts3" option.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
|
||||
set testprefix fts3matchinfo
|
||||
|
||||
proc mit {blob} {
|
||||
set scan(littleEndian) i*
|
||||
set scan(bigEndian) I*
|
||||
binary scan $blob $scan($::tcl_platform(byteOrder)) r
|
||||
return $r
|
||||
}
|
||||
db func mit mit
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(matchinfo=fts3);
|
||||
SELECT name FROM sqlite_master WHERE type = 'table';
|
||||
} {t1 t1_content t1_segments t1_segdir t1_stat}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
INSERT INTO t1(content) VALUES('I wandered lonely as a cloud');
|
||||
INSERT INTO t1(content) VALUES('That floats on high o''er vales and hills,');
|
||||
INSERT INTO t1(content) VALUES('When all at once I saw a crowd,');
|
||||
INSERT INTO t1(content) VALUES('A host, of golden daffodils,');
|
||||
SELECT mit(matchinfo(t1)) FROM t1 WHERE t1 MATCH 'I';
|
||||
} {{1 1 1 2 2} {1 1 1 2 2}}
|
||||
|
||||
# Now create an FTS4 table that does not specify matchinfo=fts3.
|
||||
#
|
||||
do_execsql_test 1.2 {
|
||||
CREATE VIRTUAL TABLE t2 USING fts4;
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
SELECT mit(matchinfo(t2)) FROM t2 WHERE t2 MATCH 'I';
|
||||
} {{1 1 1 2 2} {1 1 1 2 2}}
|
||||
|
||||
# Test some syntax-error handling.
|
||||
#
|
||||
do_catchsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE x1 USING fts4(matchinfo=fs3);
|
||||
} {1 {unrecognized matchinfo: fs3}}
|
||||
do_catchsql_test 2.1 {
|
||||
CREATE VIRTUAL TABLE x2 USING fts4(mtchinfo=fts3);
|
||||
} {1 {unrecognized parameter: mtchinfo=fts3}}
|
||||
|
||||
# Check that with fts3, the "=" character is permitted in column definitions.
|
||||
#
|
||||
do_execsql_test 3.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING fts3(mtchinfo=fts3);
|
||||
INSERT INTO t3(mtchinfo) VALUES('Beside the lake, beneath the trees');
|
||||
SELECT mtchinfo FROM t3;
|
||||
} {{Beside the lake, beneath the trees}}
|
||||
|
||||
do_execsql_test 3.2 {
|
||||
CREATE VIRTUAL TABLE xx USING FTS4;
|
||||
SELECT * FROM xx WHERE xx MATCH 'abc';
|
||||
SELECT * FROM xx WHERE xx MATCH 'a b c';
|
||||
}
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Proc [do_matchinfo_test] is used to test the FTSX matchinfo() function.
|
||||
#
|
||||
# The first argument - $tn - is a test identifier. This may be either a
|
||||
# full identifier (i.e. "fts3matchinfo-1.1") or, if global var $testprefix
|
||||
# is set, just the numeric component (i.e. "1.1").
|
||||
#
|
||||
# The second argument is the name of an FTSX table. The third is the
|
||||
# full text of a WHERE/MATCH expression to query the table for
|
||||
# (i.e. "t1 MATCH 'abc'"). The final argument - $results - should be a
|
||||
# key-value list (serialized array) with matchinfo() format specifiers
|
||||
# as keys, and the results of executing the statement:
|
||||
#
|
||||
# SELECT matchinfo($tbl, '$key') FROM $tbl WHERE $expr
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# CREATE VIRTUAL TABLE t1 USING fts4;
|
||||
# INSERT INTO t1 VALUES('abc');
|
||||
# INSERT INTO t1 VALUES('def');
|
||||
# INSERT INTO t1 VALUES('abc abc');
|
||||
#
|
||||
# do_matchinfo_test 1.1 t1 "t1 MATCH 'abc'" {
|
||||
# n {3 3}
|
||||
# p {1 1}
|
||||
# c {1 1}
|
||||
# x {{1 3 2} {2 3 2}}
|
||||
# }
|
||||
#
|
||||
# If the $results list contains keys mapped to "-" instead of a matchinfo()
|
||||
# result, then this command computes the expected results based on other
|
||||
# mappings to test the matchinfo() function. For example, the command above
|
||||
# could be changed to:
|
||||
#
|
||||
# do_matchinfo_test 1.1 t1 "t1 MATCH 'abc'" {
|
||||
# n {3 3} p {1 1} c {1 1} x {{1 3 2} {2 3 2}}
|
||||
# pcx -
|
||||
# }
|
||||
#
|
||||
# And this command would compute the expected results for matchinfo(t1, 'pcx')
|
||||
# based on the results of matchinfo(t1, 'p'), matchinfo(t1, 'c') and
|
||||
# matchinfo(t1, 'x') in order to test 'pcx'.
|
||||
#
|
||||
proc do_matchinfo_test {tn tbl expr results} {
|
||||
|
||||
foreach {fmt res} $results {
|
||||
if {$res == "-"} continue
|
||||
set resarray($fmt) $res
|
||||
}
|
||||
|
||||
set nRow 0
|
||||
foreach {fmt res} [array get resarray] {
|
||||
if {[llength $res]>$nRow} { set nRow [llength $res] }
|
||||
}
|
||||
|
||||
# Construct expected results for any formats for which the caller
|
||||
# supplied result is "-".
|
||||
#
|
||||
foreach {fmt res} $results {
|
||||
if {$res == "-"} {
|
||||
set res [list]
|
||||
for {set iRow 0} {$iRow<$nRow} {incr iRow} {
|
||||
set rowres [list]
|
||||
foreach c [split $fmt ""] {
|
||||
set rowres [concat $rowres [lindex $resarray($c) $iRow]]
|
||||
}
|
||||
lappend res $rowres
|
||||
}
|
||||
set resarray($fmt) $res
|
||||
}
|
||||
}
|
||||
|
||||
# Test each matchinfo() request individually.
|
||||
#
|
||||
foreach {fmt res} [array get resarray] {
|
||||
set sql "SELECT mit(matchinfo($tbl, '$fmt')) FROM $tbl WHERE $expr"
|
||||
do_execsql_test $tn.$fmt $sql [normalize2 $res]
|
||||
}
|
||||
|
||||
# Test them all executed together (multiple invocations of matchinfo()).
|
||||
#
|
||||
set exprlist [list]
|
||||
foreach {format res} [array get resarray] {
|
||||
lappend exprlist "mit(matchinfo($tbl, '$format'))"
|
||||
}
|
||||
set allres [list]
|
||||
for {set iRow 0} {$iRow<$nRow} {incr iRow} {
|
||||
foreach {format res} [array get resarray] {
|
||||
lappend allres [lindex $res $iRow]
|
||||
}
|
||||
}
|
||||
set sql "SELECT [join $exprlist ,] FROM $tbl WHERE $expr"
|
||||
do_execsql_test $tn.multi $sql [normalize2 $allres]
|
||||
}
|
||||
proc normalize2 {list_of_lists} {
|
||||
set res [list]
|
||||
foreach elem $list_of_lists {
|
||||
lappend res [list {*}$elem]
|
||||
}
|
||||
return $res
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 4.1.0 {
|
||||
CREATE VIRTUAL TABLE t4 USING fts4(x, y);
|
||||
INSERT INTO t4 VALUES('a b c d e', 'f g h i j');
|
||||
INSERT INTO t4 VALUES('f g h i j', 'a b c d e');
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.1.1 t4 {t4 MATCH 'a b c'} {
|
||||
p {3 3}
|
||||
c {2 2}
|
||||
x {
|
||||
{1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1}
|
||||
{0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1}
|
||||
}
|
||||
n {2 2}
|
||||
l {{5 5} {5 5}}
|
||||
a {{5 5} {5 5}}
|
||||
|
||||
s {{3 0} {0 3}}
|
||||
|
||||
xxxxxxxxxxxxxxxxxx - pcx - xpc - ccc - pppxpcpcx - laxnpc -
|
||||
xpxsscplax -
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.1.2 t4 {t4 MATCH '"g h i"'} {
|
||||
p {1 1}
|
||||
c {2 2}
|
||||
x {
|
||||
{0 1 1 1 1 1}
|
||||
{1 1 1 0 1 1}
|
||||
}
|
||||
n {2 2}
|
||||
l {{5 5} {5 5}}
|
||||
a {{5 5} {5 5}}
|
||||
|
||||
s {{0 1} {1 0}}
|
||||
|
||||
xxxxxxxxxxxxxxxxxx - pcx - xpc - ccc - pppxpcpcx - laxnpc -
|
||||
sxsxs -
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.1.3 t4 {t4 MATCH 'a b'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_test 4.1.4 t4 {t4 MATCH '"a b" c'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_test 4.1.5 t4 {t4 MATCH 'a "b c"'} { s {{2 0} {0 2}} }
|
||||
do_matchinfo_test 4.1.6 t4 {t4 MATCH 'd d'} { s {{1 0} {0 1}} }
|
||||
|
||||
do_execsql_test 4.2.0 {
|
||||
CREATE VIRTUAL TABLE t5 USING fts4;
|
||||
INSERT INTO t5 VALUES('a a a a a');
|
||||
INSERT INTO t5 VALUES('a b a b a');
|
||||
INSERT INTO t5 VALUES('c b c b c');
|
||||
INSERT INTO t5 VALUES('x x x x x');
|
||||
}
|
||||
do_matchinfo_test 4.2.1 t5 {t5 MATCH 'a a'} {
|
||||
x {{5 8 2 5 8 2} {3 8 2 3 8 2}}
|
||||
s {2 1}
|
||||
}
|
||||
do_matchinfo_test 4.2.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
do_matchinfo_test 4.2.3 t5 {t5 MATCH 'a b a'} { s {3} }
|
||||
do_matchinfo_test 4.2.4 t5 {t5 MATCH 'a a a'} { s {3 1} }
|
||||
do_matchinfo_test 4.2.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
do_matchinfo_test 4.2.6 t5 {t5 MATCH 'a OR b'} { s {1 2 1} }
|
||||
|
||||
do_execsql_test 4.3.0 "INSERT INTO t5 VALUES('x y [string repeat {b } 50000]')";
|
||||
|
||||
do_matchinfo_test 4.3.1 t5 {t5 MATCH 'a a'} {
|
||||
x {{5 8 2 5 5 5} {3 8 2 3 5 5}}
|
||||
s {2 1}
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.3.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
do_matchinfo_test 4.3.3 t5 {t5 MATCH 'a b a'} { s {3} }
|
||||
do_matchinfo_test 4.3.4 t5 {t5 MATCH 'a a a'} { s {3 1} }
|
||||
do_matchinfo_test 4.3.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
do_matchinfo_test 4.3.6 t5 {t5 MATCH 'a OR b'} { s {1 2 1 1} }
|
||||
|
||||
do_execsql_test 4.4.0 {
|
||||
INSERT INTO t5(t5) VALUES('optimize');
|
||||
UPDATE t5_segments
|
||||
SET block = zeroblob(length(block))
|
||||
WHERE length(block)>10000;
|
||||
}
|
||||
|
||||
do_matchinfo_test 4.4.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
do_matchinfo_test 4.4.1 t5 {t5 MATCH 'a a'} { s {2 1} }
|
||||
do_matchinfo_test 4.4.2 t5 {t5 MATCH 'a b'} { s {2} }
|
||||
do_matchinfo_test 4.4.3 t5 {t5 MATCH 'a b a'} { s {3} }
|
||||
do_matchinfo_test 4.4.4 t5 {t5 MATCH 'a a a'} { s {3 1} }
|
||||
do_matchinfo_test 4.4.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} }
|
||||
|
||||
do_execsql_test 4.5.0 {
|
||||
CREATE VIRTUAL TABLE t6 USING fts4(a, b, c);
|
||||
INSERT INTO t6 VALUES('a', 'b', 'c');
|
||||
}
|
||||
do_matchinfo_test 4.5.1 t6 {t6 MATCH 'a b c'} { s {{1 1 1}} }
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check the following restrictions:
|
||||
#
|
||||
# + Matchinfo flags 'a', 'l' and 'n' can only be used with fts4, not fts3.
|
||||
# + Matchinfo flag 'l' cannot be used with matchinfo=fts3.
|
||||
#
|
||||
do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE t7 USING fts3(a, b);
|
||||
INSERT INTO t7 VALUES('u v w', 'x y z');
|
||||
|
||||
CREATE VIRTUAL TABLE t8 USING fts4(a, b, matchinfo=fts3);
|
||||
INSERT INTO t8 VALUES('u v w', 'x y z');
|
||||
}
|
||||
|
||||
do_catchsql_test 5.2.1 {
|
||||
SELECT matchinfo(t7, 'a') FROM t7 WHERE t7 MATCH 'x y'
|
||||
} {1 {unrecognized matchinfo request: a}}
|
||||
do_catchsql_test 5.2.2 {
|
||||
SELECT matchinfo(t7, 'l') FROM t7 WHERE t7 MATCH 'x y'
|
||||
} {1 {unrecognized matchinfo request: l}}
|
||||
do_catchsql_test 5.2.3 {
|
||||
SELECT matchinfo(t7, 'n') FROM t7 WHERE t7 MATCH 'x y'
|
||||
} {1 {unrecognized matchinfo request: n}}
|
||||
|
||||
do_catchsql_test 5.3.1 {
|
||||
SELECT matchinfo(t8, 'l') FROM t8 WHERE t8 MATCH 'x y'
|
||||
} {1 {unrecognized matchinfo request: l}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the offsets() function handles corruption in the %_content
|
||||
# table correctly.
|
||||
#
|
||||
do_execsql_test 6.1 {
|
||||
CREATE VIRTUAL TABLE t9 USING fts4;
|
||||
INSERT INTO t9 VALUES(
|
||||
'this record is used to try to dectect corruption'
|
||||
);
|
||||
SELECT offsets(t9) FROM t9 WHERE t9 MATCH 'to';
|
||||
} {{0 0 20 2 0 0 27 2}}
|
||||
|
||||
do_catchsql_test 6.2 {
|
||||
UPDATE t9_content SET c0content = 'this record is used to';
|
||||
SELECT offsets(t9) FROM t9 WHERE t9 MATCH 'to';
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the outcome of matchinfo() when used within a query that does not
|
||||
# use the full-text index (i.e. lookup by rowid or full-table scan).
|
||||
#
|
||||
do_execsql_test 7.1 {
|
||||
CREATE VIRTUAL TABLE t10 USING fts4;
|
||||
INSERT INTO t10 VALUES('first record');
|
||||
INSERT INTO t10 VALUES('second record');
|
||||
}
|
||||
do_execsql_test 7.2 {
|
||||
SELECT typeof(matchinfo(t10)), length(matchinfo(t10)) FROM t10;
|
||||
} {blob 0 blob 0}
|
||||
do_execsql_test 7.3 {
|
||||
SELECT typeof(matchinfo(t10)), length(matchinfo(t10)) FROM t10 WHERE docid=1;
|
||||
} {blob 0}
|
||||
do_execsql_test 7.4 {
|
||||
SELECT typeof(matchinfo(t10)), length(matchinfo(t10))
|
||||
FROM t10 WHERE t10 MATCH 'record'
|
||||
} {blob 20 blob 20}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test a special case - matchinfo('nxa') with many zero length documents.
|
||||
# Special because "x" internally uses a statement used by both "n" and "a".
|
||||
# This was causing a problem at one point in the obscure case where the
|
||||
# total number of bytes of data stored in an fts3 table was greater than
|
||||
# the number of rows. i.e. when the following query returns true:
|
||||
#
|
||||
# SELECT sum(length(content)) < count(*) FROM fts4table;
|
||||
#
|
||||
do_execsql_test 8.1 {
|
||||
CREATE VIRTUAL TABLE t11 USING fts4;
|
||||
INSERT INTO t11(t11) VALUES('nodesize=24');
|
||||
INSERT INTO t11 VALUES('quitealongstringoftext');
|
||||
INSERT INTO t11 VALUES('anotherquitealongstringoftext');
|
||||
INSERT INTO t11 VALUES('athirdlongstringoftext');
|
||||
INSERT INTO t11 VALUES('andonemoreforgoodluck');
|
||||
}
|
||||
do_test 8.2 {
|
||||
for {set i 0} {$i < 200} {incr i} {
|
||||
execsql { INSERT INTO t11 VALUES('') }
|
||||
}
|
||||
execsql { INSERT INTO t11(t11) VALUES('optimize') }
|
||||
} {}
|
||||
do_execsql_test 8.3 {
|
||||
SELECT mit(matchinfo(t11, 'nxa')) FROM t11 WHERE t11 MATCH 'a*'
|
||||
} {{204 1 3 3 0} {204 1 3 3 0} {204 1 3 3 0}}
|
||||
|
||||
# Corruption related tests.
|
||||
do_execsql_test 8.4.1.1 { UPDATE t11_stat SET value = X'0000'; }
|
||||
do_catchsql_test 8.5.1.2 {
|
||||
SELECT mit(matchinfo(t11, 'nxa')) FROM t11 WHERE t11 MATCH 'a*'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_execsql_test 8.4.2.1 { UPDATE t11_stat SET value = X'00'; }
|
||||
do_catchsql_test 8.5.2.2 {
|
||||
SELECT mit(matchinfo(t11, 'nxa')) FROM t11 WHERE t11 MATCH 'a*'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
do_execsql_test 8.4.3.1 { UPDATE t11_stat SET value = NULL; }
|
||||
do_catchsql_test 8.5.3.2 {
|
||||
SELECT mit(matchinfo(t11, 'nxa')) FROM t11 WHERE t11 MATCH 'a*'
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
+94
-15
@@ -24,6 +24,8 @@ source $testdir/malloc_common.tcl
|
||||
source $testdir/fts3_common.tcl
|
||||
set DO_MALLOC_TEST 0
|
||||
|
||||
set testprefix fts3query
|
||||
|
||||
do_test fts3query-1.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3(x);
|
||||
@@ -104,9 +106,6 @@ do_test fts3query-3.3 {
|
||||
|
||||
# The following tests check that ticket 775b39dd3c has been fixed.
|
||||
#
|
||||
proc eqp {sql} {
|
||||
uplevel [list execsql "EXPLAIN QUERY PLAN $sql"]
|
||||
}
|
||||
do_test fts3query-4.1 {
|
||||
execsql {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
@@ -116,18 +115,98 @@ do_test fts3query-4.1 {
|
||||
CREATE TABLE bt(title);
|
||||
}
|
||||
} {}
|
||||
do_test fts3query-4.2 {
|
||||
eqp "SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date"
|
||||
} {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE ft VIRTUAL TABLE INDEX 1:}}
|
||||
do_test fts3query-4.3 {
|
||||
eqp "SELECT t1.number FROM ft, t1 WHERE t1.number=ft.rowid ORDER BY t1.date"
|
||||
} {0 1 {TABLE t1 WITH INDEX i1 ORDER BY} 1 0 {TABLE ft VIRTUAL TABLE INDEX 1:}}
|
||||
do_test fts3query-4.4 {
|
||||
eqp "SELECT t1.number FROM t1, bt WHERE t1.number=bt.rowid ORDER BY t1.date"
|
||||
} {0 0 {TABLE t1 WITH INDEX i1 ORDER BY} 1 1 {TABLE bt USING PRIMARY KEY}}
|
||||
do_test fts3query-4.5 {
|
||||
eqp "SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date"
|
||||
} {0 1 {TABLE t1 WITH INDEX i1 ORDER BY} 1 0 {TABLE bt USING PRIMARY KEY}}
|
||||
do_eqp_test fts3query-4.2 {
|
||||
SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
|
||||
0 1 1 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)}
|
||||
}
|
||||
do_eqp_test fts3query-4.3 {
|
||||
SELECT t1.number FROM ft, t1 WHERE t1.number=ft.rowid ORDER BY t1.date
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
|
||||
0 1 0 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)}
|
||||
}
|
||||
do_eqp_test fts3query-4.4 {
|
||||
SELECT t1.number FROM t1, bt WHERE t1.number=bt.rowid ORDER BY t1.date
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
|
||||
0 1 1 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
}
|
||||
do_eqp_test fts3query-4.5 {
|
||||
SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
|
||||
0 1 0 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
}
|
||||
|
||||
|
||||
# Test that calling matchinfo() with the wrong number of arguments, or with
|
||||
# an invalid argument returns an error.
|
||||
#
|
||||
do_execsql_test 5.1 {
|
||||
CREATE VIRTUAL TABLE t2 USING FTS4;
|
||||
INSERT INTO t2 VALUES('it was the first time in history');
|
||||
}
|
||||
do_select_tests 5.2 -errorformat {
|
||||
wrong number of arguments to function %s()
|
||||
} {
|
||||
1 "SELECT matchinfo() FROM t2 WHERE t2 MATCH 'history'" matchinfo
|
||||
3 "SELECT snippet(t2, 1, 2, 3, 4, 5, 6) FROM t2 WHERE t2 MATCH 'history'"
|
||||
snippet
|
||||
}
|
||||
do_select_tests 5.3 -errorformat {
|
||||
illegal first argument to %s
|
||||
} {
|
||||
1 "SELECT matchinfo(content) FROM t2 WHERE t2 MATCH 'history'" matchinfo
|
||||
2 "SELECT offsets(content) FROM t2 WHERE t2 MATCH 'history'" offsets
|
||||
3 "SELECT snippet(content) FROM t2 WHERE t2 MATCH 'history'" snippet
|
||||
4 "SELECT optimize(content) FROM t2 WHERE t2 MATCH 'history'" optimize
|
||||
}
|
||||
do_execsql_test 5.4.0 { UPDATE t2_content SET c0content = X'1234' }
|
||||
do_select_tests 5.4 -errorformat {
|
||||
illegal first argument to %s
|
||||
} {
|
||||
1 "SELECT matchinfo(content) FROM t2 WHERE t2 MATCH 'history'" matchinfo
|
||||
2 "SELECT offsets(content) FROM t2 WHERE t2 MATCH 'history'" offsets
|
||||
3 "SELECT snippet(content) FROM t2 WHERE t2 MATCH 'history'" snippet
|
||||
4 "SELECT optimize(content) FROM t2 WHERE t2 MATCH 'history'" optimize
|
||||
}
|
||||
do_catchsql_test 5.5.1 {
|
||||
SELECT matchinfo(t2, 'abc') FROM t2 WHERE t2 MATCH 'history'
|
||||
} {1 {unrecognized matchinfo request: b}}
|
||||
|
||||
do_execsql_test 5.5 { DROP TABLE t2 }
|
||||
|
||||
|
||||
# Test the snippet() function with 1 to 6 arguments.
|
||||
#
|
||||
do_execsql_test 6.1 {
|
||||
CREATE VIRTUAL TABLE t3 USING FTS4(a, b);
|
||||
INSERT INTO t3 VALUES('no gestures', 'another intriguing discovery by observing the hand gestures (called beats) people make while speaking. Research has shown that such gestures do more than add visual emphasis to our words (many people gesture while they''re on the telephone, for example); it seems they actually help our brains find words');
|
||||
}
|
||||
do_select_tests 6.2 {
|
||||
1 "SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'gestures'"
|
||||
{{<b>...</b>hand <b>gestures</b> (called beats) people make while speaking. Research has shown that such <b>gestures</b> do<b>...</b>}}
|
||||
|
||||
2 "SELECT snippet(t3, 'XXX') FROM t3 WHERE t3 MATCH 'gestures'"
|
||||
{{<b>...</b>hand XXXgestures</b> (called beats) people make while speaking. Research has shown that such XXXgestures</b> do<b>...</b>}}
|
||||
|
||||
3 "SELECT snippet(t3, 'XXX', 'YYY') FROM t3 WHERE t3 MATCH 'gestures'"
|
||||
{{<b>...</b>hand XXXgesturesYYY (called beats) people make while speaking. Research has shown that such XXXgesturesYYY do<b>...</b>}}
|
||||
|
||||
4 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ') FROM t3 WHERE t3 MATCH 'gestures'"
|
||||
{{ZZZhand XXXgesturesYYY (called beats) people make while speaking. Research has shown that such XXXgesturesYYY doZZZ}}
|
||||
|
||||
5 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 1) FROM t3 WHERE t3 MATCH 'gestures'"
|
||||
{{ZZZhand XXXgesturesYYY (called beats) people make while speaking. Research has shown that such XXXgesturesYYY doZZZ}}
|
||||
|
||||
6 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 0) FROM t3 WHERE t3 MATCH 'gestures'"
|
||||
{{no XXXgesturesYYY}}
|
||||
|
||||
7 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 1, 5) FROM t3 WHERE t3 MATCH 'gestures'"
|
||||
{{ZZZthe hand XXXgesturesYYY (called beatsZZZ}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+4
-1
@@ -306,6 +306,10 @@ foreach nodesize {50 500 1000 2000} {
|
||||
delete_row $iDelete
|
||||
if {0==($iTest%2)} { execsql COMMIT }
|
||||
|
||||
if {0==($iTest%2)} {
|
||||
do_test fts3rnd-1.$nodesize.$iTest.0 { fts3_integrity_check t1 } ok
|
||||
}
|
||||
|
||||
# Pick 10 terms from the vocabulary. Check that the results of querying
|
||||
# the database for the set of documents containing each of these terms
|
||||
# is the same as the result obtained by scanning the contents of the Tcl
|
||||
@@ -379,7 +383,6 @@ foreach nodesize {50 500 1000 2000} {
|
||||
set terms [list [random_term] [random_term] [random_term]]
|
||||
set nNear 11
|
||||
set match [join $terms " NEAR/$nNear "]
|
||||
set fts3 [execsql { SELECT docid FROM t1 WHERE t1 MATCH $match }]
|
||||
do_select_test fts3rnd-1.$nodesize.$iTest.7.$i {
|
||||
SELECT docid FROM t1 WHERE t1 MATCH $match
|
||||
} [simple_near $terms $nNear]
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# 2010 September 17
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !fts3||!shared_cache {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
db close
|
||||
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
|
||||
# Open two connections to the database in shared-cache mode.
|
||||
#
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db
|
||||
|
||||
# Create a virtual FTS3 table. Populate it with some initial data.
|
||||
#
|
||||
do_execsql_test fts3shared-1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts3(x);
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('We listened and looked sideways up!');
|
||||
INSERT INTO t1 VALUES('Fear at my heart, as at a cup,');
|
||||
INSERT INTO t1 VALUES('My life-blood seemed to sip!');
|
||||
INSERT INTO t1 VALUES('The stars were dim, and thick the night');
|
||||
COMMIT;
|
||||
} {}
|
||||
|
||||
# Open a write transaction and insert rows into the FTS3 table. This takes
|
||||
# a write-lock on the underlying t1_content table.
|
||||
#
|
||||
do_execsql_test fts3shared-1.2 {
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES('The steersman''s face by his lamp gleamed white;');
|
||||
} {}
|
||||
|
||||
# Now try a SELECT on the full-text table. This particular SELECT does not
|
||||
# read data from the %_content table. But it still attempts to obtain a lock
|
||||
# on that table and so the SELECT fails.
|
||||
#
|
||||
do_test fts3shared-1.3 {
|
||||
catchsql {
|
||||
BEGIN;
|
||||
SELECT rowid FROM t1 WHERE t1 MATCH 'stars'
|
||||
} db2
|
||||
} {1 {database table is locked}}
|
||||
|
||||
# Verify that the first connection can commit its transaction.
|
||||
#
|
||||
do_test fts3shared-1.4 { sqlite3_get_autocommit db } 0
|
||||
do_execsql_test fts3shared-1.5 { COMMIT } {}
|
||||
do_test fts3shared-1.6 { sqlite3_get_autocommit db } 1
|
||||
|
||||
# Verify that the second connection still has an open transaction.
|
||||
#
|
||||
do_test fts3shared-1.6 { sqlite3_get_autocommit db2 } 0
|
||||
|
||||
db close
|
||||
db2 close
|
||||
|
||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||
finish_test
|
||||
|
||||
@@ -20,7 +20,6 @@ source $testdir/tester.tcl
|
||||
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
|
||||
ifcapable !fts3 { finish_test ; return }
|
||||
source $testdir/fts3_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
set sqlite_fts3_enable_parentheses 1
|
||||
set DO_MALLOC_TEST 0
|
||||
@@ -433,10 +432,10 @@ foreach {DO_MALLOC_TEST enc} {
|
||||
{2 2 1 3 3 3 6 3 0 0 0 2 3 2}
|
||||
}]
|
||||
|
||||
# EVIDENCE-OF: R-56101-59725 If used within a SELECT that uses the
|
||||
# "query by rowid" or "linear scan" strategies, then the snippet and
|
||||
# offsets both return an an empty string, and the matchinfo function
|
||||
# returns a blob value zero bytes in size.
|
||||
# EVIDENCE-OF: R-40630-02268 If used within a SELECT that uses the
|
||||
# "query by rowid" or "linear scan" strategies, then the snippet and
|
||||
# offsets both return an empty string, and the matchinfo function
|
||||
# returns a blob value zero bytes in size.
|
||||
#
|
||||
set r 1000000 ;# A rowid that exists in table ft
|
||||
do_select_test $T.10.0 { SELECT rowid FROM ft WHERE rowid = $r } $r
|
||||
@@ -460,6 +459,5 @@ foreach {DO_MALLOC_TEST enc} {
|
||||
} {0 blob}
|
||||
}
|
||||
|
||||
|
||||
set sqlite_fts3_enable_parentheses 0
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
# 2010 August 27
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this file is testing that destructor functions associated
|
||||
# with functions created using sqlite3_create_function_v2() is
|
||||
# correctly invoked.
|
||||
#
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
|
||||
do_test func3-1.1 {
|
||||
set destroyed 0
|
||||
proc destroy {} { set ::destroyed 1 }
|
||||
sqlite3_create_function_v2 db f2 -1 any -func f2 -destroy destroy
|
||||
set destroyed
|
||||
} 0
|
||||
do_test func3-1.2 {
|
||||
sqlite3_create_function_v2 db f2 -1 utf8 -func f2
|
||||
set destroyed
|
||||
} 0
|
||||
do_test func3-1.3 {
|
||||
sqlite3_create_function_v2 db f2 -1 utf16le -func f2
|
||||
set destroyed
|
||||
} 0
|
||||
do_test func3-1.4 {
|
||||
sqlite3_create_function_v2 db f2 -1 utf16be -func f2
|
||||
set destroyed
|
||||
} 1
|
||||
|
||||
do_test func3-2.1 {
|
||||
set destroyed 0
|
||||
proc destroy {} { set ::destroyed 1 }
|
||||
sqlite3_create_function_v2 db f3 -1 utf8 -func f3 -destroy destroy
|
||||
set destroyed
|
||||
} 0
|
||||
do_test func3-2.2 {
|
||||
sqlite3_create_function_v2 db f3 -1 utf8 -func f3
|
||||
set destroyed
|
||||
} 1
|
||||
|
||||
do_test func3-3.1 {
|
||||
set destroyed 0
|
||||
proc destroy {} { set ::destroyed 1 }
|
||||
sqlite3_create_function_v2 db f3 -1 any -func f3 -destroy destroy
|
||||
set destroyed
|
||||
} 0
|
||||
do_test func3-3.2 {
|
||||
db close
|
||||
set destroyed
|
||||
} 1
|
||||
|
||||
sqlite3 db test.db
|
||||
do_test func3-4.1 {
|
||||
set destroyed 0
|
||||
set rc [catch {
|
||||
sqlite3_create_function_v2 db f3 -1 any -func f3 -step f3 -destroy destroy
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
do_test func3-4.2 { set destroyed } 1
|
||||
|
||||
finish_test
|
||||
+33
-4
@@ -404,33 +404,62 @@ do_test in-12.5 {
|
||||
do_test in-12.6 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 UNION ALL SELECT a, b FROM t2
|
||||
SELECT a, b FROM t3 UNION ALL SELECT a FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
|
||||
do_test in-12.7 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 UNION SELECT a, b FROM t2
|
||||
SELECT a, b FROM t3 UNION SELECT a FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {SELECTs to the left and right of UNION do not have the same number of result columns}}
|
||||
do_test in-12.8 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 EXCEPT SELECT a, b FROM t2
|
||||
SELECT a, b FROM t3 EXCEPT SELECT a FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {SELECTs to the left and right of EXCEPT do not have the same number of result columns}}
|
||||
do_test in-12.9 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 INTERSECT SELECT a, b FROM t2
|
||||
SELECT a, b FROM t3 INTERSECT SELECT a FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}}
|
||||
}
|
||||
|
||||
do_test in-12.10 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 UNION ALL SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
do_test in-12.11 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 UNION SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
do_test in-12.12 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 EXCEPT SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
do_test in-12.13 {
|
||||
catchsql {
|
||||
SELECT * FROM t2 WHERE a IN (
|
||||
SELECT a FROM t3 INTERSECT SELECT a, b FROM t2
|
||||
);
|
||||
}
|
||||
} {1 {only a single result allowed for a SELECT that is part of an expression}}
|
||||
|
||||
|
||||
#------------------------------------------------------------------------
|
||||
# The following tests check that NULL is handled correctly when it
|
||||
|
||||
+96
-79
@@ -420,86 +420,94 @@ unset -nocomplain ::text
|
||||
# after the transaction is commited and the blob channel
|
||||
# closed.
|
||||
#
|
||||
sqlite3_soft_heap_limit 0
|
||||
do_test incrblob-6.1 {
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection');
|
||||
} db2
|
||||
} {}
|
||||
do_test incrblob-6.2 {
|
||||
execsql {
|
||||
SELECT rowid FROM blobs
|
||||
}
|
||||
} {1 2 3}
|
||||
do_test incrblob-6.3 {
|
||||
set rc [catch {
|
||||
db incrblob blobs v 1
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {database is locked}}
|
||||
do_test incrblob-6.4 {
|
||||
set rc [catch {
|
||||
db incrblob blobs v 3
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {database is locked}}
|
||||
do_test incrblob-6.5 {
|
||||
set ::blob [db incrblob -readonly blobs v 3]
|
||||
read $::blob
|
||||
} {hello}
|
||||
do_test incrblob-6.6 {
|
||||
close $::blob
|
||||
} {}
|
||||
|
||||
do_test incrblob-6.7 {
|
||||
set ::blob [db2 incrblob blobs i 4]
|
||||
gets $::blob
|
||||
} {connection}
|
||||
do_test incrblob-6.8 {
|
||||
tell $::blob
|
||||
} {10}
|
||||
do_test incrblob-6.9 {
|
||||
seek $::blob 0
|
||||
puts -nonewline $::blob "invocation"
|
||||
flush $::blob
|
||||
} {}
|
||||
|
||||
# At this point rollback should be illegal (because
|
||||
# there is an open blob channel). But commit is also illegal because
|
||||
# the open blob is read-write.
|
||||
# This test does not work with the "memsubsys1" configuration.
|
||||
# Permutation memsubsys1 configures a very small static allocation
|
||||
# for use as page-cache memory. This causes SQLite to upgrade
|
||||
# to an exclusive lock when writing earlier than usual, which
|
||||
# makes some of these tests fail.
|
||||
#
|
||||
do_test incrblob-6.10 {
|
||||
catchsql {
|
||||
ROLLBACK;
|
||||
} db2
|
||||
} {1 {cannot rollback transaction - SQL statements in progress}}
|
||||
do_test incrblob-6.11 {
|
||||
catchsql {
|
||||
COMMIT;
|
||||
} db2
|
||||
} {1 {cannot commit transaction - SQL statements in progress}}
|
||||
|
||||
do_test incrblob-6.12 {
|
||||
execsql {
|
||||
SELECT * FROM blobs WHERE rowid = 4;
|
||||
}
|
||||
} {}
|
||||
do_test incrblob-6.13 {
|
||||
close $::blob
|
||||
} {}
|
||||
do_test incrblob-6.14 {
|
||||
catchsql {
|
||||
COMMIT;
|
||||
} db2
|
||||
} {0 {}}
|
||||
do_test incrblob-6.15 {
|
||||
execsql {
|
||||
SELECT * FROM blobs WHERE rowid = 4;
|
||||
}
|
||||
} {a different invocation}
|
||||
db2 close
|
||||
sqlite3_soft_heap_limit 0
|
||||
if {[permutation] != "memsubsys1"} {
|
||||
do_test incrblob-6.1 {
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection');
|
||||
} db2
|
||||
} {}
|
||||
do_test incrblob-6.2 {
|
||||
execsql {
|
||||
SELECT rowid FROM blobs
|
||||
}
|
||||
} {1 2 3}
|
||||
do_test incrblob-6.3 {
|
||||
set rc [catch {
|
||||
db incrblob blobs v 1
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {database is locked}}
|
||||
do_test incrblob-6.4 {
|
||||
set rc [catch {
|
||||
db incrblob blobs v 3
|
||||
} msg]
|
||||
list $rc $msg
|
||||
} {1 {database is locked}}
|
||||
do_test incrblob-6.5 {
|
||||
set ::blob [db incrblob -readonly blobs v 3]
|
||||
read $::blob
|
||||
} {hello}
|
||||
do_test incrblob-6.6 {
|
||||
close $::blob
|
||||
} {}
|
||||
|
||||
do_test incrblob-6.7 {
|
||||
set ::blob [db2 incrblob blobs i 4]
|
||||
gets $::blob
|
||||
} {connection}
|
||||
do_test incrblob-6.8 {
|
||||
tell $::blob
|
||||
} {10}
|
||||
do_test incrblob-6.9 {
|
||||
seek $::blob 0
|
||||
puts -nonewline $::blob "invocation"
|
||||
flush $::blob
|
||||
} {}
|
||||
|
||||
# At this point rollback should be illegal (because
|
||||
# there is an open blob channel). But commit is also illegal because
|
||||
# the open blob is read-write.
|
||||
#
|
||||
do_test incrblob-6.10 {
|
||||
catchsql {
|
||||
ROLLBACK;
|
||||
} db2
|
||||
} {1 {cannot rollback transaction - SQL statements in progress}}
|
||||
do_test incrblob-6.11 {
|
||||
catchsql {
|
||||
COMMIT;
|
||||
} db2
|
||||
} {1 {cannot commit transaction - SQL statements in progress}}
|
||||
|
||||
do_test incrblob-6.12 {
|
||||
execsql {
|
||||
SELECT * FROM blobs WHERE rowid = 4;
|
||||
}
|
||||
} {}
|
||||
do_test incrblob-6.13 {
|
||||
close $::blob
|
||||
} {}
|
||||
do_test incrblob-6.14 {
|
||||
catchsql {
|
||||
COMMIT;
|
||||
} db2
|
||||
} {0 {}}
|
||||
do_test incrblob-6.15 {
|
||||
execsql {
|
||||
SELECT * FROM blobs WHERE rowid = 4;
|
||||
}
|
||||
} {a different invocation}
|
||||
db2 close
|
||||
}
|
||||
sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit)
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
@@ -669,5 +677,14 @@ do_test incrblob-8.7 {
|
||||
execsql {SELECT b FROM t1 WHERE a = 314159}
|
||||
} {etilqs}
|
||||
|
||||
# The following test case exposes an instance in the blob code where
|
||||
# an error message was set using a call similar to sqlite3_mprintf(zErr),
|
||||
# where zErr is an arbitrary string. This is no good if the string contains
|
||||
# characters that can be mistaken for printf() formatting directives.
|
||||
#
|
||||
do_test incrblob-9.1 {
|
||||
list [catch { db incrblob t1 "A tricky column name %s%s" 1 } msg] $msg
|
||||
} {1 {no such column: "A tricky column name %s%s"}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
# 2010 October 20
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
|
||||
do_execsql_test incrblob3-1.1 {
|
||||
CREATE TABLE blobs(k INTEGER PRIMARY KEY, v BLOB);
|
||||
INSERT INTO blobs VALUES(1, zeroblob(100));
|
||||
INSERT INTO blobs VALUES(2, zeroblob(100));
|
||||
} {}
|
||||
|
||||
# Test the sqlite3_blob_reopen()/read()/write() functions.
|
||||
#
|
||||
do_test incrblob3-1.2 {
|
||||
set ::blob [db incrblob blobs v 1]
|
||||
puts $::blob "hello world"
|
||||
} {}
|
||||
|
||||
do_test incrblob3-1.3 {
|
||||
sqlite3_blob_reopen $::blob 2
|
||||
puts $::blob "world hello"
|
||||
} {}
|
||||
|
||||
do_test incrblob3-1.4 {
|
||||
sqlite3_blob_reopen $::blob 1
|
||||
gets $::blob
|
||||
} {hello world}
|
||||
|
||||
do_test incrblob3-1.5 {
|
||||
sqlite3_blob_reopen $::blob 2
|
||||
gets $::blob
|
||||
} {world hello}
|
||||
|
||||
do_test incrblob3-1.6 { close $::blob } {}
|
||||
|
||||
# Test some error conditions.
|
||||
#
|
||||
# incrblob3-2.1: Attempting to reopen a row that does not exist.
|
||||
# incrblob3-2.2: Attempting to reopen a row that does not contain a blob
|
||||
# or text value.
|
||||
#
|
||||
do_test incrblob3-2.1.1 {
|
||||
set ::blob [db incrblob blobs v 1]
|
||||
list [catch {sqlite3_blob_reopen $::blob 3} msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test incrblob3-2.1.2 {
|
||||
list [sqlite3_errcode db] [sqlite3_errmsg db]
|
||||
} {SQLITE_ERROR {no such rowid: 3}}
|
||||
do_test incrblob3-2.1.3 {
|
||||
list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test incrblob3-2.1.4 { close $::blob } {}
|
||||
|
||||
do_execsql_test incrblob3-2.2.1 {
|
||||
INSERT INTO blobs VALUES(3, 42);
|
||||
INSERT INTO blobs VALUES(4, 54.4);
|
||||
INSERT INTO blobs VALUES(5, NULL);
|
||||
}
|
||||
foreach {tn rowid type} {
|
||||
1 3 integer
|
||||
2 4 real
|
||||
3 5 null
|
||||
} {
|
||||
do_test incrblob3-2.2.$tn.1 {
|
||||
set ::blob [db incrblob blobs v 1]
|
||||
list [catch {sqlite3_blob_reopen $::blob $rowid} msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test incrblob3-2.2.$tn.2 {
|
||||
list [sqlite3_errcode db] [sqlite3_errmsg db]
|
||||
} "SQLITE_ERROR {cannot open value of type $type}"
|
||||
|
||||
do_test incrblob3-2.2.$tn.3 {
|
||||
list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test incrblob3-2.2.$tn.4 {
|
||||
list [catch {sqlite3_blob_read $::blob 0 10} msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test incrblob3-2.2.$tn.5 {
|
||||
list [catch {sqlite3_blob_write $::blob 0 "abcd"} msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
do_test incrblob3-2.2.$tn.6 {
|
||||
sqlite3_blob_bytes $::blob
|
||||
} {0}
|
||||
|
||||
do_test incrblob3-2.2.$tn.7 { close $::blob } {}
|
||||
}
|
||||
|
||||
# Test that passing NULL to sqlite3_blob_XXX() APIs returns SQLITE_MISUSE.
|
||||
#
|
||||
# incrblob3-3.1: sqlite3_blob_reopen()
|
||||
# incrblob3-3.2: sqlite3_blob_read()
|
||||
# incrblob3-3.3: sqlite3_blob_write()
|
||||
# incrblob3-3.4: sqlite3_blob_bytes()
|
||||
#
|
||||
do_test incrblob3-3.1 {
|
||||
list [catch {sqlite3_blob_reopen {} 3} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
|
||||
do_test incrblob3-3.2 {
|
||||
list [catch {sqlite3_blob_read {} 0 10} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
|
||||
do_test incrblob3-3.3 {
|
||||
list [catch {sqlite3_blob_write {} 0 "abcd"} msg] $msg
|
||||
} {1 SQLITE_MISUSE}
|
||||
|
||||
do_test incrblob3-3.4 { sqlite3_blob_bytes {} } {0}
|
||||
|
||||
do_test incrblob3-3.5 { sqlite3_blob_close {} } {}
|
||||
|
||||
# Test out-of-range reading and writing
|
||||
#
|
||||
do_test incrblob3-4.1 {
|
||||
set ::blob [db incrblob blobs v 1]
|
||||
sqlite3_blob_bytes $::blob
|
||||
} {100}
|
||||
do_test incrblob3-4.2 {
|
||||
list [catch { sqlite3_blob_read $::blob -1 10 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test incrblob3-4.3 {
|
||||
list [catch { sqlite3_blob_read $::blob 0 -10 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test incrblob3-4.4 {
|
||||
list [catch { sqlite3_blob_read $::blob 95 10 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test incrblob3-4.5 {
|
||||
list [catch { sqlite3_blob_write $::blob -1 "abcdefghij" 10 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test incrblob3-4.6 {
|
||||
list [catch { sqlite3_blob_write $::blob 0 "abcdefghij" -10 } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test incrblob3-4.7 {
|
||||
list [catch { sqlite3_blob_write $::blob 95 "abcdefghij" } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
do_test incrblob3-4.8 { close $::blob } {}
|
||||
|
||||
# Test that modifying the row a blob handle points to aborts the blob.
|
||||
#
|
||||
do_test incrblob3-5.1 {
|
||||
set ::blob [db incrblob blobs v 1]
|
||||
sqlite3_blob_bytes $::blob
|
||||
} {100}
|
||||
do_test incrblob3-5.2 {
|
||||
execsql { UPDATE blobs SET v = '123456789012345678901234567890' WHERE k = 1 }
|
||||
list [catch { sqlite3_blob_read $::blob 0 10 } msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
|
||||
# Test various errors that can occur in sqlite3_blob_open():
|
||||
#
|
||||
# 1. Trying to open a virtual table column.
|
||||
# 2. Trying to open a view column.
|
||||
# 3. Trying to open a column that does not exist.
|
||||
# 4. Trying to open a read/write handle on an indexed column.
|
||||
# 5. Trying to open a read/write handle on the child key of an FK constraint.
|
||||
#
|
||||
ifcapable fts3 {
|
||||
do_test incrblob3-6.1 {
|
||||
execsql {
|
||||
CREATE VIRTUAL TABLE ft USING fts3;
|
||||
INSERT INTO ft VALUES('rules to open a column to which');
|
||||
}
|
||||
|
||||
list [catch { db incrblob ft content 1 } msg] $msg
|
||||
} {1 {cannot open virtual table: ft}}
|
||||
}
|
||||
ifcapable view {
|
||||
do_test incrblob3-6.2 {
|
||||
execsql { CREATE VIEW v1 AS SELECT * FROM blobs }
|
||||
list [catch { db incrblob v1 content 1 } msg] $msg
|
||||
} {1 {cannot open view: v1}}
|
||||
}
|
||||
|
||||
do_test incrblob3-6.3 {
|
||||
list [catch { db incrblob blobs content 1 } msg] $msg
|
||||
} {1 {no such column: "content"}}
|
||||
|
||||
do_test incrblob3-6.4.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(b);
|
||||
INSERT INTO t1 VALUES(zeroblob(100), zeroblob(100));
|
||||
}
|
||||
list [catch { db incrblob t1 b 1 } msg] $msg
|
||||
} {1 {cannot open indexed column for writing}}
|
||||
do_test incrblob3-6.4.2 {
|
||||
set ::blob [db incrblob t1 a 1]
|
||||
close $::blob
|
||||
} {}
|
||||
do_test incrblob3-6.4.3 {
|
||||
set ::blob [db incrblob -readonly t1 b 1]
|
||||
close $::blob
|
||||
} {}
|
||||
|
||||
do_test incrblob3-6.5.1 {
|
||||
execsql {
|
||||
CREATE TABLE p1(a PRIMARY KEY);
|
||||
CREATE TABLE c1(a, b REFERENCES p1);
|
||||
PRAGMA foreign_keys = 1;
|
||||
INSERT INTO p1 VALUES(zeroblob(100));
|
||||
INSERT INTO c1 VALUES(zeroblob(100), zeroblob(100));
|
||||
}
|
||||
list [catch { db incrblob c1 b 1 } msg] $msg
|
||||
} {1 {cannot open foreign key column for writing}}
|
||||
|
||||
do_test incrblob3-6.5.2 {
|
||||
set ::blob [db incrblob c1 a 1]
|
||||
close $::blob
|
||||
} {}
|
||||
do_test incrblob3-6.5.3 {
|
||||
set ::blob [db incrblob -readonly c1 b 1]
|
||||
close $::blob
|
||||
} {}
|
||||
do_test incrblob3-6.5.4 {
|
||||
execsql { PRAGMA foreign_keys = 0 }
|
||||
set ::blob [db incrblob c1 b 1]
|
||||
close $::blob
|
||||
} {}
|
||||
|
||||
|
||||
# Test that sqlite3_blob_open() handles transient and persistent schema
|
||||
# errors correctly.
|
||||
#
|
||||
do_test incrblob3-7.1 {
|
||||
sqlite3 db2 test.db
|
||||
sqlite3_db_config_lookaside db2 0 0 0
|
||||
execsql { CREATE TABLE t2(x) } db2
|
||||
set ::blob [db incrblob blobs v 1]
|
||||
close $::blob
|
||||
} {}
|
||||
db2 close
|
||||
|
||||
testvfs tvfs -default 1
|
||||
tvfs filter xAccess
|
||||
tvfs script access_method
|
||||
|
||||
proc access_method {args} {
|
||||
set schemacookie [hexio_get_int [hexio_read test.db 40 4]]
|
||||
incr schemacookie
|
||||
hexio_write test.db 40 [hexio_render_int32 $schemacookie]
|
||||
|
||||
set dbversion [hexio_get_int [hexio_read test.db 24 4]]
|
||||
incr dbversion
|
||||
hexio_write test.db 24 [hexio_render_int32 $dbversion]
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
do_test incrblob3-7.2 {
|
||||
sqlite3 db test.db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
list [catch {db incrblob blobs v 1} msg] $msg
|
||||
} {1 {database schema has changed}}
|
||||
db close
|
||||
tvfs delete
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# 2010 October 26
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
set testprefix incrblobfault
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE blob(x INTEGER PRIMARY KEY, v BLOB);
|
||||
INSERT INTO blob VALUES(1, 'hello world');
|
||||
INSERT INTO blob VALUES(2, 'world hello');
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
INSERT INTO blob SELECT NULL, v FROM blob;
|
||||
}
|
||||
|
||||
do_faultsim_test 1 -prep {
|
||||
sqlite3 db test.db
|
||||
set ::blob [db incrblob blob v 1]
|
||||
} -body {
|
||||
if {[catch {sqlite3_blob_reopen $::blob 1000}]} {
|
||||
error [sqlite3_errmsg db]
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
close $::blob
|
||||
}
|
||||
|
||||
do_faultsim_test 2 -prep {
|
||||
sqlite3 db test.db
|
||||
set ::blob [db incrblob blob v 1]
|
||||
} -body {
|
||||
if {[catch {sqlite3_blob_reopen $::blob -1}]} {
|
||||
error [sqlite3_errmsg db]
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {1 {no such rowid: -1}}
|
||||
close $::blob
|
||||
}
|
||||
|
||||
do_faultsim_test 3 -prep {
|
||||
sqlite3 db test.db
|
||||
} -body {
|
||||
set ::blob [db incrblob blob v 1]
|
||||
gets $::blob
|
||||
} -test {
|
||||
faultsim_test_result {0 {hello world}}
|
||||
catch { close $::blob }
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+12
-3
@@ -532,10 +532,19 @@ do_test index-15.2 {
|
||||
INSERT INTO t1 VALUES('+',9);
|
||||
INSERT INTO t1 VALUES('+12347.E+02',10);
|
||||
INSERT INTO t1 VALUES('+12347E+02',11);
|
||||
SELECT b FROM t1 ORDER BY a;
|
||||
INSERT INTO t1 VALUES('+.125E+04',12);
|
||||
INSERT INTO t1 VALUES('-.125E+04',13);
|
||||
INSERT INTO t1 VALUES('.125E+0',14);
|
||||
INSERT INTO t1 VALUES('.125',15);
|
||||
SELECT b FROM t1 ORDER BY a, b;
|
||||
}
|
||||
} {8 5 2 1 3 6 11 9 10 4 7}
|
||||
integrity_check index-15.1
|
||||
} {13 14 15 12 8 5 2 1 3 6 10 11 9 4 7}
|
||||
do_test index-15.3 {
|
||||
execsql {
|
||||
SELECT b FROM t1 WHERE typeof(a) IN ('integer','real') ORDER BY b;
|
||||
}
|
||||
} {1 2 3 5 6 8 10 11 12 13 14 15}
|
||||
integrity_check index-15.4
|
||||
|
||||
# The following tests - index-16.* - test that when a table definition
|
||||
# includes qualifications that specify the same constraint twice only a
|
||||
|
||||
+88
-75
@@ -40,15 +40,18 @@ proc EQP {sql} {
|
||||
|
||||
# These tests are to check that "EXPLAIN QUERY PLAN" is working as expected.
|
||||
#
|
||||
do_test indexedby-1.2 {
|
||||
EQP { select * from t1 WHERE a = 10; }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-1.3 {
|
||||
EQP { select * from t1 ; }
|
||||
} {0 0 {TABLE t1}}
|
||||
do_test indexedby-1.4 {
|
||||
EQP { select * from t1, t2 WHERE c = 10; }
|
||||
} {0 1 {TABLE t2 WITH INDEX i3} 1 0 {TABLE t1}}
|
||||
do_execsql_test indexedby-1.2 {
|
||||
EXPLAIN QUERY PLAN select * from t1 WHERE a = 10;
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}}
|
||||
do_execsql_test indexedby-1.3 {
|
||||
EXPLAIN QUERY PLAN select * from t1 ;
|
||||
} {0 0 0 {SCAN TABLE t1 (~1000000 rows)}}
|
||||
do_execsql_test indexedby-1.4 {
|
||||
EXPLAIN QUERY PLAN select * from t1, t2 WHERE c = 10;
|
||||
} {
|
||||
0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)}
|
||||
0 1 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
}
|
||||
|
||||
# Parser tests. Test that an INDEXED BY or NOT INDEX clause can be
|
||||
# attached to a table in the FROM clause, but not to a sub-select or
|
||||
@@ -80,15 +83,17 @@ do_test indexedby-2.7 {
|
||||
|
||||
# Tests for single table cases.
|
||||
#
|
||||
do_test indexedby-3.1 {
|
||||
EQP { SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two'}
|
||||
} {0 0 {TABLE t1}}
|
||||
do_test indexedby-3.2 {
|
||||
EQP { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'}
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-3.3 {
|
||||
EQP { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two'}
|
||||
} {0 0 {TABLE t1 WITH INDEX i2}}
|
||||
do_execsql_test indexedby-3.1 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two'
|
||||
} {0 0 0 {SCAN TABLE t1 (~10000 rows)}}
|
||||
do_execsql_test indexedby-3.2 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}}
|
||||
do_execsql_test indexedby-3.3 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two'
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}}
|
||||
do_test indexedby-3.4 {
|
||||
catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' }
|
||||
} {1 {cannot use index: i2}}
|
||||
@@ -102,12 +107,14 @@ do_test indexedby-3.7 {
|
||||
catchsql { SELECT * FROM t1 INDEXED BY i1 ORDER BY a }
|
||||
} {0 {}}
|
||||
|
||||
do_test indexedby-3.8 {
|
||||
EQP { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e }
|
||||
} {0 0 {TABLE t3 WITH INDEX sqlite_autoindex_t3_1 ORDER BY}}
|
||||
do_test indexedby-3.9 {
|
||||
EQP { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10 }
|
||||
} {0 0 {TABLE t3 WITH INDEX sqlite_autoindex_t3_1}}
|
||||
do_execsql_test indexedby-3.8 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e
|
||||
} {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1 (~1000000 rows)}}
|
||||
do_execsql_test indexedby-3.9 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10
|
||||
} {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?) (~1 rows)}}
|
||||
do_test indexedby-3.10 {
|
||||
catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 }
|
||||
} {1 {cannot use index: sqlite_autoindex_t3_1}}
|
||||
@@ -117,12 +124,18 @@ do_test indexedby-3.11 {
|
||||
|
||||
# Tests for multiple table cases.
|
||||
#
|
||||
do_test indexedby-4.1 {
|
||||
EQP { SELECT * FROM t1, t2 WHERE a = c }
|
||||
} {0 0 {TABLE t1} 1 1 {TABLE t2 WITH INDEX i3}}
|
||||
do_test indexedby-4.2 {
|
||||
EQP { SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c }
|
||||
} {0 1 {TABLE t2} 1 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_execsql_test indexedby-4.1 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE a = c
|
||||
} {
|
||||
0 0 0 {SCAN TABLE t1 (~1000000 rows)}
|
||||
0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)}
|
||||
}
|
||||
do_execsql_test indexedby-4.2 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c
|
||||
} {
|
||||
0 0 1 {SCAN TABLE t2 (~1000000 rows)}
|
||||
0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
|
||||
}
|
||||
do_test indexedby-4.3 {
|
||||
catchsql {
|
||||
SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c
|
||||
@@ -138,15 +151,13 @@ do_test indexedby-4.4 {
|
||||
# also tests that nothing bad happens if an index refered to by
|
||||
# a CREATE VIEW statement is dropped and recreated.
|
||||
#
|
||||
do_test indexedby-5.1 {
|
||||
execsql {
|
||||
CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5;
|
||||
}
|
||||
EQP { SELECT * FROM v2 }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-5.2 {
|
||||
EQP { SELECT * FROM v2 WHERE b = 10 }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_execsql_test indexedby-5.1 {
|
||||
CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5;
|
||||
EXPLAIN QUERY PLAN SELECT * FROM v2
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~330000 rows)}}
|
||||
do_execsql_test indexedby-5.2 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~33000 rows)}}
|
||||
do_test indexedby-5.3 {
|
||||
execsql { DROP INDEX i1 }
|
||||
catchsql { SELECT * FROM v2 }
|
||||
@@ -165,51 +176,53 @@ do_test indexedby-5.5 {
|
||||
|
||||
# Test that "NOT INDEXED" may use the rowid index, but not others.
|
||||
#
|
||||
do_test indexedby-6.1 {
|
||||
EQP { SELECT * FROM t1 WHERE b = 10 ORDER BY rowid }
|
||||
} {0 0 {TABLE t1 WITH INDEX i2 ORDER BY}}
|
||||
do_test indexedby-6.2 {
|
||||
EQP { SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid }
|
||||
} {0 0 {TABLE t1 USING PRIMARY KEY ORDER BY}}
|
||||
do_execsql_test indexedby-6.1 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 10 ORDER BY rowid
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}}
|
||||
do_execsql_test indexedby-6.2 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid
|
||||
} {0 0 0 {SCAN TABLE t1 USING INTEGER PRIMARY KEY (~100000 rows)}}
|
||||
|
||||
# Test that "INDEXED BY" can be used in a DELETE statement.
|
||||
#
|
||||
do_test indexedby-7.1 {
|
||||
EQP { DELETE FROM t1 WHERE a = 5 }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-7.2 {
|
||||
EQP { DELETE FROM t1 NOT INDEXED WHERE a = 5 }
|
||||
} {0 0 {TABLE t1}}
|
||||
do_test indexedby-7.3 {
|
||||
EQP { DELETE FROM t1 INDEXED BY i1 WHERE a = 5 }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-7.4 {
|
||||
EQP { DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10}
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-7.5 {
|
||||
EQP { DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10}
|
||||
} {0 0 {TABLE t1 WITH INDEX i2}}
|
||||
do_execsql_test indexedby-7.1 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5
|
||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
|
||||
do_execsql_test indexedby-7.2 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5
|
||||
} {0 0 0 {SCAN TABLE t1 (~100000 rows)}}
|
||||
do_execsql_test indexedby-7.3 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5
|
||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
|
||||
do_execsql_test indexedby-7.4 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}}
|
||||
do_execsql_test indexedby-7.5 {
|
||||
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}}
|
||||
do_test indexedby-7.6 {
|
||||
catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5}
|
||||
} {1 {cannot use index: i2}}
|
||||
|
||||
# Test that "INDEXED BY" can be used in an UPDATE statement.
|
||||
#
|
||||
do_test indexedby-8.1 {
|
||||
EQP { UPDATE t1 SET rowid=rowid+1 WHERE a = 5 }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-8.2 {
|
||||
EQP { UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5 }
|
||||
} {0 0 {TABLE t1}}
|
||||
do_test indexedby-8.3 {
|
||||
EQP { UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 }
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-8.4 {
|
||||
EQP { UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10}
|
||||
} {0 0 {TABLE t1 WITH INDEX i1}}
|
||||
do_test indexedby-8.5 {
|
||||
EQP { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10}
|
||||
} {0 0 {TABLE t1 WITH INDEX i2}}
|
||||
do_execsql_test indexedby-8.1 {
|
||||
EXPLAIN QUERY PLAN UPDATE t1 SET rowid=rowid+1 WHERE a = 5
|
||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
|
||||
do_execsql_test indexedby-8.2 {
|
||||
EXPLAIN QUERY PLAN UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5
|
||||
} {0 0 0 {SCAN TABLE t1 (~100000 rows)}}
|
||||
do_execsql_test indexedby-8.3 {
|
||||
EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5
|
||||
} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
|
||||
do_execsql_test indexedby-8.4 {
|
||||
EXPLAIN QUERY PLAN
|
||||
UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}}
|
||||
do_execsql_test indexedby-8.5 {
|
||||
EXPLAIN QUERY PLAN
|
||||
UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}}
|
||||
do_test indexedby-8.6 {
|
||||
catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5}
|
||||
} {1 {cannot use index: i2}}
|
||||
|
||||
+6
-1
@@ -135,8 +135,13 @@ ifcapable attach {
|
||||
|
||||
# Test IO errors when replaying two hot journals from a 2-file
|
||||
# transaction. This test only runs on UNIX.
|
||||
#
|
||||
# It cannot be run under the "exclusive" permutation. In that case, the
|
||||
# locks held by the connection in the local (this) process prevent a
|
||||
# second connection from attempting the multi-file transaction.
|
||||
#
|
||||
ifcapable crashtest&&attach {
|
||||
if {![catch {sqlite3 -has_codec} r] && !$r} {
|
||||
if {![catch {sqlite3 -has-codec} r] && !$r && [permutation]!="exclusive"} {
|
||||
do_ioerr_test ioerr-6 -ckrefcount true -tclprep {
|
||||
execsql {
|
||||
ATTACH 'test2.db' as aux;
|
||||
|
||||
+5
-6
@@ -45,7 +45,7 @@ do_test jrnlmode3-1.2 {
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
} {1}
|
||||
|
||||
db close
|
||||
file delete -force test.db test.db-journal
|
||||
@@ -67,7 +67,7 @@ do_test jrnlmode3-2.2 {
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2}
|
||||
} {1}
|
||||
|
||||
# Test cases to verify that we can move from any journal_mode
|
||||
# to any other, as long as we are not in a transaction. Verify
|
||||
@@ -112,15 +112,14 @@ foreach fromjmode $all_journal_modes {
|
||||
db eval "PRAGMA journal_mode=$tojmode"
|
||||
} $fromjmode
|
||||
|
||||
# Rollback the transaction. Verify that the rollback occurred
|
||||
# if journal_mode!=OFF.
|
||||
# Rollback the transaction.
|
||||
#
|
||||
do_test jrnlmode3-3.$cnt.4 {
|
||||
db eval {
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} [expr {$fromjmode=="off"?$cnt:""}]
|
||||
} {}
|
||||
|
||||
# Now change the journal mode again. This time the new mode
|
||||
# should take.
|
||||
@@ -143,7 +142,7 @@ foreach fromjmode $all_journal_modes {
|
||||
db eval {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} [expr {$tojmode=="off"?"1":""}]
|
||||
} {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+104
-104
@@ -115,7 +115,7 @@ do_test like-2.1 {
|
||||
proc test_regexp {a b} {
|
||||
return [regexp $a $b]
|
||||
}
|
||||
db function regexp test_regexp
|
||||
db function regexp -argcount 2 test_regexp
|
||||
execsql {
|
||||
SELECT x FROM t1 WHERE x REGEXP 'abc' ORDER BY 1;
|
||||
}
|
||||
@@ -608,7 +608,7 @@ do_test like-8.4 {
|
||||
} {1 abcdef 1 ghijkl 1 mnopqr 2 abcdef 2 ghijkl 2 mnopqr}
|
||||
|
||||
|
||||
ifcapable like_opt {
|
||||
ifcapable like_opt&&!icu {
|
||||
# Evaluate SQL. Return the result set followed by the
|
||||
# and the number of full-scan steps.
|
||||
#
|
||||
@@ -673,110 +673,110 @@ ifcapable like_opt {
|
||||
regexp {INDEX i2} $res
|
||||
} {1}
|
||||
}
|
||||
}
|
||||
|
||||
# Do an SQL statement. Append the search count to the end of the result.
|
||||
#
|
||||
proc count sql {
|
||||
set ::sqlite_search_count 0
|
||||
set ::sqlite_like_count 0
|
||||
return [concat [execsql $sql] scan $::sqlite_search_count \
|
||||
like $::sqlite_like_count]
|
||||
}
|
||||
# Do an SQL statement. Append the search count to the end of the result.
|
||||
#
|
||||
proc count sql {
|
||||
set ::sqlite_search_count 0
|
||||
set ::sqlite_like_count 0
|
||||
return [concat [execsql $sql] scan $::sqlite_search_count \
|
||||
like $::sqlite_like_count]
|
||||
}
|
||||
|
||||
# The LIKE and GLOB optimizations do not work on columns with
|
||||
# affinity other than TEXT.
|
||||
# Ticket #3901
|
||||
#
|
||||
do_test like-10.1 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
CREATE TABLE t10(
|
||||
a INTEGER PRIMARY KEY,
|
||||
b INTEGER COLLATE nocase UNIQUE,
|
||||
c NUMBER COLLATE nocase UNIQUE,
|
||||
d BLOB COLLATE nocase UNIQUE,
|
||||
e COLLATE nocase UNIQUE,
|
||||
f TEXT COLLATE nocase UNIQUE
|
||||
);
|
||||
INSERT INTO t10 VALUES(1,1,1,1,1,1);
|
||||
INSERT INTO t10 VALUES(12,12,12,12,12,12);
|
||||
INSERT INTO t10 VALUES(123,123,123,123,123,123);
|
||||
INSERT INTO t10 VALUES(234,234,234,234,234,234);
|
||||
INSERT INTO t10 VALUES(345,345,345,345,345,345);
|
||||
INSERT INTO t10 VALUES(45,45,45,45,45,45);
|
||||
}
|
||||
count {
|
||||
SELECT a FROM t10 WHERE b LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.2 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE c LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.3 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE d LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.4 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE e LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.5 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 3 like 0}
|
||||
do_test like-10.6 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE a LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.10 {
|
||||
execsql {
|
||||
CREATE TABLE t10b(
|
||||
a INTEGER PRIMARY KEY,
|
||||
b INTEGER UNIQUE,
|
||||
c NUMBER UNIQUE,
|
||||
d BLOB UNIQUE,
|
||||
e UNIQUE,
|
||||
f TEXT UNIQUE
|
||||
);
|
||||
INSERT INTO t10b SELECT * FROM t10;
|
||||
}
|
||||
count {
|
||||
SELECT a FROM t10b WHERE b GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.11 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE c GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.12 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE d GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.13 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE e GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.14 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 3 like 0}
|
||||
do_test like-10.15 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE a GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
# The LIKE and GLOB optimizations do not work on columns with
|
||||
# affinity other than TEXT.
|
||||
# Ticket #3901
|
||||
#
|
||||
do_test like-10.1 {
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
CREATE TABLE t10(
|
||||
a INTEGER PRIMARY KEY,
|
||||
b INTEGER COLLATE nocase UNIQUE,
|
||||
c NUMBER COLLATE nocase UNIQUE,
|
||||
d BLOB COLLATE nocase UNIQUE,
|
||||
e COLLATE nocase UNIQUE,
|
||||
f TEXT COLLATE nocase UNIQUE
|
||||
);
|
||||
INSERT INTO t10 VALUES(1,1,1,1,1,1);
|
||||
INSERT INTO t10 VALUES(12,12,12,12,12,12);
|
||||
INSERT INTO t10 VALUES(123,123,123,123,123,123);
|
||||
INSERT INTO t10 VALUES(234,234,234,234,234,234);
|
||||
INSERT INTO t10 VALUES(345,345,345,345,345,345);
|
||||
INSERT INTO t10 VALUES(45,45,45,45,45,45);
|
||||
}
|
||||
count {
|
||||
SELECT a FROM t10 WHERE b LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.2 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE c LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.3 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE d LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.4 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE e LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.5 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 3 like 0}
|
||||
do_test like-10.6 {
|
||||
count {
|
||||
SELECT a FROM t10 WHERE a LIKE '12%' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.10 {
|
||||
execsql {
|
||||
CREATE TABLE t10b(
|
||||
a INTEGER PRIMARY KEY,
|
||||
b INTEGER UNIQUE,
|
||||
c NUMBER UNIQUE,
|
||||
d BLOB UNIQUE,
|
||||
e UNIQUE,
|
||||
f TEXT UNIQUE
|
||||
);
|
||||
INSERT INTO t10b SELECT * FROM t10;
|
||||
}
|
||||
count {
|
||||
SELECT a FROM t10b WHERE b GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.11 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE c GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.12 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE d GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.13 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE e GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
do_test like-10.14 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 3 like 0}
|
||||
do_test like-10.15 {
|
||||
count {
|
||||
SELECT a FROM t10b WHERE a GLOB '12*' ORDER BY a;
|
||||
}
|
||||
} {12 123 scan 5 like 6}
|
||||
}
|
||||
|
||||
# LIKE and GLOB where the default collating sequence is not appropriate
|
||||
# but an index with the appropriate collating sequence exists.
|
||||
|
||||
+1
-1
@@ -398,7 +398,7 @@ do_test lock-6.5 {
|
||||
# * a transaction that modified zero database pages is committed.
|
||||
#
|
||||
set temp_status unlocked
|
||||
if {$TEMP_STORE==3} {set temp_status unknown}
|
||||
if {$TEMP_STORE>=2} {set temp_status unknown}
|
||||
do_test lock-7.1 {
|
||||
set STMT [sqlite3_prepare $DB "SELECT * FROM sqlite_master" -1 TAIL]
|
||||
sqlite3_step $STMT
|
||||
|
||||
+4
-4
@@ -112,10 +112,10 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
|
||||
|
||||
sqlite3_soft_heap_limit 0
|
||||
do_test lock6-1.3 {
|
||||
sqlite3 db test.db
|
||||
catchsql {
|
||||
select * from sqlite_master;
|
||||
}
|
||||
list [catch {
|
||||
sqlite3 db test.db
|
||||
execsql { select * from sqlite_master }
|
||||
} msg] $msg
|
||||
} {1 {database is locked}}
|
||||
|
||||
do_test lock6-1.4 {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
proc do_multiclient_test {varname script} {
|
||||
|
||||
foreach code [list {
|
||||
if {[info exists ::G(valgrind)]} { db close ; continue }
|
||||
set ::code2_chan [launch_testfixture]
|
||||
set ::code3_chan [launch_testfixture]
|
||||
proc code2 {tcl} { testfixture $::code2_chan $tcl }
|
||||
@@ -58,6 +59,7 @@ proc do_multiclient_test {varname script} {
|
||||
code3 { db3 close }
|
||||
catch { close $::code2_chan }
|
||||
catch { close $::code3_chan }
|
||||
catch { db close }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-10
@@ -46,36 +46,48 @@ do_test lookaside-1.1 {
|
||||
do_test lookaside-1.2 {
|
||||
sqlite3_db_config_lookaside db 1 18 18
|
||||
} {0}
|
||||
do_test lookaside-1.3 {
|
||||
sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0
|
||||
do_test lookaside-1.3.1 {
|
||||
sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 0
|
||||
} {0 0 0}
|
||||
do_test lookaside-1.3.2 {
|
||||
sqlite3_db_status db DBSTATUS_LOOKASIDE_HIT 0
|
||||
} {0 0 0}
|
||||
do_test lookaside-1.3.3 {
|
||||
sqlite3_db_status db DBSTATUS_LOOKASIDE_MISS_SIZE 0
|
||||
} {0 0 0}
|
||||
do_test lookaside-1.3.4 {
|
||||
sqlite3_db_status db DBSTATUS_LOOKASIDE_MISS_FULL 0
|
||||
} {0 0 0}
|
||||
|
||||
do_test lookaside-1.4 {
|
||||
db eval {CREATE TABLE t1(w,x,y,z);}
|
||||
foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
|
||||
expr {$x==0 && $y<$z && $z==18}
|
||||
foreach {x y z} [sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 0] break
|
||||
set p [lindex [sqlite3_db_status db DBSTATUS_LOOKASIDE_HIT 0] 2]
|
||||
set q [lindex [sqlite3_db_status db DBSTATUS_LOOKASIDE_MISS_SIZE 0] 2]
|
||||
set r [lindex [sqlite3_db_status db DBSTATUS_LOOKASIDE_MISS_FULL 0] 2]
|
||||
expr {$x==0 && $y<$z && $z==18 && $p>0 && $q>0 && $r>0}
|
||||
} {0}
|
||||
do_test lookaside-1.5 {
|
||||
foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 1] break
|
||||
foreach {x y z} [sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 1] break
|
||||
expr {$x==0 && $y<$z && $z==18}
|
||||
} {0}
|
||||
do_test lookaside-1.6 {
|
||||
foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
|
||||
foreach {x y z} [sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 0] break
|
||||
expr {$x==0 && $y==$z && $y<18}
|
||||
} {1}
|
||||
do_test lookaside-1.7 {
|
||||
db cache flush
|
||||
foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
|
||||
foreach {x y z} [sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 0] break
|
||||
expr {$x==0 && $y==0 && $z<18}
|
||||
} {1}
|
||||
do_test lookaside-1.8 {
|
||||
db cache flush
|
||||
foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 1] break
|
||||
foreach {x y z} [sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 1] break
|
||||
expr {$x==0 && $y==0 && $z<18}
|
||||
} {1}
|
||||
do_test lookaside-1.9 {
|
||||
db cache flush
|
||||
sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0
|
||||
sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 0
|
||||
} {0 0 0}
|
||||
|
||||
do_test lookaside-2.1 {
|
||||
@@ -83,7 +95,7 @@ do_test lookaside-2.1 {
|
||||
} {0}
|
||||
do_test lookaside-2.2 {
|
||||
db eval {CREATE TABLE t2(x);}
|
||||
foreach {x y z} [sqlite3_db_status db SQLITE_DBSTATUS_LOOKASIDE_USED 0] break
|
||||
foreach {x y z} [sqlite3_db_status db DBSTATUS_LOOKASIDE_USED 0] break
|
||||
expr {$x==0 && $y<$z && $z>10 && $z<100}
|
||||
} {1}
|
||||
do_test lookaside-2.3 {
|
||||
|
||||
+1
-1
@@ -655,7 +655,7 @@ sqlite3 db test.db
|
||||
sqlite3_extended_result_codes db 1
|
||||
set ::DB [sqlite3_connection_pointer db]
|
||||
|
||||
# Turn of the Tcl interface's prepared statement caching facility in
|
||||
# Turn off the Tcl interface's prepared statement caching facility in
|
||||
# the new connnection. Then run the tests with "transient" malloc failures.
|
||||
db cache size 0
|
||||
run_test $::run_test_script 0
|
||||
|
||||
+39
-37
@@ -106,6 +106,10 @@ set FAULTSIM(cantopen-persistent) [list \
|
||||
#
|
||||
# -test Script to execute after -body.
|
||||
#
|
||||
# -install Script to execute after faultsim -injectinstall
|
||||
#
|
||||
# -uninstall Script to execute after faultsim -uninjectinstall
|
||||
#
|
||||
proc do_faultsim_test {name args} {
|
||||
global FAULTSIM
|
||||
|
||||
@@ -113,6 +117,10 @@ proc do_faultsim_test {name args} {
|
||||
set DEFAULT(-prep) ""
|
||||
set DEFAULT(-body) ""
|
||||
set DEFAULT(-test) ""
|
||||
set DEFAULT(-install) ""
|
||||
set DEFAULT(-uninstall) ""
|
||||
|
||||
fix_testname name
|
||||
|
||||
array set O [array get DEFAULT]
|
||||
array set O $args
|
||||
@@ -127,12 +135,15 @@ proc do_faultsim_test {name args} {
|
||||
set faultlist [concat $faultlist $flist]
|
||||
}
|
||||
|
||||
set testspec [list -prep $O(-prep) -body $O(-body) -test $O(-test)]
|
||||
set testspec [list -prep $O(-prep) -body $O(-body) \
|
||||
-test $O(-test) -install $O(-install) -uninstall $O(-uninstall)
|
||||
]
|
||||
foreach f [lsort -unique $faultlist] {
|
||||
eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Procedures to save and restore the current file-system state:
|
||||
#
|
||||
@@ -142,29 +153,16 @@ proc do_faultsim_test {name args} {
|
||||
# faultsim_restore_and_reopen
|
||||
# faultsim_delete_and_reopen
|
||||
#
|
||||
proc faultsim_save {} {
|
||||
foreach f [glob -nocomplain sv_test.db*] { file delete -force $f }
|
||||
foreach f [glob -nocomplain test.db*] {
|
||||
set f2 "sv_$f"
|
||||
file copy -force $f $f2
|
||||
}
|
||||
proc faultsim_save {args} { uplevel db_save $args }
|
||||
proc faultsim_save_and_close {args} { uplevel db_save_and_close $args }
|
||||
proc faultsim_restore {args} { uplevel db_restore $args }
|
||||
proc faultsim_restore_and_reopen {args} {
|
||||
uplevel db_restore_and_reopen $args
|
||||
sqlite3_extended_result_codes db 1
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
}
|
||||
proc faultsim_save_and_close {} {
|
||||
faultsim_save
|
||||
catch { db close }
|
||||
return ""
|
||||
}
|
||||
proc faultsim_restore {} {
|
||||
foreach f [glob -nocomplain test.db*] { file delete -force $f }
|
||||
foreach f2 [glob -nocomplain sv_test.db*] {
|
||||
set f [string range $f2 3 end]
|
||||
file copy -force $f2 $f
|
||||
}
|
||||
}
|
||||
proc faultsim_restore_and_reopen {{dbfile test.db}} {
|
||||
catch { db close }
|
||||
faultsim_restore
|
||||
sqlite3 db $dbfile
|
||||
proc faultsim_delete_and_reopen {args} {
|
||||
uplevel db_delete_and_reopen $args
|
||||
sqlite3_extended_result_codes db 1
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
}
|
||||
@@ -174,18 +172,12 @@ proc faultsim_integrity_check {{db db}} {
|
||||
if {$ic != "ok"} { error "Integrity check: $ic" }
|
||||
}
|
||||
|
||||
proc faultsim_delete_and_reopen {{file test.db}} {
|
||||
catch { db close }
|
||||
foreach f [glob -nocomplain test.db*] { file delete -force $f }
|
||||
sqlite3 db $file
|
||||
}
|
||||
|
||||
|
||||
# The following procs are used as [do_one_faultsim_test] callbacks when
|
||||
# injecting OOM faults into test cases.
|
||||
#
|
||||
proc oom_injectstart {nRepeat iFail} {
|
||||
sqlite3_memdebug_fail $iFail -repeat $nRepeat
|
||||
sqlite3_memdebug_fail [expr $iFail-1] -repeat $nRepeat
|
||||
}
|
||||
proc oom_injectstop {} {
|
||||
sqlite3_memdebug_fail -1
|
||||
@@ -310,6 +302,8 @@ proc do_one_faultsim_test {testname args} {
|
||||
set DEFAULT(-prep) ""
|
||||
set DEFAULT(-body) ""
|
||||
set DEFAULT(-test) ""
|
||||
set DEFAULT(-install) ""
|
||||
set DEFAULT(-uninstall) ""
|
||||
|
||||
array set O [array get DEFAULT]
|
||||
array set O $args
|
||||
@@ -323,6 +317,7 @@ proc do_one_faultsim_test {testname args} {
|
||||
"
|
||||
|
||||
eval $O(-injectinstall)
|
||||
eval $O(-install)
|
||||
|
||||
set stop 0
|
||||
for {set iFail 1} {!$stop} {incr iFail} {
|
||||
@@ -354,6 +349,7 @@ proc do_one_faultsim_test {testname args} {
|
||||
if {$nfail==0} { set stop 1 }
|
||||
}
|
||||
|
||||
eval $O(-uninstall)
|
||||
eval $O(-injectuninstall)
|
||||
}
|
||||
|
||||
@@ -422,12 +418,12 @@ proc do_malloc_test {tn args} {
|
||||
#
|
||||
catch {db close}
|
||||
catch {db2 close}
|
||||
catch {file delete -force test.db}
|
||||
catch {file delete -force test.db-journal}
|
||||
catch {file delete -force test.db-wal}
|
||||
catch {file delete -force test2.db}
|
||||
catch {file delete -force test2.db-journal}
|
||||
catch {file delete -force test2.db-wal}
|
||||
forcedelete test.db
|
||||
forcedelete test.db-journal
|
||||
forcedelete test.db-wal
|
||||
forcedelete test2.db
|
||||
forcedelete test2.db-journal
|
||||
forcedelete test2.db-wal
|
||||
if {[info exists ::mallocopts(-testdb)]} {
|
||||
file copy $::mallocopts(-testdb) test.db
|
||||
}
|
||||
@@ -526,7 +522,7 @@ proc do_malloc_test {tn args} {
|
||||
# match the expected results passed via parameter $result.
|
||||
#
|
||||
proc do_select_test {name sql result} {
|
||||
uplevel [list doPassiveTest 0 $name $sql [list 0 $result]]
|
||||
uplevel [list doPassiveTest 0 $name $sql [list 0 [list {*}$result]]]
|
||||
}
|
||||
|
||||
proc do_restart_select_test {name sql result} {
|
||||
@@ -540,6 +536,12 @@ proc do_error_test {name sql error} {
|
||||
proc doPassiveTest {isRestart name sql catchres} {
|
||||
if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 }
|
||||
|
||||
if {[info exists ::testprefix]
|
||||
&& [string is integer [string range $name 0 0]]
|
||||
} {
|
||||
set name $::testprefix.$name
|
||||
}
|
||||
|
||||
switch $::DO_MALLOC_TEST {
|
||||
0 { # No malloc failures.
|
||||
do_test $name [list set {} [uplevel [list catchsql $sql]]] $catchres
|
||||
|
||||
+13
-22
@@ -11,7 +11,6 @@
|
||||
#
|
||||
# This file contains tests of the memory allocation subsystem
|
||||
#
|
||||
# $Id: memsubsys1.test,v 1.17 2009/07/18 14:36:24 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -97,17 +96,13 @@ sqlite3_initialize
|
||||
reset_highwater_marks
|
||||
build_test_db memsubsys1-2 {PRAGMA page_size=1024}
|
||||
#show_memstats
|
||||
set MEMORY_MANAGEMENT $sqlite_options(memorymanage)
|
||||
do_test memsubsys1-2.3 {
|
||||
set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
expr {
|
||||
($pg_used*1024 + $pg_ovfl) < $max_pagecache &&
|
||||
($pg_used*(1024+$xtra_size) + $pg_ovfl) >= $max_pagecache
|
||||
}
|
||||
} 1
|
||||
} [expr ($TEMP_STORE>1 || $MEMORY_MANAGEMENT==0)*1024]
|
||||
do_test memsubsys1-2.4 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} 19
|
||||
} 20
|
||||
do_test memsubsys1-2.5 {
|
||||
set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
|
||||
} 0
|
||||
@@ -143,7 +138,7 @@ do_test memsubsys1-3.2.3 {
|
||||
} 2048
|
||||
do_test memsubsys1-3.2.4 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} 19
|
||||
} 20
|
||||
do_test memsubsys1-3.2.5 {
|
||||
set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
|
||||
} 0
|
||||
@@ -160,15 +155,11 @@ build_test_db memsubsys1-4 {PRAGMA page_size=1024}
|
||||
#show_memstats
|
||||
do_test memsubsys1-4.3 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} 49
|
||||
expr {$pg_used>=45 && $pg_used<=50}
|
||||
} 1
|
||||
do_test memsubsys1-4.4 {
|
||||
set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2]
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
expr {
|
||||
($pg_used*1024 + $pg_ovfl) < $max_pagecache &&
|
||||
($pg_used*(1024+$xtra_size) + $pg_ovfl) >= $max_pagecache
|
||||
}
|
||||
} 1
|
||||
} 0
|
||||
do_test memsubsys1-4.5 {
|
||||
set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
|
||||
expr {$maxreq<7000}
|
||||
@@ -190,7 +181,7 @@ build_test_db memsubsys1-5 {PRAGMA page_size=4096}
|
||||
#show_memstats
|
||||
do_test memsubsys1-5.3 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} 23
|
||||
} 24
|
||||
do_test memsubsys1-5.4 {
|
||||
set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
|
||||
expr {$maxreq>4096}
|
||||
@@ -216,11 +207,11 @@ build_test_db memsubsys1-6 {PRAGMA page_size=4096}
|
||||
#show_memstats
|
||||
do_test memsubsys1-6.3 {
|
||||
set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2]
|
||||
} 23
|
||||
do_test memsubsys1-6.4 {
|
||||
set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
|
||||
expr {$maxreq>4096 && $maxreq<=(4096+$xtra_size)}
|
||||
} 1
|
||||
} 24
|
||||
#do_test memsubsys1-6.4 {
|
||||
# set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2]
|
||||
# expr {$maxreq>4096 && $maxreq<=(4096+$xtra_size)}
|
||||
#} 1
|
||||
do_test memsubsys1-6.5 {
|
||||
set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2]
|
||||
} 1
|
||||
|
||||
+1
-1
@@ -151,7 +151,7 @@ ifcapable subquery {
|
||||
|
||||
select a.*, x.*
|
||||
from a, (select key,sum(period) from b group by key) as x
|
||||
where a.key=x.key;
|
||||
where a.key=x.key order by 1 desc;
|
||||
}
|
||||
} {01 data01 01 3 +1 data+1 +1 7}
|
||||
|
||||
|
||||
+16
-18
@@ -259,24 +259,22 @@ file delete -force test.db-journal
|
||||
sqlite3 db test.db
|
||||
|
||||
ifcapable explain {
|
||||
do_test misc7-14.1 {
|
||||
execsql {
|
||||
CREATE TABLE abc(a PRIMARY KEY, b, c);
|
||||
}
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE rowid = 1;
|
||||
}
|
||||
} {0 0 {TABLE abc AS t2 USING PRIMARY KEY}}
|
||||
do_test misc7-14.2 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE a = 1;
|
||||
}
|
||||
} {0 0 {TABLE abc AS t2 WITH INDEX sqlite_autoindex_abc_1}}
|
||||
do_test misc7-14.3 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 ORDER BY a;
|
||||
}
|
||||
} {0 0 {TABLE abc AS t2 WITH INDEX sqlite_autoindex_abc_1 ORDER BY}}
|
||||
do_execsql_test misc7-14.1 {
|
||||
CREATE TABLE abc(a PRIMARY KEY, b, c);
|
||||
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE rowid = 1;
|
||||
} {
|
||||
0 0 0 {SEARCH TABLE abc AS t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
|
||||
}
|
||||
do_execsql_test misc7-14.2 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE a = 1;
|
||||
} {0 0 0
|
||||
{SEARCH TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (a=?) (~1 rows)}
|
||||
}
|
||||
do_execsql_test misc7-14.3 {
|
||||
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 ORDER BY a;
|
||||
} {0 0 0
|
||||
{SCAN TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (~1000000 rows)}
|
||||
}
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
# 2010 October 29
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
set g_chunk_size 2147483648
|
||||
set g_max_chunks 32
|
||||
|
||||
# This handles appending the chunk number
|
||||
# to the end of the filename. if
|
||||
# SQLITE_MULTIPLEX_EXT_OVWR is defined, then
|
||||
# it overwrites the last 2 bytes of the
|
||||
# file name with the chunk number.
|
||||
proc multiplex_name {name chunk} {
|
||||
if {$chunk==0} { return $name }
|
||||
set num [format "%02d" $chunk]
|
||||
ifcapable {multiplex_ext_overwrite} {
|
||||
set name [string range $name 0 [expr [string length $name]-2-1]]
|
||||
}
|
||||
return $name$num
|
||||
}
|
||||
|
||||
# This saves off the parameters and calls the
|
||||
# underlying sqlite3_multiplex_set() API.
|
||||
proc multiplex_set {chunk_size max_chunks} {
|
||||
global g_chunk_size
|
||||
global g_max_chunks
|
||||
set g_chunk_size $chunk_size
|
||||
set g_max_chunks $max_chunks
|
||||
sqlite3_multiplex_set $chunk_size $max_chunks
|
||||
}
|
||||
|
||||
# This attempts to delete the base file and
|
||||
# and files with the chunk extension.
|
||||
proc multiplex_delete {name} {
|
||||
global g_max_chunks
|
||||
for {set i 0} {$i<$g_max_chunks} {incr i} {
|
||||
forcedelete [multiplex_name $name $i]
|
||||
forcedelete [multiplex_name $name-journal $i]
|
||||
forcedelete [multiplex_name $name-wal $i]
|
||||
}
|
||||
}
|
||||
|
||||
db close
|
||||
|
||||
multiplex_delete test.db
|
||||
multiplex_delete test2.db
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# multiplex-1.1.*: Test initialize and shutdown.
|
||||
|
||||
do_test multiplex-1.1 { sqlite3_multiplex_initialize nosuchvfs 1 } {SQLITE_ERROR}
|
||||
do_test multiplex-1.2 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
|
||||
do_test multiplex-1.3 { sqlite3_multiplex_initialize "" 1 } {SQLITE_MISUSE}
|
||||
do_test multiplex-1.4 { sqlite3_multiplex_shutdown } {SQLITE_OK}
|
||||
|
||||
do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 } {SQLITE_OK}
|
||||
do_test multiplex-1.6 { sqlite3_multiplex_shutdown } {SQLITE_OK}
|
||||
do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
|
||||
do_test multiplex-1.8 { sqlite3_multiplex_shutdown } {SQLITE_OK}
|
||||
|
||||
do_test multiplex-1.9 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
|
||||
do_test multiplex-1.10.1 { multiplex_set 32768 16 } {SQLITE_OK}
|
||||
do_test multiplex-1.10.2 { multiplex_set 32768 -1 } {SQLITE_MISUSE}
|
||||
do_test multiplex-1.10.3 { multiplex_set -1 16 } {SQLITE_MISUSE}
|
||||
do_test multiplex-1.10.4 { multiplex_set 31 16 } {SQLITE_MISUSE}
|
||||
do_test multiplex-1.10.5 { multiplex_set 32768 100 } {SQLITE_MISUSE}
|
||||
do_test multiplex-1.11 { sqlite3_multiplex_shutdown } {SQLITE_OK}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Some simple warm-body tests with a single database file in rollback
|
||||
# mode:
|
||||
#
|
||||
# multiplex-2.1.*: Test simple writing to a multiplex file.
|
||||
#
|
||||
# multiplex-2.2.*: More writing.
|
||||
#
|
||||
# multiplex-2.3.*: Open and close a second db.
|
||||
#
|
||||
# multiplex-2.4.*: Try to shutdown the multiplex system before closing the db
|
||||
# file. Check that this fails and the multiplex system still works
|
||||
# afterwards. Then close the database and successfully shut
|
||||
# down the multiplex system.
|
||||
#
|
||||
# multiplex-2.5.*: More reading/writing.
|
||||
#
|
||||
# multiplex-2.6.*: More reading/writing with varying small chunk sizes, as
|
||||
# well as varying journal mode.
|
||||
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set 32768 16
|
||||
|
||||
do_test multiplex-2.1.2 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA page_size=1024;
|
||||
PRAGMA auto_vacuum=OFF;
|
||||
PRAGMA journal_mode=DELETE;
|
||||
}
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, randomblob(1100));
|
||||
INSERT INTO t1 VALUES(2, randomblob(1100));
|
||||
}
|
||||
} {}
|
||||
do_test multiplex-2.1.3 { file size [multiplex_name test.db 0] } {4096}
|
||||
do_test multiplex-2.1.4 {
|
||||
execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
|
||||
} {}
|
||||
|
||||
do_test multiplex-2.2.1 {
|
||||
execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
|
||||
} {}
|
||||
do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144}
|
||||
|
||||
do_test multiplex-2.3.1 {
|
||||
sqlite3 db2 test2.db
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
do_test multiplex-2.4.1 {
|
||||
sqlite3_multiplex_shutdown
|
||||
} {SQLITE_MISUSE}
|
||||
do_test multiplex-2.4.2 {
|
||||
execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
|
||||
} {}
|
||||
do_test multiplex-2.4.4 { file size [multiplex_name test.db 0] } {7168}
|
||||
do_test multiplex-2.4.99 {
|
||||
db close
|
||||
sqlite3_multiplex_shutdown
|
||||
} {SQLITE_OK}
|
||||
|
||||
|
||||
do_test multiplex-2.5.1 {
|
||||
multiplex_delete test.db
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set 4096 16
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test multiplex-2.5.2 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA journal_mode = delete;
|
||||
PRAGMA auto_vacuum = off;
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
}
|
||||
} {delete}
|
||||
|
||||
do_test multiplex-2.5.3 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, randomblob(4000));
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, randomblob(4000));
|
||||
INSERT INTO t1 VALUES(5, 'five')
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test multiplex-2.5.4 {
|
||||
db eval {SELECT * FROM t1 WHERE a=1}
|
||||
} {1 one}
|
||||
|
||||
do_test multiplex-2.5.5 {
|
||||
db eval {SELECT * FROM t1 WHERE a=3}
|
||||
} {3 three}
|
||||
|
||||
do_test multiplex-2.5.6 {
|
||||
db eval {SELECT * FROM t1 WHERE a=5}
|
||||
} {5 five}
|
||||
|
||||
do_test multiplex-2.5.7 {
|
||||
db eval {SELECT a,length(b) FROM t1 WHERE a=2}
|
||||
} {2 4000}
|
||||
|
||||
do_test multiplex-2.5.8 {
|
||||
db eval {SELECT a,length(b) FROM t1 WHERE a=4}
|
||||
} {4 4000}
|
||||
|
||||
do_test multiplex-2.5.9 { file size [multiplex_name test.db 0] } [list $g_chunk_size]
|
||||
do_test multiplex-2.5.10 { file size [multiplex_name test.db 1] } [list $g_chunk_size]
|
||||
|
||||
do_test multiplex-2.5.99 {
|
||||
db close
|
||||
sqlite3_multiplex_shutdown
|
||||
} {SQLITE_OK}
|
||||
|
||||
|
||||
set all_journal_modes {delete persist truncate memory off}
|
||||
foreach jmode $all_journal_modes {
|
||||
for {set sz 151} {$sz<8000} {set sz [expr $sz+419]} {
|
||||
|
||||
do_test multiplex-2.6.1.$sz.$jmode {
|
||||
multiplex_delete test.db
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set $sz 32
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test multiplex-2.6.2.$sz.$jmode {
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA auto_vacuum = off;
|
||||
}
|
||||
db eval "PRAGMA journal_mode = $jmode;"
|
||||
} $jmode
|
||||
|
||||
do_test multiplex-2.6.3.$sz.$jmode {
|
||||
execsql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, randomblob($g_chunk_size));
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test multiplex-2.6.4.$sz.$jmode {
|
||||
db eval {SELECT b FROM t1 WHERE a=1}
|
||||
} {one}
|
||||
|
||||
do_test multiplex-2.6.5.$sz.$jmode {
|
||||
db eval {SELECT length(b) FROM t1 WHERE a=2}
|
||||
} [list $g_chunk_size]
|
||||
|
||||
do_test multiplex-2.6.6.$sz.$jmode { file size [multiplex_name test.db 0] } [list $g_chunk_size]
|
||||
|
||||
do_test multiplex-2.6.99.$sz.$jmode {
|
||||
db close
|
||||
sqlite3_multiplex_shutdown
|
||||
} {SQLITE_OK}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Try some tests with more than one connection to a database file. Still
|
||||
# in rollback mode.
|
||||
#
|
||||
# multiplex-3.1.*: Two connections to a single database file.
|
||||
#
|
||||
# multiplex-3.2.*: Two connections to each of several database files (that
|
||||
# are in the same multiplex group).
|
||||
#
|
||||
do_test multiplex-3.1.1 {
|
||||
multiplex_delete test.db
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set 32768 16
|
||||
} {SQLITE_OK}
|
||||
do_test multiplex-3.1.2 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA journal_mode = delete;
|
||||
PRAGMA auto_vacuum = off;
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
}
|
||||
file size [multiplex_name test.db 0]
|
||||
} {3072}
|
||||
do_test multiplex-3.1.3 {
|
||||
sqlite3 db2 test.db
|
||||
execsql { CREATE TABLE t2(a, b) } db2
|
||||
} {}
|
||||
do_test multiplex-3.1.4 {
|
||||
execsql { CREATE TABLE t3(a, b) }
|
||||
} {}
|
||||
do_test multiplex-3.1.5 {
|
||||
catchsql { CREATE TABLE t3(a, b) }
|
||||
} {1 {table t3 already exists}}
|
||||
do_test multiplex-3.1.6 {
|
||||
db close
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
do_test multiplex-3.2.1a {
|
||||
|
||||
multiplex_delete test.db
|
||||
multiplex_delete test2.db
|
||||
|
||||
sqlite3 db1a test.db
|
||||
sqlite3 db2a test2.db
|
||||
|
||||
foreach db {db1a db2a} {
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA journal_mode = delete;
|
||||
PRAGMA auto_vacuum = off;
|
||||
CREATE TABLE t1(a, b);
|
||||
} $db
|
||||
}
|
||||
|
||||
list [file size [multiplex_name test.db 0]] [file size [multiplex_name test2.db 0]]
|
||||
} {2048 2048}
|
||||
|
||||
do_test multiplex-3.2.1b {
|
||||
sqlite3 db1b test.db
|
||||
sqlite3 db2b test2.db
|
||||
} {}
|
||||
|
||||
do_test multiplex-3.2.2 { execsql { INSERT INTO t1 VALUES('x', 'y') } db1a } {}
|
||||
do_test multiplex-3.2.3 { execsql { INSERT INTO t1 VALUES('v', 'w') } db1b } {}
|
||||
do_test multiplex-3.2.4 { execsql { INSERT INTO t1 VALUES('t', 'u') } db2a } {}
|
||||
do_test multiplex-3.2.5 { execsql { INSERT INTO t1 VALUES('r', 's') } db2b } {}
|
||||
|
||||
do_test multiplex-3.2.6 {
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
|
||||
} {}
|
||||
do_test multiplex-3.2.7 {
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
|
||||
} {}
|
||||
do_test multiplex-3.2.8 {
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
|
||||
} {}
|
||||
do_test multiplex-3.2.9 {
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
|
||||
} {}
|
||||
|
||||
do_test multiplex-3.3.1 {
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
|
||||
} {}
|
||||
|
||||
do_test multiplex-3.2.X {
|
||||
foreach db {db1a db2a db2b db1b} { catch { $db close } }
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set 32768 16
|
||||
|
||||
# Return a list of all currently defined multiplexs.
|
||||
proc multiplex_list {} {
|
||||
set allq {}
|
||||
foreach q [sqlite3_multiplex_dump] {
|
||||
lappend allq [lindex $q 0]
|
||||
}
|
||||
return [lsort $allq]
|
||||
}
|
||||
|
||||
do_test multiplex-4.1.6 {
|
||||
multiplex_delete test2.db
|
||||
sqlite3 db test2.db
|
||||
db eval {CREATE TABLE t2(x); INSERT INTO t2 VALUES('tab-t2');}
|
||||
set res [multiplex_list]
|
||||
list [regexp {test2.db} $res]
|
||||
} {1}
|
||||
do_test multiplex-4.1.6a {
|
||||
sqlite3 db2 test2.db
|
||||
db2 eval {SELECT * FROM t2}
|
||||
} {tab-t2}
|
||||
do_test multiplex-4.1.7 {
|
||||
execsql {INSERT INTO t2 VALUES(zeroblob(200000))}
|
||||
} {}
|
||||
do_test multiplex-4.1.8 {
|
||||
sqlite3 db2 test2.db
|
||||
db2 eval {SELECT count(*) FROM t2}
|
||||
} {2}
|
||||
do_test multiplex-4.1.8a {
|
||||
db2 eval { DELETE FROM t2 WHERE x = 'tab-t2' }
|
||||
} {}
|
||||
do_test multiplex-4.1.8b {
|
||||
sqlite3 db2 test2.db
|
||||
db2 eval {SELECT count(*) FROM t2}
|
||||
} {1}
|
||||
|
||||
|
||||
do_test multiplex-4.1.9 {
|
||||
execsql {INSERT INTO t2 VALUES(zeroblob(200000))}
|
||||
} {}
|
||||
do_test multiplex-4.1.10 {
|
||||
set res [multiplex_list]
|
||||
list [regexp {test2.db} $res]
|
||||
} {1}
|
||||
do_test multiplex-4.1.11 {
|
||||
db2 close
|
||||
set res [multiplex_list]
|
||||
list [regexp {test2.db} $res]
|
||||
} {1}
|
||||
do_test multiplex-4.1.12 {
|
||||
db close
|
||||
multiplex_list
|
||||
} {}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests test that the multiplex VFS handles malloc and IO
|
||||
# errors.
|
||||
#
|
||||
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set 32768 16
|
||||
|
||||
do_faultsim_test multiplex-5.1 -prep {
|
||||
catch {db close}
|
||||
} -body {
|
||||
sqlite3 db test2.db
|
||||
}
|
||||
do_faultsim_test multiplex-5.2 -prep {
|
||||
catch {db close}
|
||||
} -body {
|
||||
sqlite3 db test.db
|
||||
}
|
||||
|
||||
catch { db close }
|
||||
multiplex_delete test.db
|
||||
multiplex_delete test2.db
|
||||
|
||||
do_test multiplex-5.3.prep {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA auto_vacuum = 1;
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(10, zeroblob(1200));
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test multiplex-5.3 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { DELETE FROM t1 }
|
||||
}
|
||||
|
||||
do_test multiplex-5.4.1 {
|
||||
catch { db close }
|
||||
multiplex_delete test.db
|
||||
file mkdir test.db
|
||||
list [catch { sqlite3 db test.db } msg] $msg
|
||||
} {1 {unable to open database file}}
|
||||
catch { file delete test.db }
|
||||
|
||||
do_faultsim_test multiplex-5.5 -prep {
|
||||
catch { sqlite3_multiplex_shutdown }
|
||||
} -body {
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set 32768 16
|
||||
}
|
||||
|
||||
# test that mismatch filesize is detected
|
||||
#
|
||||
# Do not run this test if $::G(perm:presql) is set. If it is set, then the
|
||||
# expected IO error will occur within the Tcl [sqlite3] wrapper, not within
|
||||
# the first SQL statement executed below. This breaks the test case.
|
||||
#
|
||||
if {0==[info exists ::G(perm:presql)] || $::G(perm:presql) == ""} {
|
||||
set all_journal_modes {delete persist truncate memory off}
|
||||
foreach jmode $all_journal_modes {
|
||||
do_test multiplex-5.6.1.$jmode {
|
||||
sqlite3_multiplex_shutdown
|
||||
multiplex_delete test.db
|
||||
sqlite3 db test.db
|
||||
db eval {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA auto_vacuum = off;
|
||||
}
|
||||
db eval "PRAGMA journal_mode = $jmode;"
|
||||
} $jmode
|
||||
do_test multiplex-5.6.2.$jmode {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, randomblob(1100));
|
||||
INSERT INTO t1 VALUES(2, randomblob(1100));
|
||||
INSERT INTO t1 VALUES(3, randomblob(1100));
|
||||
INSERT INTO t1 VALUES(4, randomblob(1100));
|
||||
INSERT INTO t1 VALUES(5, randomblob(1100));
|
||||
}
|
||||
db close
|
||||
sqlite3_multiplex_initialize "" 1
|
||||
multiplex_set 4096 16
|
||||
sqlite3 db test.db
|
||||
} {}
|
||||
do_test multiplex-5.6.3.$jmode {
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES(6, randomblob(1100));
|
||||
}
|
||||
} {1 {disk I/O error}}
|
||||
do_test multiplex-5.6.4.$jmode {
|
||||
db close
|
||||
} {}
|
||||
}
|
||||
}
|
||||
|
||||
catch { sqlite3_multiplex_shutdown }
|
||||
finish_test
|
||||
+11
-3
@@ -101,8 +101,14 @@ ifcapable threadsafe&&shared_cache {
|
||||
set enable_shared_cache [sqlite3_enable_shared_cache 1]
|
||||
foreach {mode mutexes} {
|
||||
singlethread {}
|
||||
multithread {fast static_lru static_master static_mem static_open static_prng }
|
||||
serialized {fast recursive static_lru static_master static_mem static_open static_prng}
|
||||
multithread {
|
||||
fast static_lru static_master static_mem static_open static_prng
|
||||
static_pmem
|
||||
}
|
||||
serialized {
|
||||
fast recursive static_lru static_master static_mem static_open
|
||||
static_prng static_pmem
|
||||
}
|
||||
} {
|
||||
|
||||
do_test mutex1.2.$mode.1 {
|
||||
@@ -120,7 +126,9 @@ ifcapable threadsafe&&shared_cache {
|
||||
INSERT INTO abc VALUES(1, 2, 3);
|
||||
}
|
||||
} {}
|
||||
|
||||
ifcapable !memorymanage {
|
||||
regsub { static_lru} $mutexes {} mutexes
|
||||
}
|
||||
do_test mutex1.2.$mode.3 {
|
||||
mutex_counters counters
|
||||
|
||||
|
||||
+135
-72
@@ -1062,7 +1062,7 @@ do_execsql_test pager1-6.8 {
|
||||
} {11}
|
||||
do_execsql_test pager1-6.9 { COMMIT } {}
|
||||
|
||||
do_execsql_test pager1-6.10 { PRAGMA max_page_count = 10 } {10}
|
||||
do_execsql_test pager1-6.10 { PRAGMA max_page_count = 10 } {11}
|
||||
do_execsql_test pager1-6.11 { SELECT * FROM t11 } {1 2 3 4}
|
||||
do_execsql_test pager1-6.12 { PRAGMA max_page_count } {11}
|
||||
|
||||
@@ -1107,10 +1107,10 @@ ifcapable wal {
|
||||
COMMIT;
|
||||
} {1 2} 0 -1
|
||||
|
||||
4 { PRAGMA journal_mode = WAL } wal -1 -1
|
||||
5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024]
|
||||
6 { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
|
||||
7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024]
|
||||
4 { PRAGMA journal_mode = WAL } wal -1 -1
|
||||
5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024]
|
||||
6 { PRAGMA locking_mode = NORMAL } exclusive -1 [wal_file_size 1 1024]
|
||||
7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024]
|
||||
|
||||
8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1
|
||||
9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1
|
||||
@@ -1614,7 +1614,7 @@ do_catchsql_test pager1-14.1.2 {
|
||||
} {0 {}}
|
||||
do_execsql_test pager1-14.1.3 {
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3 4}
|
||||
} {1 2}
|
||||
do_catchsql_test pager1-14.1.4 {
|
||||
BEGIN;
|
||||
INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
|
||||
@@ -1623,7 +1623,7 @@ do_catchsql_test pager1-14.1.4 {
|
||||
do_execsql_test pager1-14.1.5 {
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3 4 2 2 4 4}
|
||||
} {1 2 2 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test opening and closing the pager sub-system with different values
|
||||
@@ -1919,32 +1919,34 @@ do_test pager1-20.2.2 {
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test pager1-20.3.1 {
|
||||
faultsim_delete_and_reopen
|
||||
db func a_string a_string
|
||||
execsql {
|
||||
PRAGMA cache_size = 10;
|
||||
PRAGMA journal_mode = wal;
|
||||
BEGIN;
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y);
|
||||
INSERT INTO t1 VALUES(a_string(800));
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 4 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 8 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 16 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 32 */
|
||||
COMMIT;
|
||||
}
|
||||
} {wal}
|
||||
do_test pager1-20.3.2 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('xxxx');
|
||||
}
|
||||
recursive_select 32 t1
|
||||
execsql COMMIT
|
||||
} {}
|
||||
ifcapable wal {
|
||||
do_test pager1-20.3.1 {
|
||||
faultsim_delete_and_reopen
|
||||
db func a_string a_string
|
||||
execsql {
|
||||
PRAGMA cache_size = 10;
|
||||
PRAGMA journal_mode = wal;
|
||||
BEGIN;
|
||||
CREATE TABLE t1(x);
|
||||
CREATE TABLE t2(y);
|
||||
INSERT INTO t1 VALUES(a_string(800));
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 2 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 4 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 8 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 16 */
|
||||
INSERT INTO t1 SELECT a_string(800) FROM t1; /* 32 */
|
||||
COMMIT;
|
||||
}
|
||||
} {wal}
|
||||
do_test pager1-20.3.2 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('xxxx');
|
||||
}
|
||||
recursive_select 32 t1
|
||||
execsql COMMIT
|
||||
} {}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that a WAL database may not be opened if:
|
||||
@@ -1952,28 +1954,30 @@ do_test pager1-20.3.2 {
|
||||
# pager1-21.1.*: The VFS has an iVersion less than 2, or
|
||||
# pager1-21.2.*: The VFS does not provide xShmXXX() methods.
|
||||
#
|
||||
do_test pager1-21.0 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def');
|
||||
INSERT INTO ko DEFAULT VALUES;
|
||||
}
|
||||
} {wal}
|
||||
do_test pager1-21.1 {
|
||||
testvfs tv -noshm 1
|
||||
sqlite3 db2 test.db -vfs tv
|
||||
catchsql { SELECT * FROM ko } db2
|
||||
} {1 {unable to open database file}}
|
||||
db2 close
|
||||
tv delete
|
||||
do_test pager1-21.2 {
|
||||
testvfs tv -iversion 1
|
||||
sqlite3 db2 test.db -vfs tv
|
||||
catchsql { SELECT * FROM ko } db2
|
||||
} {1 {unable to open database file}}
|
||||
db2 close
|
||||
tv delete
|
||||
ifcapable wal {
|
||||
do_test pager1-21.0 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def');
|
||||
INSERT INTO ko DEFAULT VALUES;
|
||||
}
|
||||
} {wal}
|
||||
do_test pager1-21.1 {
|
||||
testvfs tv -noshm 1
|
||||
sqlite3 db2 test.db -vfs tv
|
||||
catchsql { SELECT * FROM ko } db2
|
||||
} {1 {unable to open database file}}
|
||||
db2 close
|
||||
tv delete
|
||||
do_test pager1-21.2 {
|
||||
testvfs tv -iversion 1
|
||||
sqlite3 db2 test.db -vfs tv
|
||||
catchsql { SELECT * FROM ko } db2
|
||||
} {1 {unable to open database file}}
|
||||
db2 close
|
||||
tv delete
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that a "PRAGMA wal_checkpoint":
|
||||
@@ -2254,24 +2258,26 @@ do_test pager1.27.1 {
|
||||
# the same database.
|
||||
#
|
||||
catch { db close }
|
||||
do_multiclient_test tn {
|
||||
do_test pager1-28.$tn.1 {
|
||||
sql1 {
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
}
|
||||
} {wal}
|
||||
do_test pager1-28.$tn.2 { sql2 { SELECT * FROM t1 } } {a b}
|
||||
ifcapable wal {
|
||||
do_multiclient_test tn {
|
||||
do_test pager1-28.$tn.1 {
|
||||
sql1 {
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
}
|
||||
} {wal}
|
||||
do_test pager1-28.$tn.2 { sql2 { SELECT * FROM t1 } } {a b}
|
||||
|
||||
do_test pager1-28.$tn.3 { sql1 { PRAGMA locking_mode=exclusive } } {exclusive}
|
||||
do_test pager1-28.$tn.4 {
|
||||
csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); }
|
||||
} {1 {database is locked}}
|
||||
code2 { db2 close ; sqlite3 db2 test.db }
|
||||
do_test pager1-28.$tn.4 {
|
||||
sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT }
|
||||
} {}
|
||||
do_test pager1-28.$tn.3 { sql1 { PRAGMA locking_mode=exclusive } } {exclusive}
|
||||
do_test pager1-28.$tn.4 {
|
||||
csql1 { BEGIN; INSERT INTO t1 VALUES('c', 'd'); }
|
||||
} {1 {database is locked}}
|
||||
code2 { db2 close ; sqlite3 db2 test.db }
|
||||
do_test pager1-28.$tn.4 {
|
||||
sql1 { INSERT INTO t1 VALUES('c', 'd'); COMMIT }
|
||||
} {}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -2347,5 +2353,62 @@ do_test pager1-29.2 {
|
||||
file size test.db
|
||||
} [expr 4096*3]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that if an empty database file (size 0 bytes) is opened in
|
||||
# exclusive-locking mode, any journal file is deleted from the file-system
|
||||
# without being rolled back. And that the RESERVED lock obtained while
|
||||
# doing this is not released.
|
||||
#
|
||||
do_test pager1-30.1 {
|
||||
db close
|
||||
file delete test.db
|
||||
file delete test.db-journal
|
||||
set fd [open test.db-journal w]
|
||||
seek $fd [expr 512+1032*2]
|
||||
puts -nonewline $fd x
|
||||
close $fd
|
||||
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA locking_mode=EXCLUSIVE;
|
||||
SELECT count(*) FROM sqlite_master;
|
||||
PRAGMA lock_status;
|
||||
}
|
||||
} {exclusive 0 main reserved temp closed}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that if the "page-size" field in a journal-header is 0, the journal
|
||||
# file can still be rolled back. This is required for backward compatibility -
|
||||
# versions of SQLite prior to 3.5.8 always set this field to zero.
|
||||
#
|
||||
do_test pager1-31.1 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
PRAGMA cache_size = 10;
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(x, y, UNIQUE(x, y));
|
||||
INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500));
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1;
|
||||
BEGIN;
|
||||
UPDATE t1 SET y = randomblob(1499);
|
||||
}
|
||||
file copy test.db test.db2
|
||||
file copy test.db-journal test.db2-journal
|
||||
|
||||
hexio_write test.db2-journal 24 00000000
|
||||
sqlite3 db2 test.db2
|
||||
execsql { PRAGMA integrity_check } db2
|
||||
} {ok}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ do_test pager2-2.1 {
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {off 1 2}
|
||||
} {off}
|
||||
do_test pager2-2.2 {
|
||||
faultsim_delete_and_reopen
|
||||
execsql {
|
||||
|
||||
+25
-16
@@ -673,21 +673,30 @@ do_faultsim_test pagerfault-14a -prep {
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 {}} {1 {SQL logic error or missing database}}
|
||||
}
|
||||
do_faultsim_test pagerfault-14b -prep {
|
||||
catch { db2 close }
|
||||
faultsim_restore_and_reopen
|
||||
sqlite3 db2 ""
|
||||
db2 eval { PRAGMA page_size = 4096; CREATE TABLE xx(a) }
|
||||
} -body {
|
||||
sqlite3_backup B db2 main db main
|
||||
B step 200
|
||||
set rc [B finish]
|
||||
if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR}
|
||||
if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] }
|
||||
set {} {}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
|
||||
# If TEMP_STORE is 2 or greater, then the database [db2] will be created
|
||||
# as an in-memory database. This test will not work in that case, as it
|
||||
# is not possible to change the page-size of an in-memory database. Even
|
||||
# using the backup API.
|
||||
#
|
||||
if {$TEMP_STORE<2} {
|
||||
do_faultsim_test pagerfault-14b -prep {
|
||||
catch { db2 close }
|
||||
faultsim_restore_and_reopen
|
||||
sqlite3 db2 ""
|
||||
db2 eval { PRAGMA page_size = 4096; CREATE TABLE xx(a) }
|
||||
} -body {
|
||||
sqlite3_backup B db2 main db main
|
||||
B step 200
|
||||
set rc [B finish]
|
||||
if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR}
|
||||
if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] }
|
||||
set {} {}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 {sqlite3_backup_init() failed}}
|
||||
}
|
||||
}
|
||||
|
||||
do_faultsim_test pagerfault-14c -prep {
|
||||
catch { db2 close }
|
||||
faultsim_restore_and_reopen
|
||||
@@ -705,7 +714,7 @@ do_faultsim_test pagerfault-14c -prep {
|
||||
if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] }
|
||||
set {} {}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
faultsim_test_result {0 {}} {1 {sqlite3_backup_init() failed}}
|
||||
}
|
||||
|
||||
do_test pagerfault-15-pre1 {
|
||||
@@ -1091,8 +1100,8 @@ do_faultsim_test pagerfault-22 -prep {
|
||||
# PagerRollback(<file-db>)
|
||||
#
|
||||
do_faultsim_test pagerfault-23 -prep {
|
||||
foreach f [glob -nocomplain test.db*] { file delete -force $f }
|
||||
sqlite3 db :memory:
|
||||
foreach f [glob -nocomplain test.db*] { file delete -force $f }
|
||||
db eval {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE t1(a, b);
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# 2011 January 28
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
if {[permutation] == "inmemory_journal"} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Create a database with page-size 2048 bytes that uses 2 pages. Populate
|
||||
# it so that if the page-size is changed to 1024 bytes and the db vacuumed,
|
||||
# the new db size is 3 pages.
|
||||
#
|
||||
do_test pagerfault3-pre1 {
|
||||
execsql {
|
||||
PRAGMA page_size = 2048;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(randomblob(1200));
|
||||
PRAGMA page_count;
|
||||
}
|
||||
} {2}
|
||||
do_test pagerfault3-pre2 {
|
||||
faultsim_save_and_close
|
||||
faultsim_restore_and_reopen
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
VACUUM;
|
||||
PRAGMA page_count;
|
||||
}
|
||||
} {3}
|
||||
|
||||
# Now do the page-size change and VACUUM with IO error injection. When
|
||||
# an IO error is injected into the final xSync() of the commit, the pager
|
||||
# will have to extend the db file from 3072 to 4096 byts when rolling
|
||||
# back the hot-journal file. This is a special case in pager_truncate().
|
||||
#
|
||||
do_faultsim_test pagerfault3-1 -faults ioerr-transient -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
VACUUM;
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
faultsim_integrity_check
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -21,6 +21,14 @@ source $testdir/tester.tcl
|
||||
#
|
||||
do_not_use_codec
|
||||
|
||||
# Only works with a mode-2 pcache where all pcaches share a single set
|
||||
# of pages.
|
||||
#
|
||||
ifcapable {!memorymanage && threadsafe} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# The pcache module limits the number of pages available to purgeable
|
||||
# caches to the sum of the 'cache_size' values for the set of open
|
||||
# caches. This block of tests, pcache-1.*, test that the library behaves
|
||||
|
||||
+49
-8
@@ -25,6 +25,7 @@ db close
|
||||
# -presql SQL (default "")
|
||||
# -files LIST-OF-FILES (default $::ALLTESTS)
|
||||
# -prefix NAME (default "$::NAME.")
|
||||
# -dbconfig SCRIPT (default "")
|
||||
#
|
||||
proc test_suite {name args} {
|
||||
|
||||
@@ -34,6 +35,7 @@ proc test_suite {name args} {
|
||||
set default(-description) "no description supplied (fixme)"
|
||||
set default(-files) ""
|
||||
set default(-prefix) "${name}."
|
||||
set default(-dbconfig) ""
|
||||
|
||||
array set options [array get default]
|
||||
if {[llength $args]%2} {
|
||||
@@ -48,7 +50,6 @@ proc test_suite {name args} {
|
||||
|
||||
set ::testspec($name) [array get options]
|
||||
lappend ::testsuitelist $name
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -69,6 +70,7 @@ proc test_set {args} {
|
||||
foreach f $a { set t($f) 1 }
|
||||
} else {
|
||||
foreach f $a { array unset t $f }
|
||||
foreach f $a { array unset t */$f }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,13 +86,17 @@ proc test_set {args} {
|
||||
#
|
||||
set alltests [list]
|
||||
foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] }
|
||||
foreach f [glob -nocomplain $testdir/../ext/rtree/*.test] {
|
||||
lappend alltests $f
|
||||
}
|
||||
|
||||
if {$::tcl_platform(platform)!="unix"} {
|
||||
set alltests [test_set $alltests -exclude crash.test crash2.test]
|
||||
}
|
||||
set alltests [test_set $alltests -exclude {
|
||||
all.test async.test quick.test veryquick.test
|
||||
memleak.test permutations.test soak.test fts3.test
|
||||
mallocAll.test
|
||||
mallocAll.test rtree.test
|
||||
}]
|
||||
|
||||
set allquicktests [test_set $alltests -exclude {
|
||||
@@ -105,7 +111,7 @@ set allquicktests [test_set $alltests -exclude {
|
||||
thread003.test thread004.test thread005.test trans2.test vacuum3.test
|
||||
incrvacuum_ioerr.test autovacuum_crash.test btree8.test shared_err.test
|
||||
vtab_err.test walslow.test walcrash.test
|
||||
walthread.test
|
||||
walthread.test rtree3.test
|
||||
}]
|
||||
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
|
||||
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
|
||||
@@ -132,6 +138,17 @@ test_suite "veryquick" -prefix "" -description {
|
||||
test_set $allquicktests -exclude *malloc* *ioerr* *fault*
|
||||
]
|
||||
|
||||
test_suite "valgrind" -prefix "" -description {
|
||||
Run the "veryquick" test suite with a couple of multi-process tests (that
|
||||
fail under valgrind) omitted.
|
||||
} -files [
|
||||
test_set $allquicktests -exclude *malloc* *ioerr* *fault*
|
||||
] -initialize {
|
||||
set ::G(valgrind) 1
|
||||
} -shutdown {
|
||||
unset -nocomplain ::G(valgrind)
|
||||
}
|
||||
|
||||
test_suite "quick" -prefix "" -description {
|
||||
Quick test suite. Runs in around 10 minutes on a workstation.
|
||||
} -files [
|
||||
@@ -154,14 +171,16 @@ test_suite "threads" -prefix "" -description {
|
||||
}
|
||||
|
||||
test_suite "fts3" -prefix "" -description {
|
||||
All FTS3 tests except fts3malloc.test and fts3rnd.test.
|
||||
All FTS3 tests except fts3rnd.test.
|
||||
} -files {
|
||||
fts3aa.test fts3ab.test fts3ac.test fts3ad.test fts3ae.test
|
||||
fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test
|
||||
fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test
|
||||
fts3atoken.test fts3b.test fts3c.test fts3cov.test fts3d.test
|
||||
fts3e.test fts3expr.test fts3expr2.test fts3near.test
|
||||
fts3query.test fts3snippet.test
|
||||
fts3defer.test fts3defer2.test fts3e.test fts3expr.test fts3expr2.test
|
||||
fts3near.test fts3query.test fts3shared.test fts3snippet.test
|
||||
|
||||
fts3fault.test fts3malloc.test fts3matchinfo.test
|
||||
}
|
||||
|
||||
|
||||
@@ -176,7 +195,7 @@ test_suite "coverage-wal" -description {
|
||||
} -files {
|
||||
wal.test wal2.test wal3.test walmode.test
|
||||
walbak.test walhook.test walcrash2.test walcksum.test
|
||||
walfault.test walbig.test
|
||||
walfault.test walbig.test walnoshm.test
|
||||
}
|
||||
|
||||
test_suite "coverage-pager" -description {
|
||||
@@ -582,6 +601,7 @@ ifcapable mem5 {
|
||||
} -initialize {
|
||||
catch {db close}
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_memstatus 0
|
||||
sqlite3_config_heap 40000000 16
|
||||
sqlite3_config_lookaside 0 0
|
||||
install_malloc_faultsim 1
|
||||
@@ -734,6 +754,24 @@ test_suite "wal" -description {
|
||||
fts3am.test fts3an.test fts3ao.test fts3b.test
|
||||
fts3c.test fts3d.test fts3e.test fts3query.test
|
||||
}
|
||||
|
||||
test_suite "rtree" -description {
|
||||
All R-tree related tests. Provides coverage of source file rtree.c.
|
||||
} -files [glob -nocomplain $::testdir/../ext/rtree/*.test]
|
||||
|
||||
test_suite "no_optimization" -description {
|
||||
Run test scripts with optimizations disabled using the
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) interface.
|
||||
} -files {
|
||||
where.test where2.test where3.test where4.test where5.test
|
||||
where6.test where7.test where8.test where9.test
|
||||
whereA.test whereB.test wherelimit.test
|
||||
select1.test select2.test select3.test select4.test select5.test
|
||||
select7.test select8.test selectA.test selectC.test
|
||||
} -dbconfig {
|
||||
optimization_control $::dbhandle all 0
|
||||
}
|
||||
|
||||
# End of tests
|
||||
#############################################################################
|
||||
|
||||
@@ -755,11 +793,13 @@ proc run_tests {name args} {
|
||||
set ::G(perm:prefix) $options(-prefix)
|
||||
set ::G(perm:presql) $options(-presql)
|
||||
set ::G(isquick) 1
|
||||
set ::G(perm:dbconfig) $options(-dbconfig)
|
||||
|
||||
uplevel $options(-initialize)
|
||||
|
||||
foreach file [lsort $options(-files)] {
|
||||
slave_test_file $::testdir/$file
|
||||
if {[file tail $file] == $file} { set file [file join $::testdir $file] }
|
||||
slave_test_file $file
|
||||
}
|
||||
|
||||
uplevel $options(-shutdown)
|
||||
@@ -767,6 +807,7 @@ proc run_tests {name args} {
|
||||
unset ::G(perm:name)
|
||||
unset ::G(perm:prefix)
|
||||
unset ::G(perm:presql)
|
||||
unset ::G(perm:dbconfig)
|
||||
}
|
||||
|
||||
proc run_test_suite {name} {
|
||||
|
||||
+11
-11
@@ -1379,21 +1379,21 @@ ifcapable lock_proxy_pragmas&&prefer_proxy_locking {
|
||||
|
||||
set sqlite_hostid_num 2
|
||||
do_test pragma-16.7 {
|
||||
sqlite3 db test2.db
|
||||
execsql {
|
||||
PRAGMA lock_proxy_file=":auto:";
|
||||
}
|
||||
catchsql {
|
||||
select * from sqlite_master;
|
||||
}
|
||||
list [catch {
|
||||
sqlite3 db test2.db
|
||||
execsql {
|
||||
PRAGMA lock_proxy_file=":auto:";
|
||||
select * from sqlite_master;
|
||||
}
|
||||
} msg] $msg
|
||||
} {1 {database is locked}}
|
||||
db close
|
||||
|
||||
do_test pragma-16.8 {
|
||||
sqlite3 db test2.db
|
||||
catchsql {
|
||||
select * from sqlite_master;
|
||||
}
|
||||
list [catch {
|
||||
sqlite3 db test2.db
|
||||
execsql { select * from sqlite_master }
|
||||
} msg] $msg
|
||||
} {1 {database is locked}}
|
||||
|
||||
db2 close
|
||||
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
# 2010 September 1
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
db close
|
||||
|
||||
do_test quota-1.1 { sqlite3_quota_initialize nosuchvfs 1 } {SQLITE_ERROR}
|
||||
do_test quota-1.2 { sqlite3_quota_initialize "" 1 } {SQLITE_OK}
|
||||
do_test quota-1.3 { sqlite3_quota_initialize "" 1 } {SQLITE_MISUSE}
|
||||
do_test quota-1.4 { sqlite3_quota_shutdown } {SQLITE_OK}
|
||||
|
||||
do_test quota-1.5 { sqlite3_quota_initialize "" 0 } {SQLITE_OK}
|
||||
do_test quota-1.6 { sqlite3_quota_shutdown } {SQLITE_OK}
|
||||
do_test quota-1.7 { sqlite3_quota_initialize "" 1 } {SQLITE_OK}
|
||||
do_test quota-1.8 { sqlite3_quota_shutdown } {SQLITE_OK}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Some simple warm-body tests with a single database file in rollback
|
||||
# mode:
|
||||
#
|
||||
# quota-2.1.*: Test that SQLITE_FULL is returned if the database would
|
||||
# exceed the configured quota.
|
||||
#
|
||||
# quota-2.2.*: Test that SQLITE_FULL is not returned and the database
|
||||
# grows if the callback extends the quota when the database
|
||||
# attempts to grow beyond the configured quota.
|
||||
#
|
||||
# quota-2.3.*: Open and close a db that is not part of any quota group. At
|
||||
# one point this was causing mutex refs to be leaked.
|
||||
#
|
||||
# quota-2.4.*: Try to shutdown the quota system before closing the db
|
||||
# file. Check that this fails and the quota system still works
|
||||
# afterwards. Then close the database and successfully shut
|
||||
# down the quota system.
|
||||
#
|
||||
sqlite3_quota_initialize "" 1
|
||||
|
||||
proc quota_check {filename limitvar size} {
|
||||
upvar $limitvar limit
|
||||
|
||||
lappend ::quota [set limit] $size
|
||||
if {[info exists ::quota_request_ok]} { set limit $size }
|
||||
}
|
||||
|
||||
do_test quota-2.1.1 {
|
||||
sqlite3_quota_set *test.db 4096 quota_check
|
||||
} {SQLITE_OK}
|
||||
do_test quota-2.1.2 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA page_size=1024;
|
||||
PRAGMA auto_vacuum=OFF;
|
||||
PRAGMA journal_mode=DELETE;
|
||||
}
|
||||
set ::quota [list]
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, randomblob(1100));
|
||||
INSERT INTO t1 VALUES(2, randomblob(1100));
|
||||
}
|
||||
set ::quota
|
||||
} {}
|
||||
do_test quota-2.1.3 { file size test.db } {4096}
|
||||
do_test quota-2.1.4 {
|
||||
catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
|
||||
} {1 {database or disk is full}}
|
||||
do_test quota-2.1.5 { set ::quota } {4096 5120}
|
||||
|
||||
set ::quota_request_ok 1
|
||||
set ::quota [list]
|
||||
do_test quota-2.2.1 {
|
||||
execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
|
||||
} {}
|
||||
do_test quota-2.2.2 { set ::quota } {4096 5120}
|
||||
do_test quota-2.2.3 { file size test.db } {5120}
|
||||
unset ::quota_request_ok
|
||||
|
||||
do_test quota-2.3.1 {
|
||||
sqlite3 db2 bak.db
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
do_test quota-2.4.1 {
|
||||
sqlite3_quota_shutdown
|
||||
} {SQLITE_MISUSE}
|
||||
set ::quota [list]
|
||||
do_test quota-2.4.2 {
|
||||
catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
|
||||
} {1 {database or disk is full}}
|
||||
do_test quota-2.4.3 { set ::quota } {5120 6144}
|
||||
do_test quota-2.4.4 { file size test.db } {5120}
|
||||
do_test quota-2.4.99 {
|
||||
db close
|
||||
sqlite3_quota_shutdown
|
||||
} {SQLITE_OK}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Try some tests with more than one connection to a database file. Still
|
||||
# in rollback mode.
|
||||
#
|
||||
# quota-3.1.*: Two connections to a single database file.
|
||||
#
|
||||
# quota-3.2.*: Two connections to each of several database files (that
|
||||
# are in the same quota group).
|
||||
#
|
||||
proc quota_check {filename limitvar size} {
|
||||
upvar $limitvar limit
|
||||
lappend ::quota [set limit] $size
|
||||
if {[info exists ::quota_request_ok]} { set limit $size }
|
||||
}
|
||||
|
||||
do_test quota-3.1.1 {
|
||||
file delete -force test.db
|
||||
sqlite3_quota_initialize "" 1
|
||||
sqlite3_quota_set *test.db 4096 quota_check
|
||||
} {SQLITE_OK}
|
||||
do_test quota-3.1.2 {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA journal_mode = delete;
|
||||
PRAGMA auto_vacuum = off;
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
}
|
||||
file size test.db
|
||||
} {3072}
|
||||
do_test quota-3.1.3 {
|
||||
sqlite3 db2 test.db
|
||||
set ::quota [list]
|
||||
execsql { CREATE TABLE t2(a, b) } db2
|
||||
set ::quota
|
||||
} {}
|
||||
do_test quota-3.1.4 {
|
||||
catchsql { CREATE TABLE t3(a, b) }
|
||||
} {1 {database or disk is full}}
|
||||
do_test quota-3.1.5 {
|
||||
set ::quota_request_ok 1
|
||||
execsql { CREATE TABLE t3(a, b) }
|
||||
} {}
|
||||
do_test quota-3.1.6 {
|
||||
db close
|
||||
db2 close
|
||||
sqlite3_quota_set *test.db 0 {}
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test quota-3.2.1 {
|
||||
file delete force test.db test2.db
|
||||
|
||||
sqlite3_quota_set * 4096 {}
|
||||
sqlite3 db1a test.db
|
||||
sqlite3 db2a test2.db
|
||||
|
||||
foreach db {db1a db2a} {
|
||||
execsql {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA journal_mode = delete;
|
||||
PRAGMA auto_vacuum = off;
|
||||
CREATE TABLE t1(a, b);
|
||||
} $db
|
||||
}
|
||||
|
||||
sqlite3 db1b test.db
|
||||
sqlite3 db2b test2.db
|
||||
|
||||
list [file size test.db] [file size test2.db]
|
||||
} {2048 2048}
|
||||
|
||||
catch { unset ::quota_request_ok }
|
||||
|
||||
do_test quota-3.2.2 { execsql { INSERT INTO t1 VALUES('x', 'y') } db1a } {}
|
||||
do_test quota-3.2.3 { execsql { INSERT INTO t1 VALUES('v', 'w') } db1b } {}
|
||||
do_test quota-3.2.4 { execsql { INSERT INTO t1 VALUES('t', 'u') } db2a } {}
|
||||
do_test quota-3.2.5 { execsql { INSERT INTO t1 VALUES('r', 's') } db2b } {}
|
||||
|
||||
do_test quota-3.2.6 {
|
||||
catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
|
||||
} {1 {database or disk is full}}
|
||||
do_test quota-3.2.7 {
|
||||
catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
|
||||
} {1 {database or disk is full}}
|
||||
do_test quota-3.2.8 {
|
||||
catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
|
||||
} {1 {database or disk is full}}
|
||||
do_test quota-3.2.9 {
|
||||
catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
|
||||
} {1 {database or disk is full}}
|
||||
|
||||
set ::quota [list]
|
||||
proc quota_callback {file limitvar size} {
|
||||
upvar $limitvar limit
|
||||
lappend ::quota $file $size
|
||||
set limit 0
|
||||
}
|
||||
sqlite3_quota_set * 4096 quota_callback
|
||||
do_test quota-3.3.1 {
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
|
||||
execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
|
||||
set ::quota
|
||||
} [list [file join [pwd] test.db] 5120]
|
||||
|
||||
do_test quota-3.2.X {
|
||||
foreach db {db1a db2a db2b db1b} { catch { $db close } }
|
||||
sqlite3_quota_set * 0 {}
|
||||
} {SQLITE_OK}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Quotas are deleted when unused and when there limit is set to zero
|
||||
#
|
||||
|
||||
# Return a list of all currently defined quotas. Each quota is identified
|
||||
# by its pattern.
|
||||
proc quota_list {} {
|
||||
set allq {}
|
||||
foreach q [sqlite3_quota_dump] {
|
||||
lappend allq [lindex $q 0]
|
||||
}
|
||||
return [lsort $allq]
|
||||
}
|
||||
|
||||
do_test quota-4.1.1 {
|
||||
sqlite3_quota_set *test.db 0 {}
|
||||
quota_list
|
||||
} {}
|
||||
do_test quota-4.1.2 {
|
||||
sqlite3_quota_set *test.db 4096 {}
|
||||
quota_list
|
||||
} {*test.db}
|
||||
do_test quota-4.1.3 {
|
||||
sqlite3_quota_set *test2.db 0 {}
|
||||
quota_list
|
||||
} {*test.db}
|
||||
do_test quota-4.1.4 {
|
||||
sqlite3_quota_set *test2.db 100000 {}
|
||||
quota_list
|
||||
} {*test.db *test2.db}
|
||||
do_test quota-4.1.5 {
|
||||
sqlite3_quota_set *test.db 0 {}
|
||||
quota_list
|
||||
} {*test2.db}
|
||||
do_test quota-4.1.6 {
|
||||
file delete -force test2.db test2.db-journal test2.db-wal
|
||||
sqlite3 db test2.db
|
||||
db eval {CREATE TABLE t2(x); INSERT INTO t2 VALUES('tab-t2');}
|
||||
quota_list
|
||||
} {*test2.db}
|
||||
do_test quota-4.1.7 {
|
||||
catchsql {INSERT INTO t2 VALUES(zeroblob(200000))}
|
||||
} {1 {database or disk is full}}
|
||||
do_test quota-4.1.8 {
|
||||
sqlite3 db2 test2.db
|
||||
db2 eval {SELECT * FROM t2}
|
||||
} {tab-t2}
|
||||
do_test quota-4.1.9 {
|
||||
sqlite3_quota_set *test2.db 0 {}
|
||||
catchsql {INSERT INTO t2 VALUES(zeroblob(200000))}
|
||||
} {0 {}}
|
||||
do_test quota-4.1.10 {
|
||||
quota_list
|
||||
} {*test2.db}
|
||||
do_test quota-4.1.11 {
|
||||
db2 close
|
||||
quota_list
|
||||
} {*test2.db}
|
||||
do_test quota-4.1.12 {
|
||||
db close
|
||||
quota_list
|
||||
} {}
|
||||
|
||||
do_test quota-4.2.1 {
|
||||
sqlite3_quota_set A 1000 {}
|
||||
sqlite3_quota_set B 1000 {}
|
||||
sqlite3_quota_set C 1000 {}
|
||||
sqlite3_quota_set D 1000 {}
|
||||
quota_list
|
||||
} {A B C D}
|
||||
do_test quota-4.2.2 {
|
||||
sqlite3_quota_set C 0 {}
|
||||
sqlite3_quota_set B 0 {}
|
||||
quota_list
|
||||
} {A D}
|
||||
do_test quota-4.2.3 {
|
||||
sqlite3_quota_set A 0 {}
|
||||
sqlite3_quota_set D 0 {}
|
||||
quota_list
|
||||
} {}
|
||||
do_test quota-4.2.4 {
|
||||
sqlite3_quota_set A 1000 {}
|
||||
sqlite3_quota_set B 1000 {}
|
||||
sqlite3_quota_set C 1000 {}
|
||||
sqlite3_quota_set A 0 {}
|
||||
sqlite3_quota_set B 0 {}
|
||||
sqlite3_quota_set C 0 {}
|
||||
quota_list
|
||||
} {}
|
||||
do_test quota-4.2.5 {
|
||||
sqlite3_quota_set A 1000 {}
|
||||
sqlite3_quota_set B 1000 {}
|
||||
sqlite3_quota_set C 1000 {}
|
||||
sqlite3_quota_set C 0 {}
|
||||
sqlite3_quota_set B 0 {}
|
||||
sqlite3_quota_set A 0 {}
|
||||
quota_list
|
||||
} {}
|
||||
|
||||
do_test quota-4.3.1 {
|
||||
sqlite3_quota_set A 1000 quota_callback
|
||||
sqlite3 db A
|
||||
sqlite3_quota_set A 0 quota_callback
|
||||
db close
|
||||
quota_list
|
||||
} {}
|
||||
|
||||
do_test quota-4.4.1 {
|
||||
sqlite3_quota_set A 1000 quota_callback
|
||||
sqlite3_quota_shutdown
|
||||
} {SQLITE_OK}
|
||||
do_test quota-4.4.2 {
|
||||
quota_list
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests test that the quota VFS handles malloc and IO
|
||||
# errors.
|
||||
#
|
||||
|
||||
sqlite3_quota_initialize "" 1
|
||||
sqlite3_quota_set *test.db 4096 {}
|
||||
|
||||
do_faultsim_test quota-5.1 -prep {
|
||||
catch {db close}
|
||||
} -body {
|
||||
sqlite3 db test2.db
|
||||
}
|
||||
do_faultsim_test quota-5.2 -prep {
|
||||
catch {db close}
|
||||
} -body {
|
||||
sqlite3 db test.db
|
||||
}
|
||||
|
||||
catch { db close }
|
||||
file delete -force test.db
|
||||
|
||||
do_test quota-5.3.prep {
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA auto_vacuum = 1;
|
||||
PRAGMA page_size = 1024;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(10, zeroblob(1200));
|
||||
}
|
||||
faultsim_save_and_close
|
||||
} {}
|
||||
do_faultsim_test quota-5.3 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { DELETE FROM t1 }
|
||||
}
|
||||
|
||||
do_test quota-5.4.1 {
|
||||
catch { db close }
|
||||
file delete -force test.db
|
||||
file mkdir test.db
|
||||
list [catch { sqlite3 db test.db } msg] $msg
|
||||
} {1 {unable to open database file}}
|
||||
|
||||
do_faultsim_test quota-5.5 -prep {
|
||||
catch { sqlite3_quota_shutdown }
|
||||
} -body {
|
||||
sqlite3_quota_initialize "" 1
|
||||
}
|
||||
|
||||
do_faultsim_test quota-5.6 -prep {
|
||||
catch { sqlite3_quota_shutdown }
|
||||
sqlite3_quota_initialize "" 1
|
||||
} -body {
|
||||
sqlite3_quota_set * 4096 {}
|
||||
}
|
||||
|
||||
catch { sqlite3_quota_shutdown }
|
||||
finish_test
|
||||
@@ -0,0 +1,14 @@
|
||||
########################################################
|
||||
TOP=/home/drh/sqlite/sqlite
|
||||
|
||||
TCL_FLAGS=-I/home/drh/tcltk/86linux
|
||||
LIBTCL=/home/drh/tcltk/86linux/libtcl8.6.a -lm -ldl -lpthread
|
||||
|
||||
BCC = gcc
|
||||
TCC = gcc -ansi -g $(CFLAGS)
|
||||
NAWK = awk
|
||||
AR = ar cr
|
||||
RANLIB = ranlib
|
||||
THREADLIB = -lpthread -ldl -lz
|
||||
include $(TOP)/main.mk
|
||||
########################################################
|
||||
@@ -0,0 +1,332 @@
|
||||
|
||||
set rcsid {$Id: $}
|
||||
|
||||
# Documentation for this script. This may be output to stderr
|
||||
# if the script is invoked incorrectly. See the [process_options]
|
||||
# proc below.
|
||||
#
|
||||
set ::USAGE_MESSAGE {
|
||||
This Tcl script is used to test the various configurations required
|
||||
before releasing a new version. Supported command line options (all
|
||||
optional) are:
|
||||
|
||||
-makefile PATH-TO-MAKEFILE (default "releasetest.mk")
|
||||
-platform PLATFORM (see below)
|
||||
-quick BOOLEAN (default "0")
|
||||
|
||||
The default value for -makefile is "./releasetest.mk".
|
||||
|
||||
The script determines the default value for -platform using the
|
||||
$tcl_platform(os) and $tcl_platform(machine) variables. Supported
|
||||
platforms are "Linux-x86", "Linux-x86_64" and "Darwin-i386".
|
||||
|
||||
If the -quick option is set to true, then the "veryquick.test" script
|
||||
is run for all compilation configurations. Otherwise, sometimes "all.test"
|
||||
is run, sometimes "veryquick.test".
|
||||
|
||||
Almost any SQLite makefile (except those generated by configure - see below)
|
||||
should work. The following properties are required:
|
||||
|
||||
* The makefile should support the "fulltest" target.
|
||||
* The makefile should support the variable "OPTS" as a way to pass
|
||||
options from the make command line to lemon and the C compiler.
|
||||
|
||||
More precisely, the following invocation must be supported:
|
||||
|
||||
make -f $::MAKEFILE fulltest OPTS="-DSQLITE_SECURE_DELETE=1 -DSQLITE_DEBUG=1"
|
||||
|
||||
Makefiles generated by the sqlite configure program cannot be used as
|
||||
they do not respect the OPTS variable.
|
||||
|
||||
Example Makefile contents:
|
||||
|
||||
########################################################
|
||||
TOP=/home/dan/work/sqlite/sqlite
|
||||
|
||||
TCL_FLAGS=-I/home/dan/tcl/include
|
||||
LIBTCL=-L/home/dan/tcl/lib -ltcl
|
||||
|
||||
BCC = gcc
|
||||
TCC = gcc -ansi -g $(CFLAGS)
|
||||
NAWK = awk
|
||||
AR = ar cr
|
||||
RANLIB = ranlib
|
||||
THREADLIB = -lpthread -ldl
|
||||
include $(TOP)/main.mk
|
||||
########################################################
|
||||
}
|
||||
|
||||
array set ::Configs {
|
||||
"Default" {
|
||||
-O2
|
||||
}
|
||||
"Unlock-Notify" {
|
||||
-O2
|
||||
-DSQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
-DSQLITE_THREADSAFE
|
||||
-DOS_UNIX
|
||||
-DSQLITE_TCL_DEFAULT_FULLMUTEX=1
|
||||
}
|
||||
"Secure-Delete" {
|
||||
-O2
|
||||
-DSQLITE_SECURE_DELETE=1
|
||||
-DSQLITE_SOUNDEX=1
|
||||
}
|
||||
"Update-Delete-Limit" {
|
||||
-O2
|
||||
-DSQLITE_DEFAULT_FILE_FORMAT=4
|
||||
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
|
||||
}
|
||||
"Debug-One" {
|
||||
-O2
|
||||
-DSQLITE_DEBUG=1
|
||||
-DSQLITE_MEMDEBUG=1
|
||||
-DSQLITE_MUTEX_NOOP=1
|
||||
-DSQLITE_TCL_DEFAULT_FULLMUTEX=1
|
||||
-DSQLITE_ENABLE_FTS3=1
|
||||
-DSQLITE_ENABLE_RTREE=1
|
||||
-DSQLITE_ENABLE_MEMSYS5=1
|
||||
-DSQLITE_ENABLE_MEMSYS3=1
|
||||
-DSQLITE_ENABLE_COLUMN_METADATA=1
|
||||
}
|
||||
"Device-One" {
|
||||
-O2
|
||||
-DSQLITE_DEBUG=1
|
||||
-DSQLITE_DEFAULT_AUTOVACUUM=1
|
||||
-DSQLITE_DEFAULT_CACHE_SIZE=64
|
||||
-DSQLITE_DEFAULT_PAGE_SIZE=1024
|
||||
-DSQLITE_DEFAULT_TEMP_CACHE_SIZE=32
|
||||
-DSQLITE_DISABLE_LFS=1
|
||||
-DSQLITE_ENABLE_ATOMIC_WRITE=1
|
||||
-DSQLITE_ENABLE_IOTRACE=1
|
||||
-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
|
||||
-DSQLITE_MAX_PAGE_SIZE=4096
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION=1
|
||||
-DSQLITE_OMIT_PROGRESS_CALLBACK=1
|
||||
-DSQLITE_OMIT_VIRTUALTABLE=1
|
||||
-DSQLITE_TEMP_STORE=3
|
||||
}
|
||||
"Device-Two" {
|
||||
-DSQLITE_4_BYTE_ALIGNED_MALLOC=1
|
||||
-DSQLITE_DEFAULT_AUTOVACUUM=1
|
||||
-DSQLITE_DEFAULT_CACHE_SIZE=1000
|
||||
-DSQLITE_DEFAULT_LOCKING_MODE=0
|
||||
-DSQLITE_DEFAULT_PAGE_SIZE=1024
|
||||
-DSQLITE_DEFAULT_TEMP_CACHE_SIZE=1000
|
||||
-DSQLITE_DISABLE_LFS=1
|
||||
-DSQLITE_ENABLE_FTS3=1
|
||||
-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
|
||||
-DSQLITE_ENABLE_RTREE=1
|
||||
-DSQLITE_MAX_COMPOUND_SELECT=50
|
||||
-DSQLITE_MAX_PAGE_SIZE=32768
|
||||
-DSQLITE_OMIT_TRACE=1
|
||||
-DSQLITE_TEMP_STORE=3
|
||||
-DSQLITE_THREADSAFE=2
|
||||
}
|
||||
"Locking-Style" {
|
||||
-O2
|
||||
-DSQLITE_ENABLE_LOCKING_STYLE=1
|
||||
}
|
||||
"OS-X" {
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION=1
|
||||
-DSQLITE_DEFAULT_MEMSTATUS=0
|
||||
-DSQLITE_THREADSAFE=2
|
||||
-DSQLITE_OS_UNIX=1
|
||||
-DSQLITE_ENABLE_LOCKING_STYLE=1
|
||||
-DUSE_PREAD=1
|
||||
-DSQLITE_ENABLE_RTREE=1
|
||||
-DSQLITE_ENABLE_FTS3=1
|
||||
-DSQLITE_ENABLE_FTS3_PARENTHESIS=1
|
||||
-DSQLITE_DEFAULT_CACHE_SIZE=1000
|
||||
-DSQLITE_MAX_LENGTH=2147483645
|
||||
-DSQLITE_MAX_VARIABLE_NUMBER=500000
|
||||
-DSQLITE_DEBUG=1
|
||||
-DSQLITE_PREFER_PROXY_LOCKING=1
|
||||
}
|
||||
"Extra-Robustness" {
|
||||
-DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
|
||||
}
|
||||
}
|
||||
|
||||
array set ::Platforms {
|
||||
Linux-x86_64 {
|
||||
"Secure-Delete" test
|
||||
"Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test"
|
||||
"Update-Delete-Limit" test
|
||||
"Debug-One" test
|
||||
"Extra-Robustness" test
|
||||
"Device-Two" test
|
||||
"Default" "threadtest test"
|
||||
"Device-One" fulltest
|
||||
}
|
||||
Linux-i686 {
|
||||
"Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test"
|
||||
"Device-One" test
|
||||
"Device-Two" test
|
||||
"Default" "threadtest fulltest"
|
||||
}
|
||||
Darwin-i386 {
|
||||
"Locking-Style" test
|
||||
"OS-X" "threadtest fulltest"
|
||||
}
|
||||
}
|
||||
|
||||
# End of configuration section.
|
||||
#########################################################################
|
||||
#########################################################################
|
||||
|
||||
foreach {key value} [array get ::Platforms] {
|
||||
foreach {v t} $value {
|
||||
if {0==[info exists ::Configs($v)]} {
|
||||
puts stderr "No such configuration: \"$v\""
|
||||
exit -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc run_test_suite {name testtarget config} {
|
||||
|
||||
# Tcl variable $opts is used to build up the value used to set the
|
||||
# OPTS Makefile variable. Variable $cflags holds the value for
|
||||
# CFLAGS. The makefile will pass OPTS to both gcc and lemon, but
|
||||
# CFLAGS is only passed to gcc.
|
||||
#
|
||||
set cflags ""
|
||||
set opts ""
|
||||
foreach arg $config {
|
||||
if {[string match -D* $arg]} {
|
||||
lappend opts $arg
|
||||
} else {
|
||||
lappend cflags $arg
|
||||
}
|
||||
}
|
||||
|
||||
set cflags [join $cflags " "]
|
||||
set opts [join $opts " "]
|
||||
append opts " -DSQLITE_NO_SYNC=1 -DHAVE_USLEEP"
|
||||
|
||||
# Set the sub-directory to use.
|
||||
#
|
||||
set dir [string tolower [string map {- _ " " _} $name]]
|
||||
|
||||
if {$::tcl_platform(platform)=="windows"} {
|
||||
append opts " -DSQLITE_OS_WIN=1"
|
||||
} elseif {$::tcl_platform(platform)=="os2"} {
|
||||
append opts " -DSQLITE_OS_OS2=1"
|
||||
} else {
|
||||
append opts " -DSQLITE_OS_UNIX=1"
|
||||
}
|
||||
|
||||
# Run the test.
|
||||
#
|
||||
set makefile [file normalize $::MAKEFILE]
|
||||
file mkdir $dir
|
||||
puts -nonewline "Testing configuration \"$name\" (logfile=$dir/test.log)..."
|
||||
flush stdout
|
||||
|
||||
set makecmd [concat \
|
||||
[list exec make -C $dir -f $makefile clean] \
|
||||
$testtarget \
|
||||
[list CFLAGS=$cflags OPTS=$opts >& $dir/test.log] \
|
||||
]
|
||||
|
||||
set tm1 [clock seconds]
|
||||
set rc [catch $makecmd]
|
||||
set tm2 [clock seconds]
|
||||
|
||||
set minutes [expr {($tm2-$tm1)/60}]
|
||||
set seconds [expr {($tm2-$tm1)%60}]
|
||||
puts -nonewline [format " (%d:%.2d) " $minutes $seconds]
|
||||
if {$rc} {
|
||||
puts "FAILED."
|
||||
} else {
|
||||
puts "Ok."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# This proc processes the command line options passed to this script.
|
||||
# Currently the only option supported is "-makefile", default
|
||||
# "releasetest.mk". Set the ::MAKEFILE variable to the value of this
|
||||
# option.
|
||||
#
|
||||
proc process_options {argv} {
|
||||
set ::MAKEFILE releasetest.mk ;# Default value
|
||||
set ::QUICK 0 ;# Default value
|
||||
set platform $::tcl_platform(os)-$::tcl_platform(machine)
|
||||
|
||||
for {set i 0} {$i < [llength $argv]} {incr i} {
|
||||
switch -- [lindex $argv $i] {
|
||||
-makefile {
|
||||
incr i
|
||||
set ::MAKEFILE [lindex $argv $i]
|
||||
}
|
||||
|
||||
-platform {
|
||||
incr i
|
||||
set platform [lindex $argv $i]
|
||||
}
|
||||
|
||||
-quick {
|
||||
incr i
|
||||
set ::QUICK [lindex $argv $i]
|
||||
}
|
||||
|
||||
default {
|
||||
puts stderr ""
|
||||
puts stderr [string trim $::USAGE_MESSAGE]
|
||||
exit -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set ::MAKEFILE [file normalize $::MAKEFILE]
|
||||
|
||||
if {0==[info exists ::Platforms($platform)]} {
|
||||
puts "Unknown platform: $platform"
|
||||
puts -nonewline "Set the -platform option to "
|
||||
set print [list]
|
||||
foreach p [array names ::Platforms] {
|
||||
lappend print "\"$p\""
|
||||
}
|
||||
lset print end "or [lindex $print end]"
|
||||
puts "[join $print {, }]."
|
||||
exit
|
||||
}
|
||||
|
||||
set ::CONFIGLIST $::Platforms($platform)
|
||||
puts "Running the following configurations for $platform:"
|
||||
puts " [string trim $::CONFIGLIST]"
|
||||
}
|
||||
|
||||
# Main routine.
|
||||
#
|
||||
proc main {argv} {
|
||||
|
||||
# Process any command line options.
|
||||
process_options $argv
|
||||
|
||||
foreach {zConfig target} $::CONFIGLIST {
|
||||
if {$::QUICK} {set target test}
|
||||
set config_options $::Configs($zConfig)
|
||||
|
||||
run_test_suite $zConfig $target $config_options
|
||||
|
||||
# If the configuration included the SQLITE_DEBUG option, then remove
|
||||
# it and run veryquick.test. If it did not include the SQLITE_DEBUG option
|
||||
# add it and run veryquick.test.
|
||||
set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*]
|
||||
if {$debug_idx < 0} {
|
||||
run_test_suite "${zConfig}_debug" test [
|
||||
concat $config_options -DSQLITE_DEBUG=1
|
||||
]
|
||||
} else {
|
||||
run_test_suite "${zConfig}_ndebug" test [
|
||||
lreplace $config_options $debug_idx $debug_idx
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
main $argv
|
||||
+5
-26
@@ -1,3 +1,4 @@
|
||||
# 2008 June 23
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
@@ -6,34 +7,12 @@
|
||||
#***********************************************************************
|
||||
# This file runs all rtree related tests.
|
||||
#
|
||||
# $Id: rtree.test,v 1.3 2009/05/25 14:17:35 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/permutations.test
|
||||
|
||||
rename finish_test rtree_finish_test
|
||||
proc finish_test {} {}
|
||||
|
||||
set RTREE_EXCLUDE { }
|
||||
if {[info exists G(isquick)] && $G(isquick)} {
|
||||
set RTREE_EXCLUDE rtree3.test
|
||||
ifcapable rtree {
|
||||
run_test_suite rtree
|
||||
}
|
||||
|
||||
set rtreedir [file join $testdir .. ext rtree]
|
||||
|
||||
foreach testfile [lsort -dictionary [glob -nocomplain $rtreedir/*.test]] {
|
||||
set tail [file tail $testfile]
|
||||
if {[lsearch -exact $RTREE_EXCLUDE $tail]>=0} continue
|
||||
source $testfile
|
||||
catch {db close}
|
||||
if {$sqlite_open_file_count>0} {
|
||||
puts "$tail did not close all files: $sqlite_open_file_count"
|
||||
fail_test $tail
|
||||
set sqlite_open_file_count 0
|
||||
}
|
||||
}
|
||||
|
||||
set sqlite_open_file_count 0
|
||||
rtree_finish_test
|
||||
rename finish_test {}
|
||||
rename rtree_finish_test finish_test
|
||||
finish_test
|
||||
|
||||
+24
-1
@@ -905,7 +905,7 @@ if {[wal_is_wal_mode]==0} {
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12}
|
||||
}
|
||||
|
||||
db close
|
||||
@@ -1019,4 +1019,27 @@ do_multiclient_test tn {
|
||||
} {1 2 3 4}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This next block of tests verifies that a problem reported on the mailing
|
||||
# list has been resolved. At one point the second "CREATE TABLE t6" would
|
||||
# fail as table t6 still existed in the internal cache of the db schema
|
||||
# (even though it had been removed from the database by the ROLLBACK
|
||||
# command).
|
||||
#
|
||||
sqlite3 db test.db
|
||||
do_execsql_test savepoint-17.1 {
|
||||
BEGIN;
|
||||
CREATE TABLE t6(a, b);
|
||||
INSERT INTO t6 VALUES(1, 2);
|
||||
SAVEPOINT one;
|
||||
INSERT INTO t6 VALUES(3, 4);
|
||||
ROLLBACK TO one;
|
||||
SELECT * FROM t6;
|
||||
ROLLBACK;
|
||||
} {1 2}
|
||||
|
||||
do_execsql_test savepoint-17.2 {
|
||||
CREATE TABLE t6(a, b);
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
# 2010 September 28
|
||||
#
|
||||
# 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 regression tests for SQLite library. The
|
||||
# focus of this file is testing that a trigger may have the same
|
||||
# name as an index, view or table in the same database.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
# Test organization:
|
||||
#
|
||||
# schema4-1.*: Dropping and creating triggers and other objects where
|
||||
# triggers and at least on other object share a name.
|
||||
#
|
||||
# schema4-2.*: Renaming tables where there is a trigger that shares the
|
||||
# name of the table or one of its indices.
|
||||
#
|
||||
|
||||
do_execsql_test schema4-1.1 {
|
||||
CREATE TABLE log(x, a, b);
|
||||
CREATE TABLE tbl(a, b);
|
||||
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE VIEW v1 AS SELECT * FROM tbl;
|
||||
CREATE INDEX i1 ON tbl(a);
|
||||
} {}
|
||||
|
||||
do_execsql_test schema4-1.2 {
|
||||
CREATE TRIGGER t1 AFTER INSERT ON tbl BEGIN
|
||||
INSERT INTO log VALUES('after insert', new.a, new.b);
|
||||
END;
|
||||
CREATE TRIGGER v1 AFTER UPDATE ON tbl BEGIN
|
||||
INSERT INTO log VALUES('after update', new.a, new.b);
|
||||
END;
|
||||
CREATE TRIGGER i1 AFTER DELETE ON tbl BEGIN
|
||||
INSERT INTO log VALUES('after delete', old.a, old.b);
|
||||
END;
|
||||
} {}
|
||||
|
||||
do_execsql_test schema4-1.3 {
|
||||
INSERT INTO tbl VALUES(1, 2);
|
||||
UPDATE tbl SET b=a+b, a=a+1;
|
||||
DELETE FROM tbl;
|
||||
|
||||
SELECT x, a, b FROM log;
|
||||
} {{after insert} 1 2 {after update} 2 3 {after delete} 2 3}
|
||||
|
||||
do_execsql_test schema4-1.4 {
|
||||
DELETE FROM log;
|
||||
|
||||
DROP INDEX i1;
|
||||
DROP TABLE t1;
|
||||
DROP VIEW v1;
|
||||
|
||||
INSERT INTO tbl VALUES(1, 2);
|
||||
UPDATE tbl SET b=a+b, a=a+1;
|
||||
DELETE FROM tbl;
|
||||
|
||||
SELECT x, a, b FROM log;
|
||||
} {{after insert} 1 2 {after update} 2 3 {after delete} 2 3}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test schema4-1.5 {
|
||||
DELETE FROM log;
|
||||
INSERT INTO tbl VALUES(1, 2);
|
||||
UPDATE tbl SET b=a+b, a=a+1;
|
||||
DELETE FROM tbl;
|
||||
SELECT x, a, b FROM log;
|
||||
} {{after insert} 1 2 {after update} 2 3 {after delete} 2 3}
|
||||
|
||||
do_execsql_test schema4-1.6 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE VIEW v1 AS SELECT * FROM tbl;
|
||||
CREATE INDEX i1 ON tbl(a);
|
||||
} {}
|
||||
|
||||
ifcapable fts3 {
|
||||
do_execsql_test schema4-1.7 {
|
||||
DROP TABLE t1;
|
||||
CREATE VIRTUAL TABLE t1 USING fts3;
|
||||
} {}
|
||||
|
||||
do_execsql_test schema4-1.8 {
|
||||
DELETE FROM log;
|
||||
DROP TABLE t1;
|
||||
INSERT INTO tbl VALUES(1, 2);
|
||||
UPDATE tbl SET b=a+b, a=a+1;
|
||||
DELETE FROM tbl;
|
||||
SELECT x, a, b FROM log;
|
||||
} {{after insert} 1 2 {after update} 2 3 {after delete} 2 3}
|
||||
}
|
||||
|
||||
ifcapable altertable {
|
||||
drop_all_tables
|
||||
do_execsql_test schema4-2.1 {
|
||||
CREATE TABLE log(x, a, b);
|
||||
CREATE TABLE tbl(a, b);
|
||||
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
} {}
|
||||
|
||||
do_execsql_test schema4-2.2 {
|
||||
CREATE TRIGGER t1 AFTER INSERT ON tbl BEGIN
|
||||
INSERT INTO log VALUES('after insert', new.a, new.b);
|
||||
END;
|
||||
CREATE TRIGGER i1 AFTER DELETE ON tbl BEGIN
|
||||
INSERT INTO log VALUES('after delete', old.a, old.b);
|
||||
END;
|
||||
} {}
|
||||
|
||||
do_execsql_test schema4-2.3 { ALTER TABLE t1 RENAME TO t2 } {}
|
||||
|
||||
do_execsql_test schema4-2.4 {
|
||||
INSERT INTO tbl VALUES('a', 'b');
|
||||
DELETE FROM tbl;
|
||||
SELECT * FROM log;
|
||||
} {{after insert} a b {after delete} a b}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test schema4-2.5 {
|
||||
DELETE FROM log;
|
||||
INSERT INTO tbl VALUES('c', 'd');
|
||||
DELETE FROM tbl;
|
||||
SELECT * FROM log;
|
||||
} {{after insert} c d {after delete} c d}
|
||||
|
||||
do_execsql_test schema4-2.6 {
|
||||
CREATE TEMP TRIGGER x1 AFTER UPDATE ON tbl BEGIN
|
||||
INSERT INTO log VALUES('after update', new.a, new.b);
|
||||
END;
|
||||
|
||||
CREATE TEMP TABLE x1(x);
|
||||
INSERT INTO x1 VALUES(123);
|
||||
} {}
|
||||
|
||||
do_execsql_test schema4-2.8 {
|
||||
select sql from sqlite_temp_master WHERE type='table';
|
||||
} {{CREATE TABLE x1(x)}}
|
||||
|
||||
do_execsql_test schema4-2.7 { ALTER TABLE tbl RENAME TO tbl2 } {}
|
||||
|
||||
do_execsql_test schema4-2.9 {
|
||||
select sql from sqlite_temp_master WHERE type='table';
|
||||
} {{CREATE TABLE x1(x)}}
|
||||
|
||||
do_execsql_test schema4-2.10 {
|
||||
DELETE FROM log;
|
||||
INSERT INTO tbl2 VALUES('e', 'f');
|
||||
UPDATE tbl2 SET a='g', b='h';
|
||||
DELETE FROM tbl2;
|
||||
SELECT * FROM log;
|
||||
} {{after insert} e f {after update} g h {after delete} g h}
|
||||
|
||||
do_execsql_test schema4-2.11 {
|
||||
INSERT INTO x1 VALUES(456);
|
||||
SELECT * FROM x1
|
||||
} {123 456}
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -458,6 +458,7 @@ do_test select6-8.6 {
|
||||
do_test select6-9.1 {
|
||||
execsql {
|
||||
SELECT a.x, b.x FROM t1 AS a, (SELECT x FROM t1 LIMIT 2) AS b
|
||||
ORDER BY 1, 2
|
||||
}
|
||||
} {1 1 1 2 2 1 2 2 3 1 3 2 4 1 4 2}
|
||||
do_test select6-9.2 {
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
db close
|
||||
|
||||
ifcapable !shared_cache {
|
||||
@@ -166,5 +168,14 @@ do_test shared2-5.1 {
|
||||
db close
|
||||
db2 close
|
||||
|
||||
# The following test verifies that shared-cache mode does not automatically
|
||||
# turn on exclusive-locking mode for some reason.
|
||||
do_multiclient_test {tn} {
|
||||
sql1 { CREATE TABLE t1(a, b) }
|
||||
sql2 { CREATE TABLE t2(a, b) }
|
||||
do_test shared2-6.$tn.1 { sql1 { SELECT * FROM t2 } } {}
|
||||
do_test shared2-6.$tn.2 { sql2 { SELECT * FROM t1 } } {}
|
||||
}
|
||||
|
||||
sqlite3_enable_shared_cache $::enable_shared_cache
|
||||
finish_test
|
||||
|
||||
+10
-5
@@ -27,14 +27,18 @@ db func a_string a_string
|
||||
|
||||
register_dbstat_vtab db
|
||||
do_execsql_test stat-0.0 {
|
||||
PRAGMA auto_vacuum = OFF;
|
||||
CREATE VIRTUAL TABLE temp.stat USING dbstat;
|
||||
SELECT * FROM stat;
|
||||
} {}
|
||||
do_execsql_test stat-0.1 {
|
||||
PRAGMA journal_mode = WAL;
|
||||
PRAGMA journal_mode = delete;
|
||||
SELECT * FROM stat;
|
||||
} {wal delete sqlite_master / 1 leaf 0 0 916 0}
|
||||
|
||||
ifcapable wal {
|
||||
do_execsql_test stat-0.1 {
|
||||
PRAGMA journal_mode = WAL;
|
||||
PRAGMA journal_mode = delete;
|
||||
SELECT * FROM stat;
|
||||
} {wal delete sqlite_master / 1 leaf 0 0 916 0}
|
||||
}
|
||||
|
||||
do_test stat-1.0 {
|
||||
execsql {
|
||||
@@ -140,6 +144,7 @@ sqlite3 db test.db
|
||||
register_dbstat_vtab db
|
||||
breakpoint
|
||||
do_execsql_test stat-5.1 {
|
||||
PRAGMA auto_vacuum = OFF;
|
||||
CREATE VIRTUAL TABLE temp.stat USING dbstat;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(zeroblob(1513));
|
||||
|
||||
@@ -35,6 +35,7 @@ do_test stmt-1.2 {
|
||||
} {1}
|
||||
do_test stmt-1.3 {
|
||||
execsql {
|
||||
PRAGMA temp_store = file;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
# 2010 November 19
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
|
||||
set testprefix superlock
|
||||
|
||||
# Test organization:
|
||||
#
|
||||
# 1.*: Test superlock on a rollback database. Test that once the db is
|
||||
# superlocked, it is not possible for a second client to read from
|
||||
# it.
|
||||
#
|
||||
# 2.*: Test superlock on a WAL database with zero frames in the WAL file.
|
||||
# Test that once the db is superlocked, it is not possible to read,
|
||||
# write or checkpoint the db.
|
||||
#
|
||||
# 3.*: As 2.*, for WAL databases with one or more frames in the WAL.
|
||||
#
|
||||
# 4.*: As 2.*, for WAL databases with one or more checkpointed frames
|
||||
# in the WAL.
|
||||
#
|
||||
# 5.*: Test that a call to sqlite3demo_superlock() uses the busy handler
|
||||
# correctly to wait for existing clients to clear on a WAL database.
|
||||
# And returns SQLITE_BUSY if no busy handler is defined or the busy
|
||||
# handler returns 0 before said clients relinquish their locks.
|
||||
#
|
||||
# 6.*: Test that if a superlocked WAL database is overwritten, existing
|
||||
# clients run the recovery to build the new wal-index after the
|
||||
# superlock is released.
|
||||
#
|
||||
#
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
PRAGMA journal_mode = DELETE;
|
||||
} {delete}
|
||||
|
||||
do_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock}
|
||||
do_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}}
|
||||
do_test 1.4 { unlock } {}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
PRAGMA journal_mode = WAL;
|
||||
} {wal}
|
||||
|
||||
do_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock}
|
||||
do_catchsql_test 2.3 { SELECT * FROM t1 } {1 {database is locked}}
|
||||
do_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
|
||||
do_catchsql_test 2.5 { PRAGMA wal_checkpoint } {1 {database is locked}}
|
||||
do_test 2.6 { unlock } {}
|
||||
|
||||
do_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) }
|
||||
|
||||
do_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock}
|
||||
do_catchsql_test 3.3 { SELECT * FROM t1 } {1 {database is locked}}
|
||||
do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
|
||||
do_catchsql_test 3.5 { PRAGMA wal_checkpoint } {1 {database is locked}}
|
||||
do_test 3.6 { unlock } {}
|
||||
|
||||
do_execsql_test 4.1 { PRAGMA wal_checkpoint } {}
|
||||
|
||||
do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock}
|
||||
do_catchsql_test 4.3 { SELECT * FROM t1 } {1 {database is locked}}
|
||||
do_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
|
||||
do_catchsql_test 4.5 { PRAGMA wal_checkpoint } {1 {database is locked}}
|
||||
do_test 4.6 { unlock } {}
|
||||
|
||||
do_multiclient_test tn {
|
||||
|
||||
proc busyhandler {x} {
|
||||
switch -- $x {
|
||||
1 { sql1 "COMMIT" }
|
||||
2 { sql2 "COMMIT" }
|
||||
3 { sql3 "COMMIT" }
|
||||
}
|
||||
lappend ::busylist $x
|
||||
return 1
|
||||
}
|
||||
set ::busylist [list]
|
||||
|
||||
do_test 5.$tn.1 {
|
||||
sql1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
PRAGMA journal_mode = WAL;
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
}
|
||||
} {wal}
|
||||
|
||||
do_test 5.$tn.2 {
|
||||
sql1 { BEGIN ; SELECT * FROM t1 }
|
||||
sql2 { BEGIN ; INSERT INTO t1 VALUES(3, 4) }
|
||||
sql3 { BEGIN ; SELECT * FROM t1 }
|
||||
} {1 2}
|
||||
|
||||
do_test 5.$tn.3 {
|
||||
set ::busylist [list]
|
||||
sqlite3demo_superlock unlock test.db "" busyhandler
|
||||
set ::busylist
|
||||
} {0 1 2 3}
|
||||
|
||||
do_test 5.$tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
|
||||
do_test 5.$tn.5 {
|
||||
csql3 { INSERT INTO t1 VALUES(5, 6) }
|
||||
} {1 {database is locked}}
|
||||
do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {1 {database is locked}}
|
||||
|
||||
do_test 5.$tn.7 { unlock } {}
|
||||
|
||||
|
||||
do_test 5.$tn.8 {
|
||||
sql1 { BEGIN ; SELECT * FROM t1 }
|
||||
sql2 { BEGIN ; INSERT INTO t1 VALUES(5, 6) }
|
||||
sql3 { BEGIN ; SELECT * FROM t1 }
|
||||
} {1 2 3 4}
|
||||
|
||||
do_test 5.$tn.9 {
|
||||
list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
|
||||
} {1 {database is locked}}
|
||||
do_test 5.$tn.10 {
|
||||
sql1 COMMIT
|
||||
list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
|
||||
} {1 {database is locked}}
|
||||
do_test 5.$tn.11 {
|
||||
sql2 COMMIT
|
||||
list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
|
||||
} {1 {database is locked}}
|
||||
do_test 5.$tn.12 {
|
||||
sql3 COMMIT
|
||||
list [catch {sqlite3demo_superlock unlock test.db} msg] $msg
|
||||
} {0 unlock}
|
||||
unlock
|
||||
|
||||
|
||||
do_test 5.$tn.13 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6}
|
||||
do_test 5.$tn.14 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
|
||||
do_test 5.$tn.15 { sqlite3demo_superlock unlock test.db } {unlock}
|
||||
do_test 5.$tn.16 { unlock } {}
|
||||
do_test 5.$tn.17 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
|
||||
do_test 5.$tn.18 { sql1 { SELECT * FROM t1 } } {1 2 3 4 5 6}
|
||||
do_test 5.$tn.19 { sql2 { SELECT * FROM t1 } } {1 2 3 4 5 6}
|
||||
}
|
||||
|
||||
proc read_content {file} {
|
||||
if {[file exists $file]==0} {return ""}
|
||||
set fd [open $file]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
set content [read $fd]
|
||||
close $fd
|
||||
return $content
|
||||
}
|
||||
|
||||
proc write_content {file content} {
|
||||
set fd [open $file w+]
|
||||
fconfigure $fd -encoding binary -translation binary
|
||||
puts -nonewline $fd $content
|
||||
close $fd
|
||||
}
|
||||
|
||||
# Both $file1 and $file2 are database files. This function takes a
|
||||
# superlock on each, then exchanges the content of the two files (i.e.
|
||||
# overwrites $file1 with the initial contents of $file2, and overwrites
|
||||
# $file2 with the initial contents of $file1). The contents of any WAL
|
||||
# file is also exchanged.
|
||||
#
|
||||
proc db_swap {file1 file2} {
|
||||
sqlite3demo_superlock unlock1 $file1
|
||||
sqlite3demo_superlock unlock2 $file2
|
||||
|
||||
set db1 [read_content $file1]
|
||||
set db2 [read_content $file2]
|
||||
write_content $file1 $db2
|
||||
write_content $file2 $db1
|
||||
|
||||
set wal1 [read_content ${file1}-wal]
|
||||
set wal2 [read_content ${file2}-wal]
|
||||
write_content ${file1}-wal $wal2
|
||||
write_content ${file2}-wal $wal1
|
||||
|
||||
unlock1
|
||||
unlock2
|
||||
}
|
||||
|
||||
forcedelete test.db
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 6.1 {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
PRAGMA aux.journal_mode = wal;
|
||||
CREATE TABLE aux.t2(x, y);
|
||||
INSERT INTO aux.t2 VALUES('a', 'b');
|
||||
PRAGMA schema_version = 450;
|
||||
DETACH aux;
|
||||
|
||||
PRAGMA main.journal_mode = wal;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
SELECT * FROM t1;
|
||||
} {wal wal 1 2 3 4}
|
||||
|
||||
|
||||
db_swap test.db2 test.db
|
||||
do_catchsql_test 6.2 { SELECT * FROM t1 } {1 {no such table: t1}}
|
||||
do_catchsql_test 6.3 { SELECT * FROM t2 } {0 {a b}}
|
||||
|
||||
db_swap test.db2 test.db
|
||||
do_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}}
|
||||
do_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}}
|
||||
|
||||
do_execsql_test 6.6 { PRAGMA wal_checkpoint }
|
||||
|
||||
db_swap test.db2 test.db
|
||||
do_catchsql_test 6.7 { SELECT * FROM t1 } {1 {no such table: t1}}
|
||||
do_catchsql_test 6.8 { SELECT * FROM t2 } {0 {a b}}
|
||||
|
||||
db_swap test.db2 test.db
|
||||
do_catchsql_test 6.9 { SELECT * FROM t1 } {0 {1 2 3 4}}
|
||||
do_catchsql_test 6.10 { SELECT * FROM t2 } {1 {no such table: t2}}
|
||||
|
||||
do_execsql_test 6.11 {
|
||||
PRAGMA journal_mode = delete;
|
||||
PRAGMA page_size = 512;
|
||||
VACUUM;
|
||||
PRAGMA journal_mode = wal;
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
} {delete wal}
|
||||
|
||||
db_swap test.db2 test.db
|
||||
do_catchsql_test 6.12 { SELECT * FROM t1 } {1 {no such table: t1}}
|
||||
do_catchsql_test 6.13 { SELECT * FROM t2 } {0 {a b}}
|
||||
|
||||
db_swap test.db2 test.db
|
||||
do_catchsql_test 6.14 { SELECT * FROM t1 } {0 {1 2 3 4 5 6}}
|
||||
do_catchsql_test 6.15 { SELECT * FROM t2 } {1 {no such table: t2}}
|
||||
|
||||
finish_test
|
||||
+1
-1
@@ -59,7 +59,7 @@ do_test tempdb-2.1 {
|
||||
# number of open files in the test cases below.
|
||||
#
|
||||
set jrnl_in_memory [expr {[permutation] eq "inmemory_journal"}]
|
||||
set subj_in_memory [expr {$jrnl_in_memory || $TEMP_STORE == 3}]
|
||||
set subj_in_memory [expr {$jrnl_in_memory || $TEMP_STORE>=2}]
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
+163
-7
@@ -109,6 +109,10 @@ if {[info command sqlite_orig]==""} {
|
||||
if {[info exists ::G(perm:presql)]} {
|
||||
[lindex $args 0] eval $::G(perm:presql)
|
||||
}
|
||||
if {[info exists ::G(perm:dbconfig)]} {
|
||||
set ::dbhandle [lindex $args 0]
|
||||
uplevel #0 $::G(perm:dbconfig)
|
||||
}
|
||||
set res
|
||||
} else {
|
||||
# This command is not opening a new database connection. Pass the
|
||||
@@ -245,6 +249,10 @@ reset_db
|
||||
#
|
||||
if {[info exists TC(count)]} return
|
||||
|
||||
# Make sure memory statistics are enabled.
|
||||
#
|
||||
sqlite3_config_memstatus 1
|
||||
|
||||
# Initialize the test counters and set up commands to access them.
|
||||
# Or, if this is a slave interpreter, set up aliases to write the
|
||||
# counters in the parent interpreter.
|
||||
@@ -299,6 +307,8 @@ proc do_test {name cmd expected} {
|
||||
|
||||
global argv cmdlinearg
|
||||
|
||||
fix_testname name
|
||||
|
||||
sqlite3_memdebug_settitle $name
|
||||
|
||||
# if {[llength $argv]==0} {
|
||||
@@ -331,14 +341,100 @@ proc do_test {name cmd expected} {
|
||||
}
|
||||
flush stdout
|
||||
}
|
||||
|
||||
proc fix_testname {varname} {
|
||||
upvar $varname testname
|
||||
if {[info exists ::testprefix]
|
||||
&& [string is digit [string range $testname 0 0]]
|
||||
} {
|
||||
set testname "${::testprefix}-$testname"
|
||||
}
|
||||
}
|
||||
|
||||
proc do_execsql_test {testname sql result} {
|
||||
uplevel do_test $testname [list "execsql {$sql}"] [list $result]
|
||||
proc do_execsql_test {testname sql {result {}}} {
|
||||
fix_testname testname
|
||||
uplevel do_test $testname [list "execsql {$sql}"] [list [list {*}$result]]
|
||||
}
|
||||
proc do_catchsql_test {testname sql result} {
|
||||
fix_testname testname
|
||||
uplevel do_test $testname [list "catchsql {$sql}"] [list $result]
|
||||
}
|
||||
proc do_eqp_test {name sql res} {
|
||||
uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Usage: do_select_tests PREFIX ?SWITCHES? TESTLIST
|
||||
#
|
||||
# Where switches are:
|
||||
#
|
||||
# -errorformat FMTSTRING
|
||||
# -count
|
||||
# -query SQL
|
||||
# -tclquery TCL
|
||||
# -repair TCL
|
||||
#
|
||||
proc do_select_tests {prefix args} {
|
||||
|
||||
set testlist [lindex $args end]
|
||||
set switches [lrange $args 0 end-1]
|
||||
|
||||
set errfmt ""
|
||||
set countonly 0
|
||||
set tclquery ""
|
||||
set repair ""
|
||||
|
||||
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 "-query"]} {
|
||||
set tclquery [list execsql [lindex $switches [incr i]]]
|
||||
} elseif {$n>=2 && [string equal -length $n $s "-tclquery"]} {
|
||||
set tclquery [lindex $switches [incr i]]
|
||||
} elseif {$n>=2 && [string equal -length $n $s "-errorformat"]} {
|
||||
set errfmt [lindex $switches [incr i]]
|
||||
} elseif {$n>=2 && [string equal -length $n $s "-repair"]} {
|
||||
set repair [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"
|
||||
}
|
||||
|
||||
eval $repair
|
||||
foreach {tn sql res} $testlist {
|
||||
if {$tclquery != ""} {
|
||||
execsql $sql
|
||||
uplevel do_test ${prefix}.$tn [list $tclquery] [list [list {*}$res]]
|
||||
} elseif {$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]
|
||||
}
|
||||
eval $repair
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
proc delete_all_data {} {
|
||||
db eval {SELECT tbl_name AS t FROM sqlite_master WHERE type = 'table'} {
|
||||
db eval "DELETE FROM '[string map {' ''} $t]'"
|
||||
}
|
||||
}
|
||||
|
||||
# Run an SQL script.
|
||||
# Return the number of microseconds per statement.
|
||||
@@ -357,6 +453,7 @@ proc speed_trial {name numstmt units sql} {
|
||||
puts [format {%12d uS %s %s} $tm $rate $u2]
|
||||
global total_time
|
||||
set total_time [expr {$total_time+$tm}]
|
||||
lappend ::speed_trial_times $name $tm
|
||||
}
|
||||
proc speed_trial_tcl {name numstmt units script} {
|
||||
puts -nonewline [format {%-21.21s } $name...]
|
||||
@@ -372,10 +469,12 @@ proc speed_trial_tcl {name numstmt units script} {
|
||||
puts [format {%12d uS %s %s} $tm $rate $u2]
|
||||
global total_time
|
||||
set total_time [expr {$total_time+$tm}]
|
||||
lappend ::speed_trial_times $name $tm
|
||||
}
|
||||
proc speed_trial_init {name} {
|
||||
global total_time
|
||||
set total_time 0
|
||||
set ::speed_trial_times [list]
|
||||
sqlite3 versdb :memory:
|
||||
set vers [versdb one {SELECT sqlite_source_id()}]
|
||||
versdb close
|
||||
@@ -384,6 +483,16 @@ proc speed_trial_init {name} {
|
||||
proc speed_trial_summary {name} {
|
||||
global total_time
|
||||
puts [format {%-21.21s %12d uS TOTAL} $name $total_time]
|
||||
|
||||
if { 0 } {
|
||||
sqlite3 versdb :memory:
|
||||
set vers [lindex [versdb one {SELECT sqlite_source_id()}] 0]
|
||||
versdb close
|
||||
puts "CREATE TABLE IF NOT EXISTS time(version, script, test, us);"
|
||||
foreach {test us} $::speed_trial_times {
|
||||
puts "INSERT INTO time VALUES('$vers', '$name', '$test', $us);"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Run this routine last
|
||||
@@ -596,9 +705,25 @@ proc stepsql {dbptr sql} {
|
||||
|
||||
# Delete a file or directory
|
||||
#
|
||||
proc forcedelete {filename} {
|
||||
if {[catch {file delete -force $filename}]} {
|
||||
exec rm -rf $filename
|
||||
proc forcedelete {args} {
|
||||
foreach filename $args {
|
||||
# On windows, sometimes even a [file delete -force] can fail just after
|
||||
# a file is closed. The cause is usually "tag-alongs" - programs like
|
||||
# anti-virus software, automatic backup tools and various explorer
|
||||
# extensions that keep a file open a little longer than we expect, causing
|
||||
# the delete to fail.
|
||||
#
|
||||
# The solution is to wait a short amount of time before retrying the
|
||||
# delete.
|
||||
#
|
||||
set nRetry 50 ;# Maximum number of retries.
|
||||
set nDelay 100 ;# Delay in ms before retrying.
|
||||
for {set i 0} {$i<$nRetry} {incr i} {
|
||||
set rc [catch {file delete -force $filename} msg]
|
||||
if {$rc==0} break
|
||||
after $nDelay
|
||||
}
|
||||
if {$rc} { error $msg }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1100,9 +1225,9 @@ proc drop_all_tables {{db db}} {
|
||||
}
|
||||
foreach {t type} [$db eval "
|
||||
SELECT name, type FROM $master
|
||||
WHERE type IN('table', 'view') AND name NOT like 'sqlite_%'
|
||||
WHERE type IN('table', 'view') AND name NOT LIKE 'sqliteX_%' ESCAPE 'X'
|
||||
"] {
|
||||
$db eval "DROP $type $t"
|
||||
$db eval "DROP $type \"$t\""
|
||||
}
|
||||
}
|
||||
ifcapable trigger&&foreignkey {
|
||||
@@ -1254,8 +1379,39 @@ proc sql36231 {sql} {
|
||||
return ""
|
||||
}
|
||||
|
||||
proc db_save {} {
|
||||
foreach f [glob -nocomplain sv_test.db*] { forcedelete $f }
|
||||
foreach f [glob -nocomplain test.db*] {
|
||||
set f2 "sv_$f"
|
||||
file copy -force $f $f2
|
||||
}
|
||||
}
|
||||
proc db_save_and_close {} {
|
||||
db_save
|
||||
catch { db close }
|
||||
return ""
|
||||
}
|
||||
proc db_restore {} {
|
||||
foreach f [glob -nocomplain test.db*] { forcedelete $f }
|
||||
foreach f2 [glob -nocomplain sv_test.db*] {
|
||||
set f [string range $f2 3 end]
|
||||
file copy -force $f2 $f
|
||||
}
|
||||
}
|
||||
proc db_restore_and_reopen {{dbfile test.db}} {
|
||||
catch { db close }
|
||||
db_restore
|
||||
sqlite3 db $dbfile
|
||||
}
|
||||
proc db_delete_and_reopen {{file test.db}} {
|
||||
catch { db close }
|
||||
foreach f [glob -nocomplain test.db*] { file delete -force $f }
|
||||
sqlite3 db $file
|
||||
}
|
||||
|
||||
# If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
|
||||
# to non-zero, then set the global variable $AUTOVACUUM to 1.
|
||||
set AUTOVACUUM $sqlite_options(default_autovacuum)
|
||||
|
||||
source $testdir/thread_common.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
@@ -1272,6 +1272,129 @@ static void cgt_pager_1(int nMs){
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------
|
||||
** Test case "dynamic_triggers"
|
||||
**
|
||||
** Two threads executing statements that cause deeply nested triggers
|
||||
** to fire. And one thread busily creating and deleting triggers. This
|
||||
** is an attempt to find a bug reported to us.
|
||||
*/
|
||||
|
||||
static char *dynamic_triggers_1(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
int nDrop = 0;
|
||||
int nCreate = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
while( !timetostop(&err) ){
|
||||
int i;
|
||||
|
||||
for(i=1; i<9; i++){
|
||||
char *zSql = sqlite3_mprintf(
|
||||
"CREATE TRIGGER itr%d BEFORE INSERT ON t%d BEGIN "
|
||||
"INSERT INTO t%d VALUES(new.x, new.y);"
|
||||
"END;", i, i, i+1
|
||||
);
|
||||
execsql(&err, &db, zSql);
|
||||
sqlite3_free(zSql);
|
||||
nCreate++;
|
||||
}
|
||||
|
||||
for(i=1; i<9; i++){
|
||||
char *zSql = sqlite3_mprintf(
|
||||
"CREATE TRIGGER dtr%d BEFORE DELETE ON t%d BEGIN "
|
||||
"DELETE FROM t%d WHERE x = old.x; "
|
||||
"END;", i, i, i+1
|
||||
);
|
||||
execsql(&err, &db, zSql);
|
||||
sqlite3_free(zSql);
|
||||
nCreate++;
|
||||
}
|
||||
|
||||
for(i=1; i<9; i++){
|
||||
char *zSql = sqlite3_mprintf("DROP TRIGGER itr%d", i);
|
||||
execsql(&err, &db, zSql);
|
||||
sqlite3_free(zSql);
|
||||
nDrop++;
|
||||
}
|
||||
|
||||
for(i=1; i<9; i++){
|
||||
char *zSql = sqlite3_mprintf("DROP TRIGGER dtr%d", i);
|
||||
execsql(&err, &db, zSql);
|
||||
sqlite3_free(zSql);
|
||||
nDrop++;
|
||||
}
|
||||
}
|
||||
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("%d created, %d dropped", nCreate, nDrop);
|
||||
}
|
||||
|
||||
static char *dynamic_triggers_2(int iTid, int iArg){
|
||||
Error err = {0}; /* Error code and message */
|
||||
Sqlite db = {0}; /* SQLite database connection */
|
||||
i64 iVal = 0;
|
||||
int nInsert = 0;
|
||||
int nDelete = 0;
|
||||
|
||||
opendb(&err, &db, "test.db", 0);
|
||||
while( !timetostop(&err) ){
|
||||
do {
|
||||
iVal = (iVal+1)%100;
|
||||
execsql(&err, &db, "INSERT INTO t1 VALUES(:iX, :iY+1)", &iVal, &iVal);
|
||||
nInsert++;
|
||||
} while( iVal );
|
||||
|
||||
do {
|
||||
iVal = (iVal+1)%100;
|
||||
execsql(&err, &db, "DELETE FROM t1 WHERE x = :iX", &iVal);
|
||||
nDelete++;
|
||||
} while( iVal );
|
||||
}
|
||||
|
||||
print_and_free_err(&err);
|
||||
return sqlite3_mprintf("%d inserts, %d deletes", nInsert, nDelete);
|
||||
}
|
||||
|
||||
static void dynamic_triggers(int nMs){
|
||||
Error err = {0};
|
||||
Sqlite db = {0};
|
||||
Threadset threads = {0};
|
||||
|
||||
opendb(&err, &db, "test.db", 1);
|
||||
sql_script(&err, &db,
|
||||
"PRAGMA page_size = 1024;"
|
||||
"PRAGMA journal_mode = WAL;"
|
||||
"CREATE TABLE t1(x, y);"
|
||||
"CREATE TABLE t2(x, y);"
|
||||
"CREATE TABLE t3(x, y);"
|
||||
"CREATE TABLE t4(x, y);"
|
||||
"CREATE TABLE t5(x, y);"
|
||||
"CREATE TABLE t6(x, y);"
|
||||
"CREATE TABLE t7(x, y);"
|
||||
"CREATE TABLE t8(x, y);"
|
||||
"CREATE TABLE t9(x, y);"
|
||||
);
|
||||
|
||||
setstoptime(&err, nMs);
|
||||
|
||||
sqlite3_enable_shared_cache(1);
|
||||
launch_thread(&err, &threads, dynamic_triggers_2, 0);
|
||||
launch_thread(&err, &threads, dynamic_triggers_2, 0);
|
||||
sqlite3_enable_shared_cache(0);
|
||||
|
||||
sleep(2);
|
||||
|
||||
launch_thread(&err, &threads, dynamic_triggers_2, 0);
|
||||
launch_thread(&err, &threads, dynamic_triggers_1, 0);
|
||||
|
||||
join_all_threads(&err, &threads);
|
||||
|
||||
print_and_free_err(&err);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv){
|
||||
struct ThreadTest {
|
||||
void (*xTest)(int);
|
||||
@@ -1286,6 +1409,7 @@ int main(int argc, char **argv){
|
||||
{ walthread5, "walthread5", 1000 },
|
||||
|
||||
{ cgt_pager_1, "cgt_pager_1", 0 },
|
||||
{ dynamic_triggers, "dynamic_triggers", 20000 },
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 2010 September 20
|
||||
#
|
||||
# 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 regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests to verify that ticket [313723c356] has been
|
||||
# fixed.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
|
||||
ifcapable !wal { finish_test ; return }
|
||||
|
||||
do_execsql_test tkt-313723c356.1 {
|
||||
PRAGMA page_size = 1024;
|
||||
PRAGMA journal_mode = WAL;
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES(randomblob(400), randomblob(400));
|
||||
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
|
||||
INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1;
|
||||
} {wal}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test tkt-313723c356.2 -faults shmerr* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
sqlite3 db2 test.db
|
||||
db eval { SELECT * FROM t1 }
|
||||
db2 eval { UPDATE t1 SET a = randomblob(399) }
|
||||
db2 close
|
||||
} -body {
|
||||
# At this point, the cache contains all of table t1 and none of index i1. The
|
||||
# cache is out of date. When the bug existed and the right xShmLock() fails
|
||||
# in the following statement, the internal cache of the WAL header was
|
||||
# being updated, but the contents of the page-cache not flushed. This causes
|
||||
# the integrity-check in the "-test" code to fail, as it is comparing the
|
||||
# cached (out-of-date) version of table t1 with the on disk (up-to-date)
|
||||
# version of index i1.
|
||||
#
|
||||
execsql { SELECT min(rowid) FROM t1 }
|
||||
} -test {
|
||||
faultsim_test_result {0 1}
|
||||
faultsim_integrity_check
|
||||
}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,321 @@
|
||||
# 2010 October 6
|
||||
#
|
||||
# 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 regression tests for SQLite library. Specifically,
|
||||
# it tests that ticket [38cb5df375078d3f9711482d2a1615d09f6b3f33] has
|
||||
# been resolved.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test tkt-38cb5df375.0 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 SELECT a+2 FROM t1;
|
||||
INSERT INTO t1 SELECT a+4 FROM t1;
|
||||
}
|
||||
} {}
|
||||
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.1.$ii {
|
||||
execsql {
|
||||
SELECT * FROM (SELECT * FROM t1 ORDER BY a)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 4 5 6 7 8 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.2.$ii {
|
||||
execsql {
|
||||
SELECT 9 FROM (SELECT * FROM t1)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {9 9 9 9 9 9 9 9 1 2 3 4 5 6 7 8} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.3.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.4.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4} {
|
||||
do_test tkt-38cb5df375.5.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1)
|
||||
UNION SELECT 9 FROM (SELECT a FROM t1)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11} {
|
||||
do_test tkt-38cb5df375.11.$ii {
|
||||
execsql {
|
||||
SELECT * FROM (SELECT * FROM t1 ORDER BY a LIMIT 3)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11} {
|
||||
do_test tkt-38cb5df375.12.$ii {
|
||||
execsql {
|
||||
SELECT 9 FROM (SELECT * FROM t1)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {9 9 9 9 9 9 9 9 1 2 3} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6} {
|
||||
do_test tkt-38cb5df375.13.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 3)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 1 2 3} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6} {
|
||||
do_test tkt-38cb5df375.14.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1 LIMIT 3)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1 LIMIT 3)
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0 0 0 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.21.$ii {
|
||||
execsql {
|
||||
SELECT * FROM (SELECT * FROM t1 ORDER BY a)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 4 5 6 7 8 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.22.$ii {
|
||||
execsql {
|
||||
SELECT 9 FROM (SELECT * FROM t1)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 4 5 6 7 8 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.23.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a)
|
||||
ORDER BY 1 DESC
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} {
|
||||
do_test tkt-38cb5df375.24.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11} {
|
||||
do_test tkt-38cb5df375.31.$ii {
|
||||
execsql {
|
||||
SELECT * FROM (SELECT * FROM t1 ORDER BY a LIMIT 3)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9 10 11} {
|
||||
do_test tkt-38cb5df375.32.$ii {
|
||||
execsql {
|
||||
SELECT 9 FROM (SELECT * FROM t1)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.33.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 4)
|
||||
UNION ALL SELECT 90+a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 4 91 92 93} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.34.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 2)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 5)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 1 2 2 3 4 5} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.35.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5)
|
||||
UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 1 2 2 3 4 5} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.35b.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5)
|
||||
UNION ALL SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 4 5 11 12} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.35c.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5)
|
||||
UNION SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 4 5 11 12} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.35d.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5)
|
||||
INTERSECT SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.35e.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5)
|
||||
EXCEPT SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {3 4 5} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.36.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1 LIMIT 3)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1 LIMIT 4)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0 0 0 9 9 9 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.37.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1 LIMIT 3)
|
||||
UNION SELECT 9 FROM (SELECT a FROM t1 LIMIT 4)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0 9} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
do_test tkt-38cb5df375.38.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1 LIMIT 3)
|
||||
EXCEPT SELECT 9 FROM (SELECT a FROM t1 LIMIT 4)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0} 0 [expr {$ii-1}]]
|
||||
}
|
||||
|
||||
foreach ii {1 2 3 4 5 6 7 8 9} {
|
||||
do_test tkt-38cb5df375.41.$ii {
|
||||
execsql {
|
||||
SELECT 0 FROM (SELECT * FROM t1 LIMIT 3)
|
||||
UNION ALL SELECT 9 FROM (SELECT a FROM t1 LIMIT 4)
|
||||
UNION ALL SELECT 88 FROM (SELECT a FROM t1 LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {0 0 0 9 9 9 9 88 88} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9} {
|
||||
do_test tkt-38cb5df375.42.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 3)
|
||||
UNION ALL SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 4)
|
||||
UNION ALL SELECT a+20 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 11 12 13 14 21 22} 0 [expr {$ii-1}]]
|
||||
}
|
||||
foreach ii {1 2 3 4 5 6 7 8 9} {
|
||||
do_test tkt-38cb5df375.43.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 3)
|
||||
UNION SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 4)
|
||||
UNION SELECT a+20 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2)
|
||||
ORDER BY 1
|
||||
LIMIT $::ii;
|
||||
}
|
||||
} [lrange {1 2 3 11 12 13 14 21 22} 0 [expr {$ii-1}]]
|
||||
}
|
||||
|
||||
foreach ii {1 2 3 4 5 6 7} {
|
||||
set jj [expr {7-$ii}]
|
||||
do_test tkt-38cb5df375.51.$ii {
|
||||
execsql {
|
||||
SELECT a FROM (SELECT * FROM t1 ORDER BY a)
|
||||
EXCEPT SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT $::ii)
|
||||
ORDER BY a DESC
|
||||
LIMIT $::jj;
|
||||
}
|
||||
} [lrange {8 7 6 5 4 3 2 1} 0 [expr {$jj-1}]]
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,46 @@
|
||||
# 2010 September 30
|
||||
#
|
||||
# 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 regression tests for SQLite library. Specifically,
|
||||
# it tests that ticket [3998683a16a7076e08f5585c1f4816414c8c653a] where in
|
||||
# floating point values with a decimal point at the beginning or end
|
||||
# of the mantissa are used.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test tkt-3998683a16.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(x, y REAL);
|
||||
INSERT INTO t1 VALUES(1, '1.0');
|
||||
INSERT INTO t1 VALUES(2, '.125');
|
||||
INSERT INTO t1 VALUES(3, '123.');
|
||||
INSERT INTO t1 VALUES(4, '123.e+2');
|
||||
INSERT INTO t1 VALUES(5, '.125e+3');
|
||||
INSERT INTO t1 VALUES(6, '123e4');
|
||||
INSERT INTO t1 VALUES(11, ' 1.0');
|
||||
INSERT INTO t1 VALUES(12, ' .125');
|
||||
INSERT INTO t1 VALUES(13, ' 123.');
|
||||
INSERT INTO t1 VALUES(14, ' 123.e+2');
|
||||
INSERT INTO t1 VALUES(15, ' .125e+3');
|
||||
INSERT INTO t1 VALUES(16, ' 123e4');
|
||||
INSERT INTO t1 VALUES(21, '1.0 ');
|
||||
INSERT INTO t1 VALUES(22, '.125 ');
|
||||
INSERT INTO t1 VALUES(23, '123. ');
|
||||
INSERT INTO t1 VALUES(24, '123.e+2 ');
|
||||
INSERT INTO t1 VALUES(25, '.125e+3 ');
|
||||
INSERT INTO t1 VALUES(26, '123e4 ');
|
||||
SELECT x FROM t1 WHERE typeof(y)=='real' ORDER BY x;
|
||||
}
|
||||
} {1 2 3 4 5 6 11 12 13 14 15 16 21 22 23 24 25 26}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,53 @@
|
||||
# 2011 January 15
|
||||
#
|
||||
# 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 regression tests for SQLite library.
|
||||
#
|
||||
# This file implements tests to verify that ticket [5d863f876e] has been
|
||||
# fixed.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
|
||||
do_multiclient_test tn {
|
||||
do_test $tn.1 {
|
||||
sql1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
PRAGMA journal_mode = WAL;
|
||||
VACUUM;
|
||||
PRAGMA journal_mode = DELETE;
|
||||
}
|
||||
} {wal delete}
|
||||
|
||||
do_test $tn.2 {
|
||||
sql2 { SELECT * FROM t1 }
|
||||
} {1 2 3 4}
|
||||
|
||||
do_test $tn.3 {
|
||||
sql1 {
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
PRAGMA journal_mode = WAL;
|
||||
VACUUM;
|
||||
PRAGMA journal_mode = DELETE;
|
||||
}
|
||||
} {wal delete}
|
||||
|
||||
do_test $tn.2 {
|
||||
sql2 { PRAGMA integrity_check }
|
||||
} {ok}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user