000001  # 2008-10-04
000002  #
000003  # The author disclaims copyright to this source code.  In place of
000004  # a legal notice, here is a blessing:
000005  #
000006  #    May you do good and not evil.
000007  #    May you find forgiveness for yourself and forgive others.
000008  #    May you share freely, never taking more than you give.
000009  #
000010  #***********************************************************************
000011  #
000012  
000013  set testdir [file dirname $argv0]
000014  source $testdir/tester.tcl
000015  set ::testprefix indexedby
000016  
000017  # Create a schema with some indexes.
000018  #
000019  do_test indexedby-1.1 {
000020    execsql {
000021      CREATE TABLE t1(a, b);
000022      CREATE INDEX i1 ON t1(a);
000023      CREATE INDEX i2 ON t1(b);
000024  
000025      CREATE TABLE t2(c, d);
000026      CREATE INDEX i3 ON t2(c);
000027      CREATE INDEX i4 ON t2(d);
000028  
000029      CREATE TABLE t3(e PRIMARY KEY, f);
000030  
000031      CREATE VIEW v1 AS SELECT * FROM t1;
000032    }
000033  } {}
000034  
000035  # Explain Query Plan
000036  #
000037  proc EQP {sql} {
000038    uplevel "execsql {EXPLAIN QUERY PLAN $sql}"
000039  }
000040  
000041  # These tests are to check that "EXPLAIN QUERY PLAN" is working as expected.
000042  #
000043  do_eqp_test indexedby-1.2 {
000044    select * from t1 WHERE a = 10; 
000045  } {SEARCH t1 USING INDEX i1 (a=?)}
000046  do_eqp_test indexedby-1.3 {
000047    select * from t1 ; 
000048  } {SCAN t1}
000049  do_eqp_test indexedby-1.4 {
000050    select * from t1, t2 WHERE c = 10; 
000051  } {
000052    QUERY PLAN
000053    |--SEARCH t2 USING INDEX i3 (c=?)
000054    `--SCAN t1
000055  }
000056  
000057  # Parser tests. Test that an INDEXED BY or NOT INDEX clause can be 
000058  # attached to a table in the FROM clause, but not to a sub-select or
000059  # SQL view. Also test that specifying an index that does not exist or
000060  # is attached to a different table is detected as an error.
000061  #
000062  # X-EVIDENCE-OF: R-07004-11522 -- syntax diagram qualified-table-name
000063  # 
000064  # EVIDENCE-OF: R-58230-57098 The "INDEXED BY index-name" phrase
000065  # specifies that the named index must be used in order to look up values
000066  # on the preceding table.
000067  #
000068  do_test indexedby-2.1 {
000069    execsql { SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two'}
000070  } {}
000071  do_test indexedby-2.1b {
000072    execsql { SELECT * FROM main.t1 NOT INDEXED WHERE a = 'one' AND b = 'two'}
000073  } {}
000074  do_test indexedby-2.2 {
000075    execsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'}
000076  } {}
000077  do_test indexedby-2.2b {
000078    execsql { SELECT * FROM main.t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'}
000079  } {}
000080  do_test indexedby-2.3 {
000081    execsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two'}
000082  } {}
000083  # EVIDENCE-OF: R-44699-55558 The INDEXED BY clause does not give the
000084  # optimizer hints about which index to use; it gives the optimizer a
000085  # requirement of which index to use.
000086  # EVIDENCE-OF: R-15800-25719 If index-name does not exist or cannot be
000087  # used for the query, then the preparation of the SQL statement fails.
000088  #
000089  do_test indexedby-2.4 {
000090    catchsql { SELECT * FROM t1 INDEXED BY i3 WHERE a = 'one' AND b = 'two'}
000091  } {1 {no such index: i3}}
000092  
000093  # EVIDENCE-OF: R-05301-32681 If the query optimizer is unable to use the
000094  # index specified by the INDEXED BY clause, then the query will fail
000095  # with an error.
000096  do_test indexedby-2.4.1 {
000097    catchsql { SELECT b FROM t1 INDEXED BY i1 WHERE b = 'two' }
000098  } {0 {}}
000099  
000100  do_test indexedby-2.5 {
000101    catchsql { SELECT * FROM t1 INDEXED BY i5 WHERE a = 'one' AND b = 'two'}
000102  } {1 {no such index: i5}}
000103  do_test indexedby-2.6 {
000104    catchsql { SELECT * FROM t1 INDEXED BY WHERE a = 'one' AND b = 'two'}
000105  } {1 {near "WHERE": syntax error}}
000106  do_test indexedby-2.7 {
000107    catchsql { SELECT * FROM v1 INDEXED BY i1 WHERE a = 'one' }
000108  } {1 {no such index: i1}}
000109  
000110  
000111  # Tests for single table cases.
000112  #
000113  # EVIDENCE-OF: R-37002-28871 The "NOT INDEXED" clause specifies that no
000114  # index shall be used when accessing the preceding table, including
000115  # implied indices create by UNIQUE and PRIMARY KEY constraints. However,
000116  # the rowid can still be used to look up entries even when "NOT INDEXED"
000117  # is specified.
000118  #
000119  do_eqp_test indexedby-3.1 {
000120    SELECT * FROM t1 WHERE a = 'one' AND b = 'two'
000121  } {/SEARCH t1 USING INDEX/}
000122  do_eqp_test indexedby-3.1.1 {
000123    SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two'
000124  } {SCAN t1}
000125  do_eqp_test indexedby-3.1.2 {
000126    SELECT * FROM t1 NOT INDEXED WHERE rowid=1
000127  } {/SEARCH t1 USING INTEGER PRIMARY KEY .rowid=/}
000128  
000129  
000130  do_eqp_test indexedby-3.2 {
000131    SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'
000132  } {SEARCH t1 USING INDEX i1 (a=?)}
000133  do_eqp_test indexedby-3.3 {
000134    SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two'
000135  } {SEARCH t1 USING INDEX i2 (b=?)}
000136  do_test indexedby-3.4 {
000137    catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' }
000138  } {0 {}}
000139  do_test indexedby-3.5 {
000140    catchsql { SELECT * FROM t1 INDEXED BY i2 ORDER BY a }
000141  } {0 {}}
000142  do_test indexedby-3.6 {
000143    catchsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' }
000144  } {0 {}}
000145  do_test indexedby-3.7 {
000146    catchsql { SELECT * FROM t1 INDEXED BY i1 ORDER BY a }
000147  } {0 {}}
000148  
000149  do_eqp_test indexedby-3.8 {
000150    SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e 
000151  } {SCAN t3 USING INDEX sqlite_autoindex_t3_1}
000152  do_eqp_test indexedby-3.9 {
000153    SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10 
000154  } {SEARCH t3 USING INDEX sqlite_autoindex_t3_1 (e=?)}
000155  do_test indexedby-3.10 {
000156    catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 }
000157  } {0 {}}
000158  do_test indexedby-3.11 {
000159    catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_2 WHERE f = 10 }
000160  } {1 {no such index: sqlite_autoindex_t3_2}}
000161  
000162  # Tests for multiple table cases.
000163  #
000164  do_eqp_test indexedby-4.1 {
000165    SELECT * FROM t1, t2 WHERE a = c 
000166  } {
000167    QUERY PLAN
000168    |--SCAN t1
000169    `--SEARCH t2 USING INDEX i3 (c=?)
000170  }
000171  do_eqp_test indexedby-4.2 {
000172    SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c 
000173  } {
000174    QUERY PLAN
000175    |--SCAN t1 USING INDEX i1
000176    `--SEARCH t2 USING INDEX i3 (c=?)
000177  }
000178  do_test indexedby-4.3 {
000179    catchsql {
000180      SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c
000181    }
000182  } {0 {}}
000183  do_test indexedby-4.4 {
000184    catchsql {
000185      SELECT * FROM t2 INDEXED BY i3, t1 INDEXED BY i1 WHERE a=c
000186    }
000187  } {0 {}}
000188  
000189  # Test embedding an INDEXED BY in a CREATE VIEW statement. This block
000190  # also tests that nothing bad happens if an index refered to by
000191  # a CREATE VIEW statement is dropped and recreated.
000192  #
000193  do_execsql_test indexedby-5.1 {
000194    CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5;
000195    EXPLAIN QUERY PLAN SELECT * FROM v2 
000196  } {/*SEARCH t1 USING INDEX i1 (a>?)*/}
000197  do_execsql_test indexedby-5.2 {
000198    EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10 
000199  } {/*SEARCH t1 USING INDEX i1 (a>?)*/}
000200  do_test indexedby-5.3 {
000201    execsql { DROP INDEX i1 }
000202    catchsql { SELECT * FROM v2 }
000203  } {1 {no such index: i1}}
000204  do_test indexedby-5.4 {
000205    # Recreate index i1 in such a way as it cannot be used by the view query.
000206    execsql { CREATE INDEX i1 ON t1(b) }
000207    catchsql { SELECT * FROM v2 }
000208  } {0 {}}
000209  do_test indexedby-5.5 {
000210    # Drop and recreate index i1 again. This time, create it so that it can
000211    # be used by the query.
000212    execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t1(a) }
000213    catchsql { SELECT * FROM v2 }
000214  } {0 {}}
000215  
000216  # Test that "NOT INDEXED" may use the rowid index, but not others.
000217  # 
000218  do_eqp_test indexedby-6.1 {
000219    SELECT * FROM t1 WHERE b = 10 ORDER BY rowid 
000220  } {SEARCH t1 USING INDEX i2 (b=?)}
000221  do_eqp_test indexedby-6.2 {
000222    SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid 
000223  } {SCAN t1}
000224  
000225  # EVIDENCE-OF: R-40297-14464 The INDEXED BY phrase forces the SQLite
000226  # query planner to use a particular named index on a DELETE, SELECT, or
000227  # UPDATE statement.
000228  #
000229  # Test that "INDEXED BY" can be used in a DELETE statement.
000230  # 
000231  do_eqp_test indexedby-7.1 {
000232    DELETE FROM t1 WHERE a = 5 
000233  } {SEARCH t1 USING INDEX i1 (a=?)}
000234  do_eqp_test indexedby-7.2 {
000235    DELETE FROM t1 NOT INDEXED WHERE a = 5 
000236  } {SCAN t1}
000237  do_eqp_test indexedby-7.3 {
000238    DELETE FROM t1 INDEXED BY i1 WHERE a = 5 
000239  } {SEARCH t1 USING INDEX i1 (a=?)}
000240  do_eqp_test indexedby-7.4 {
000241    DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10
000242  } {SEARCH t1 USING INDEX i1 (a=?)}
000243  do_eqp_test indexedby-7.5 {
000244    DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10
000245  } {SEARCH t1 USING INDEX i2 (b=?)}
000246  do_test indexedby-7.6 {
000247    catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5}
000248  } {0 {}}
000249  
000250  # Test that "INDEXED BY" can be used in an UPDATE statement.
000251  # 
000252  do_eqp_test indexedby-8.1 {
000253    UPDATE t1 SET rowid=rowid+1 WHERE a = 5 
000254  } {SEARCH t1 USING COVERING INDEX i1 (a=?)}
000255  do_eqp_test indexedby-8.2 {
000256    UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5 
000257  } {SCAN t1}
000258  do_eqp_test indexedby-8.3 {
000259    UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 
000260  } {SEARCH t1 USING COVERING INDEX i1 (a=?)}
000261  do_eqp_test indexedby-8.4 {
000262    UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10
000263  } {SEARCH t1 USING INDEX i1 (a=?)}
000264  do_eqp_test indexedby-8.5 {
000265    UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10
000266  } {SEARCH t1 USING INDEX i2 (b=?)}
000267  do_test indexedby-8.6 {
000268    catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5}
000269  } {0 {}}
000270  
000271  # Test that bug #3560 is fixed.
000272  #
000273  do_test indexedby-9.1 {
000274    execsql {
000275      CREATE TABLE maintable( id integer);
000276      CREATE TABLE joinme(id_int integer, id_text text);
000277      CREATE INDEX joinme_id_text_idx on joinme(id_text);
000278      CREATE INDEX joinme_id_int_idx on joinme(id_int);
000279    }
000280  } {}
000281  do_test indexedby-9.2 {
000282    catchsql {
000283      select * from maintable as m inner join
000284      joinme as j indexed by joinme_id_text_idx
000285      on ( m.id  = j.id_int)
000286    }
000287  } {0 {}}
000288  do_test indexedby-9.3 {
000289    catchsql { select * from maintable, joinme INDEXED by joinme_id_text_idx }
000290  } {0 {}}
000291  
000292  # Make sure we can still create tables, indices, and columns whose name
000293  # is "indexed".
000294  #
000295  do_test indexedby-10.1 {
000296    execsql {
000297      CREATE TABLE indexed(x,y);
000298      INSERT INTO indexed VALUES(1,2);
000299      SELECT * FROM indexed;
000300    }
000301  } {1 2}
000302  do_test indexedby-10.2 {
000303    execsql {
000304      CREATE INDEX i10 ON indexed(x);
000305      SELECT * FROM indexed indexed by i10 where x>0;
000306    }
000307  } {1 2}
000308  do_test indexedby-10.3 {
000309    execsql {
000310      DROP TABLE indexed;
000311      CREATE TABLE t10(indexed INTEGER);
000312      INSERT INTO t10 VALUES(1);
000313      CREATE INDEX indexed ON t10(indexed);
000314      SELECT * FROM t10 indexed by indexed WHERE indexed>0
000315    }
000316  } {1}
000317  
000318  #-------------------------------------------------------------------------
000319  # Ensure that the rowid at the end of each index entry may be used
000320  # for equality constraints in the same way as other indexed fields.
000321  #
000322  do_execsql_test 11.1 {
000323    CREATE TABLE x1(a, b TEXT);
000324    CREATE INDEX x1i ON x1(a, b);
000325    INSERT INTO x1 VALUES(1, 1);
000326    INSERT INTO x1 VALUES(1, 1);
000327    INSERT INTO x1 VALUES(1, 1);
000328    INSERT INTO x1 VALUES(1, 1);
000329  }
000330  do_execsql_test 11.2 {
000331    SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid=3;
000332  } {1 1 3}
000333  do_execsql_test 11.3 {
000334    SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3';
000335  } {1 1 3}
000336  do_execsql_test 11.4 {
000337    SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3.0';
000338  } {1 1 3}
000339  do_eqp_test 11.5 {
000340    SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3.0';
000341  } {SEARCH x1 USING COVERING INDEX x1i (a=? AND b=? AND rowid=?)}
000342  
000343  do_execsql_test 11.6 {
000344    CREATE TABLE x2(c INTEGER PRIMARY KEY, a, b TEXT);
000345    CREATE INDEX x2i ON x2(a, b);
000346    INSERT INTO x2 VALUES(1, 1, 1);
000347    INSERT INTO x2 VALUES(2, 1, 1);
000348    INSERT INTO x2 VALUES(3, 1, 1);
000349    INSERT INTO x2 VALUES(4, 1, 1);
000350  }
000351  do_execsql_test 11.7 {
000352    SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c=3;
000353  } {1 1 3}
000354  do_execsql_test 11.8 {
000355    SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3';
000356  } {1 1 3}
000357  do_execsql_test 11.9 {
000358    SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3.0';
000359  } {1 1 3}
000360  do_eqp_test 11.10 {
000361    SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3.0';
000362  } {SEARCH x2 USING COVERING INDEX x2i (a=? AND b=? AND rowid=?)}
000363  
000364  #-------------------------------------------------------------------------
000365  # Check INDEXED BY works (throws an exception) with partial indexes that 
000366  # cannot be used.
000367  do_execsql_test 12.1 {
000368    CREATE TABLE o1(x INTEGER PRIMARY KEY, y, z);
000369    CREATE INDEX p1 ON o1(z);
000370    CREATE INDEX p2 ON o1(y) WHERE z=1;
000371  }
000372  do_catchsql_test 12.2 {
000373    SELECT * FROM o1 INDEXED BY p2 ORDER BY 1;
000374  } {1 {no query solution}}
000375  do_execsql_test 12.3 {
000376    DROP INDEX p1;
000377    DROP INDEX p2;
000378    CREATE INDEX p2 ON o1(y) WHERE z=1;
000379    CREATE INDEX p1 ON o1(z);
000380  }
000381  do_catchsql_test 12.4 {
000382    SELECT * FROM o1 INDEXED BY p2 ORDER BY 1;
000383  } {1 {no query solution}}
000384  
000385  finish_test