github.com/jdgcs/sqlite3@v1.12.1-0.20210908114423-bc5f96e4dd51/testdata/tcl/index7.test (about) 1 # 2013-11-04 2 # 3 # The author disclaims copyright to this source code. In place of 4 # a legal notice, here is a blessing: 5 # 6 # May you do good and not evil. 7 # May you find forgiveness for yourself and forgive others. 8 # May you share freely, never taking more than you give. 9 # 10 #*********************************************************************** 11 # 12 # Test cases for partial indices in WITHOUT ROWID tables 13 # 14 15 16 set testdir [file dirname $argv0] 17 source $testdir/tester.tcl 18 19 ifcapable !vtab { 20 finish_test 21 return 22 } 23 24 # Capture the output of a pragma in a TEMP table. 25 # 26 proc capture_pragma {db tabname sql} { 27 $db eval "DROP TABLE IF EXISTS temp.$tabname" 28 set once 1 29 $db eval $sql x { 30 if {$once} { 31 set once 0 32 set ins "INSERT INTO $tabname VALUES" 33 set crtab "CREATE TEMP TABLE $tabname " 34 set sep "(" 35 foreach col $x(*) { 36 append ins ${sep}\$x($col) 37 append crtab ${sep}\"$col\" 38 set sep , 39 } 40 append ins ) 41 append crtab ) 42 $db eval $crtab 43 } 44 $db eval $ins 45 } 46 } 47 48 49 load_static_extension db wholenumber; 50 do_test index7-1.1 { 51 # Able to parse and manage partial indices 52 execsql { 53 CREATE TABLE t1(a,b,c PRIMARY KEY) WITHOUT rowid; 54 CREATE INDEX t1a ON t1(a) WHERE a IS NOT NULL; 55 CREATE INDEX t1b ON t1(b) WHERE b>10; 56 CREATE VIRTUAL TABLE nums USING wholenumber; 57 INSERT INTO t1(a,b,c) 58 SELECT CASE WHEN value%3!=0 THEN value END, value, value 59 FROM nums WHERE value<=20; 60 SELECT count(a), count(b) FROM t1; 61 PRAGMA integrity_check; 62 } 63 } {14 20 ok} 64 65 # (The "partial" column of the PRAGMA index_list output is...) 66 # EVIDENCE-OF: R-34457-09668 "1" if the index is a partial index and "0" 67 # if not. 68 # 69 do_test index7-1.1a { 70 capture_pragma db out {PRAGMA index_list(t1)} 71 db eval {SELECT "name", "partial", '|' FROM out ORDER BY "name"} 72 } {sqlite_autoindex_t1_1 0 | t1a 1 | t1b 1 |} 73 74 # Make sure the count(*) optimization works correctly with 75 # partial indices. Ticket [a5c8ed66cae16243be6] 2013-10-03. 76 # 77 do_execsql_test index7-1.1.1 { 78 SELECT count(*) FROM t1; 79 } {20} 80 81 # Error conditions during parsing... 82 # 83 do_test index7-1.2 { 84 catchsql { 85 CREATE INDEX bad1 ON t1(a,b) WHERE x IS NOT NULL; 86 } 87 } {1 {no such column: x}} 88 do_test index7-1.3 { 89 catchsql { 90 CREATE INDEX bad1 ON t1(a,b) WHERE EXISTS(SELECT * FROM t1); 91 } 92 } {1 {subqueries prohibited in partial index WHERE clauses}} 93 do_test index7-1.4 { 94 catchsql { 95 CREATE INDEX bad1 ON t1(a,b) WHERE a!=?1; 96 } 97 } {1 {parameters prohibited in partial index WHERE clauses}} 98 do_test index7-1.5 { 99 catchsql { 100 CREATE INDEX bad1 ON t1(a,b) WHERE a!=random(); 101 } 102 } {1 {non-deterministic functions prohibited in partial index WHERE clauses}} 103 do_test index7-1.6 { 104 catchsql { 105 CREATE INDEX bad1 ON t1(a,b) WHERE a NOT LIKE 'abc%'; 106 } 107 } {0 {}} 108 do_execsql_test index7-1.7 { 109 INSERT INTO t1(a,b,c) 110 VALUES('abcde',1,101),('abdef',2,102),('xyz',3,103),('abcz',4,104); 111 SELECT c FROM t1 WHERE a NOT LIKE 'abc%' AND a=7 ORDER BY +b; 112 } {7} 113 do_execsql_test index7-1.7eqp { 114 EXPLAIN QUERY PLAN 115 SELECT b FROM t1 WHERE a NOT LIKE 'abc%' AND a=7 ORDER BY +b; 116 } {/SEARCH t1 USING COVERING INDEX bad1 /} 117 do_execsql_test index7-1.8 { 118 DELETE FROM t1 WHERE c>=101; 119 DROP INDEX IF EXISTS bad1; 120 } {} 121 122 do_test index7-1.10 { 123 execsql { 124 ANALYZE; 125 SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; 126 PRAGMA integrity_check; 127 } 128 } {t1 {20 1} t1a {14 1} t1b {10 1} ok} 129 130 # STAT1 shows the partial indices have a reduced number of 131 # rows. 132 # 133 do_test index7-1.11 { 134 execsql { 135 UPDATE t1 SET a=b; 136 ANALYZE; 137 SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; 138 PRAGMA integrity_check; 139 } 140 } {t1 {20 1} t1a {20 1} t1b {10 1} ok} 141 142 do_test index7-1.11b { 143 execsql { 144 UPDATE t1 SET a=NULL WHERE b%3!=0; 145 UPDATE t1 SET b=b+100; 146 ANALYZE; 147 SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; 148 PRAGMA integrity_check; 149 } 150 } {t1 {20 1} t1a {6 1} t1b {20 1} ok} 151 152 do_test index7-1.12 { 153 execsql { 154 UPDATE t1 SET a=CASE WHEN b%3!=0 THEN b END; 155 UPDATE t1 SET b=b-100; 156 ANALYZE; 157 SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; 158 PRAGMA integrity_check; 159 } 160 } {t1 {20 1} t1a {13 1} t1b {10 1} ok} 161 162 do_test index7-1.13 { 163 execsql { 164 DELETE FROM t1 WHERE b BETWEEN 8 AND 12; 165 ANALYZE; 166 SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; 167 PRAGMA integrity_check; 168 } 169 } {t1 {15 1} t1a {10 1} t1b {8 1} ok} 170 171 do_test index7-1.14 { 172 execsql { 173 REINDEX; 174 ANALYZE; 175 SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; 176 PRAGMA integrity_check; 177 } 178 } {t1 {15 1} t1a {10 1} t1b {8 1} ok} 179 180 do_test index7-1.15 { 181 execsql { 182 CREATE INDEX t1c ON t1(c); 183 ANALYZE; 184 SELECT idx, stat FROM sqlite_stat1 ORDER BY idx; 185 PRAGMA integrity_check; 186 } 187 } {t1 {15 1} t1a {10 1} t1b {8 1} t1c {15 1} ok} 188 189 # Queries use partial indices at appropriate times. 190 # 191 do_test index7-2.1 { 192 execsql { 193 CREATE TABLE t2(a,b PRIMARY KEY) without rowid; 194 INSERT INTO t2(a,b) SELECT value, value FROM nums WHERE value<1000; 195 UPDATE t2 SET a=NULL WHERE b%5==0; 196 CREATE INDEX t2a1 ON t2(a) WHERE a IS NOT NULL; 197 SELECT count(*) FROM t2 WHERE a IS NOT NULL; 198 } 199 } {800} 200 do_test index7-2.2 { 201 execsql { 202 EXPLAIN QUERY PLAN 203 SELECT * FROM t2 WHERE a=5; 204 } 205 } {/(SCAN|SEARCH) t2 USING COVERING INDEX t2a1 /} 206 ifcapable stat4 { 207 do_test index7-2.3stat4 { 208 execsql { 209 EXPLAIN QUERY PLAN 210 SELECT * FROM t2 WHERE a IS NOT NULL; 211 } 212 } {/(SCAN|SEARCH) t2 USING COVERING INDEX t2a1 /} 213 } else { 214 do_test index7-2.3stat4 { 215 execsql { 216 EXPLAIN QUERY PLAN 217 SELECT * FROM t2 WHERE a IS NOT NULL AND a>0; 218 } 219 } {/(SCAN|SEARCH) t2 USING COVERING INDEX t2a1 /} 220 } 221 do_test index7-2.4 { 222 execsql { 223 EXPLAIN QUERY PLAN 224 SELECT * FROM t2 WHERE a IS NULL; 225 } 226 } {~/INDEX t2a1/} 227 228 do_execsql_test index7-2.101 { 229 DROP INDEX t2a1; 230 UPDATE t2 SET a=b, b=b+10000; 231 SELECT b FROM t2 WHERE a=15; 232 } {10015} 233 do_execsql_test index7-2.102 { 234 CREATE INDEX t2a2 ON t2(a) WHERE a<100 OR a>200; 235 SELECT b FROM t2 WHERE a=15; 236 PRAGMA integrity_check; 237 } {10015 ok} 238 do_execsql_test index7-2.102eqp { 239 EXPLAIN QUERY PLAN 240 SELECT b FROM t2 WHERE a=15; 241 } {~/.*INDEX t2a2.*/} 242 do_execsql_test index7-2.103 { 243 SELECT b FROM t2 WHERE a=15 AND a<100; 244 } {10015} 245 do_execsql_test index7-2.103eqp { 246 EXPLAIN QUERY PLAN 247 SELECT b FROM t2 WHERE a=15 AND a<100; 248 } {/.*INDEX t2a2.*/} 249 do_execsql_test index7-2.104 { 250 SELECT b FROM t2 WHERE a=515 AND a>200; 251 } {10515} 252 do_execsql_test index7-2.104eqp { 253 EXPLAIN QUERY PLAN 254 SELECT b FROM t2 WHERE a=515 AND a>200; 255 } {/.*INDEX t2a2.*/} 256 257 # Partial UNIQUE indices 258 # 259 do_execsql_test index7-3.1 { 260 CREATE TABLE t3(a,b PRIMARY KEY) without rowid; 261 INSERT INTO t3 SELECT value, value FROM nums WHERE value<200; 262 UPDATE t3 SET a=999 WHERE b%5!=0; 263 CREATE UNIQUE INDEX t3a ON t3(a) WHERE a<>999; 264 } {} 265 do_test index7-3.2 { 266 # unable to insert a duplicate row a-value that is not 999. 267 catchsql { 268 INSERT INTO t3(a,b) VALUES(150, 'test1'); 269 } 270 } {1 {UNIQUE constraint failed: t3.a}} 271 do_test index7-3.3 { 272 # can insert multiple rows with a==999 because such rows are not 273 # part of the unique index. 274 catchsql { 275 INSERT INTO t3(a,b) VALUES(999, 'test1'), (999, 'test2'); 276 } 277 } {0 {}} 278 do_execsql_test index7-3.4 { 279 SELECT count(*) FROM t3 WHERE a=999; 280 } {162} 281 integrity_check index7-3.5 282 283 do_execsql_test index7-4.0 { 284 VACUUM; 285 PRAGMA integrity_check; 286 } {ok} 287 288 # Silently ignore database name qualifiers in partial indices. 289 # 290 do_execsql_test index7-5.0 { 291 CREATE INDEX t3b ON t3(b) WHERE xyzzy.t3.b BETWEEN 5 AND 10; 292 /* ^^^^^-- ignored */ 293 ANALYZE; 294 SELECT count(*) FROM t3 WHERE t3.b BETWEEN 5 AND 10; 295 SELECT stat+0 FROM sqlite_stat1 WHERE idx='t3b'; 296 } {6 6} 297 298 # Verify that the problem identified by ticket [98d973b8f5] has been fixed. 299 # 300 do_execsql_test index7-6.1 { 301 CREATE TABLE t5(a, b); 302 CREATE TABLE t4(c, d); 303 INSERT INTO t5 VALUES(1, 'xyz'); 304 INSERT INTO t4 VALUES('abc', 'not xyz'); 305 SELECT * FROM (SELECT * FROM t5 WHERE a=1 AND b='xyz'), t4 WHERE c='abc'; 306 } { 307 1 xyz abc {not xyz} 308 } 309 do_execsql_test index7-6.2 { 310 CREATE INDEX i4 ON t4(c) WHERE d='xyz'; 311 SELECT * FROM (SELECT * FROM t5 WHERE a=1 AND b='xyz'), t4 WHERE c='abc'; 312 } { 313 1 xyz abc {not xyz} 314 } 315 do_execsql_test index7-6.3 { 316 CREATE VIEW v4 AS SELECT * FROM t4; 317 INSERT INTO t4 VALUES('def', 'xyz'); 318 SELECT * FROM v4 WHERE d='xyz' AND c='def' 319 } { 320 def xyz 321 } 322 do_eqp_test index7-6.4 { 323 SELECT * FROM v4 WHERE d='xyz' AND c='def' 324 } {SEARCH t4 USING INDEX i4 (c=?)} 325 326 do_catchsql_test index7-6.5 { 327 CREATE INDEX t5a ON t5(a) WHERE a=#1; 328 } {1 {near "#1": syntax error}} 329 330 do_execsql_test index7-7.0 { 331 CREATE TABLE t6(x, y); 332 INSERT INTO t6 VALUES(1, 1); 333 INSERT INTO t6 VALUES(0, 0); 334 SELECT * FROM t6 WHERE y IS TRUE ORDER BY x; 335 } {1 1} 336 337 do_execsql_test index7-7.1 { 338 CREATE INDEX i6 ON t6(x) WHERE y IS NOT TRUE; 339 SELECT * FROM t6 WHERE y IS TRUE ORDER BY x; 340 } {1 1} 341 342 # 2020-05-27. tag-20200527-1. 343 # Incomplete stat1 information on a table with few rows should still use the 344 # index. 345 reset_db 346 do_execsql_test index7-8.1 { 347 CREATE TABLE t1(x INTEGER PRIMARY KEY, y); 348 CREATE INDEX t1y ON t1(y) WHERE y IS NOT NULL; 349 INSERT INTO t1(x) VALUES(1),(2); 350 ANALYZE; 351 EXPLAIN QUERY PLAN SELECT 1 FROM t1 WHERE y=5; 352 } {/SEARCH t1 USING COVERING INDEX t1y/} 353 354 355 finish_test