1f52bb8d3Sdan# 2013 August 3 2f52bb8d3Sdan# 3f52bb8d3Sdan# The author disclaims copyright to this source code. In place of 4f52bb8d3Sdan# a legal notice, here is a blessing: 5f52bb8d3Sdan# 6f52bb8d3Sdan# May you do good and not evil. 7f52bb8d3Sdan# May you find forgiveness for yourself and forgive others. 8f52bb8d3Sdan# May you share freely, never taking more than you give. 9f52bb8d3Sdan# 10f52bb8d3Sdan#*********************************************************************** 11f52bb8d3Sdan# 120106e378Sdan# This file contains automated tests used to verify that the sqlite_stat4 130106e378Sdan# functionality is working. 140106e378Sdan# 15f52bb8d3Sdan 16f52bb8d3Sdanset testdir [file dirname $argv0] 17f52bb8d3Sdansource $testdir/tester.tcl 18f52bb8d3Sdanset testprefix analyze9 19f52bb8d3Sdan 20ddc2d6e8Sdanifcapable !stat4 { 21ddc2d6e8Sdan finish_test 22ddc2d6e8Sdan return 23ddc2d6e8Sdan} 24ddc2d6e8Sdan 25f52bb8d3Sdanproc s {blob} { 26f52bb8d3Sdan set ret "" 27f52bb8d3Sdan binary scan $blob c* bytes 28f52bb8d3Sdan foreach b $bytes { 29f52bb8d3Sdan set t [binary format c $b] 30f52bb8d3Sdan if {[string is print $t]} { 31f52bb8d3Sdan append ret $t 32f52bb8d3Sdan } else { 33f52bb8d3Sdan append ret . 34f52bb8d3Sdan } 35f52bb8d3Sdan } 36f52bb8d3Sdan return $ret 37f52bb8d3Sdan} 38f52bb8d3Sdandb function s s 39f52bb8d3Sdan 40dd6e1f19Sdando_execsql_test 1.0 { 41dd6e1f19Sdan CREATE TABLE t1(a TEXT, b TEXT); 42dd6e1f19Sdan INSERT INTO t1 VALUES('(0)', '(0)'); 43dd6e1f19Sdan INSERT INTO t1 VALUES('(1)', '(1)'); 44dd6e1f19Sdan INSERT INTO t1 VALUES('(2)', '(2)'); 45dd6e1f19Sdan INSERT INTO t1 VALUES('(3)', '(3)'); 46dd6e1f19Sdan INSERT INTO t1 VALUES('(4)', '(4)'); 47dd6e1f19Sdan CREATE INDEX i1 ON t1(a, b); 48f52bb8d3Sdan} {} 49f52bb8d3Sdan 50e043201dSdan 51f52bb8d3Sdando_execsql_test 1.1 { 52f52bb8d3Sdan ANALYZE; 53f52bb8d3Sdan} {} 54f52bb8d3Sdan 5522d73b1cSdando_execsql_test 1.2 { 56e043201dSdan SELECT tbl,idx,nEq,nLt,nDLt,test_decode(sample) FROM sqlite_stat4; 57e043201dSdan} { 58dd6e1f19Sdan t1 i1 {1 1 1} {0 0 0} {0 0 0} {(0) (0) 1} 59dd6e1f19Sdan t1 i1 {1 1 1} {1 1 1} {1 1 1} {(1) (1) 2} 60dd6e1f19Sdan t1 i1 {1 1 1} {2 2 2} {2 2 2} {(2) (2) 3} 61dd6e1f19Sdan t1 i1 {1 1 1} {3 3 3} {3 3 3} {(3) (3) 4} 62dd6e1f19Sdan t1 i1 {1 1 1} {4 4 4} {4 4 4} {(4) (4) 5} 63e043201dSdan} 64e043201dSdan 6522d73b1cSdanif {[permutation] != "utf16"} { 6622d73b1cSdan do_execsql_test 1.3 { 67f52bb8d3Sdan SELECT tbl,idx,nEq,nLt,nDLt,s(sample) FROM sqlite_stat4; 68f52bb8d3Sdan } { 69dd6e1f19Sdan t1 i1 {1 1 1} {0 0 0} {0 0 0} ....(0)(0) 70dd6e1f19Sdan t1 i1 {1 1 1} {1 1 1} {1 1 1} ....(1)(1). 71dd6e1f19Sdan t1 i1 {1 1 1} {2 2 2} {2 2 2} ....(2)(2). 72dd6e1f19Sdan t1 i1 {1 1 1} {3 3 3} {3 3 3} ....(3)(3). 73dd6e1f19Sdan t1 i1 {1 1 1} {4 4 4} {4 4 4} ....(4)(4). 74f52bb8d3Sdan } 7522d73b1cSdan} 76f52bb8d3Sdan 77f52bb8d3Sdan 78e043201dSdan#------------------------------------------------------------------------- 79e043201dSdan# This is really just to test SQL user function "test_decode". 80e043201dSdan# 81e043201dSdanreset_db 82e043201dSdando_execsql_test 2.1 { 83e043201dSdan CREATE TABLE t1(a, b, c); 84e043201dSdan INSERT INTO t1 VALUES('some text', 14, NULL); 85e043201dSdan INSERT INTO t1 VALUES(22.0, NULL, x'656667'); 86e043201dSdan CREATE INDEX i1 ON t1(a, b, c); 87e043201dSdan ANALYZE; 88e043201dSdan SELECT test_decode(sample) FROM sqlite_stat4; 89e043201dSdan} { 90dd6e1f19Sdan {22.0 NULL x'656667' 2} 91dd6e1f19Sdan {{some text} 14 NULL 1} 92e043201dSdan} 93e043201dSdan 94e043201dSdan#------------------------------------------------------------------------- 95e043201dSdan# 96e043201dSdanreset_db 97e043201dSdando_execsql_test 3.1 { 98e043201dSdan CREATE TABLE t2(a, b); 99e043201dSdan CREATE INDEX i2 ON t2(a, b); 100e043201dSdan BEGIN; 101e043201dSdan} 102e043201dSdan 103e043201dSdando_test 3.2 { 104e043201dSdan for {set i 0} {$i < 1000} {incr i} { 105e043201dSdan set a [expr $i / 10] 106e043201dSdan set b [expr int(rand() * 15.0)] 107e043201dSdan execsql { INSERT INTO t2 VALUES($a, $b) } 108e043201dSdan } 109e043201dSdan execsql COMMIT 110e043201dSdan} {} 111e043201dSdan 112e043201dSdandb func lindex lindex 113e043201dSdan 114e043201dSdan# Each value of "a" occurs exactly 10 times in the table. 115e043201dSdan# 116e043201dSdando_execsql_test 3.3.1 { 117e043201dSdan SELECT count(*) FROM t2 GROUP BY a; 118e043201dSdan} [lrange [string repeat "10 " 100] 0 99] 119e043201dSdan 120e043201dSdan# The first element in the "nEq" list of all samples should therefore be 10. 121e043201dSdan# 122e043201dSdando_execsql_test 3.3.2 { 123e043201dSdan ANALYZE; 124e043201dSdan SELECT lindex(nEq, 0) FROM sqlite_stat4; 125e043201dSdan} [lrange [string repeat "10 " 100] 0 23] 126e043201dSdan 127dd6e1f19Sdan#------------------------------------------------------------------------- 128dd6e1f19Sdan# 129dd6e1f19Sdando_execsql_test 3.4 { 130dd6e1f19Sdan DROP TABLE IF EXISTS t1; 131dd6e1f19Sdan CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); 132dd6e1f19Sdan INSERT INTO t1 VALUES(1, 1, 'one-a'); 133dd6e1f19Sdan INSERT INTO t1 VALUES(11, 1, 'one-b'); 134dd6e1f19Sdan INSERT INTO t1 VALUES(21, 1, 'one-c'); 135dd6e1f19Sdan INSERT INTO t1 VALUES(31, 1, 'one-d'); 136dd6e1f19Sdan INSERT INTO t1 VALUES(41, 1, 'one-e'); 137dd6e1f19Sdan INSERT INTO t1 VALUES(51, 1, 'one-f'); 138dd6e1f19Sdan INSERT INTO t1 VALUES(61, 1, 'one-g'); 139dd6e1f19Sdan INSERT INTO t1 VALUES(71, 1, 'one-h'); 140dd6e1f19Sdan INSERT INTO t1 VALUES(81, 1, 'one-i'); 141dd6e1f19Sdan INSERT INTO t1 VALUES(91, 1, 'one-j'); 142dd6e1f19Sdan INSERT INTO t1 SELECT a+1,2,'two' || substr(c,4) FROM t1; 143dd6e1f19Sdan INSERT INTO t1 SELECT a+2,3,'three'||substr(c,4) FROM t1 WHERE c GLOB 'one-*'; 144dd6e1f19Sdan INSERT INTO t1 SELECT a+3,4,'four'||substr(c,4) FROM t1 WHERE c GLOB 'one-*'; 145dd6e1f19Sdan INSERT INTO t1 SELECT a+4,5,'five'||substr(c,4) FROM t1 WHERE c GLOB 'one-*'; 146dd6e1f19Sdan INSERT INTO t1 SELECT a+5,6,'six'||substr(c,4) FROM t1 WHERE c GLOB 'one-*'; 147dd6e1f19Sdan CREATE INDEX t1b ON t1(b); 148dd6e1f19Sdan ANALYZE; 149dd6e1f19Sdan SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND 60; 150dd6e1f19Sdan} {three-d three-e three-f} 151e043201dSdan 1523bc9f74fSdan 1531f616ad8Sdan#------------------------------------------------------------------------- 1541f616ad8Sdan# These tests verify that the sample selection for stat4 appears to be 1551f616ad8Sdan# working as designed. 1561f616ad8Sdan# 1571f616ad8Sdan 1581f616ad8Sdanreset_db 1591f616ad8Sdandb func lindex lindex 1601f616ad8Sdandb func lrange lrange 1611f616ad8Sdan 1621f616ad8Sdando_execsql_test 4.0 { 1631f616ad8Sdan DROP TABLE IF EXISTS t1; 1641f616ad8Sdan CREATE TABLE t1(a, b, c); 1651f616ad8Sdan CREATE INDEX i1 ON t1(c, b, a); 1661f616ad8Sdan} 1671f616ad8Sdan 1681f616ad8Sdan 1691f616ad8Sdanproc insert_filler_rows_n {iStart args} { 1701f616ad8Sdan set A(-ncopy) 1 1711f616ad8Sdan set A(-nval) 1 1721f616ad8Sdan 1731f616ad8Sdan foreach {k v} $args { 1741f616ad8Sdan if {[info exists A($k)]==0} { error "no such option: $k" } 1751f616ad8Sdan set A($k) $v 1761f616ad8Sdan } 1771f616ad8Sdan if {[llength $args] % 2} { 1781f616ad8Sdan error "option requires an argument: [lindex $args end]" 1791f616ad8Sdan } 1801f616ad8Sdan 1811f616ad8Sdan for {set i 0} {$i < $A(-nval)} {incr i} { 1821f616ad8Sdan set iVal [expr $iStart+$i] 1831f616ad8Sdan for {set j 0} {$j < $A(-ncopy)} {incr j} { 1841f616ad8Sdan execsql { INSERT INTO t1 VALUES($iVal, $iVal, $iVal) } 1851f616ad8Sdan } 1861f616ad8Sdan } 1871f616ad8Sdan} 1881f616ad8Sdan 1891f616ad8Sdando_test 4.1 { 1901f616ad8Sdan execsql { BEGIN } 1911f616ad8Sdan insert_filler_rows_n 0 -ncopy 10 -nval 19 1921f616ad8Sdan insert_filler_rows_n 20 -ncopy 1 -nval 100 1931f616ad8Sdan 1941f616ad8Sdan execsql { 1951f616ad8Sdan INSERT INTO t1(c, b, a) VALUES(200, 1, 'a'); 1961f616ad8Sdan INSERT INTO t1(c, b, a) VALUES(200, 1, 'b'); 1971f616ad8Sdan INSERT INTO t1(c, b, a) VALUES(200, 1, 'c'); 1981f616ad8Sdan 1991f616ad8Sdan INSERT INTO t1(c, b, a) VALUES(200, 2, 'e'); 2001f616ad8Sdan INSERT INTO t1(c, b, a) VALUES(200, 2, 'f'); 2011f616ad8Sdan 2021f616ad8Sdan INSERT INTO t1(c, b, a) VALUES(201, 3, 'g'); 2031f616ad8Sdan INSERT INTO t1(c, b, a) VALUES(201, 4, 'h'); 2041f616ad8Sdan 2051f616ad8Sdan ANALYZE; 2061f616ad8Sdan SELECT count(*) FROM sqlite_stat4; 2071f616ad8Sdan SELECT count(*) FROM t1; 2081f616ad8Sdan } 2091f616ad8Sdan} {24 297} 2101f616ad8Sdan 2111f616ad8Sdando_execsql_test 4.2 { 2121f616ad8Sdan SELECT 2131f616ad8Sdan neq, 2141f616ad8Sdan lrange(nlt, 0, 2), 2151f616ad8Sdan lrange(ndlt, 0, 2), 2161f616ad8Sdan lrange(test_decode(sample), 0, 2) 2171f616ad8Sdan FROM sqlite_stat4 2181f616ad8Sdan ORDER BY rowid LIMIT 16; 2191f616ad8Sdan} { 2201f616ad8Sdan {10 10 10 1} {0 0 0} {0 0 0} {0 0 0} 2211f616ad8Sdan {10 10 10 1} {10 10 10} {1 1 1} {1 1 1} 2221f616ad8Sdan {10 10 10 1} {20 20 20} {2 2 2} {2 2 2} 2231f616ad8Sdan {10 10 10 1} {30 30 30} {3 3 3} {3 3 3} 2241f616ad8Sdan {10 10 10 1} {40 40 40} {4 4 4} {4 4 4} 2251f616ad8Sdan {10 10 10 1} {50 50 50} {5 5 5} {5 5 5} 2261f616ad8Sdan {10 10 10 1} {60 60 60} {6 6 6} {6 6 6} 2271f616ad8Sdan {10 10 10 1} {70 70 70} {7 7 7} {7 7 7} 2281f616ad8Sdan {10 10 10 1} {80 80 80} {8 8 8} {8 8 8} 2291f616ad8Sdan {10 10 10 1} {90 90 90} {9 9 9} {9 9 9} 2301f616ad8Sdan {10 10 10 1} {100 100 100} {10 10 10} {10 10 10} 2311f616ad8Sdan {10 10 10 1} {110 110 110} {11 11 11} {11 11 11} 2321f616ad8Sdan {10 10 10 1} {120 120 120} {12 12 12} {12 12 12} 2331f616ad8Sdan {10 10 10 1} {130 130 130} {13 13 13} {13 13 13} 2341f616ad8Sdan {10 10 10 1} {140 140 140} {14 14 14} {14 14 14} 2351f616ad8Sdan {10 10 10 1} {150 150 150} {15 15 15} {15 15 15} 2361f616ad8Sdan} 2371f616ad8Sdan 2381f616ad8Sdando_execsql_test 4.3 { 2391f616ad8Sdan SELECT 2401f616ad8Sdan neq, 2411f616ad8Sdan lrange(nlt, 0, 2), 2421f616ad8Sdan lrange(ndlt, 0, 2), 2431f616ad8Sdan lrange(test_decode(sample), 0, 1) 2441f616ad8Sdan FROM sqlite_stat4 2451f616ad8Sdan ORDER BY rowid DESC LIMIT 2; 2461f616ad8Sdan} { 2471d2b3c1fSdrh {2 1 1 1} {295 296 296} {120 122 125} {201 4} 2481d2b3c1fSdrh {5 3 1 1} {290 290 290} {119 119 119} {200 1} 2491f616ad8Sdan} 2501f616ad8Sdan 2511f616ad8Sdando_execsql_test 4.4 { SELECT count(DISTINCT c) FROM t1 WHERE c<201 } 120 2521f616ad8Sdando_execsql_test 4.5 { SELECT count(DISTINCT c) FROM t1 WHERE c<200 } 119 2531f616ad8Sdan 2541f616ad8Sdan# Check that the perioidic samples are present. 2551f616ad8Sdando_execsql_test 4.6 { 2561f616ad8Sdan SELECT count(*) FROM sqlite_stat4 2571f616ad8Sdan WHERE lindex(test_decode(sample), 3) IN 2581f616ad8Sdan ('34', '68', '102', '136', '170', '204', '238', '272') 2591f616ad8Sdan} {8} 2601f616ad8Sdan 261af2583c8Sdanreset_db 262af2583c8Sdando_test 4.7 { 263af2583c8Sdan execsql { 264af2583c8Sdan BEGIN; 265af2583c8Sdan CREATE TABLE t1(o,t INTEGER PRIMARY KEY); 266af2583c8Sdan CREATE INDEX i1 ON t1(o); 267af2583c8Sdan } 268af2583c8Sdan for {set i 0} {$i<10000} {incr i [expr (($i<1000)?1:10)]} { 269af2583c8Sdan execsql { INSERT INTO t1 VALUES('x', $i) } 270af2583c8Sdan } 271af2583c8Sdan execsql { 272af2583c8Sdan COMMIT; 273af2583c8Sdan ANALYZE; 274af2583c8Sdan SELECT count(*) FROM sqlite_stat4; 275af2583c8Sdan } 276af2583c8Sdan} {8} 277af2583c8Sdando_execsql_test 4.8 { 278af2583c8Sdan SELECT test_decode(sample) FROM sqlite_stat4; 279af2583c8Sdan} { 280af2583c8Sdan {x 211} {x 423} {x 635} {x 847} 281af2583c8Sdan {x 1590} {x 3710} {x 5830} {x 7950} 282af2583c8Sdan} 283af2583c8Sdan 284af2583c8Sdan 2853bc9f74fSdan#------------------------------------------------------------------------- 2863bc9f74fSdan# The following would cause a crash at one point. 2873bc9f74fSdan# 2883bc9f74fSdanreset_db 2893bc9f74fSdando_execsql_test 5.1 { 2903bc9f74fSdan PRAGMA encoding = 'utf-16'; 2913bc9f74fSdan CREATE TABLE t0(v); 2923bc9f74fSdan ANALYZE; 2933bc9f74fSdan} 2941f616ad8Sdan 2950adbed8aSdan#------------------------------------------------------------------------- 296c367d4c0Sdan# This was also crashing (corrupt sqlite_stat4 table). 2970adbed8aSdan# 2980adbed8aSdanreset_db 2990adbed8aSdando_execsql_test 6.1 { 3000adbed8aSdan CREATE TABLE t1(a, b); 3010adbed8aSdan CREATE INDEX i1 ON t1(a); 3020adbed8aSdan CREATE INDEX i2 ON t1(b); 3030adbed8aSdan INSERT INTO t1 VALUES(1, 1); 3040adbed8aSdan INSERT INTO t1 VALUES(2, 2); 3050adbed8aSdan INSERT INTO t1 VALUES(3, 3); 3060adbed8aSdan INSERT INTO t1 VALUES(4, 4); 3070adbed8aSdan INSERT INTO t1 VALUES(5, 5); 3080adbed8aSdan ANALYZE; 3090adbed8aSdan PRAGMA writable_schema = 1; 3100adbed8aSdan CREATE TEMP TABLE x1 AS 3110adbed8aSdan SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat4 3120adbed8aSdan ORDER BY (rowid%5), rowid; 3130adbed8aSdan DELETE FROM sqlite_stat4; 3140adbed8aSdan INSERT INTO sqlite_stat4 SELECT * FROM x1; 3150adbed8aSdan PRAGMA writable_schema = 0; 3160adbed8aSdan ANALYZE sqlite_master; 3170adbed8aSdan} 3180adbed8aSdando_execsql_test 6.2 { 3190adbed8aSdan SELECT * FROM t1 WHERE a = 'abc'; 3200adbed8aSdan} 3210adbed8aSdan 322c367d4c0Sdan#------------------------------------------------------------------------- 323c367d4c0Sdan# The following tests experiment with adding corrupted records to the 324c367d4c0Sdan# 'sample' column of the sqlite_stat4 table. 325c367d4c0Sdan# 326c367d4c0Sdanreset_db 327c367d4c0Sdansqlite3_db_config_lookaside db 0 0 0 328c367d4c0Sdan 329597515d7Sdandatabase_may_be_corrupt 330c367d4c0Sdando_execsql_test 7.1 { 331c367d4c0Sdan CREATE TABLE t1(a, b); 332c367d4c0Sdan CREATE INDEX i1 ON t1(a, b); 333c367d4c0Sdan INSERT INTO t1 VALUES(1, 1); 334c367d4c0Sdan INSERT INTO t1 VALUES(2, 2); 335c367d4c0Sdan INSERT INTO t1 VALUES(3, 3); 336c367d4c0Sdan INSERT INTO t1 VALUES(4, 4); 337c367d4c0Sdan INSERT INTO t1 VALUES(5, 5); 338c367d4c0Sdan ANALYZE; 339c367d4c0Sdan UPDATE sqlite_stat4 SET sample = X'' WHERE rowid = 1; 340c367d4c0Sdan ANALYZE sqlite_master; 341c367d4c0Sdan} 342c367d4c0Sdan 343c367d4c0Sdando_execsql_test 7.2 { 344c367d4c0Sdan UPDATE sqlite_stat4 SET sample = X'FFFF'; 345c367d4c0Sdan ANALYZE sqlite_master; 346c367d4c0Sdan SELECT * FROM t1 WHERE a = 1; 347c367d4c0Sdan} {1 1} 348c367d4c0Sdan 349585c8232Sdando_execsql_test 7.3 { 350585c8232Sdan ANALYZE; 351585c8232Sdan UPDATE sqlite_stat4 SET neq = '0 0 0'; 352585c8232Sdan ANALYZE sqlite_master; 353585c8232Sdan SELECT * FROM t1 WHERE a = 1; 354585c8232Sdan} {1 1} 355585c8232Sdan 356585c8232Sdando_execsql_test 7.4 { 357585c8232Sdan ANALYZE; 358585c8232Sdan UPDATE sqlite_stat4 SET ndlt = '0 0 0'; 359585c8232Sdan ANALYZE sqlite_master; 360585c8232Sdan SELECT * FROM t1 WHERE a = 3; 361585c8232Sdan} {3 3} 362585c8232Sdan 363585c8232Sdando_execsql_test 7.5 { 364585c8232Sdan ANALYZE; 365585c8232Sdan UPDATE sqlite_stat4 SET nlt = '0 0 0'; 366585c8232Sdan ANALYZE sqlite_master; 367585c8232Sdan SELECT * FROM t1 WHERE a = 5; 368585c8232Sdan} {5 5} 369585c8232Sdan 370597515d7Sdandatabase_never_corrupt 371597515d7Sdan 37222d73b1cSdan#------------------------------------------------------------------------- 37322d73b1cSdan# 37422d73b1cSdanreset_db 37522d73b1cSdando_execsql_test 8.1 { 37622d73b1cSdan CREATE TABLE t1(x TEXT); 37722d73b1cSdan CREATE INDEX i1 ON t1(x); 37822d73b1cSdan INSERT INTO t1 VALUES('1'); 37922d73b1cSdan INSERT INTO t1 VALUES('2'); 38022d73b1cSdan INSERT INTO t1 VALUES('3'); 38122d73b1cSdan INSERT INTO t1 VALUES('4'); 38222d73b1cSdan ANALYZE; 38322d73b1cSdan} 38422d73b1cSdando_execsql_test 8.2 { 38522d73b1cSdan SELECT * FROM t1 WHERE x = 3; 38622d73b1cSdan} {3} 387585c8232Sdan 3880e8194a9Sdan#------------------------------------------------------------------------- 3890e8194a9Sdan# Check that the bug fixed by [91733bc485] really is fixed. 3900e8194a9Sdan# 3910e8194a9Sdanreset_db 3920e8194a9Sdando_execsql_test 9.1 { 3930e8194a9Sdan CREATE TABLE t1(a, b, c, d, e); 3940e8194a9Sdan CREATE INDEX i1 ON t1(a, b, c, d); 3950e8194a9Sdan CREATE INDEX i2 ON t1(e); 3960e8194a9Sdan} 3970e8194a9Sdando_test 9.2 { 3980e8194a9Sdan execsql BEGIN; 3990e8194a9Sdan for {set i 0} {$i < 100} {incr i} { 4000e8194a9Sdan execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])" 4010e8194a9Sdan } 402*f71b8123Sdrh for {set i 0} {$i < 21} {incr i} { 4030e8194a9Sdan execsql "INSERT INTO t1 VALUES('x', 'y', 'z', 101, $i)" 4040e8194a9Sdan } 4050e8194a9Sdan for {set i 102} {$i < 200} {incr i} { 4060e8194a9Sdan execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])" 4070e8194a9Sdan } 4080e8194a9Sdan execsql COMMIT 4090e8194a9Sdan execsql ANALYZE 4100e8194a9Sdan} {} 4110e8194a9Sdan 4120e8194a9Sdando_eqp_test 9.3.1 { 4130e8194a9Sdan SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=101 AND e=5; 4140e8194a9Sdan} {/t1 USING INDEX i2/} 4150e8194a9Sdando_eqp_test 9.3.2 { 4160e8194a9Sdan SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=99 AND e=5; 4170e8194a9Sdan} {/t1 USING INDEX i1/} 4180e8194a9Sdan 4190e8194a9Sdanset value_d [expr 101] 4200e8194a9Sdando_eqp_test 9.4.1 { 4210e8194a9Sdan SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5 4220e8194a9Sdan} {/t1 USING INDEX i2/} 4230e8194a9Sdanset value_d [expr 99] 4240e8194a9Sdando_eqp_test 9.4.2 { 4250e8194a9Sdan SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5 4260e8194a9Sdan} {/t1 USING INDEX i1/} 4270e8194a9Sdan 4284a1aa385Sdan#------------------------------------------------------------------------- 4294a1aa385Sdan# Check that the planner takes stat4 data into account when considering 4304a1aa385Sdan# "IS NULL" and "IS NOT NULL" constraints. 4314a1aa385Sdan# 4324a1aa385Sdando_execsql_test 10.1.1 { 4334a1aa385Sdan DROP TABLE IF EXISTS t3; 4344a1aa385Sdan CREATE TABLE t3(a, b); 4354a1aa385Sdan CREATE INDEX t3a ON t3(a); 4364a1aa385Sdan CREATE INDEX t3b ON t3(b); 4374a1aa385Sdan} 4384a1aa385Sdando_test 10.1.2 { 4394a1aa385Sdan for {set i 1} {$i < 100} {incr i} { 4404a1aa385Sdan if {$i>90} { set a $i } else { set a NULL } 4414a1aa385Sdan set b [expr $i % 5] 4424a1aa385Sdan execsql "INSERT INTO t3 VALUES($a, $b)" 4434a1aa385Sdan } 4444a1aa385Sdan execsql ANALYZE 4454a1aa385Sdan} {} 4464a1aa385Sdando_eqp_test 10.1.3 { 4474a1aa385Sdan SELECT * FROM t3 WHERE a IS NULL AND b = 2 4484a1aa385Sdan} {/t3 USING INDEX t3b/} 4494a1aa385Sdando_eqp_test 10.1.4 { 4504a1aa385Sdan SELECT * FROM t3 WHERE a IS NOT NULL AND b = 2 4514a1aa385Sdan} {/t3 USING INDEX t3a/} 4524a1aa385Sdan 4534a1aa385Sdando_execsql_test 10.2.1 { 4544a1aa385Sdan DROP TABLE IF EXISTS t3; 4554a1aa385Sdan CREATE TABLE t3(x, a, b); 4564a1aa385Sdan CREATE INDEX t3a ON t3(x, a); 4574a1aa385Sdan CREATE INDEX t3b ON t3(x, b); 4584a1aa385Sdan} 4594a1aa385Sdando_test 10.2.2 { 4604a1aa385Sdan for {set i 1} {$i < 100} {incr i} { 4614a1aa385Sdan if {$i>90} { set a $i } else { set a NULL } 4624a1aa385Sdan set b [expr $i % 5] 4634a1aa385Sdan execsql "INSERT INTO t3 VALUES('xyz', $a, $b)" 4644a1aa385Sdan } 4654a1aa385Sdan execsql ANALYZE 4664a1aa385Sdan} {} 4674a1aa385Sdando_eqp_test 10.2.3 { 4684a1aa385Sdan SELECT * FROM t3 WHERE x = 'xyz' AND a IS NULL AND b = 2 4694a1aa385Sdan} {/t3 USING INDEX t3b/} 4704a1aa385Sdando_eqp_test 10.2.4 { 4714a1aa385Sdan SELECT * FROM t3 WHERE x = 'xyz' AND a IS NOT NULL AND b = 2 4724a1aa385Sdan} {/t3 USING INDEX t3a/} 4734a1aa385Sdan 4744a1aa385Sdan#------------------------------------------------------------------------- 4754a1aa385Sdan# Check that stat4 data is used correctly with non-default collation 4764a1aa385Sdan# sequences. 4774a1aa385Sdan# 4784a1aa385Sdanforeach {tn schema} { 4794a1aa385Sdan 1 { 4804a1aa385Sdan CREATE TABLE t4(a COLLATE nocase, b); 4814a1aa385Sdan CREATE INDEX t4a ON t4(a); 4824a1aa385Sdan CREATE INDEX t4b ON t4(b); 4834a1aa385Sdan } 4844a1aa385Sdan 2 { 4854a1aa385Sdan CREATE TABLE t4(a, b); 4864a1aa385Sdan CREATE INDEX t4a ON t4(a COLLATE nocase); 4874a1aa385Sdan CREATE INDEX t4b ON t4(b); 4884a1aa385Sdan } 4894a1aa385Sdan} { 4904a1aa385Sdan drop_all_tables 4914a1aa385Sdan do_test 11.$tn.1 { execsql $schema } {} 4924a1aa385Sdan 4934a1aa385Sdan do_test 11.$tn.2 { 4944a1aa385Sdan for {set i 0} {$i < 100} {incr i} { 4954a1aa385Sdan if { ($i % 10)==0 } { set a ABC } else { set a DEF } 4964a1aa385Sdan set b [expr $i % 5] 4974a1aa385Sdan execsql { INSERT INTO t4 VALUES($a, $b) } 4984a1aa385Sdan } 4994a1aa385Sdan execsql ANALYZE 5004a1aa385Sdan } {} 5014a1aa385Sdan 5024a1aa385Sdan do_eqp_test 11.$tn.3 { 5034a1aa385Sdan SELECT * FROM t4 WHERE a = 'def' AND b = 3; 5044a1aa385Sdan } {/t4 USING INDEX t4b/} 5054a1aa385Sdan 5064a1aa385Sdan if {$tn==1} { 5074a1aa385Sdan set sql "SELECT * FROM t4 WHERE a = 'abc' AND b = 3;" 5084a1aa385Sdan do_eqp_test 11.$tn.4 $sql {/t4 USING INDEX t4a/} 5094a1aa385Sdan } else { 5104a1aa385Sdan 5114a1aa385Sdan set sql "SELECT * FROM t4 WHERE a = 'abc' COLLATE nocase AND b = 3;" 5124a1aa385Sdan do_eqp_test 11.$tn.5 $sql {/t4 USING INDEX t4a/} 5134a1aa385Sdan 5144a1aa385Sdan set sql "SELECT * FROM t4 WHERE a COLLATE nocase = 'abc' AND b = 3;" 5154a1aa385Sdan do_eqp_test 11.$tn.6 $sql {/t4 USING INDEX t4a/} 5164a1aa385Sdan } 5174a1aa385Sdan} 5184a1aa385Sdan 5194a1aa385Sdanforeach {tn schema} { 5204a1aa385Sdan 1 { 5214a1aa385Sdan CREATE TABLE t4(x, a COLLATE nocase, b); 5224a1aa385Sdan CREATE INDEX t4a ON t4(x, a); 5234a1aa385Sdan CREATE INDEX t4b ON t4(x, b); 5244a1aa385Sdan } 5254a1aa385Sdan 2 { 5264a1aa385Sdan CREATE TABLE t4(x, a, b); 5274a1aa385Sdan CREATE INDEX t4a ON t4(x, a COLLATE nocase); 5284a1aa385Sdan CREATE INDEX t4b ON t4(x, b); 5294a1aa385Sdan } 5304a1aa385Sdan} { 5314a1aa385Sdan drop_all_tables 5324a1aa385Sdan do_test 12.$tn.1 { execsql $schema } {} 5334a1aa385Sdan 5344a1aa385Sdan do_test 12.$tn.2 { 5354a1aa385Sdan for {set i 0} {$i < 100} {incr i} { 5364a1aa385Sdan if { ($i % 10)==0 } { set a ABC } else { set a DEF } 5374a1aa385Sdan set b [expr $i % 5] 5384a1aa385Sdan execsql { INSERT INTO t4 VALUES(X'abcdef', $a, $b) } 5394a1aa385Sdan } 5404a1aa385Sdan execsql ANALYZE 5414a1aa385Sdan } {} 5424a1aa385Sdan 5434a1aa385Sdan do_eqp_test 12.$tn.3 { 5444a1aa385Sdan SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'def' AND b = 3; 5454a1aa385Sdan } {/t4 USING INDEX t4b/} 5464a1aa385Sdan 5474a1aa385Sdan if {$tn==1} { 5484a1aa385Sdan set sql "SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'abc' AND b = 3;" 5494a1aa385Sdan do_eqp_test 12.$tn.4 $sql {/t4 USING INDEX t4a/} 5504a1aa385Sdan } else { 5514a1aa385Sdan set sql { 5524a1aa385Sdan SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'abc' COLLATE nocase AND b = 3 5534a1aa385Sdan } 5544a1aa385Sdan do_eqp_test 12.$tn.5 $sql {/t4 USING INDEX t4a/} 5554a1aa385Sdan set sql { 5564a1aa385Sdan SELECT * FROM t4 WHERE x=X'abcdef' AND a COLLATE nocase = 'abc' AND b = 3 5574a1aa385Sdan } 5584a1aa385Sdan do_eqp_test 12.$tn.6 $sql {/t4 USING INDEX t4a/} 5594a1aa385Sdan } 5604a1aa385Sdan} 5614a1aa385Sdan 562575ab2f8Sdan#------------------------------------------------------------------------- 563575ab2f8Sdan# Check that affinities are taken into account when using stat4 data to 564575ab2f8Sdan# estimate the number of rows scanned by a rowid constraint. 565575ab2f8Sdan# 566575ab2f8Sdandrop_all_tables 567575ab2f8Sdando_test 13.1 { 568575ab2f8Sdan execsql { 5699881d60dSdan CREATE TABLE t1(a, b, c, d); 570575ab2f8Sdan CREATE INDEX i1 ON t1(a); 571575ab2f8Sdan CREATE INDEX i2 ON t1(b, c); 572575ab2f8Sdan } 573575ab2f8Sdan for {set i 0} {$i<100} {incr i} { 574575ab2f8Sdan if {$i %2} {set a abc} else {set a def} 575575ab2f8Sdan execsql { INSERT INTO t1(rowid, a, b, c) VALUES($i, $a, $i, $i) } 576575ab2f8Sdan } 577575ab2f8Sdan execsql ANALYZE 578575ab2f8Sdan} {} 579575ab2f8Sdando_eqp_test 13.2.1 { 580aa9933c1Sdan SELECT * FROM t1 WHERE a='abc' AND rowid<15 AND b<12 5818210233cSdrh} {/SEARCH t1 USING INDEX i1/} 582575ab2f8Sdando_eqp_test 13.2.2 { 583aa9933c1Sdan SELECT * FROM t1 WHERE a='abc' AND rowid<'15' AND b<12 5848210233cSdrh} {/SEARCH t1 USING INDEX i1/} 585575ab2f8Sdando_eqp_test 13.3.1 { 586aa9933c1Sdan SELECT * FROM t1 WHERE a='abc' AND rowid<100 AND b<12 5878210233cSdrh} {/SEARCH t1 USING INDEX i2/} 588575ab2f8Sdando_eqp_test 13.3.2 { 589aa9933c1Sdan SELECT * FROM t1 WHERE a='abc' AND rowid<'100' AND b<12 5908210233cSdrh} {/SEARCH t1 USING INDEX i2/} 591575ab2f8Sdan 592575ab2f8Sdan#------------------------------------------------------------------------- 593575ab2f8Sdan# Check also that affinities are taken into account when using stat4 data 594575ab2f8Sdan# to estimate the number of rows scanned by any other constraint on a 595575ab2f8Sdan# column other than the leftmost. 596575ab2f8Sdan# 597575ab2f8Sdandrop_all_tables 598575ab2f8Sdando_test 14.1 { 599575ab2f8Sdan execsql { CREATE TABLE t1(a, b INTEGER, c) } 600575ab2f8Sdan for {set i 0} {$i<100} {incr i} { 601575ab2f8Sdan set c [expr $i % 3] 602575ab2f8Sdan execsql { INSERT INTO t1 VALUES('ott', $i, $c) } 603575ab2f8Sdan } 604575ab2f8Sdan execsql { 605575ab2f8Sdan CREATE INDEX i1 ON t1(a, b); 606575ab2f8Sdan CREATE INDEX i2 ON t1(c); 607575ab2f8Sdan ANALYZE; 608575ab2f8Sdan } 609575ab2f8Sdan} {} 610575ab2f8Sdando_eqp_test 13.2.1 { 611575ab2f8Sdan SELECT * FROM t1 WHERE a='ott' AND b<10 AND c=1 6128210233cSdrh} {/SEARCH t1 USING INDEX i1/} 613575ab2f8Sdando_eqp_test 13.2.2 { 614575ab2f8Sdan SELECT * FROM t1 WHERE a='ott' AND b<'10' AND c=1 6158210233cSdrh} {/SEARCH t1 USING INDEX i1/} 616575ab2f8Sdan 61784f48296Sdan#------------------------------------------------------------------------- 61884f48296Sdan# By default, 16 non-periodic samples are collected for the stat4 table. 61984f48296Sdan# The following tests attempt to verify that the most common keys are 62084f48296Sdan# being collected. 62184f48296Sdan# 62284f48296Sdanproc check_stat4 {tn} { 62384f48296Sdan db eval ANALYZE 62484f48296Sdan db eval {SELECT a, b, c, d FROM t1} { 62584f48296Sdan incr k($a) 62684f48296Sdan incr k([list $a $b]) 62784f48296Sdan incr k([list $a $b $c]) 62884f48296Sdan if { [info exists k([list $a $b $c $d])]==0 } { incr nRow } 62984f48296Sdan incr k([list $a $b $c $d]) 63084f48296Sdan } 631f52bb8d3Sdan 63284f48296Sdan set L [list] 63384f48296Sdan foreach key [array names k] { 63484f48296Sdan lappend L [list $k($key) $key] 63584f48296Sdan } 63684f48296Sdan 63784f48296Sdan set nSample $nRow 63884f48296Sdan if {$nSample>16} {set nSample 16} 63984f48296Sdan 64084f48296Sdan set nThreshold [lindex [lsort -decr -integer -index 0 $L] [expr $nSample-1] 0] 64184f48296Sdan foreach key [array names k] { 64284f48296Sdan if {$k($key)>$nThreshold} { 64384f48296Sdan set expect($key) 1 64484f48296Sdan } 64584f48296Sdan if {$k($key)==$nThreshold} { 64684f48296Sdan set possible($key) 1 64784f48296Sdan } 64884f48296Sdan } 64984f48296Sdan 65084f48296Sdan 65184f48296Sdan set nPossible [expr $nSample - [llength [array names expect]]] 65284f48296Sdan 65384f48296Sdan #puts "EXPECT: [array names expect]" 65484f48296Sdan #puts "POSSIBLE($nPossible/[array size possible]): [array names possible]" 65584f48296Sdan #puts "HAVE: [db eval {SELECT test_decode(sample) FROM sqlite_stat4 WHERE idx='i1'}]" 65684f48296Sdan 65784f48296Sdan db eval {SELECT test_decode(sample) AS s FROM sqlite_stat4 WHERE idx='i1'} { 65884f48296Sdan set seen 0 65984f48296Sdan for {set i 0} {$i<4} {incr i} { 66084f48296Sdan unset -nocomplain expect([lrange $s 0 $i]) 66184f48296Sdan if {[info exists possible([lrange $s 0 $i])]} { 66284f48296Sdan set seen 1 66384f48296Sdan unset -nocomplain possible([lrange $s 0 $i]) 66484f48296Sdan } 66584f48296Sdan } 66684f48296Sdan if {$seen} {incr nPossible -1} 66784f48296Sdan } 66884f48296Sdan if {$nPossible<0} {set nPossible 0} 66984f48296Sdan 67084f48296Sdan set res [list [llength [array names expect]] $nPossible] 67184f48296Sdan uplevel [list do_test $tn [list set {} $res] {0 0}] 67284f48296Sdan} 67384f48296Sdan 67484f48296Sdandrop_all_tables 67584f48296Sdando_test 14.1.1 { 67684f48296Sdan execsql { 67784f48296Sdan CREATE TABLE t1(a,b,c,d); 67884f48296Sdan CREATE INDEX i1 ON t1(a,b,c,d); 67984f48296Sdan } 68084f48296Sdan for {set i 0} {$i < 160} {incr i} { 68184f48296Sdan execsql { INSERT INTO t1 VALUES($i,$i,$i,$i) } 68284f48296Sdan if {($i % 10)==0} { execsql { INSERT INTO t1 VALUES($i,$i,$i,$i) } } 68384f48296Sdan } 68484f48296Sdan} {} 68584f48296Sdancheck_stat4 14.1.2 68684f48296Sdan 68784f48296Sdando_test 14.2.1 { 68884f48296Sdan execsql { DELETE FROM t1 } 68984f48296Sdan for {set i 0} {$i < 1600} {incr i} { 69084f48296Sdan execsql { INSERT INTO t1 VALUES($i/10,$i/17,$i/27,$i/37) } 69184f48296Sdan } 69284f48296Sdan} {} 69384f48296Sdancheck_stat4 14.2.2 69484f48296Sdan 69584f48296Sdando_test 14.3.1 { 69684f48296Sdan for {set i 0} {$i < 10} {incr i} { 69784f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 69884f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 69984f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70084f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70184f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70284f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70384f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70484f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70584f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70684f48296Sdan execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) } 70784f48296Sdan } 70884f48296Sdan} {} 70984f48296Sdancheck_stat4 14.3.2 71084f48296Sdan 711b49d1047Sdando_test 14.4.1 { 712b49d1047Sdan execsql {DELETE FROM t1} 713b49d1047Sdan for {set i 1} {$i < 160} {incr i} { 714b49d1047Sdan set b [expr $i % 10] 715b49d1047Sdan if {$b==0 || $b==2} {set b 1} 716b49d1047Sdan execsql { INSERT INTO t1 VALUES($i/10,$b,$i,$i) } 717b49d1047Sdan } 718b49d1047Sdan} {} 719b49d1047Sdancheck_stat4 14.4.2 720b49d1047Sdandb func lrange lrange 721b49d1047Sdandb func lindex lindex 722b49d1047Sdando_execsql_test 14.4.3 { 723b49d1047Sdan SELECT lrange(test_decode(sample), 0, 1) AS s FROM sqlite_stat4 724b49d1047Sdan WHERE lindex(s, 1)=='1' ORDER BY rowid 725b49d1047Sdan} { 726b49d1047Sdan {0 1} {1 1} {2 1} {3 1} 727b49d1047Sdan {4 1} {5 1} {6 1} {7 1} 728b49d1047Sdan {8 1} {9 1} {10 1} {11 1} 729b49d1047Sdan {12 1} {13 1} {14 1} {15 1} 730b49d1047Sdan} 731b49d1047Sdan 732b49d1047Sdan#------------------------------------------------------------------------- 733b49d1047Sdan# Test that nothing untoward happens if the stat4 table contains entries 734b49d1047Sdan# for indexes that do not exist. Or NULL values in the idx column. 735b49d1047Sdan# Or NULL values in any of the other columns. 736b49d1047Sdan# 737b49d1047Sdandrop_all_tables 738b49d1047Sdando_execsql_test 15.1 { 739b49d1047Sdan CREATE TABLE x1(a, b, UNIQUE(a, b)); 740b49d1047Sdan INSERT INTO x1 VALUES(1, 2); 741b49d1047Sdan INSERT INTO x1 VALUES(3, 4); 742b49d1047Sdan INSERT INTO x1 VALUES(5, 6); 743b49d1047Sdan ANALYZE; 744b49d1047Sdan INSERT INTO sqlite_stat4 VALUES(NULL, NULL, NULL, NULL, NULL, NULL); 745b49d1047Sdan} 746b49d1047Sdandb close 747b49d1047Sdansqlite3 db test.db 748b49d1047Sdando_execsql_test 15.2 { SELECT * FROM x1 } {1 2 3 4 5 6} 749b49d1047Sdan 750b49d1047Sdando_execsql_test 15.3 { 751b49d1047Sdan INSERT INTO sqlite_stat4 VALUES(42, 42, 42, 42, 42, 42); 752b49d1047Sdan} 753b49d1047Sdandb close 754b49d1047Sdansqlite3 db test.db 755b49d1047Sdando_execsql_test 15.4 { SELECT * FROM x1 } {1 2 3 4 5 6} 756b49d1047Sdan 757b49d1047Sdando_execsql_test 15.5 { 758b49d1047Sdan UPDATE sqlite_stat1 SET stat = NULL; 759b49d1047Sdan} 760b49d1047Sdandb close 761b49d1047Sdansqlite3 db test.db 762b49d1047Sdando_execsql_test 15.6 { SELECT * FROM x1 } {1 2 3 4 5 6} 763b49d1047Sdan 764b49d1047Sdando_execsql_test 15.7 { 765b49d1047Sdan ANALYZE; 766b49d1047Sdan UPDATE sqlite_stat1 SET tbl = 'no such tbl'; 767b49d1047Sdan} 768b49d1047Sdandb close 769b49d1047Sdansqlite3 db test.db 770b49d1047Sdando_execsql_test 15.8 { SELECT * FROM x1 } {1 2 3 4 5 6} 771b49d1047Sdan 772b49d1047Sdando_execsql_test 15.9 { 773b49d1047Sdan ANALYZE; 774b49d1047Sdan UPDATE sqlite_stat4 SET neq = NULL, nlt=NULL, ndlt=NULL; 775b49d1047Sdan} 776b49d1047Sdandb close 777b49d1047Sdansqlite3 db test.db 778b49d1047Sdando_execsql_test 15.10 { SELECT * FROM x1 } {1 2 3 4 5 6} 779b49d1047Sdan 780b49d1047Sdan# This is just for coverage.... 781b49d1047Sdando_execsql_test 15.11 { 782b49d1047Sdan ANALYZE; 783b49d1047Sdan UPDATE sqlite_stat1 SET stat = stat || ' unordered'; 784b49d1047Sdan} 785b49d1047Sdandb close 786b49d1047Sdansqlite3 db test.db 787b49d1047Sdando_execsql_test 15.12 { SELECT * FROM x1 } {1 2 3 4 5 6} 788b49d1047Sdan 789b49d1047Sdan#------------------------------------------------------------------------- 790b49d1047Sdan# Test that allocations used for sqlite_stat4 samples are included in 791b49d1047Sdan# the quantity returned by SQLITE_DBSTATUS_SCHEMA_USED. 792b49d1047Sdan# 793b49d1047Sdanset one [string repeat x 1000] 794b49d1047Sdanset two [string repeat x 2000] 795b49d1047Sdando_test 16.1 { 796b49d1047Sdan reset_db 797b49d1047Sdan execsql { 798b49d1047Sdan CREATE TABLE t1(a, UNIQUE(a)); 799b49d1047Sdan INSERT INTO t1 VALUES($one); 800b49d1047Sdan ANALYZE; 801b49d1047Sdan } 802b49d1047Sdan set nByte [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] 803b49d1047Sdan 804b49d1047Sdan reset_db 805b49d1047Sdan execsql { 806b49d1047Sdan CREATE TABLE t1(a, UNIQUE(a)); 807b49d1047Sdan INSERT INTO t1 VALUES($two); 808b49d1047Sdan ANALYZE; 809b49d1047Sdan } 810b49d1047Sdan set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1] 811c8606e41Sdrh puts -nonewline " (nByte=$nByte nByte2=$nByte2)" 812b49d1047Sdan 813c8606e41Sdrh expr {$nByte2 > $nByte+900 && $nByte2 < $nByte+1100} 814b49d1047Sdan} {1} 815b49d1047Sdan 816b49d1047Sdan#------------------------------------------------------------------------- 817b49d1047Sdan# Test that stat4 data may be used with partial indexes. 818b49d1047Sdan# 819b49d1047Sdando_test 17.1 { 820b49d1047Sdan reset_db 821b49d1047Sdan execsql { 822b49d1047Sdan CREATE TABLE t1(a, b, c, d); 823b49d1047Sdan CREATE INDEX i1 ON t1(a, b) WHERE d IS NOT NULL; 824b49d1047Sdan INSERT INTO t1 VALUES(-1, -1, -1, NULL); 825b49d1047Sdan INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; 826b49d1047Sdan INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; 827b49d1047Sdan INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; 828b49d1047Sdan INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; 829b49d1047Sdan INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; 830b49d1047Sdan INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1; 831b49d1047Sdan } 832b49d1047Sdan 833b49d1047Sdan for {set i 0} {$i < 32} {incr i} { 834b49d1047Sdan if {$i<8} {set b 0} else { set b $i } 835b49d1047Sdan execsql { INSERT INTO t1 VALUES($i%2, $b, $i/2, 'abc') } 836b49d1047Sdan } 837b49d1047Sdan execsql {ANALYZE main.t1} 838b49d1047Sdan} {} 839b49d1047Sdan 840b49d1047Sdando_catchsql_test 17.1.2 { 841b49d1047Sdan ANALYZE temp.t1; 842b49d1047Sdan} {1 {no such table: temp.t1}} 843b49d1047Sdan 844b49d1047Sdando_eqp_test 17.2 { 845b49d1047Sdan SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=10 AND c=10; 846b49d1047Sdan} {/USING INDEX i1/} 847b49d1047Sdando_eqp_test 17.3 { 848b49d1047Sdan SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=0 AND c=10; 849b49d1047Sdan} {/USING INDEX i1/} 850b49d1047Sdan 851b49d1047Sdando_execsql_test 17.4 { 852abfa6d52Sdrh CREATE INDEX i2 ON t1(c, d); 853b49d1047Sdan ANALYZE main.i2; 854b49d1047Sdan} 855b49d1047Sdando_eqp_test 17.5 { 856b49d1047Sdan SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=10 AND c=10; 857b49d1047Sdan} {/USING INDEX i1/} 858b49d1047Sdando_eqp_test 17.6 { 859b49d1047Sdan SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=0 AND c=10; 860b49d1047Sdan} {/USING INDEX i2/} 861b49d1047Sdan 862b49d1047Sdan#------------------------------------------------------------------------- 863b49d1047Sdan# 864b49d1047Sdando_test 18.1 { 865b49d1047Sdan reset_db 866b49d1047Sdan execsql { 867b49d1047Sdan CREATE TABLE t1(a, b); 868b49d1047Sdan CREATE INDEX i1 ON t1(a, b); 869b49d1047Sdan } 870b49d1047Sdan for {set i 0} {$i < 9} {incr i} { 871b49d1047Sdan execsql { 872b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 873b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 874b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 875b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 876b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 877b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 878b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 879b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 880b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 881b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 882b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 883b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 884b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 885b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 886b49d1047Sdan INSERT INTO t1 VALUES($i, 0); 887b49d1047Sdan } 888b49d1047Sdan } 889b49d1047Sdan execsql ANALYZE 890b49d1047Sdan execsql { SELECT count(*) FROM sqlite_stat4 } 891b49d1047Sdan} {9} 892b49d1047Sdan 893b49d1047Sdan#------------------------------------------------------------------------- 894b49d1047Sdan# For coverage. 895b49d1047Sdan# 896b49d1047Sdanifcapable view { 897b49d1047Sdan do_test 19.1 { 898b49d1047Sdan reset_db 899b49d1047Sdan execsql { 900b49d1047Sdan CREATE TABLE t1(x, y); 901b49d1047Sdan CREATE INDEX i1 ON t1(x, y); 902b49d1047Sdan CREATE VIEW v1 AS SELECT * FROM t1; 903b49d1047Sdan ANALYZE; 904b49d1047Sdan } 905b49d1047Sdan } {} 906b49d1047Sdan} 907b49d1047Sdanifcapable auth { 908b49d1047Sdan proc authproc {op args} { 909b49d1047Sdan if {$op == "SQLITE_ANALYZE"} { return "SQLITE_DENY" } 910b49d1047Sdan return "SQLITE_OK" 911b49d1047Sdan } 912b49d1047Sdan do_test 19.2 { 913b49d1047Sdan reset_db 914b49d1047Sdan db auth authproc 915b49d1047Sdan execsql { 916b49d1047Sdan CREATE TABLE t1(x, y); 917b49d1047Sdan CREATE VIEW v1 AS SELECT * FROM t1; 918b49d1047Sdan } 919b49d1047Sdan catchsql ANALYZE 920b49d1047Sdan } {1 {not authorized}} 921b49d1047Sdan} 922b49d1047Sdan 923b13af4c5Sdan#------------------------------------------------------------------------- 924b13af4c5Sdan# 925b13af4c5Sdanreset_db 926b13af4c5Sdanproc r {args} { expr rand() } 927b13af4c5Sdandb func r r 928b13af4c5Sdandb func lrange lrange 929b13af4c5Sdando_test 20.1 { 930b13af4c5Sdan execsql { 931b13af4c5Sdan CREATE TABLE t1(a,b,c,d); 932b13af4c5Sdan CREATE INDEX i1 ON t1(a,b,c,d); 933b13af4c5Sdan } 934b13af4c5Sdan for {set i 0} {$i < 16} {incr i} { 935b13af4c5Sdan execsql { 936b13af4c5Sdan INSERT INTO t1 VALUES($i, r(), r(), r()); 937b13af4c5Sdan INSERT INTO t1 VALUES($i, $i, r(), r()); 938b13af4c5Sdan INSERT INTO t1 VALUES($i, $i, $i, r()); 939b13af4c5Sdan INSERT INTO t1 VALUES($i, $i, $i, $i); 940b13af4c5Sdan INSERT INTO t1 VALUES($i, $i, $i, $i); 941b13af4c5Sdan INSERT INTO t1 VALUES($i, $i, $i, r()); 942b13af4c5Sdan INSERT INTO t1 VALUES($i, $i, r(), r()); 943b13af4c5Sdan INSERT INTO t1 VALUES($i, r(), r(), r()); 944b13af4c5Sdan } 945b13af4c5Sdan } 946b13af4c5Sdan} {} 947b13af4c5Sdando_execsql_test 20.2 { ANALYZE } 948b13af4c5Sdanfor {set i 0} {$i<16} {incr i} { 949b13af4c5Sdan set val "$i $i $i $i" 950b13af4c5Sdan do_execsql_test 20.3.$i { 951b13af4c5Sdan SELECT count(*) FROM sqlite_stat4 952b13af4c5Sdan WHERE lrange(test_decode(sample), 0, 3)=$val 953b13af4c5Sdan } {1} 954b13af4c5Sdan} 955b13af4c5Sdan 956fd984b81Sdan#------------------------------------------------------------------------- 957fd984b81Sdan# 958fd984b81Sdanreset_db 959fd984b81Sdan 960fd984b81Sdando_execsql_test 21.0 { 961fd984b81Sdan CREATE TABLE t2(a, b); 962fd984b81Sdan CREATE INDEX i2 ON t2(a); 963fd984b81Sdan} 964fd984b81Sdan 965fd984b81Sdando_test 21.1 { 966fd984b81Sdan for {set i 1} {$i < 100} {incr i} { 967fd984b81Sdan execsql { 968fd984b81Sdan INSERT INTO t2 VALUES(CASE WHEN $i < 80 THEN 'one' ELSE 'two' END, $i) 969fd984b81Sdan } 970fd984b81Sdan } 971fd984b81Sdan execsql ANALYZE 972fd984b81Sdan} {} 973fd984b81Sdan 974fd984b81Sdan# Condition (a='one') matches 80% of the table. (rowid<10) reduces this to 975fd984b81Sdan# 10%, but (rowid<50) only reduces it to 50%. So in the first case below 976fd984b81Sdan# the index is used. In the second, it is not. 977fd984b81Sdan# 978fd984b81Sdando_eqp_test 21.2 { 979fd984b81Sdan SELECT * FROM t2 WHERE a='one' AND rowid < 10 980fd984b81Sdan} {/*USING INDEX i2 (a=? AND rowid<?)*/} 981fd984b81Sdando_eqp_test 21.3 { 982fd984b81Sdan SELECT * FROM t2 WHERE a='one' AND rowid < 50 983fd984b81Sdan} {/*USING INTEGER PRIMARY KEY*/} 984fd984b81Sdan 985fd984b81Sdan#------------------------------------------------------------------------- 986fd984b81Sdan# 987fd984b81Sdanreset_db 988fd984b81Sdando_execsql_test 22.0 { 989fd984b81Sdan CREATE TABLE t3(a, b, c, d, PRIMARY KEY(a, b)) WITHOUT ROWID; 99003c3905fSdrh SELECT * FROM t3; 99103c3905fSdrh} {} 992fd984b81Sdando_execsql_test 22.1 { 993fd984b81Sdan WITH r(x) AS ( 994fd984b81Sdan SELECT 1 995fd984b81Sdan UNION ALL 996fd984b81Sdan SELECT x+1 FROM r WHERE x<=100 997fd984b81Sdan ) 998fd984b81Sdan 999fd984b81Sdan INSERT INTO t3 SELECT 1000fd984b81Sdan CASE WHEN (x>45 AND x<96) THEN 'B' ELSE 'A' END, /* Column "a" */ 1001fd984b81Sdan x, /* Column "b" */ 1002fd984b81Sdan CASE WHEN (x<51) THEN 'one' ELSE 'two' END, /* Column "c" */ 1003fd984b81Sdan x /* Column "d" */ 1004fd984b81Sdan FROM r; 1005fd984b81Sdan 1006fd984b81Sdan CREATE INDEX i3 ON t3(c); 1007fd984b81Sdan CREATE INDEX i4 ON t3(d); 1008fd984b81Sdan ANALYZE; 1009fd984b81Sdan} 1010fd984b81Sdan 1011fd984b81Sdan# Expression (c='one' AND a='B') matches 5 table rows. But (c='one' AND a=A') 1012fd984b81Sdan# matches 45. Expression (d<?) matches 20. Neither index is a covering index. 1013fd984b81Sdan# 1014fd984b81Sdan# Therefore, with stat4 data, SQLite prefers (c='one' AND a='B') over (d<20), 1015fd984b81Sdan# and (d<20) over (c='one' AND a='A'). 1016fd984b81Sdanforeach {tn where res} { 1017fd984b81Sdan 1 "c='one' AND a='B' AND d < 20" {/*INDEX i3 (c=? AND a=?)*/} 1018fd984b81Sdan 2 "c='one' AND a='A' AND d < 20" {/*INDEX i4 (d<?)*/} 1019fd984b81Sdan} { 1020fd984b81Sdan do_eqp_test 22.2.$tn "SELECT * FROM t3 WHERE $where" $res 1021fd984b81Sdan} 1022fd984b81Sdan 102339caccf8Sdanproc int_to_char {i} { 102439caccf8Sdan set ret "" 102539caccf8Sdan set char [list a b c d e f g h i j] 102639caccf8Sdan foreach {div} {1000 100 10 1} { 102739caccf8Sdan append ret [lindex $char [expr ($i / $div) % 10]] 102839caccf8Sdan } 102939caccf8Sdan set ret 103039caccf8Sdan} 103139caccf8Sdandb func int_to_char int_to_char 103239caccf8Sdan 103339caccf8Sdando_execsql_test 23.0 { 103439caccf8Sdan CREATE TABLE t4( 103539caccf8Sdan a COLLATE nocase, b, c, 103639caccf8Sdan d, e, f, 103739caccf8Sdan PRIMARY KEY(c, b, a) 103839caccf8Sdan ) WITHOUT ROWID; 103939caccf8Sdan CREATE INDEX i41 ON t4(e); 104039caccf8Sdan CREATE INDEX i42 ON t4(f); 104139caccf8Sdan 104239caccf8Sdan WITH data(a, b, c, d, e, f) AS ( 104339caccf8Sdan SELECT int_to_char(0), 'xyz', 'zyx', '*', 0, 0 104439caccf8Sdan UNION ALL 104539caccf8Sdan SELECT 104639caccf8Sdan int_to_char(f+1), b, c, d, (e+1) % 2, f+1 104739caccf8Sdan FROM data WHERE f<1024 104839caccf8Sdan ) 104939caccf8Sdan INSERT INTO t4 SELECT a, b, c, d, e, f FROM data; 105039caccf8Sdan ANALYZE; 105139caccf8Sdan} {} 105239caccf8Sdan 105339caccf8Sdando_eqp_test 23.1 { 105439caccf8Sdan SELECT * FROM t4 WHERE 105539caccf8Sdan (e=1 AND b='xyz' AND c='zyx' AND a<'AEA') AND f<300 105647b1d68fSdrh -- Formerly used index i41. But i41 is not a covering index whereas 105747b1d68fSdrh -- the PRIMARY KEY is a covering index, and so as of 2017-10-15, the 105847b1d68fSdrh -- PRIMARY KEY is preferred. 10598210233cSdrh} {SEARCH t4 USING PRIMARY KEY (c=? AND b=? AND a<?)} 106039caccf8Sdando_eqp_test 23.2 { 106139caccf8Sdan SELECT * FROM t4 WHERE 106239caccf8Sdan (e=1 AND b='xyz' AND c='zyx' AND a<'JJJ') AND f<300 10638210233cSdrh} {SEARCH t4 USING INDEX i42 (f<?)} 106439caccf8Sdan 106543fbe5e2Sdando_execsql_test 24.0 { 106643fbe5e2Sdan CREATE TABLE t5(c, d, b, e, a, PRIMARY KEY(a, b, c)) WITHOUT ROWID; 106743fbe5e2Sdan WITH data(a, b, c, d, e) AS ( 106843fbe5e2Sdan SELECT 'z', 'y', 0, 0, 0 106943fbe5e2Sdan UNION ALL 107043fbe5e2Sdan SELECT 107143fbe5e2Sdan a, CASE WHEN b='y' THEN 'n' ELSE 'y' END, c+1, e/250, e+1 107243fbe5e2Sdan FROM data 107343fbe5e2Sdan WHERE e<1000 107443fbe5e2Sdan ) 107543fbe5e2Sdan INSERT INTO t5(a, b, c, d, e) SELECT * FROM data; 107643fbe5e2Sdan CREATE INDEX t5d ON t5(d); 107743fbe5e2Sdan CREATE INDEX t5e ON t5(e); 107843fbe5e2Sdan ANALYZE; 107943fbe5e2Sdan} 108043fbe5e2Sdan 108143fbe5e2Sdanforeach {tn where eqp} { 108243fbe5e2Sdan 1 "d=0 AND a='z' AND b='n' AND e<200" {/*t5d (d=? AND a=? AND b=?)*/} 108343fbe5e2Sdan 2 "d=0 AND a='z' AND b='n' AND e<100" {/*t5e (e<?)*/} 108443fbe5e2Sdan 108543fbe5e2Sdan 3 "d=0 AND e<300" {/*t5d (d=?)*/} 108643fbe5e2Sdan 4 "d=0 AND e<200" {/*t5e (e<?)*/} 108743fbe5e2Sdan} { 108843fbe5e2Sdan do_eqp_test 24.$tn "SeLeCt * FROM t5 WHERE $where" $eqp 108943fbe5e2Sdan} 109043fbe5e2Sdan 1091f741e049Sdan#------------------------------------------------------------------------- 1092f741e049Sdan# Test that if stat4 data is available but cannot be used because the 1093f741e049Sdan# rhs of a range constraint is a complex expression, the default estimates 1094f741e049Sdan# are used instead. 1095f741e049Sdanifcapable stat4&&cte { 1096f741e049Sdan do_execsql_test 25.1 { 1097f741e049Sdan CREATE TABLE t6(a, b); 1098f741e049Sdan WITH ints(i,j) AS ( 1099f741e049Sdan SELECT 1,1 UNION ALL SELECT i+1,j+1 FROM ints WHERE i<100 1100f741e049Sdan ) INSERT INTO t6 SELECT * FROM ints; 1101f741e049Sdan CREATE INDEX aa ON t6(a); 1102f741e049Sdan CREATE INDEX bb ON t6(b); 1103f741e049Sdan ANALYZE; 1104f741e049Sdan } 1105f741e049Sdan 1106f741e049Sdan # Term (b<?) is estimated at 25%. Better than (a<30) but not as 1107f741e049Sdan # good as (a<20). 110803c3905fSdrh do_eqp_test 25.2.1 { SELECT * FROM t6 WHERE a<30 AND b<? } \ 11098210233cSdrh {SEARCH t6 USING INDEX bb (b<?)} 111003c3905fSdrh do_eqp_test 25.2.2 { SELECT * FROM t6 WHERE a<20 AND b<? } \ 11118210233cSdrh {SEARCH t6 USING INDEX aa (a<?)} 1112f741e049Sdan 1113f741e049Sdan # Term (b BETWEEN ? AND ?) is estimated at 1/64. 1114f741e049Sdan do_eqp_test 25.3.1 { 1115f741e049Sdan SELECT * FROM t6 WHERE a BETWEEN 5 AND 10 AND b BETWEEN ? AND ? 11168210233cSdrh } {SEARCH t6 USING INDEX bb (b>? AND b<?)} 1117f741e049Sdan 1118f741e049Sdan # Term (b BETWEEN ? AND 60) is estimated to return roughly 15 rows - 1119f741e049Sdan # 60 from (b<=60) multiplied by 0.25 for the b>=? term. Better than 1120f741e049Sdan # (a<20) but not as good as (a<10). 1121f741e049Sdan do_eqp_test 25.4.1 { 1122f741e049Sdan SELECT * FROM t6 WHERE a < 10 AND (b BETWEEN ? AND 60) 11238210233cSdrh } {SEARCH t6 USING INDEX aa (a<?)} 112403c3905fSdrh 1125f741e049Sdan do_eqp_test 25.4.2 { 1126f741e049Sdan SELECT * FROM t6 WHERE a < 20 AND (b BETWEEN ? AND 60) 11278210233cSdrh } {SEARCH t6 USING INDEX bb (b>? AND b<?)} 1128f741e049Sdan} 1129f741e049Sdan 1130a3d0c136Sdan#------------------------------------------------------------------------- 1131a3d0c136Sdan# Check that a problem in they way stat4 data is used has been 1132a3d0c136Sdan# resolved (see below). 1133a3d0c136Sdan# 1134a3d0c136Sdanreset_db 1135a0d56aefSdando_test 26.1.1 { 1136a3d0c136Sdan db transaction { 1137a3d0c136Sdan execsql { 1138a3d0c136Sdan CREATE TABLE t1(x, y, z); 1139a3d0c136Sdan CREATE INDEX t1xy ON t1(x, y); 1140a3d0c136Sdan CREATE INDEX t1z ON t1(z); 1141a3d0c136Sdan } 1142a3d0c136Sdan for {set i 0} {$i < 10000} {incr i} { 1143a3d0c136Sdan execsql { INSERT INTO t1(x, y) VALUES($i, $i) } 1144a3d0c136Sdan } 1145a3d0c136Sdan for {set i 0} {$i < 10} {incr i} { 1146a3d0c136Sdan execsql { 1147a3d0c136Sdan WITH cnt(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM cnt WHERE x<100) 1148a3d0c136Sdan INSERT INTO t1(x, y) SELECT 10000+$i, x FROM cnt; 1149a3d0c136Sdan INSERT INTO t1(x, y) SELECT 10000+$i, 100; 1150a3d0c136Sdan } 1151a3d0c136Sdan } 1152a3d0c136Sdan execsql { 1153a3d0c136Sdan UPDATE t1 SET z = rowid / 20; 1154a3d0c136Sdan ANALYZE; 1155a3d0c136Sdan } 1156a3d0c136Sdan } 1157a3d0c136Sdan} {} 1158a3d0c136Sdan 1159a0d56aefSdando_execsql_test 26.1.2 { 1160a3d0c136Sdan SELECT count(*) FROM t1 WHERE x = 10000 AND y < 50; 1161a3d0c136Sdan} {49} 1162a0d56aefSdando_execsql_test 26.1.3 { 1163a3d0c136Sdan SELECT count(*) FROM t1 WHERE z = 444; 1164a3d0c136Sdan} {20} 1165a3d0c136Sdan 1166a3d0c136Sdan# The analyzer knows that any (z=?) expression matches 20 rows. So it 1167a3d0c136Sdan# will use index "t1z" if the estimate of hits for (x=10000 AND y<50) 1168a3d0c136Sdan# is greater than 20 rows. 1169a3d0c136Sdan# 1170a3d0c136Sdan# And it should be. The analyzer has a stat4 sample as follows: 1171a3d0c136Sdan# 1172a3d0c136Sdan# sample=(x=10000, y=100) nLt=(10000 10099) 1173a3d0c136Sdan# 1174a3d0c136Sdan# There should be no other samples that start with (x=10000). So it knows 1175a3d0c136Sdan# that (x=10000 AND y<50) must match somewhere between 0 and 99 rows, but 11764fb48e4eSdrh# no more than that. Guessing less than 20 is therefore unreasonable. 1177a3d0c136Sdan# 1178a3d0c136Sdan# At one point though, due to a problem in whereKeyStats(), the planner was 1179a3d0c136Sdan# estimating that (x=10000 AND y<50) would match only 2 rows. 1180a3d0c136Sdan# 1181a0d56aefSdando_eqp_test 26.1.4 { 1182a3d0c136Sdan SELECT * FROM t1 WHERE x = 10000 AND y < 50 AND z = 444; 11838210233cSdrh} {SEARCH t1 USING INDEX t1z (z=?)} 1184a3d0c136Sdan 1185a3d0c136Sdan 1186a0d56aefSdan# This test - 26.2.* - tests that another manifestation of the same problem 1187a0d56aefSdan# is no longer present in the library. Assuming: 1188a0d56aefSdan# 1189a0d56aefSdan# CREATE INDEX t1xy ON t1(x, y) 1190a0d56aefSdan# 1191a0d56aefSdan# and that have samples for index t1xy as follows: 1192a0d56aefSdan# 1193a0d56aefSdan# 1194a0d56aefSdan# sample=('A', 70) nEq=(100, 2) nLt=(900, 970) 1195a0d56aefSdan# sample=('B', 70) nEq=(100, 2) nLt=(1000, 1070) 1196a0d56aefSdan# 1197a0d56aefSdan# the planner should estimate that (x = 'B' AND y > 25) matches 76 rows 1198a0d56aefSdan# (70 * 2/3 + 30). Before, due to the problem, the planner was estimating 1199a0d56aefSdan# that this matched 100 rows. 1200a0d56aefSdan# 1201a0d56aefSdanreset_db 1202a0d56aefSdando_execsql_test 26.2.1 { 1203a0d56aefSdan BEGIN; 1204a0d56aefSdan CREATE TABLE t1(x, y, z); 1205a0d56aefSdan CREATE INDEX i1 ON t1(x, y); 1206a0d56aefSdan CREATE INDEX i2 ON t1(z); 1207a0d56aefSdan 1208a0d56aefSdan WITH 1209a0d56aefSdan cnt(y) AS (SELECT 0 UNION ALL SELECT y+1 FROM cnt WHERE y<99), 1210a0d56aefSdan letters(x) AS ( 1211a0d56aefSdan SELECT 'A' UNION SELECT 'B' UNION SELECT 'C' UNION SELECT 'D' 1212a0d56aefSdan ) 1213a0d56aefSdan INSERT INTO t1(x, y) SELECT x, y FROM letters, cnt; 1214a0d56aefSdan 1215a0d56aefSdan WITH 1216a0d56aefSdan letters(x) AS ( 1217a0d56aefSdan SELECT 'A' UNION SELECT 'B' UNION SELECT 'C' UNION SELECT 'D' 1218a0d56aefSdan ) 1219a0d56aefSdan INSERT INTO t1(x, y) SELECT x, 70 FROM letters; 1220a0d56aefSdan 1221a0d56aefSdan WITH 1222a0d56aefSdan cnt(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM cnt WHERE i<9999) 1223a0d56aefSdan INSERT INTO t1(x, y) SELECT i, i FROM cnt; 1224a0d56aefSdan 1225a0d56aefSdan UPDATE t1 SET z = (rowid / 95); 1226a0d56aefSdan ANALYZE; 1227a0d56aefSdan COMMIT; 1228a0d56aefSdan} 1229a0d56aefSdan 1230a0d56aefSdando_eqp_test 26.2.2 { 1231a0d56aefSdan SELECT * FROM t1 WHERE x='B' AND y>25 AND z=?; 12328210233cSdrh} {SEARCH t1 USING INDEX i1 (x=? AND y>?)} 1233a3d0c136Sdan 1234a3d0c136Sdan 123584f48296Sdanfinish_test 1236