Merge from trunk: (1) Recent bug fixes (2) STAT4 support (3) win32-longpath
support. FossilOrigin-Name: e7ebc8f74fe91dee26f952fdf49e427b45448667
This commit is contained in:
+1
-1
@@ -847,7 +847,7 @@ do_test alter-14.2 {
|
||||
set system_table_list {1 sqlite_master}
|
||||
catchsql ANALYZE
|
||||
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
|
||||
ifcapable stat3 { lappend system_table_list 4 sqlite_stat3 }
|
||||
ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 }
|
||||
|
||||
foreach {tn tbl} $system_table_list {
|
||||
do_test alter-15.$tn.1 {
|
||||
|
||||
@@ -143,6 +143,11 @@ do_test alter4-2.6 {
|
||||
alter table t1 add column d DEFAULT CURRENT_TIME;
|
||||
}
|
||||
} {1 {Cannot add a column with non-constant default}}
|
||||
do_test alter4-2.7 {
|
||||
catchsql {
|
||||
alter table t1 add column d default (-+1);
|
||||
}
|
||||
} {1 {Cannot add a column with non-constant default}}
|
||||
do_test alter4-2.99 {
|
||||
execsql {
|
||||
DROP TABLE t1;
|
||||
|
||||
+17
-17
@@ -288,7 +288,7 @@ do_test analyze-4.3 {
|
||||
} {}
|
||||
|
||||
# Verify that DROP TABLE and DROP INDEX remove entries from the
|
||||
# sqlite_stat1 and sqlite_stat3 tables.
|
||||
# sqlite_stat1, sqlite_stat3 and sqlite_stat4 tables.
|
||||
#
|
||||
do_test analyze-5.0 {
|
||||
execsql {
|
||||
@@ -306,12 +306,13 @@ do_test analyze-5.0 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
ifcapable stat4 {set stat sqlite_stat4} else {set stat sqlite_stat3}
|
||||
do_test analyze-5.1 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
execsql "
|
||||
SELECT DISTINCT idx FROM $stat ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM $stat ORDER BY 1;
|
||||
"
|
||||
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.2 {
|
||||
@@ -321,12 +322,12 @@ do_test analyze-5.2 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
do_test analyze-5.3 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
execsql "
|
||||
SELECT DISTINCT idx FROM $stat ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM $stat ORDER BY 1;
|
||||
"
|
||||
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
|
||||
}
|
||||
do_test analyze-5.4 {
|
||||
@@ -336,12 +337,12 @@ do_test analyze-5.4 {
|
||||
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
|
||||
}
|
||||
} {t4i1 t4i2 t4}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
do_test analyze-5.5 {
|
||||
execsql {
|
||||
SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
|
||||
}
|
||||
execsql "
|
||||
SELECT DISTINCT idx FROM $stat ORDER BY 1;
|
||||
SELECT DISTINCT tbl FROM $stat ORDER BY 1;
|
||||
"
|
||||
} {t4i1 t4i2 t4}
|
||||
}
|
||||
|
||||
@@ -360,5 +361,4 @@ do_test analyze-99.1 {
|
||||
}
|
||||
} {1 {malformed database schema (sqlite_stat1) - near "nonsense": syntax error}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+8
-3
@@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@@ -95,7 +95,13 @@ do_test analyze3-1.1.1 {
|
||||
COMMIT;
|
||||
ANALYZE;
|
||||
}
|
||||
} {}
|
||||
|
||||
ifcapable stat4 {
|
||||
execsql { SELECT count(*)>0 FROM sqlite_stat4; }
|
||||
} else {
|
||||
execsql { SELECT count(*)>0 FROM sqlite_stat3; }
|
||||
}
|
||||
} {1}
|
||||
|
||||
do_eqp_test analyze3-1.1.2 {
|
||||
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
|
||||
@@ -312,7 +318,6 @@ do_test analyze3-3.1 {
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
|
||||
do_test analyze3-3.2.1 {
|
||||
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy]
|
||||
sqlite3_expired $S
|
||||
|
||||
+42
-8
@@ -10,14 +10,14 @@
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file implements tests for SQLite library. The focus of the tests
|
||||
# in this file is the use of the sqlite_stat3 histogram data on tables
|
||||
# in this file is the use of the sqlite_stat4 histogram data on tables
|
||||
# with many repeated values and only a few distinct values.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@@ -28,6 +28,17 @@ proc eqp {sql {db db}} {
|
||||
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
|
||||
}
|
||||
|
||||
proc alpha {blob} {
|
||||
set ret ""
|
||||
foreach c [split $blob {}] {
|
||||
if {[string is alpha $c]} {append ret $c}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
db func alpha alpha
|
||||
|
||||
db func lindex lindex
|
||||
|
||||
unset -nocomplain i t u v w x y z
|
||||
do_test analyze5-1.0 {
|
||||
db eval {CREATE TABLE t1(t,u,v TEXT COLLATE nocase,w,x,y,z)}
|
||||
@@ -55,17 +66,40 @@ do_test analyze5-1.0 {
|
||||
CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s
|
||||
CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3
|
||||
ANALYZE;
|
||||
SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
ifcapable stat4 {
|
||||
db eval {
|
||||
SELECT DISTINCT lindex(test_decode(sample),0)
|
||||
FROM sqlite_stat4 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
} else {
|
||||
db eval {
|
||||
SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
|
||||
}
|
||||
}
|
||||
} {alpha bravo charlie delta}
|
||||
|
||||
do_test analyze5-1.1 {
|
||||
db eval {SELECT DISTINCT lower(sample) FROM sqlite_stat3 WHERE idx='t1v'
|
||||
ORDER BY 1}
|
||||
ifcapable stat4 {
|
||||
db eval {
|
||||
SELECT DISTINCT lower(lindex(test_decode(sample), 0))
|
||||
FROM sqlite_stat4 WHERE idx='t1v' ORDER BY 1
|
||||
}
|
||||
} else {
|
||||
db eval {
|
||||
SELECT lower(sample) FROM sqlite_stat3 WHERE idx='t1v' ORDER BY 1
|
||||
}
|
||||
}
|
||||
} {alpha bravo charlie delta}
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
|
||||
ifcapable stat4 {
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat4 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 8 t1u 8 t1v 8 t1w 8 t1x 8 t1y 9 t1z 8}
|
||||
} else {
|
||||
do_test analyze5-1.2 {
|
||||
db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
|
||||
} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
|
||||
}
|
||||
|
||||
# Verify that range queries generate the correct row count estimates
|
||||
#
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
+5
-4
@@ -82,14 +82,14 @@ do_test analyze7-3.1 {
|
||||
do_test analyze7-3.2.1 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
|
||||
ifcapable stat3 {
|
||||
# If ENABLE_STAT3 is defined, SQLite comes up with a different estimated
|
||||
ifcapable stat4||stat3 {
|
||||
# If ENABLE_STAT4 is defined, SQLite comes up with a different estimated
|
||||
# row count for (c=2) than it does for (c=?).
|
||||
do_test analyze7-3.2.2 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
|
||||
} else {
|
||||
# If ENABLE_STAT3 is not defined, the expected row count for (c=2) is the
|
||||
# If ENABLE_STAT4 is not defined, the expected row count for (c=2) is the
|
||||
# same as that for (c=?).
|
||||
do_test analyze7-3.2.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
|
||||
@@ -98,7 +98,8 @@ ifcapable stat3 {
|
||||
do_test analyze7-3.3 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
|
||||
ifcapable {!stat3} {
|
||||
|
||||
ifcapable {!stat4 && !stat3} {
|
||||
do_test analyze7-3.4 {
|
||||
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
|
||||
|
||||
+13
-1
@@ -16,7 +16,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@@ -84,7 +84,19 @@ do_test 2.1 {
|
||||
# There are many more values of c between 0 and 100000 than there are
|
||||
# between 800000 and 900000. So t1c is more selective for the latter
|
||||
# range.
|
||||
#
|
||||
# Test 3.2 is a little unstable. It depends on the planner estimating
|
||||
# that (b BETWEEN 50 AND 54) will match more rows than (c BETWEEN
|
||||
# 800000 AND 900000). Which is a pretty close call (50 vs. 32), so
|
||||
# the planner could get it wrong with an unlucky set of samples. This
|
||||
# case happens to work, but others ("b BETWEEN 40 AND 44" for example)
|
||||
# will fail.
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 50 AND 54;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 100000;
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 800000 AND 900000;
|
||||
} {50 376 32}
|
||||
do_test 3.1 {
|
||||
eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000}
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
# 2013 August 3
|
||||
#
|
||||
# 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 contains automated tests used to verify that the sqlite_stat4
|
||||
# functionality is working.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix analyze9
|
||||
|
||||
ifcapable !stat4 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc s {blob} {
|
||||
set ret ""
|
||||
binary scan $blob c* bytes
|
||||
foreach b $bytes {
|
||||
set t [binary format c $b]
|
||||
if {[string is print $t]} {
|
||||
append ret $t
|
||||
} else {
|
||||
append ret .
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
db function s s
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a TEXT, b TEXT);
|
||||
INSERT INTO t1 VALUES('(0)', '(0)');
|
||||
INSERT INTO t1 VALUES('(1)', '(1)');
|
||||
INSERT INTO t1 VALUES('(2)', '(2)');
|
||||
INSERT INTO t1 VALUES('(3)', '(3)');
|
||||
INSERT INTO t1 VALUES('(4)', '(4)');
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
} {}
|
||||
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
ANALYZE;
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
SELECT tbl,idx,nEq,nLt,nDLt,test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
t1 i1 {1 1 1} {0 0 0} {0 0 0} {(0) (0) 1}
|
||||
t1 i1 {1 1 1} {1 1 1} {1 1 1} {(1) (1) 2}
|
||||
t1 i1 {1 1 1} {2 2 2} {2 2 2} {(2) (2) 3}
|
||||
t1 i1 {1 1 1} {3 3 3} {3 3 3} {(3) (3) 4}
|
||||
t1 i1 {1 1 1} {4 4 4} {4 4 4} {(4) (4) 5}
|
||||
}
|
||||
|
||||
if {[permutation] != "utf16"} {
|
||||
do_execsql_test 1.3 {
|
||||
SELECT tbl,idx,nEq,nLt,nDLt,s(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
t1 i1 {1 1 1} {0 0 0} {0 0 0} ....(0)(0)
|
||||
t1 i1 {1 1 1} {1 1 1} {1 1 1} ....(1)(1).
|
||||
t1 i1 {1 1 1} {2 2 2} {2 2 2} ....(2)(2).
|
||||
t1 i1 {1 1 1} {3 3 3} {3 3 3} ....(3)(3).
|
||||
t1 i1 {1 1 1} {4 4 4} {4 4 4} ....(4)(4).
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This is really just to test SQL user function "test_decode".
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES('some text', 14, NULL);
|
||||
INSERT INTO t1 VALUES(22.0, NULL, x'656667');
|
||||
CREATE INDEX i1 ON t1(a, b, c);
|
||||
ANALYZE;
|
||||
SELECT test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
{22.0 NULL x'656667' 2}
|
||||
{{some text} 14 NULL 1}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE INDEX i2 ON t2(a, b);
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
do_test 3.2 {
|
||||
for {set i 0} {$i < 1000} {incr i} {
|
||||
set a [expr $i / 10]
|
||||
set b [expr int(rand() * 15.0)]
|
||||
execsql { INSERT INTO t2 VALUES($a, $b) }
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
db func lindex lindex
|
||||
|
||||
# Each value of "a" occurs exactly 10 times in the table.
|
||||
#
|
||||
do_execsql_test 3.3.1 {
|
||||
SELECT count(*) FROM t2 GROUP BY a;
|
||||
} [lrange [string repeat "10 " 100] 0 99]
|
||||
|
||||
# The first element in the "nEq" list of all samples should therefore be 10.
|
||||
#
|
||||
do_execsql_test 3.3.2 {
|
||||
ANALYZE;
|
||||
SELECT lindex(nEq, 0) FROM sqlite_stat4;
|
||||
} [lrange [string repeat "10 " 100] 0 23]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 3.4 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(1, 1, 'one-a');
|
||||
INSERT INTO t1 VALUES(11, 1, 'one-b');
|
||||
INSERT INTO t1 VALUES(21, 1, 'one-c');
|
||||
INSERT INTO t1 VALUES(31, 1, 'one-d');
|
||||
INSERT INTO t1 VALUES(41, 1, 'one-e');
|
||||
INSERT INTO t1 VALUES(51, 1, 'one-f');
|
||||
INSERT INTO t1 VALUES(61, 1, 'one-g');
|
||||
INSERT INTO t1 VALUES(71, 1, 'one-h');
|
||||
INSERT INTO t1 VALUES(81, 1, 'one-i');
|
||||
INSERT INTO t1 VALUES(91, 1, 'one-j');
|
||||
INSERT INTO t1 SELECT a+1,2,'two' || substr(c,4) FROM t1;
|
||||
INSERT INTO t1 SELECT a+2,3,'three'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
INSERT INTO t1 SELECT a+3,4,'four'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
INSERT INTO t1 SELECT a+4,5,'five'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
INSERT INTO t1 SELECT a+5,6,'six'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
ANALYZE;
|
||||
SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND 60;
|
||||
} {three-d three-e three-f}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# These tests verify that the sample selection for stat4 appears to be
|
||||
# working as designed.
|
||||
#
|
||||
|
||||
reset_db
|
||||
db func lindex lindex
|
||||
db func lrange lrange
|
||||
|
||||
do_execsql_test 4.0 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX i1 ON t1(c, b, a);
|
||||
}
|
||||
|
||||
|
||||
proc insert_filler_rows_n {iStart args} {
|
||||
set A(-ncopy) 1
|
||||
set A(-nval) 1
|
||||
|
||||
foreach {k v} $args {
|
||||
if {[info exists A($k)]==0} { error "no such option: $k" }
|
||||
set A($k) $v
|
||||
}
|
||||
if {[llength $args] % 2} {
|
||||
error "option requires an argument: [lindex $args end]"
|
||||
}
|
||||
|
||||
for {set i 0} {$i < $A(-nval)} {incr i} {
|
||||
set iVal [expr $iStart+$i]
|
||||
for {set j 0} {$j < $A(-ncopy)} {incr j} {
|
||||
execsql { INSERT INTO t1 VALUES($iVal, $iVal, $iVal) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_test 4.1 {
|
||||
execsql { BEGIN }
|
||||
insert_filler_rows_n 0 -ncopy 10 -nval 19
|
||||
insert_filler_rows_n 20 -ncopy 1 -nval 100
|
||||
|
||||
execsql {
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 1, 'a');
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 1, 'b');
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 1, 'c');
|
||||
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 2, 'e');
|
||||
INSERT INTO t1(c, b, a) VALUES(200, 2, 'f');
|
||||
|
||||
INSERT INTO t1(c, b, a) VALUES(201, 3, 'g');
|
||||
INSERT INTO t1(c, b, a) VALUES(201, 4, 'h');
|
||||
|
||||
ANALYZE;
|
||||
SELECT count(*) FROM sqlite_stat4;
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
} {24 297}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
SELECT
|
||||
neq,
|
||||
lrange(nlt, 0, 2),
|
||||
lrange(ndlt, 0, 2),
|
||||
lrange(test_decode(sample), 0, 2)
|
||||
FROM sqlite_stat4
|
||||
ORDER BY rowid LIMIT 16;
|
||||
} {
|
||||
{10 10 10 1} {0 0 0} {0 0 0} {0 0 0}
|
||||
{10 10 10 1} {10 10 10} {1 1 1} {1 1 1}
|
||||
{10 10 10 1} {20 20 20} {2 2 2} {2 2 2}
|
||||
{10 10 10 1} {30 30 30} {3 3 3} {3 3 3}
|
||||
{10 10 10 1} {40 40 40} {4 4 4} {4 4 4}
|
||||
{10 10 10 1} {50 50 50} {5 5 5} {5 5 5}
|
||||
{10 10 10 1} {60 60 60} {6 6 6} {6 6 6}
|
||||
{10 10 10 1} {70 70 70} {7 7 7} {7 7 7}
|
||||
{10 10 10 1} {80 80 80} {8 8 8} {8 8 8}
|
||||
{10 10 10 1} {90 90 90} {9 9 9} {9 9 9}
|
||||
{10 10 10 1} {100 100 100} {10 10 10} {10 10 10}
|
||||
{10 10 10 1} {110 110 110} {11 11 11} {11 11 11}
|
||||
{10 10 10 1} {120 120 120} {12 12 12} {12 12 12}
|
||||
{10 10 10 1} {130 130 130} {13 13 13} {13 13 13}
|
||||
{10 10 10 1} {140 140 140} {14 14 14} {14 14 14}
|
||||
{10 10 10 1} {150 150 150} {15 15 15} {15 15 15}
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
SELECT
|
||||
neq,
|
||||
lrange(nlt, 0, 2),
|
||||
lrange(ndlt, 0, 2),
|
||||
lrange(test_decode(sample), 0, 1)
|
||||
FROM sqlite_stat4
|
||||
ORDER BY rowid DESC LIMIT 2;
|
||||
} {
|
||||
{2 1 1 1} {295 296 296} {120 122 125} {201 4}
|
||||
{5 3 1 1} {290 290 292} {119 119 121} {200 1}
|
||||
}
|
||||
|
||||
do_execsql_test 4.4 { SELECT count(DISTINCT c) FROM t1 WHERE c<201 } 120
|
||||
do_execsql_test 4.5 { SELECT count(DISTINCT c) FROM t1 WHERE c<200 } 119
|
||||
|
||||
# Check that the perioidic samples are present.
|
||||
do_execsql_test 4.6 {
|
||||
SELECT count(*) FROM sqlite_stat4
|
||||
WHERE lindex(test_decode(sample), 3) IN
|
||||
('34', '68', '102', '136', '170', '204', '238', '272')
|
||||
} {8}
|
||||
|
||||
reset_db
|
||||
do_test 4.7 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(o,t INTEGER PRIMARY KEY);
|
||||
CREATE INDEX i1 ON t1(o);
|
||||
}
|
||||
for {set i 0} {$i<10000} {incr i [expr (($i<1000)?1:10)]} {
|
||||
execsql { INSERT INTO t1 VALUES('x', $i) }
|
||||
}
|
||||
execsql {
|
||||
COMMIT;
|
||||
ANALYZE;
|
||||
SELECT count(*) FROM sqlite_stat4;
|
||||
}
|
||||
} {8}
|
||||
do_execsql_test 4.8 {
|
||||
SELECT test_decode(sample) FROM sqlite_stat4;
|
||||
} {
|
||||
{x 211} {x 423} {x 635} {x 847}
|
||||
{x 1590} {x 3710} {x 5830} {x 7950}
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following would cause a crash at one point.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
PRAGMA encoding = 'utf-16';
|
||||
CREATE TABLE t0(v);
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This was also crashing (corrupt sqlite_stat4 table).
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
CREATE INDEX i2 ON t1(b);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
INSERT INTO t1 VALUES(4, 4);
|
||||
INSERT INTO t1 VALUES(5, 5);
|
||||
ANALYZE;
|
||||
PRAGMA writable_schema = 1;
|
||||
CREATE TEMP TABLE x1 AS
|
||||
SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat4
|
||||
ORDER BY (rowid%5), rowid;
|
||||
DELETE FROM sqlite_stat4;
|
||||
INSERT INTO sqlite_stat4 SELECT * FROM x1;
|
||||
PRAGMA writable_schema = 0;
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
do_execsql_test 6.2 {
|
||||
SELECT * FROM t1 WHERE a = 'abc';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests experiment with adding corrupted records to the
|
||||
# 'sample' column of the sqlite_stat4 table.
|
||||
#
|
||||
reset_db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
|
||||
do_execsql_test 7.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3);
|
||||
INSERT INTO t1 VALUES(4, 4);
|
||||
INSERT INTO t1 VALUES(5, 5);
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET sample = X'' WHERE rowid = 1;
|
||||
ANALYZE sqlite_master;
|
||||
}
|
||||
|
||||
do_execsql_test 7.2 {
|
||||
UPDATE sqlite_stat4 SET sample = X'FFFF';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 1;
|
||||
} {1 1}
|
||||
|
||||
do_execsql_test 7.3 {
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET neq = '0 0 0';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 1;
|
||||
} {1 1}
|
||||
|
||||
do_execsql_test 7.4 {
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET ndlt = '0 0 0';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 3;
|
||||
} {3 3}
|
||||
|
||||
do_execsql_test 7.5 {
|
||||
ANALYZE;
|
||||
UPDATE sqlite_stat4 SET nlt = '0 0 0';
|
||||
ANALYZE sqlite_master;
|
||||
SELECT * FROM t1 WHERE a = 5;
|
||||
} {5 5}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 8.1 {
|
||||
CREATE TABLE t1(x TEXT);
|
||||
CREATE INDEX i1 ON t1(x);
|
||||
INSERT INTO t1 VALUES('1');
|
||||
INSERT INTO t1 VALUES('2');
|
||||
INSERT INTO t1 VALUES('3');
|
||||
INSERT INTO t1 VALUES('4');
|
||||
ANALYZE;
|
||||
}
|
||||
do_execsql_test 8.2 {
|
||||
SELECT * FROM t1 WHERE x = 3;
|
||||
} {3}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that the bug fixed by [91733bc485] really is fixed.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 9.1 {
|
||||
CREATE TABLE t1(a, b, c, d, e);
|
||||
CREATE INDEX i1 ON t1(a, b, c, d);
|
||||
CREATE INDEX i2 ON t1(e);
|
||||
}
|
||||
do_test 9.2 {
|
||||
execsql BEGIN;
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
|
||||
}
|
||||
for {set i 0} {$i < 20} {incr i} {
|
||||
execsql "INSERT INTO t1 VALUES('x', 'y', 'z', 101, $i)"
|
||||
}
|
||||
for {set i 102} {$i < 200} {incr i} {
|
||||
execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
|
||||
}
|
||||
execsql COMMIT
|
||||
execsql ANALYZE
|
||||
} {}
|
||||
|
||||
do_eqp_test 9.3.1 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=101 AND e=5;
|
||||
} {/t1 USING INDEX i2/}
|
||||
do_eqp_test 9.3.2 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=99 AND e=5;
|
||||
} {/t1 USING INDEX i1/}
|
||||
|
||||
set value_d [expr 101]
|
||||
do_eqp_test 9.4.1 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
|
||||
} {/t1 USING INDEX i2/}
|
||||
set value_d [expr 99]
|
||||
do_eqp_test 9.4.2 {
|
||||
SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
|
||||
} {/t1 USING INDEX i1/}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
# 2013 August 3
|
||||
#
|
||||
# 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 contains automated tests used to verify that the current build
|
||||
# (which must be either ENABLE_STAT3 or ENABLE_STAT4) works with both stat3
|
||||
# and stat4 data.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix analyzeA
|
||||
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
# Populate the stat3 table according to the current contents of the db
|
||||
#
|
||||
proc populate_stat3 {{bDropTable 1}} {
|
||||
# Open a second connection on database "test.db" and run ANALYZE. If this
|
||||
# is an ENABLE_STAT3 build, this is all that is required to create and
|
||||
# populate the sqlite_stat3 table.
|
||||
#
|
||||
sqlite3 db2 test.db
|
||||
execsql { ANALYZE }
|
||||
|
||||
# Now, if this is an ENABLE_STAT4 build, create and populate the
|
||||
# sqlite_stat3 table based on the stat4 data gathered by the ANALYZE
|
||||
# above. Then drop the sqlite_stat4 table.
|
||||
#
|
||||
ifcapable stat4 {
|
||||
db2 func lindex lindex
|
||||
execsql {
|
||||
PRAGMA writable_schema = on;
|
||||
CREATE TABLE sqlite_stat3(tbl,idx,neq,nlt,ndlt,sample);
|
||||
INSERT INTO sqlite_stat3
|
||||
SELECT DISTINCT tbl, idx,
|
||||
lindex(neq,0), lindex(nlt,0), lindex(ndlt,0), test_extract(sample, 0)
|
||||
FROM sqlite_stat4;
|
||||
} db2
|
||||
if {$bDropTable} { execsql {DROP TABLE sqlite_stat4} db2 }
|
||||
execsql { PRAGMA writable_schema = off }
|
||||
}
|
||||
|
||||
# Modify the database schema cookie to ensure that the other connection
|
||||
# reloads the schema.
|
||||
#
|
||||
execsql {
|
||||
CREATE TABLE obscure_tbl_nm(x);
|
||||
DROP TABLE obscure_tbl_nm;
|
||||
} db2
|
||||
db2 close
|
||||
}
|
||||
|
||||
# Populate the stat4 table according to the current contents of the db
|
||||
#
|
||||
proc populate_stat4 {{bDropTable 1}} {
|
||||
sqlite3 db2 test.db
|
||||
execsql { ANALYZE }
|
||||
|
||||
ifcapable stat3 {
|
||||
execsql {
|
||||
PRAGMA writable_schema = on;
|
||||
CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample);
|
||||
INSERT INTO sqlite_stat4
|
||||
SELECT tbl, idx, neq, nlt, ndlt, sqlite_record(sample)
|
||||
FROM sqlite_stat3;
|
||||
} db2
|
||||
if {$bDropTable} { execsql {DROP TABLE sqlite_stat3} db2 }
|
||||
execsql { PRAGMA writable_schema = off }
|
||||
}
|
||||
|
||||
# Modify the database schema cookie to ensure that the other connection
|
||||
# reloads the schema.
|
||||
#
|
||||
execsql {
|
||||
CREATE TABLE obscure_tbl_nm(x);
|
||||
DROP TABLE obscure_tbl_nm;
|
||||
} db2
|
||||
db2 close
|
||||
}
|
||||
|
||||
# Populate the stat4 table according to the current contents of the db.
|
||||
# Leave deceptive data in the stat3 table. This data should be ignored
|
||||
# in favour of that from the stat4 table.
|
||||
#
|
||||
proc populate_both {} {
|
||||
ifcapable stat4 { populate_stat3 0 }
|
||||
ifcapable stat3 { populate_stat4 0 }
|
||||
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
PRAGMA writable_schema = on;
|
||||
UPDATE sqlite_stat3 SET idx =
|
||||
CASE idx WHEN 't1b' THEN 't1c' ELSE 't1b'
|
||||
END;
|
||||
PRAGMA writable_schema = off;
|
||||
CREATE TABLE obscure_tbl_nm(x);
|
||||
DROP TABLE obscure_tbl_nm;
|
||||
} db2
|
||||
db2 close
|
||||
}
|
||||
|
||||
foreach {tn analyze_cmd} {
|
||||
1 populate_stat4
|
||||
2 populate_stat3
|
||||
3 populate_both
|
||||
} {
|
||||
reset_db
|
||||
do_test 1.$tn.1 {
|
||||
execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) }
|
||||
for {set i 0} {$i < 100} {incr i} {
|
||||
set c [expr int(pow(1.1,$i)/100)]
|
||||
set b [expr 125 - int(pow(1.1,99-$i))/100]
|
||||
execsql {INSERT INTO t1 VALUES($i, $b, $c)}
|
||||
}
|
||||
} {}
|
||||
|
||||
execsql { CREATE INDEX t1b ON t1(b) }
|
||||
execsql { CREATE INDEX t1c ON t1(c) }
|
||||
$analyze_cmd
|
||||
|
||||
do_execsql_test 1.$tn.2.1 { SELECT count(*) FROM t1 WHERE b=31 } 1
|
||||
do_execsql_test 1.$tn.2.2 { SELECT count(*) FROM t1 WHERE c=0 } 49
|
||||
do_execsql_test 1.$tn.2.3 { SELECT count(*) FROM t1 WHERE b=125 } 49
|
||||
do_execsql_test 1.$tn.2.4 { SELECT count(*) FROM t1 WHERE c=16 } 1
|
||||
|
||||
do_eqp_test 1.$tn.2.5 {
|
||||
SELECT * FROM t1 WHERE b = 31 AND c = 0;
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
|
||||
do_eqp_test 1.$tn.2.6 {
|
||||
SELECT * FROM t1 WHERE b = 125 AND c = 16;
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?)}}
|
||||
|
||||
do_execsql_test 1.$tn.3.1 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 0 AND 50
|
||||
} {6}
|
||||
do_execsql_test 1.$tn.3.2 {
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 50
|
||||
} {90}
|
||||
do_execsql_test 1.$tn.3.3 {
|
||||
SELECT count(*) FROM t1 WHERE b BETWEEN 75 AND 125
|
||||
} {90}
|
||||
do_execsql_test 1.$tn.3.4 {
|
||||
SELECT count(*) FROM t1 WHERE c BETWEEN 75 AND 125
|
||||
} {6}
|
||||
|
||||
do_eqp_test 1.$tn.3.5 {
|
||||
SELECT * FROM t1 WHERE b BETWEEN 0 AND 50 AND c BETWEEN 0 AND 50
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
|
||||
|
||||
do_eqp_test 1.$tn.3.6 {
|
||||
SELECT * FROM t1 WHERE b BETWEEN 75 AND 125 AND c BETWEEN 75 AND 125
|
||||
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+8
-4
@@ -2325,10 +2325,14 @@ ifcapable compound&&subquery {
|
||||
}
|
||||
}
|
||||
}
|
||||
ifcapable stat3 {
|
||||
set stat3 "sqlite_stat3 "
|
||||
ifcapable stat4 {
|
||||
set stat4 "sqlite_stat4 "
|
||||
} else {
|
||||
set stat3 ""
|
||||
ifcapable stat3 {
|
||||
set stat4 "sqlite_stat3 "
|
||||
} else {
|
||||
set stat4 ""
|
||||
}
|
||||
}
|
||||
do_test auth-5.2 {
|
||||
execsql {
|
||||
@@ -2337,7 +2341,7 @@ ifcapable compound&&subquery {
|
||||
WHERE type='table'
|
||||
ORDER BY name
|
||||
}
|
||||
} "sqlite_stat1 ${stat3}t1 t2 t3 t4"
|
||||
} "sqlite_stat1 ${stat4}t1 t2 t3 t4"
|
||||
}
|
||||
|
||||
# Ticket #3944
|
||||
|
||||
+2
-2
@@ -61,7 +61,7 @@ proc lookaside {db} {
|
||||
}
|
||||
}
|
||||
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
set STAT3 1
|
||||
} else {
|
||||
set STAT3 0
|
||||
@@ -214,7 +214,7 @@ foreach ::lookaside_buffer_size {0 64 120} {
|
||||
# much greater than just that reported by DBSTATUS_SCHEMA_USED in this
|
||||
# case.
|
||||
#
|
||||
# Some of the memory used for sqlite_stat3 is unaccounted for by
|
||||
# Some of the memory used for sqlite_stat4 is unaccounted for by
|
||||
# dbstatus.
|
||||
#
|
||||
# Finally, on osx the estimate of memory used by the schema may be
|
||||
|
||||
+3
-3
@@ -149,15 +149,15 @@ do_test index6-2.2 {
|
||||
SELECT * FROM t2 WHERE a=5;
|
||||
}
|
||||
} {/.* TABLE t2 USING INDEX t2a1 .*/}
|
||||
ifcapable stat3 {
|
||||
do_test index6-2.3stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
do_test index6-2.3stat4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a IS NOT NULL;
|
||||
}
|
||||
} {/.* TABLE t2 USING INDEX t2a1 .*/}
|
||||
} else {
|
||||
do_test index6-2.3stat3 {
|
||||
do_test index6-2.3stat4 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a IS NOT NULL AND a>0;
|
||||
|
||||
+31
-1
@@ -15,6 +15,7 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix mallocA
|
||||
|
||||
# Only run these tests if memory debugging is turned on.
|
||||
#
|
||||
@@ -40,7 +41,6 @@ db eval {
|
||||
db close
|
||||
copy_file test.db test.db.bu
|
||||
|
||||
|
||||
do_malloc_test mallocA-1 -testdb test.db.bu -sqlbody {
|
||||
ANALYZE
|
||||
}
|
||||
@@ -53,6 +53,7 @@ do_malloc_test mallocA-1.2 -testdb test.db.bu -sqlbody {
|
||||
do_malloc_test mallocA-1.3 -testdb test.db.bu -sqlbody {
|
||||
ANALYZE main.t1
|
||||
}
|
||||
|
||||
ifcapable reindex {
|
||||
do_malloc_test mallocA-2 -testdb test.db.bu -sqlbody {
|
||||
REINDEX;
|
||||
@@ -68,6 +69,35 @@ ifcapable reindex {
|
||||
}
|
||||
}
|
||||
|
||||
reset_db
|
||||
sqlite3_db_config_lookaside db 0 0 0
|
||||
do_execsql_test 6-prep {
|
||||
CREATE TABLE t1(a, b);
|
||||
CREATE INDEX i1 ON t1(a, b);
|
||||
INSERT INTO t1 VALUES('abc', 'w'); -- rowid=1
|
||||
INSERT INTO t1 VALUES('abc', 'x'); -- rowid=2
|
||||
INSERT INTO t1 VALUES('abc', 'y'); -- rowid=3
|
||||
INSERT INTO t1 VALUES('abc', 'z'); -- rowid=4
|
||||
|
||||
INSERT INTO t1 VALUES('def', 'w'); -- rowid=5
|
||||
INSERT INTO t1 VALUES('def', 'x'); -- rowid=6
|
||||
INSERT INTO t1 VALUES('def', 'y'); -- rowid=7
|
||||
INSERT INTO t1 VALUES('def', 'z'); -- rowid=8
|
||||
|
||||
ANALYZE;
|
||||
}
|
||||
|
||||
do_faultsim_test 6.1 -faults oom* -body {
|
||||
execsql { SELECT rowid FROM t1 WHERE a='abc' AND b='x' }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 2]
|
||||
}
|
||||
do_faultsim_test 6.2 -faults oom* -body {
|
||||
execsql { SELECT rowid FROM t1 WHERE a='abc' AND b<'y' }
|
||||
} -test {
|
||||
faultsim_test_result [list 0 {1 2}]
|
||||
}
|
||||
|
||||
# Ensure that no file descriptors were leaked.
|
||||
do_test malloc-99.X {
|
||||
catch {db close}
|
||||
|
||||
@@ -503,6 +503,8 @@ test_suite "utf16" -description {
|
||||
pragma encoding = 'UTF-16'
|
||||
} -files {
|
||||
alter.test alter3.test
|
||||
analyze.test analyze3.test analyze4.test analyze5.test analyze6.test
|
||||
analyze7.test analyze8.test analyze9.test analyzeA.test
|
||||
auth.test bind.test blob.test capi2.test capi3.test collate1.test
|
||||
collate2.test collate3.test collate4.test collate5.test collate6.test
|
||||
conflict.test date.test delete.test expr.test fkey1.test func.test
|
||||
|
||||
@@ -242,4 +242,30 @@ SELECT * FROM t1;}
|
||||
5,"this is 5"}}
|
||||
}
|
||||
|
||||
# Import columns containing quoted strings
|
||||
do_test shell5-1.9 {
|
||||
set out [open shell5.csv w]
|
||||
puts $out {1,"",11}
|
||||
puts $out {2,"x",22}
|
||||
puts $out {3,"""",33}
|
||||
puts $out {4,"hello",44}
|
||||
puts $out "5,55,\"\"\r"
|
||||
puts $out {6,66,"x"}
|
||||
puts $out {7,77,""""}
|
||||
puts $out {8,88,"hello"}
|
||||
puts $out {"",9,99}
|
||||
puts $out {"x",10,110}
|
||||
puts $out {"""",11,121}
|
||||
puts $out {"hello",12,132}
|
||||
close $out
|
||||
file delete -force test.db
|
||||
catchcmd test.db {.mode csv
|
||||
CREATE TABLE t1(a,b,c);
|
||||
.import shell5.csv t1
|
||||
}
|
||||
sqlite3 db test.db
|
||||
db eval {SELECT *, '|' FROM t1 ORDER BY rowid}
|
||||
} {1 {} 11 | 2 x 22 | 3 {"} 33 | 4 hello 44 | 5 55 {} | 6 66 x | 7 77 {"} | 8 88 hello | {} 9 99 | x 10 110 | {"} 11 121 | hello 12 132 |}
|
||||
db close
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -268,6 +268,7 @@ do_test table-5.2.1 {
|
||||
DROP TABLE IF EXISTS sqlite_stat1;
|
||||
DROP TABLE IF EXISTS sqlite_stat2;
|
||||
DROP TABLE IF EXISTS sqlite_stat3;
|
||||
DROP TABLE IF EXISTS sqlite_stat4;
|
||||
SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_stat*';
|
||||
}
|
||||
} {}
|
||||
|
||||
+104
-34
@@ -14,7 +14,7 @@
|
||||
# $Id: tester.tcl,v 1.143 2009/04/09 01:23:49 drh Exp $
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The commands provided by the code in this file to help with creating
|
||||
# The commands provided by the code in this file to help with creating
|
||||
# test cases are as follows:
|
||||
#
|
||||
# Commands to manipulate the db and the file-system at a high level:
|
||||
@@ -42,6 +42,7 @@
|
||||
#
|
||||
# Commands to execute/explain SQL statements:
|
||||
#
|
||||
# memdbsql SQL
|
||||
# stepsql DB SQL
|
||||
# execsql2 SQL
|
||||
# explain_no_trace SQL
|
||||
@@ -80,7 +81,7 @@
|
||||
# presql
|
||||
#
|
||||
|
||||
# Set the precision of FP arithmatic used by the interpreter. And
|
||||
# Set the precision of FP arithmatic used by the interpreter. And
|
||||
# configure SQLite to take database file locks on the page that begins
|
||||
# 64KB into the database file instead of the one 1GB in. This means
|
||||
# the code that handles that special case can be tested without creating
|
||||
@@ -90,7 +91,7 @@ set tcl_precision 15
|
||||
sqlite3_test_control_pending_byte 0x0010000
|
||||
|
||||
|
||||
# If the pager codec is available, create a wrapper for the [sqlite3]
|
||||
# If the pager codec is available, create a wrapper for the [sqlite3]
|
||||
# command that appends "-key {xyzzy}" to the command line. i.e. this:
|
||||
#
|
||||
# sqlite3 db test.db
|
||||
@@ -122,7 +123,7 @@ if {[info command sqlite_orig]==""} {
|
||||
}
|
||||
set res
|
||||
} else {
|
||||
# This command is not opening a new database connection. Pass the
|
||||
# This command is not opening a new database connection. Pass the
|
||||
# arguments through to the C implementation as the are.
|
||||
#
|
||||
uplevel 1 sqlite_orig $args
|
||||
@@ -291,6 +292,66 @@ proc do_delete_file {force args} {
|
||||
}
|
||||
}
|
||||
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
proc do_remove_win32_dir {args} {
|
||||
set nRetry [getFileRetries] ;# Maximum number of retries.
|
||||
set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
|
||||
|
||||
foreach dirName $args {
|
||||
# On windows, sometimes even a [remove_win32_dir] can fail just after
|
||||
# a directory is emptied. 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
|
||||
# removal.
|
||||
#
|
||||
if {$nRetry > 0} {
|
||||
for {set i 0} {$i < $nRetry} {incr i} {
|
||||
set rc [catch {
|
||||
remove_win32_dir $dirName
|
||||
} msg]
|
||||
if {$rc == 0} break
|
||||
if {$nDelay > 0} { after $nDelay }
|
||||
}
|
||||
if {$rc} { error $msg }
|
||||
} else {
|
||||
remove_win32_dir $dirName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc do_delete_win32_file {args} {
|
||||
set nRetry [getFileRetries] ;# Maximum number of retries.
|
||||
set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
|
||||
|
||||
foreach fileName $args {
|
||||
# On windows, sometimes even a [delete_win32_file] 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.
|
||||
#
|
||||
if {$nRetry > 0} {
|
||||
for {set i 0} {$i < $nRetry} {incr i} {
|
||||
set rc [catch {
|
||||
delete_win32_file $fileName
|
||||
} msg]
|
||||
if {$rc == 0} break
|
||||
if {$nDelay > 0} { after $nDelay }
|
||||
}
|
||||
if {$rc} { error $msg }
|
||||
} else {
|
||||
delete_win32_file $fileName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proc execpresql {handle args} {
|
||||
trace remove execution $handle enter [list execpresql $handle]
|
||||
if {[info exists ::G(perm:presql)]} {
|
||||
@@ -312,8 +373,8 @@ proc do_not_use_codec {} {
|
||||
#
|
||||
if {[info exists cmdlinearg]==0} {
|
||||
|
||||
# Parse any options specified in the $argv array. This script accepts the
|
||||
# following options:
|
||||
# Parse any options specified in the $argv array. This script accepts the
|
||||
# following options:
|
||||
#
|
||||
# --pause
|
||||
# --soft-heap-limit=NN
|
||||
@@ -342,7 +403,7 @@ if {[info exists cmdlinearg]==0} {
|
||||
foreach a $argv {
|
||||
switch -regexp -- $a {
|
||||
{^-+pause$} {
|
||||
# Wait for user input before continuing. This is to give the user an
|
||||
# Wait for user input before continuing. This is to give the user an
|
||||
# opportunity to connect profiling tools to the process.
|
||||
puts -nonewline "Press RETURN to begin..."
|
||||
flush stdout
|
||||
@@ -405,8 +466,8 @@ if {[info exists cmdlinearg]==0} {
|
||||
# Install the malloc layer used to inject OOM errors. And the 'automatic'
|
||||
# extensions. This only needs to be done once for the process.
|
||||
#
|
||||
sqlite3_shutdown
|
||||
install_malloc_faultsim 1
|
||||
sqlite3_shutdown
|
||||
install_malloc_faultsim 1
|
||||
sqlite3_initialize
|
||||
autoinstall_test_functions
|
||||
|
||||
@@ -516,7 +577,7 @@ proc incr_ntest {} {
|
||||
}
|
||||
|
||||
|
||||
# Invoke the do_test procedure to run a single test
|
||||
# Invoke the do_test procedure to run a single test
|
||||
#
|
||||
proc do_test {name cmd expected} {
|
||||
global argv cmdlinearg
|
||||
@@ -525,7 +586,7 @@ proc do_test {name cmd expected} {
|
||||
|
||||
sqlite3_memdebug_settitle $name
|
||||
|
||||
# if {[llength $argv]==0} {
|
||||
# if {[llength $argv]==0} {
|
||||
# set go 1
|
||||
# } else {
|
||||
# set go 0
|
||||
@@ -628,7 +689,7 @@ proc do_realnum_test {name cmd expected} {
|
||||
|
||||
proc fix_testname {varname} {
|
||||
upvar $varname testname
|
||||
if {[info exists ::testprefix]
|
||||
if {[info exists ::testprefix]
|
||||
&& [string is digit [string range $testname 0 0]]
|
||||
} {
|
||||
set testname "${::testprefix}-$testname"
|
||||
@@ -640,7 +701,7 @@ proc normalize_list {L} {
|
||||
foreach l $L {lappend L2 $l}
|
||||
set L2
|
||||
}
|
||||
|
||||
|
||||
proc do_execsql_test {testname sql {result {}}} {
|
||||
fix_testname testname
|
||||
uplevel do_test [list $testname] [list "execsql {$sql}"] [list [list {*}$result]]
|
||||
@@ -726,7 +787,7 @@ proc delete_all_data {} {
|
||||
}
|
||||
}
|
||||
|
||||
# Run an SQL script.
|
||||
# Run an SQL script.
|
||||
# Return the number of microseconds per statement.
|
||||
#
|
||||
proc speed_trial {name numstmt units sql} {
|
||||
@@ -990,6 +1051,15 @@ proc execsql2 {sql} {
|
||||
return $result
|
||||
}
|
||||
|
||||
# Use a temporary in-memory database to execute SQL statements
|
||||
#
|
||||
proc memdbsql {sql} {
|
||||
sqlite3 memdb :memory:
|
||||
set result [memdb eval $sql]
|
||||
memdb close
|
||||
return $result
|
||||
}
|
||||
|
||||
# Use the non-callback API to execute multiple SQL statements
|
||||
#
|
||||
proc stepsql {dbptr sql} {
|
||||
@@ -1104,7 +1174,7 @@ proc crashsql {args} {
|
||||
set crashfile ""
|
||||
set dc ""
|
||||
set sql [lindex $args end]
|
||||
|
||||
|
||||
for {set ii 0} {$ii < [llength $args]-1} {incr ii 2} {
|
||||
set z [lindex $args $ii]
|
||||
set n [string length $z]
|
||||
@@ -1123,7 +1193,7 @@ proc crashsql {args} {
|
||||
error "Compulsory option -file missing"
|
||||
}
|
||||
|
||||
# $crashfile gets compared to the native filename in
|
||||
# $crashfile gets compared to the native filename in
|
||||
# cfSync(), which can be different then what TCL uses by
|
||||
# default, so here we force it to the "nativename" format.
|
||||
set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]]
|
||||
@@ -1158,7 +1228,7 @@ proc crashsql {args} {
|
||||
set r [catch {
|
||||
exec [info nameofexec] crash.tcl >@stdout
|
||||
} msg]
|
||||
|
||||
|
||||
# Windows/ActiveState TCL returns a slightly different
|
||||
# error message. We map that to the expected message
|
||||
# so that we don't have to change all of the test
|
||||
@@ -1168,7 +1238,7 @@ proc crashsql {args} {
|
||||
set msg "child process exited abnormally"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lappend r $msg
|
||||
}
|
||||
|
||||
@@ -1194,7 +1264,7 @@ proc run_ioerr_prep {} {
|
||||
# Usage: do_ioerr_test <test number> <options...>
|
||||
#
|
||||
# This proc is used to implement test cases that check that IO errors
|
||||
# are correctly handled. The first argument, <test number>, is an integer
|
||||
# are correctly handled. The first argument, <test number>, is an integer
|
||||
# used to name the tests executed by this proc. Options are as follows:
|
||||
#
|
||||
# -tclprep TCL script to run to prepare test.
|
||||
@@ -1223,7 +1293,7 @@ proc do_ioerr_test {testname args} {
|
||||
# TEMPORARY: For 3.5.9, disable testing of extended result codes. There are
|
||||
# a couple of obscure IO errors that do not return them.
|
||||
set ::ioerropts(-erc) 0
|
||||
|
||||
|
||||
# Create a single TCL script from the TCL and SQL specified
|
||||
# as the body of the test.
|
||||
set ::ioerrorbody {}
|
||||
@@ -1247,7 +1317,7 @@ proc do_ioerr_test {testname args} {
|
||||
set ::TN $n
|
||||
incr ::ioerropts(-count) -1
|
||||
if {$::ioerropts(-count)<0} break
|
||||
|
||||
|
||||
# Skip this IO error if it was specified with the "-exclude" option.
|
||||
if {[info exists ::ioerropts(-exclude)]} {
|
||||
if {[lsearch $::ioerropts(-exclude) $n]!=-1} continue
|
||||
@@ -1256,7 +1326,7 @@ proc do_ioerr_test {testname args} {
|
||||
restore_prng_state
|
||||
}
|
||||
|
||||
# Delete the files test.db and test2.db, then execute the TCL and
|
||||
# Delete the files test.db and test2.db, then execute the TCL and
|
||||
# SQL (in that order) to prepare for the test case.
|
||||
do_test $testname.$n.1 {
|
||||
run_ioerr_prep
|
||||
@@ -1274,7 +1344,7 @@ proc do_ioerr_test {testname args} {
|
||||
}] $n
|
||||
|
||||
# Execute the TCL script created for the body of this test. If
|
||||
# at least N IO operations performed by SQLite as a result of
|
||||
# at least N IO operations performed by SQLite as a result of
|
||||
# the script, the Nth will fail.
|
||||
do_test $testname.$n.3 {
|
||||
set ::sqlite_io_error_hit 0
|
||||
@@ -1328,12 +1398,12 @@ proc do_ioerr_test {testname args} {
|
||||
set ::sqlite_io_error_hit 0
|
||||
set ::sqlite_io_error_pending 0
|
||||
|
||||
# Check that no page references were leaked. There should be
|
||||
# a single reference if there is still an active transaction,
|
||||
# Check that no page references were leaked. There should be
|
||||
# a single reference if there is still an active transaction,
|
||||
# or zero otherwise.
|
||||
#
|
||||
# UPDATE: If the IO error occurs after a 'BEGIN' but before any
|
||||
# locks are established on database files (i.e. if the error
|
||||
# locks are established on database files (i.e. if the error
|
||||
# occurs while attempting to detect a hot-journal file), then
|
||||
# there may 0 page references and an active transaction according
|
||||
# to [sqlite3_get_autocommit].
|
||||
@@ -1349,7 +1419,7 @@ proc do_ioerr_test {testname args} {
|
||||
} {1}
|
||||
}
|
||||
|
||||
# If there is an open database handle and no open transaction,
|
||||
# If there is an open database handle and no open transaction,
|
||||
# and the pager is not running in exclusive-locking mode,
|
||||
# check that the pager is in "unlocked" state. Theoretically,
|
||||
# if a call to xUnlock() failed due to an IO error the underlying
|
||||
@@ -1453,7 +1523,7 @@ proc allcksum {{db db}} {
|
||||
}
|
||||
|
||||
# Generate a checksum based on the contents of a single database with
|
||||
# a database connection. The name of the database is $dbname.
|
||||
# a database connection. The name of the database is $dbname.
|
||||
# Examples of $dbname are "temp" or "main".
|
||||
#
|
||||
proc dbcksum {db dbname} {
|
||||
@@ -1547,8 +1617,8 @@ proc drop_all_tables {{db db}} {
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# If a test script is executed with global variable $::G(perm:name) set to
|
||||
# "wal", then the tests are run in WAL mode. Otherwise, they should be run
|
||||
# in rollback mode. The following Tcl procs are used to make this less
|
||||
# "wal", then the tests are run in WAL mode. Otherwise, they should be run
|
||||
# in rollback mode. The following Tcl procs are used to make this less
|
||||
# intrusive:
|
||||
#
|
||||
# wal_set_journal_mode ?DB?
|
||||
@@ -1563,9 +1633,9 @@ proc drop_all_tables {{db db}} {
|
||||
# Otherwise (if not running a WAL permutation) this is a no-op.
|
||||
#
|
||||
# wal_is_wal_mode
|
||||
#
|
||||
#
|
||||
# Returns true if this test should be run in WAL mode. False otherwise.
|
||||
#
|
||||
#
|
||||
proc wal_is_wal_mode {} {
|
||||
expr {[permutation] eq "wal"}
|
||||
}
|
||||
@@ -1666,10 +1736,10 @@ proc slave_test_file {zFile} {
|
||||
}
|
||||
set ::sqlite_open_file_count 0
|
||||
|
||||
# Test that the global "shared-cache" setting was not altered by
|
||||
# Test that the global "shared-cache" setting was not altered by
|
||||
# the test script.
|
||||
#
|
||||
ifcapable shared_cache {
|
||||
ifcapable shared_cache {
|
||||
set res [expr {[sqlite3_enable_shared_cache] == $scs}]
|
||||
do_test ${tail}-sharedcachesetting [list set {} $res] 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
# 2013 August 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.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix tkt-9f2eb3abac
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE TABLE t1(a,b,c,d,e, PRIMARY KEY(a,b,c,d,e));
|
||||
SELECT * FROM t1 WHERE a=? AND b=? AND c=? AND d=? AND e=?;
|
||||
} {}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
CREATE TABLE "a" (
|
||||
"b" integer NOT NULL,
|
||||
"c" integer NOT NULL,
|
||||
PRIMARY KEY ("b", "c")
|
||||
);
|
||||
|
||||
CREATE TABLE "d" (
|
||||
"e" integer NOT NULL,
|
||||
"g" integer NOT NULL,
|
||||
"f" integer NOT NULL,
|
||||
"h" integer NOT NULL,
|
||||
"i" character(10) NOT NULL,
|
||||
"j" int,
|
||||
PRIMARY KEY ("e", "g", "f", "h")
|
||||
);
|
||||
|
||||
CREATE TABLE "d_to_a" (
|
||||
"f_e" integer NOT NULL,
|
||||
"f_g" integer NOT NULL,
|
||||
"f_f" integer NOT NULL,
|
||||
"f_h" integer NOT NULL,
|
||||
"t_b" integer NOT NULL,
|
||||
"t_c" integer NOT NULL,
|
||||
"r" character NOT NULL,
|
||||
"s" integer,
|
||||
PRIMARY KEY ("f_e", "f_g", "f_f", "f_h", "t_b", "t_c")
|
||||
);
|
||||
|
||||
INSERT INTO d (g, e, h, f, j, i) VALUES ( 1, 1, 1, 1, 1, 1 );
|
||||
INSERT INTO a (b, c) VALUES ( 1, 1 );
|
||||
INSERT INTO d_to_a VALUES (1, 1, 1, 1, 1, 1, 1, 1);
|
||||
|
||||
DELETE FROM d_to_a
|
||||
WHERE f_g = 1 AND f_e = 1 AND f_h = 1 AND f_f = 1 AND t_b = 1 AND t_c = 1;
|
||||
|
||||
SELECT * FROM d_to_a;
|
||||
} {}
|
||||
|
||||
faultsim_delete_and_reopen
|
||||
do_execsql_test 2.0 { CREATE TABLE t1(a,b,c,d,e, PRIMARY KEY(a,b,c,d,e)) }
|
||||
do_execsql_test 2.1 { CREATE TABLE t2(x) }
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 3 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
execsql { SELECT 1 FROM sqlite_master }
|
||||
} -body {
|
||||
execsql { SELECT * FROM t1,t2 WHERE a=? AND b=? AND c=? AND d=? AND e=? }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -16,11 +16,26 @@
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
ifcapable !stat3 {
|
||||
ifcapable !stat4&&!stat3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
proc s {blob} {
|
||||
set ret ""
|
||||
binary scan $blob c* bytes
|
||||
foreach b $bytes {
|
||||
set t [binary format c $b]
|
||||
if {[string is print $t]} {
|
||||
append ret $t
|
||||
} else {
|
||||
append ret .
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
db function s s
|
||||
|
||||
do_test tkt-cbd05-1.1 {
|
||||
db eval {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT UNIQUE NOT NULL);
|
||||
@@ -39,18 +54,30 @@ do_test tkt-cbd05-1.1 {
|
||||
}
|
||||
} {10}
|
||||
do_test tkt-cbd05-1.2 {
|
||||
db eval {
|
||||
ANALYZE;
|
||||
db eval { ANALYZE; }
|
||||
ifcapable stat4 {
|
||||
db eval {
|
||||
PRAGMA writable_schema = 1;
|
||||
CREATE VIEW vvv AS
|
||||
SELECT tbl,idx,neq,nlt,ndlt,test_extract(sample,0) AS sample
|
||||
FROM sqlite_stat4;
|
||||
PRAGMA writable_schema = 0;
|
||||
}
|
||||
} else {
|
||||
db eval {
|
||||
CREATE VIEW vvv AS
|
||||
SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat3;
|
||||
}
|
||||
}
|
||||
} {}
|
||||
do_test tkt-cbd05-1.3 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat3
|
||||
SELECT tbl,idx,group_concat(s(sample),' ')
|
||||
FROM vvv
|
||||
WHERE idx = 't1_x'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {/t1 t1_x .[ ABCDEFGHI]{10}./}
|
||||
} {t1 t1_x { A B C D E F G H I}}
|
||||
|
||||
do_test tkt-cbd05-2.1 {
|
||||
db eval {
|
||||
@@ -77,11 +104,11 @@ do_test tkt-cbd05-2.2 {
|
||||
} {}
|
||||
do_test tkt-cbd05-2.3 {
|
||||
execsql {
|
||||
SELECT tbl,idx,group_concat(sample,' ')
|
||||
FROM sqlite_stat3
|
||||
SELECT tbl,idx,group_concat(s(sample),' ')
|
||||
FROM vvv
|
||||
WHERE idx = 't1_x'
|
||||
GROUP BY tbl,idx
|
||||
}
|
||||
} {/t1 t1_x .[ ABCDEFGHI]{10}./}
|
||||
} {t1 t1_x { A B C D E F G H I}}
|
||||
|
||||
finish_test
|
||||
|
||||
+32
-3
@@ -781,11 +781,11 @@ do_test where9-6.8.2 {
|
||||
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
|
||||
}
|
||||
} {1 {no query solution}}
|
||||
ifcapable stat3 {
|
||||
ifcapable stat4||stat3 {
|
||||
# When STAT3 is enabled, the "b NOT NULL" terms get translated
|
||||
# into b>NULL, which can be satified by the index t1b. It is a very
|
||||
# expensive way to do the query, but it works, and so a solution is possible.
|
||||
do_test where9-6.8.3-stat3 {
|
||||
do_test where9-6.8.3-stat4 {
|
||||
catchsql {
|
||||
UPDATE t1 INDEXED BY t1b SET a=a+100
|
||||
WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
|
||||
@@ -793,7 +793,7 @@ ifcapable stat3 {
|
||||
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
|
||||
}
|
||||
} {0 {}}
|
||||
do_test where9-6.8.4-stat3 {
|
||||
do_test where9-6.8.4-stat4 {
|
||||
catchsql {
|
||||
DELETE FROM t1 INDEXED BY t1b
|
||||
WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
|
||||
@@ -851,6 +851,11 @@ do_test where9-7.0 {
|
||||
INSERT INTO t6 SELECT * FROM t5;
|
||||
ANALYZE t5;
|
||||
}
|
||||
ifcapable stat3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { DROP TABLE IF EXISTS sqlite_stat3 }
|
||||
db2 close
|
||||
}
|
||||
} {}
|
||||
do_test where9-7.1.1 {
|
||||
count_steps {
|
||||
@@ -951,4 +956,28 @@ do_test where9-9.1 {
|
||||
}
|
||||
} {1 2 3 4 8 9}
|
||||
|
||||
# Fix for ticket [bc878246eafe0f52c519e29049b2fe4a99491b27]
|
||||
# Incorrect result when OR is used in a join to the right of a LEFT JOIN
|
||||
#
|
||||
do_test where9-10.1 {
|
||||
db eval {
|
||||
CREATE TABLE t101 (id INTEGER PRIMARY KEY);
|
||||
INSERT INTO t101 VALUES (1);
|
||||
SELECT * FROM t101 AS t0
|
||||
LEFT JOIN t101 AS t1 ON t1.id BETWEEN 10 AND 20
|
||||
JOIN t101 AS t2 ON (t2.id = t0.id OR (t2.id<>555 AND t2.id=t1.id));
|
||||
}
|
||||
} {1 {} 1}
|
||||
do_test where9-10.2 {
|
||||
db eval {
|
||||
CREATE TABLE t102 (id TEXT UNIQUE NOT NULL);
|
||||
INSERT INTO t102 VALUES ('1');
|
||||
SELECT * FROM t102 AS t0
|
||||
LEFT JOIN t102 AS t1 ON t1.id GLOB 'abc%'
|
||||
JOIN t102 AS t2 ON (t2.id = t0.id OR (t2.id<>555 AND t2.id=t1.id));
|
||||
}
|
||||
} {1 {} 1}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# 2013 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 script is testing the file name handling provided
|
||||
# by the "win32-longpath" VFS.
|
||||
#
|
||||
|
||||
if {$tcl_platform(platform)!="windows"} return
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix win32longpath
|
||||
|
||||
db close
|
||||
set path [file nativename [get_pwd]]
|
||||
sqlite3 db [file join $path test.db] -vfs win32-longpath
|
||||
|
||||
do_test 1.1 {
|
||||
db eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
INSERT INTO t1 VALUES(3);
|
||||
INSERT INTO t1 VALUES(4);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {1 2 3 4}
|
||||
|
||||
set longPath(1) \\\\?\\$path\\[pid]
|
||||
make_win32_dir $longPath(1)
|
||||
|
||||
set longPath(2) $longPath(1)\\[string repeat X 255]
|
||||
make_win32_dir $longPath(2)
|
||||
|
||||
set longPath(3) $longPath(2)\\[string repeat Y 255]
|
||||
make_win32_dir $longPath(3)
|
||||
|
||||
set fileName $longPath(3)\\test.db
|
||||
|
||||
do_test 1.2 {
|
||||
list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg
|
||||
} {1 {unable to open database file}}
|
||||
|
||||
sqlite3 db3 $fileName -vfs win32-longpath
|
||||
|
||||
do_test 1.3 {
|
||||
db3 eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
CREATE TABLE t1(x);
|
||||
INSERT INTO t1 VALUES(5);
|
||||
INSERT INTO t1 VALUES(6);
|
||||
INSERT INTO t1 VALUES(7);
|
||||
INSERT INTO t1 VALUES(8);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {5 6 7 8}
|
||||
|
||||
db3 close
|
||||
# puts " Database exists \{[exists_win32_path $fileName]\}"
|
||||
|
||||
sqlite3 db3 $fileName -vfs win32-longpath
|
||||
|
||||
do_test 1.4 {
|
||||
db3 eval {
|
||||
PRAGMA journal_mode = WAL;
|
||||
}
|
||||
} {wal}
|
||||
|
||||
do_test 1.5 {
|
||||
db3 eval {
|
||||
BEGIN EXCLUSIVE;
|
||||
INSERT INTO t1 VALUES(9);
|
||||
INSERT INTO t1 VALUES(10);
|
||||
INSERT INTO t1 VALUES(11);
|
||||
INSERT INTO t1 VALUES(12);
|
||||
SELECT x FROM t1 ORDER BY x;
|
||||
COMMIT;
|
||||
}
|
||||
} {5 6 7 8 9 10 11 12}
|
||||
|
||||
db3 close
|
||||
# puts " Database exists \{[exists_win32_path $fileName]\}"
|
||||
|
||||
do_delete_win32_file $fileName
|
||||
# puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}"
|
||||
|
||||
do_remove_win32_dir $longPath(3)
|
||||
do_remove_win32_dir $longPath(2)
|
||||
do_remove_win32_dir $longPath(1)
|
||||
|
||||
finish_test
|
||||
Reference in New Issue
Block a user