1# 2008-10-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 13set testdir [file dirname $argv0] 14source $testdir/tester.tcl 15set ::testprefix indexedby 16 17# Create a schema with some indexes. 18# 19do_test indexedby-1.1 { 20 execsql { 21 CREATE TABLE t1(a, b); 22 CREATE INDEX i1 ON t1(a); 23 CREATE INDEX i2 ON t1(b); 24 25 CREATE TABLE t2(c, d); 26 CREATE INDEX i3 ON t2(c); 27 CREATE INDEX i4 ON t2(d); 28 29 CREATE TABLE t3(e PRIMARY KEY, f); 30 31 CREATE VIEW v1 AS SELECT * FROM t1; 32 } 33} {} 34 35# Explain Query Plan 36# 37proc EQP {sql} { 38 uplevel "execsql {EXPLAIN QUERY PLAN $sql}" 39} 40 41# These tests are to check that "EXPLAIN QUERY PLAN" is working as expected. 42# 43do_execsql_test indexedby-1.2 { 44 EXPLAIN QUERY PLAN select * from t1 WHERE a = 10; 45} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} 46do_execsql_test indexedby-1.3 { 47 EXPLAIN QUERY PLAN select * from t1 ; 48} {0 0 0 {SCAN TABLE t1}} 49do_execsql_test indexedby-1.4 { 50 EXPLAIN QUERY PLAN select * from t1, t2 WHERE c = 10; 51} { 52 0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?)} 53 0 1 0 {SCAN TABLE t1} 54} 55 56# Parser tests. Test that an INDEXED BY or NOT INDEX clause can be 57# attached to a table in the FROM clause, but not to a sub-select or 58# SQL view. Also test that specifying an index that does not exist or 59# is attached to a different table is detected as an error. 60# 61# EVIDENCE-OF: R-07004-11522 -- syntax diagram qualified-table-name 62# 63# EVIDENCE-OF: R-58230-57098 The "INDEXED BY index-name" phrase 64# specifies that the named index must be used in order to look up values 65# on the preceding table. 66# 67do_test indexedby-2.1 { 68 execsql { SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two'} 69} {} 70do_test indexedby-2.1b { 71 execsql { SELECT * FROM main.t1 NOT INDEXED WHERE a = 'one' AND b = 'two'} 72} {} 73do_test indexedby-2.2 { 74 execsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'} 75} {} 76do_test indexedby-2.2b { 77 execsql { SELECT * FROM main.t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'} 78} {} 79do_test indexedby-2.3 { 80 execsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two'} 81} {} 82# EVIDENCE-OF: R-44699-55558 The INDEXED BY clause does not give the 83# optimizer hints about which index to use; it gives the optimizer a 84# requirement of which index to use. 85# EVIDENCE-OF: R-15800-25719 If index-name does not exist or cannot be 86# used for the query, then the preparation of the SQL statement fails. 87# 88do_test indexedby-2.4 { 89 catchsql { SELECT * FROM t1 INDEXED BY i3 WHERE a = 'one' AND b = 'two'} 90} {1 {no such index: i3}} 91 92# EVIDENCE-OF: R-62112-42456 If the query optimizer is unable to use the 93# index specified by the INDEX BY clause, then the query will fail with 94# an error. 95do_test indexedby-2.4.1 { 96 catchsql { SELECT b FROM t1 INDEXED BY i1 WHERE b = 'two' } 97} {1 {no query solution}} 98 99do_test indexedby-2.5 { 100 catchsql { SELECT * FROM t1 INDEXED BY i5 WHERE a = 'one' AND b = 'two'} 101} {1 {no such index: i5}} 102do_test indexedby-2.6 { 103 catchsql { SELECT * FROM t1 INDEXED BY WHERE a = 'one' AND b = 'two'} 104} {1 {near "WHERE": syntax error}} 105do_test indexedby-2.7 { 106 catchsql { SELECT * FROM v1 INDEXED BY i1 WHERE a = 'one' } 107} {1 {no such index: i1}} 108 109 110# Tests for single table cases. 111# 112# EVIDENCE-OF: R-37002-28871 The "NOT INDEXED" clause specifies that no 113# index shall be used when accessing the preceding table, including 114# implied indices create by UNIQUE and PRIMARY KEY constraints. However, 115# the rowid can still be used to look up entries even when "NOT INDEXED" 116# is specified. 117# 118do_execsql_test indexedby-3.1 { 119 EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a = 'one' AND b = 'two' 120} {/SEARCH TABLE t1 USING INDEX/} 121do_execsql_test indexedby-3.1.1 { 122 EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two' 123} {0 0 0 {SCAN TABLE t1}} 124do_execsql_test indexedby-3.1.2 { 125 EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE rowid=1 126} {/SEARCH TABLE t1 USING INTEGER PRIMARY KEY .rowid=/} 127 128 129do_execsql_test indexedby-3.2 { 130 EXPLAIN QUERY PLAN 131 SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two' 132} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} 133do_execsql_test indexedby-3.3 { 134 EXPLAIN QUERY PLAN 135 SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two' 136} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} 137do_test indexedby-3.4 { 138 catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' } 139} {1 {no query solution}} 140do_test indexedby-3.5 { 141 catchsql { SELECT * FROM t1 INDEXED BY i2 ORDER BY a } 142} {1 {no query solution}} 143do_test indexedby-3.6 { 144 catchsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' } 145} {0 {}} 146do_test indexedby-3.7 { 147 catchsql { SELECT * FROM t1 INDEXED BY i1 ORDER BY a } 148} {0 {}} 149 150do_execsql_test indexedby-3.8 { 151 EXPLAIN QUERY PLAN 152 SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e 153} {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1}} 154do_execsql_test indexedby-3.9 { 155 EXPLAIN QUERY PLAN 156 SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10 157} {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?)}} 158do_test indexedby-3.10 { 159 catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 } 160} {1 {no query solution}} 161do_test indexedby-3.11 { 162 catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_2 WHERE f = 10 } 163} {1 {no such index: sqlite_autoindex_t3_2}} 164 165# Tests for multiple table cases. 166# 167do_execsql_test indexedby-4.1 { 168 EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE a = c 169} { 170 0 0 0 {SCAN TABLE t1} 171 0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?)} 172} 173do_execsql_test indexedby-4.2 { 174 EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c 175} { 176 0 0 1 {SCAN TABLE t2} 177 0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)} 178} 179do_test indexedby-4.3 { 180 catchsql { 181 SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c 182 } 183} {1 {no query solution}} 184do_test indexedby-4.4 { 185 catchsql { 186 SELECT * FROM t2 INDEXED BY i3, t1 INDEXED BY i1 WHERE a=c 187 } 188} {1 {no query solution}} 189 190# Test embedding an INDEXED BY in a CREATE VIEW statement. This block 191# also tests that nothing bad happens if an index refered to by 192# a CREATE VIEW statement is dropped and recreated. 193# 194do_execsql_test indexedby-5.1 { 195 CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5; 196 EXPLAIN QUERY PLAN SELECT * FROM v2 197} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}} 198do_execsql_test indexedby-5.2 { 199 EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10 200} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}} 201do_test indexedby-5.3 { 202 execsql { DROP INDEX i1 } 203 catchsql { SELECT * FROM v2 } 204} {1 {no such index: i1}} 205do_test indexedby-5.4 { 206 # Recreate index i1 in such a way as it cannot be used by the view query. 207 execsql { CREATE INDEX i1 ON t1(b) } 208 catchsql { SELECT * FROM v2 } 209} {1 {no query solution}} 210do_test indexedby-5.5 { 211 # Drop and recreate index i1 again. This time, create it so that it can 212 # be used by the query. 213 execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t1(a) } 214 catchsql { SELECT * FROM v2 } 215} {0 {}} 216 217# Test that "NOT INDEXED" may use the rowid index, but not others. 218# 219do_execsql_test indexedby-6.1 { 220 EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 10 ORDER BY rowid 221} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} 222do_execsql_test indexedby-6.2 { 223 EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid 224} {0 0 0 {SCAN TABLE t1}} 225 226# EVIDENCE-OF: R-40297-14464 The INDEXED BY phrase forces the SQLite 227# query planner to use a particular named index on a DELETE, SELECT, or 228# UPDATE statement. 229# 230# Test that "INDEXED BY" can be used in a DELETE statement. 231# 232do_execsql_test indexedby-7.1 { 233 EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5 234} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} 235do_execsql_test indexedby-7.2 { 236 EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5 237} {0 0 0 {SCAN TABLE t1}} 238do_execsql_test indexedby-7.3 { 239 EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 240} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} 241do_execsql_test indexedby-7.4 { 242 EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10 243} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} 244do_execsql_test indexedby-7.5 { 245 EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10 246} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} 247do_test indexedby-7.6 { 248 catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5} 249} {1 {no query solution}} 250 251# Test that "INDEXED BY" can be used in an UPDATE statement. 252# 253do_execsql_test indexedby-8.1 { 254 EXPLAIN QUERY PLAN UPDATE t1 SET rowid=rowid+1 WHERE a = 5 255} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}} 256do_execsql_test indexedby-8.2 { 257 EXPLAIN QUERY PLAN UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5 258} {0 0 0 {SCAN TABLE t1}} 259do_execsql_test indexedby-8.3 { 260 EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 261} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}} 262do_execsql_test indexedby-8.4 { 263 EXPLAIN QUERY PLAN 264 UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10 265} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}} 266do_execsql_test indexedby-8.5 { 267 EXPLAIN QUERY PLAN 268 UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10 269} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}} 270do_test indexedby-8.6 { 271 catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5} 272} {1 {no query solution}} 273 274# Test that bug #3560 is fixed. 275# 276do_test indexedby-9.1 { 277 execsql { 278 CREATE TABLE maintable( id integer); 279 CREATE TABLE joinme(id_int integer, id_text text); 280 CREATE INDEX joinme_id_text_idx on joinme(id_text); 281 CREATE INDEX joinme_id_int_idx on joinme(id_int); 282 } 283} {} 284do_test indexedby-9.2 { 285 catchsql { 286 select * from maintable as m inner join 287 joinme as j indexed by joinme_id_text_idx 288 on ( m.id = j.id_int) 289 } 290} {1 {no query solution}} 291do_test indexedby-9.3 { 292 catchsql { select * from maintable, joinme INDEXED by joinme_id_text_idx } 293} {1 {no query solution}} 294 295# Make sure we can still create tables, indices, and columns whose name 296# is "indexed". 297# 298do_test indexedby-10.1 { 299 execsql { 300 CREATE TABLE indexed(x,y); 301 INSERT INTO indexed VALUES(1,2); 302 SELECT * FROM indexed; 303 } 304} {1 2} 305do_test indexedby-10.2 { 306 execsql { 307 CREATE INDEX i10 ON indexed(x); 308 SELECT * FROM indexed indexed by i10 where x>0; 309 } 310} {1 2} 311do_test indexedby-10.3 { 312 execsql { 313 DROP TABLE indexed; 314 CREATE TABLE t10(indexed INTEGER); 315 INSERT INTO t10 VALUES(1); 316 CREATE INDEX indexed ON t10(indexed); 317 SELECT * FROM t10 indexed by indexed WHERE indexed>0 318 } 319} {1} 320 321#------------------------------------------------------------------------- 322# Ensure that the rowid at the end of each index entry may be used 323# for equality constraints in the same way as other indexed fields. 324# 325do_execsql_test 11.1 { 326 CREATE TABLE x1(a, b TEXT); 327 CREATE INDEX x1i ON x1(a, b); 328 INSERT INTO x1 VALUES(1, 1); 329 INSERT INTO x1 VALUES(1, 1); 330 INSERT INTO x1 VALUES(1, 1); 331 INSERT INTO x1 VALUES(1, 1); 332} 333do_execsql_test 11.2 { 334 SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid=3; 335} {1 1 3} 336do_execsql_test 11.3 { 337 SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3'; 338} {1 1 3} 339do_execsql_test 11.4 { 340 SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3.0'; 341} {1 1 3} 342do_eqp_test 11.5 { 343 SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3.0'; 344} {0 0 0 {SEARCH TABLE x1 USING COVERING INDEX x1i (a=? AND b=? AND rowid=?)}} 345 346do_execsql_test 11.6 { 347 CREATE TABLE x2(c INTEGER PRIMARY KEY, a, b TEXT); 348 CREATE INDEX x2i ON x2(a, b); 349 INSERT INTO x2 VALUES(1, 1, 1); 350 INSERT INTO x2 VALUES(2, 1, 1); 351 INSERT INTO x2 VALUES(3, 1, 1); 352 INSERT INTO x2 VALUES(4, 1, 1); 353} 354do_execsql_test 11.7 { 355 SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c=3; 356} {1 1 3} 357do_execsql_test 11.8 { 358 SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3'; 359} {1 1 3} 360do_execsql_test 11.9 { 361 SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3.0'; 362} {1 1 3} 363do_eqp_test 11.10 { 364 SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3.0'; 365} {0 0 0 {SEARCH TABLE x2 USING COVERING INDEX x2i (a=? AND b=? AND rowid=?)}} 366 367finish_test 368