sl@0: # 2004 November 10 sl@0: # sl@0: # The author disclaims copyright to this source code. In place of sl@0: # a legal notice, here is a blessing: sl@0: # sl@0: # May you do good and not evil. sl@0: # May you find forgiveness for yourself and forgive others. sl@0: # May you share freely, never taking more than you give. sl@0: # sl@0: #************************************************************************* sl@0: # This file implements regression tests for SQLite library. The sl@0: # focus of this script is testing the ALTER TABLE statement. sl@0: # sl@0: # $Id: alter.test,v 1.30 2008/05/09 14:17:52 drh Exp $ sl@0: # sl@0: sl@0: set testdir [file dirname $argv0] sl@0: source $testdir/tester.tcl sl@0: sl@0: # If SQLITE_OMIT_ALTERTABLE is defined, omit this file. sl@0: ifcapable !altertable { sl@0: finish_test sl@0: return sl@0: } sl@0: sl@0: #---------------------------------------------------------------------- sl@0: # Test organization: sl@0: # sl@0: # alter-1.1.* - alter-1.7.*: Basic tests of ALTER TABLE, including tables sl@0: # with implicit and explicit indices. These tests came from an earlier sl@0: # fork of SQLite that also supported ALTER TABLE. sl@0: # alter-1.8.*: Tests for ALTER TABLE when the table resides in an sl@0: # attached database. sl@0: # alter-1.9.*: Tests for ALTER TABLE when their is whitespace between the sl@0: # table name and left parenthesis token. i.e: sl@0: # "CREATE TABLE abc (a, b, c);" sl@0: # alter-2.*: Test error conditions and messages. sl@0: # alter-3.*: Test ALTER TABLE on tables that have TRIGGERs attached to them. sl@0: # alter-4.*: Test ALTER TABLE on tables that have AUTOINCREMENT fields. sl@0: # ... sl@0: # alter-12.*: Test ALTER TABLE on views. sl@0: # sl@0: sl@0: # Create some tables to rename. Be sure to include some TEMP tables sl@0: # and some tables with odd names. sl@0: # sl@0: do_test alter-1.1 { sl@0: ifcapable tempdb { sl@0: set ::temp TEMP sl@0: } else { sl@0: set ::temp {} sl@0: } sl@0: execsql [subst -nocommands { sl@0: CREATE TABLE t1(a,b); sl@0: INSERT INTO t1 VALUES(1,2); sl@0: CREATE TABLE [t1'x1](c UNIQUE, b PRIMARY KEY); sl@0: INSERT INTO [t1'x1] VALUES(3,4); sl@0: CREATE INDEX t1i1 ON T1(B); sl@0: CREATE INDEX t1i2 ON t1(a,b); sl@0: CREATE INDEX i3 ON [t1'x1](b,c); sl@0: CREATE $::temp TABLE "temp table"(e,f,g UNIQUE); sl@0: CREATE INDEX i2 ON [temp table](f); sl@0: INSERT INTO [temp table] VALUES(5,6,7); sl@0: }] sl@0: execsql { sl@0: SELECT 't1', * FROM t1; sl@0: SELECT 't1''x1', * FROM "t1'x1"; sl@0: SELECT * FROM [temp table]; sl@0: } sl@0: } {t1 1 2 t1'x1 3 4 5 6 7} sl@0: do_test alter-1.2 { sl@0: execsql [subst { sl@0: CREATE $::temp TABLE objlist(type, name, tbl_name); sl@0: INSERT INTO objlist SELECT type, name, tbl_name sl@0: FROM sqlite_master WHERE NAME!='objlist'; sl@0: }] sl@0: ifcapable tempdb { sl@0: execsql { sl@0: INSERT INTO objlist SELECT type, name, tbl_name sl@0: FROM sqlite_temp_master WHERE NAME!='objlist'; sl@0: } sl@0: } sl@0: sl@0: execsql { sl@0: SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name; sl@0: } sl@0: } [list \ sl@0: table t1 t1 \ sl@0: index t1i1 t1 \ sl@0: index t1i2 t1 \ sl@0: table t1'x1 t1'x1 \ sl@0: index i3 t1'x1 \ sl@0: index {sqlite_autoindex_t1'x1_1} t1'x1 \ sl@0: index {sqlite_autoindex_t1'x1_2} t1'x1 \ sl@0: table {temp table} {temp table} \ sl@0: index i2 {temp table} \ sl@0: index {sqlite_autoindex_temp table_1} {temp table} \ sl@0: ] sl@0: sl@0: # Make some changes sl@0: # sl@0: integrity_check alter-1.3.0 sl@0: do_test alter-1.3 { sl@0: execsql { sl@0: ALTER TABLE [T1] RENAME to [-t1-]; sl@0: ALTER TABLE "t1'x1" RENAME TO T2; sl@0: ALTER TABLE [temp table] RENAME to TempTab; sl@0: } sl@0: } {} sl@0: integrity_check alter-1.3.1 sl@0: do_test alter-1.4 { sl@0: execsql { sl@0: SELECT 't1', * FROM [-t1-]; sl@0: SELECT 't2', * FROM t2; sl@0: SELECT * FROM temptab; sl@0: } sl@0: } {t1 1 2 t2 3 4 5 6 7} sl@0: do_test alter-1.5 { sl@0: execsql { sl@0: DELETE FROM objlist; sl@0: INSERT INTO objlist SELECT type, name, tbl_name sl@0: FROM sqlite_master WHERE NAME!='objlist'; sl@0: } sl@0: catchsql { sl@0: INSERT INTO objlist SELECT type, name, tbl_name sl@0: FROM sqlite_temp_master WHERE NAME!='objlist'; sl@0: } sl@0: execsql { sl@0: SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name; sl@0: } sl@0: } [list \ sl@0: table -t1- -t1- \ sl@0: index t1i1 -t1- \ sl@0: index t1i2 -t1- \ sl@0: table T2 T2 \ sl@0: index i3 T2 \ sl@0: index {sqlite_autoindex_T2_1} T2 \ sl@0: index {sqlite_autoindex_T2_2} T2 \ sl@0: table {TempTab} {TempTab} \ sl@0: index i2 {TempTab} \ sl@0: index {sqlite_autoindex_TempTab_1} {TempTab} \ sl@0: ] sl@0: sl@0: # Make sure the changes persist after restarting the database. sl@0: # (The TEMP table will not persist, of course.) sl@0: # sl@0: ifcapable tempdb { sl@0: do_test alter-1.6 { sl@0: db close sl@0: sqlite3 db test.db sl@0: set DB [sqlite3_connection_pointer db] sl@0: execsql { sl@0: CREATE TEMP TABLE objlist(type, name, tbl_name); sl@0: INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master; sl@0: INSERT INTO objlist sl@0: SELECT type, name, tbl_name FROM sqlite_temp_master sl@0: WHERE NAME!='objlist'; sl@0: SELECT type, name, tbl_name FROM objlist sl@0: ORDER BY tbl_name, type desc, name; sl@0: } sl@0: } [list \ sl@0: table -t1- -t1- \ sl@0: index t1i1 -t1- \ sl@0: index t1i2 -t1- \ sl@0: table T2 T2 \ sl@0: index i3 T2 \ sl@0: index {sqlite_autoindex_T2_1} T2 \ sl@0: index {sqlite_autoindex_T2_2} T2 \ sl@0: ] sl@0: } else { sl@0: execsql { sl@0: DROP TABLE TempTab; sl@0: } sl@0: } sl@0: sl@0: # Make sure the ALTER TABLE statements work with the sl@0: # non-callback API sl@0: # sl@0: do_test alter-1.7 { sl@0: stepsql $DB { sl@0: ALTER TABLE [-t1-] RENAME to [*t1*]; sl@0: ALTER TABLE T2 RENAME TO []; sl@0: } sl@0: execsql { sl@0: DELETE FROM objlist; sl@0: INSERT INTO objlist SELECT type, name, tbl_name sl@0: FROM sqlite_master WHERE NAME!='objlist'; sl@0: } sl@0: catchsql { sl@0: INSERT INTO objlist SELECT type, name, tbl_name sl@0: FROM sqlite_temp_master WHERE NAME!='objlist'; sl@0: } sl@0: execsql { sl@0: SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name; sl@0: } sl@0: } [list \ sl@0: table *t1* *t1* \ sl@0: index t1i1 *t1* \ sl@0: index t1i2 *t1* \ sl@0: table \ sl@0: index i3 \ sl@0: index {sqlite_autoindex__1} \ sl@0: index {sqlite_autoindex__2} \ sl@0: ] sl@0: sl@0: # Check that ALTER TABLE works on attached databases. sl@0: # sl@0: ifcapable attach { sl@0: do_test alter-1.8.1 { sl@0: file delete -force test2.db sl@0: file delete -force test2.db-journal sl@0: execsql { sl@0: ATTACH 'test2.db' AS aux; sl@0: } sl@0: } {} sl@0: do_test alter-1.8.2 { sl@0: execsql { sl@0: CREATE TABLE t4(a PRIMARY KEY, b, c); sl@0: CREATE TABLE aux.t4(a PRIMARY KEY, b, c); sl@0: CREATE INDEX i4 ON t4(b); sl@0: CREATE INDEX aux.i4 ON t4(b); sl@0: } sl@0: } {} sl@0: do_test alter-1.8.3 { sl@0: execsql { sl@0: INSERT INTO t4 VALUES('main', 'main', 'main'); sl@0: INSERT INTO aux.t4 VALUES('aux', 'aux', 'aux'); sl@0: SELECT * FROM t4 WHERE a = 'main'; sl@0: } sl@0: } {main main main} sl@0: do_test alter-1.8.4 { sl@0: execsql { sl@0: ALTER TABLE t4 RENAME TO t5; sl@0: SELECT * FROM t4 WHERE a = 'aux'; sl@0: } sl@0: } {aux aux aux} sl@0: do_test alter-1.8.5 { sl@0: execsql { sl@0: SELECT * FROM t5; sl@0: } sl@0: } {main main main} sl@0: do_test alter-1.8.6 { sl@0: execsql { sl@0: SELECT * FROM t5 WHERE b = 'main'; sl@0: } sl@0: } {main main main} sl@0: do_test alter-1.8.7 { sl@0: execsql { sl@0: ALTER TABLE aux.t4 RENAME TO t5; sl@0: SELECT * FROM aux.t5 WHERE b = 'aux'; sl@0: } sl@0: } {aux aux aux} sl@0: } sl@0: sl@0: do_test alter-1.9.1 { sl@0: execsql { sl@0: CREATE TABLE tbl1 (a, b, c); sl@0: INSERT INTO tbl1 VALUES(1, 2, 3); sl@0: } sl@0: } {} sl@0: do_test alter-1.9.2 { sl@0: execsql { sl@0: SELECT * FROM tbl1; sl@0: } sl@0: } {1 2 3} sl@0: do_test alter-1.9.3 { sl@0: execsql { sl@0: ALTER TABLE tbl1 RENAME TO tbl2; sl@0: SELECT * FROM tbl2; sl@0: } sl@0: } {1 2 3} sl@0: do_test alter-1.9.4 { sl@0: execsql { sl@0: DROP TABLE tbl2; sl@0: } sl@0: } {} sl@0: sl@0: # Test error messages sl@0: # sl@0: do_test alter-2.1 { sl@0: catchsql { sl@0: ALTER TABLE none RENAME TO hi; sl@0: } sl@0: } {1 {no such table: none}} sl@0: do_test alter-2.2 { sl@0: execsql { sl@0: CREATE TABLE t3(p,q,r); sl@0: } sl@0: catchsql { sl@0: ALTER TABLE [] RENAME TO t3; sl@0: } sl@0: } {1 {there is already another table or index with this name: t3}} sl@0: do_test alter-2.3 { sl@0: catchsql { sl@0: ALTER TABLE [] RENAME TO i3; sl@0: } sl@0: } {1 {there is already another table or index with this name: i3}} sl@0: do_test alter-2.4 { sl@0: catchsql { sl@0: ALTER TABLE SqLiTe_master RENAME TO master; sl@0: } sl@0: } {1 {table sqlite_master may not be altered}} sl@0: do_test alter-2.5 { sl@0: catchsql { sl@0: ALTER TABLE t3 RENAME TO sqlite_t3; sl@0: } sl@0: } {1 {object name reserved for internal use: sqlite_t3}} sl@0: do_test alter-2.6 { sl@0: catchsql { sl@0: ALTER TABLE t3 ADD COLUMN (ALTER TABLE t3 ADD COLUMN); sl@0: } sl@0: } {1 {near "(": syntax error}} sl@0: sl@0: # If this compilation does not include triggers, omit the alter-3.* tests. sl@0: ifcapable trigger { sl@0: sl@0: #----------------------------------------------------------------------- sl@0: # Tests alter-3.* test ALTER TABLE on tables that have triggers. sl@0: # sl@0: # alter-3.1.*: ALTER TABLE with triggers. sl@0: # alter-3.2.*: Test that the ON keyword cannot be used as a database, sl@0: # table or column name unquoted. This is done because part of the sl@0: # ALTER TABLE code (specifically the implementation of SQL function sl@0: # "sqlite_alter_trigger") will break in this case. sl@0: # alter-3.3.*: ALTER TABLE with TEMP triggers (todo). sl@0: # sl@0: sl@0: # An SQL user-function for triggers to fire, so that we know they sl@0: # are working. sl@0: proc trigfunc {args} { sl@0: set ::TRIGGER $args sl@0: } sl@0: db func trigfunc trigfunc sl@0: sl@0: do_test alter-3.1.0 { sl@0: execsql { sl@0: CREATE TABLE t6(a, b, c); sl@0: CREATE TRIGGER trig1 AFTER INSERT ON t6 BEGIN sl@0: SELECT trigfunc('trig1', new.a, new.b, new.c); sl@0: END; sl@0: } sl@0: } {} sl@0: do_test alter-3.1.1 { sl@0: execsql { sl@0: INSERT INTO t6 VALUES(1, 2, 3); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig1 1 2 3} sl@0: do_test alter-3.1.2 { sl@0: execsql { sl@0: ALTER TABLE t6 RENAME TO t7; sl@0: INSERT INTO t7 VALUES(4, 5, 6); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig1 4 5 6} sl@0: do_test alter-3.1.3 { sl@0: execsql { sl@0: DROP TRIGGER trig1; sl@0: } sl@0: } {} sl@0: do_test alter-3.1.4 { sl@0: execsql { sl@0: CREATE TRIGGER trig2 AFTER INSERT ON main.t7 BEGIN sl@0: SELECT trigfunc('trig2', new.a, new.b, new.c); sl@0: END; sl@0: INSERT INTO t7 VALUES(1, 2, 3); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig2 1 2 3} sl@0: do_test alter-3.1.5 { sl@0: execsql { sl@0: ALTER TABLE t7 RENAME TO t8; sl@0: INSERT INTO t8 VALUES(4, 5, 6); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig2 4 5 6} sl@0: do_test alter-3.1.6 { sl@0: execsql { sl@0: DROP TRIGGER trig2; sl@0: } sl@0: } {} sl@0: do_test alter-3.1.7 { sl@0: execsql { sl@0: CREATE TRIGGER trig3 AFTER INSERT ON main.'t8'BEGIN sl@0: SELECT trigfunc('trig3', new.a, new.b, new.c); sl@0: END; sl@0: INSERT INTO t8 VALUES(1, 2, 3); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig3 1 2 3} sl@0: do_test alter-3.1.8 { sl@0: execsql { sl@0: ALTER TABLE t8 RENAME TO t9; sl@0: INSERT INTO t9 VALUES(4, 5, 6); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig3 4 5 6} sl@0: sl@0: # Make sure "ON" cannot be used as a database, table or column name without sl@0: # quoting. Otherwise the sqlite_alter_trigger() function might not work. sl@0: file delete -force test3.db sl@0: file delete -force test3.db-journal sl@0: ifcapable attach { sl@0: do_test alter-3.2.1 { sl@0: catchsql { sl@0: ATTACH 'test3.db' AS ON; sl@0: } sl@0: } {1 {near "ON": syntax error}} sl@0: do_test alter-3.2.2 { sl@0: catchsql { sl@0: ATTACH 'test3.db' AS 'ON'; sl@0: } sl@0: } {0 {}} sl@0: do_test alter-3.2.3 { sl@0: catchsql { sl@0: CREATE TABLE ON.t1(a, b, c); sl@0: } sl@0: } {1 {near "ON": syntax error}} sl@0: do_test alter-3.2.4 { sl@0: catchsql { sl@0: CREATE TABLE 'ON'.t1(a, b, c); sl@0: } sl@0: } {0 {}} sl@0: do_test alter-3.2.4 { sl@0: catchsql { sl@0: CREATE TABLE 'ON'.ON(a, b, c); sl@0: } sl@0: } {1 {near "ON": syntax error}} sl@0: do_test alter-3.2.5 { sl@0: catchsql { sl@0: CREATE TABLE 'ON'.'ON'(a, b, c); sl@0: } sl@0: } {0 {}} sl@0: } sl@0: do_test alter-3.2.6 { sl@0: catchsql { sl@0: CREATE TABLE t10(a, ON, c); sl@0: } sl@0: } {1 {near "ON": syntax error}} sl@0: do_test alter-3.2.7 { sl@0: catchsql { sl@0: CREATE TABLE t10(a, 'ON', c); sl@0: } sl@0: } {0 {}} sl@0: do_test alter-3.2.8 { sl@0: catchsql { sl@0: CREATE TRIGGER trig4 AFTER INSERT ON ON BEGIN SELECT 1; END; sl@0: } sl@0: } {1 {near "ON": syntax error}} sl@0: ifcapable attach { sl@0: do_test alter-3.2.9 { sl@0: catchsql { sl@0: CREATE TRIGGER 'on'.trig4 AFTER INSERT ON 'ON' BEGIN SELECT 1; END; sl@0: } sl@0: } {0 {}} sl@0: } sl@0: do_test alter-3.2.10 { sl@0: execsql { sl@0: DROP TABLE t10; sl@0: } sl@0: } {} sl@0: sl@0: do_test alter-3.3.1 { sl@0: execsql [subst { sl@0: CREATE TABLE tbl1(a, b, c); sl@0: CREATE $::temp TRIGGER trig1 AFTER INSERT ON tbl1 BEGIN sl@0: SELECT trigfunc('trig1', new.a, new.b, new.c); sl@0: END; sl@0: }] sl@0: } {} sl@0: do_test alter-3.3.2 { sl@0: execsql { sl@0: INSERT INTO tbl1 VALUES('a', 'b', 'c'); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig1 a b c} sl@0: do_test alter-3.3.3 { sl@0: execsql { sl@0: ALTER TABLE tbl1 RENAME TO tbl2; sl@0: INSERT INTO tbl2 VALUES('d', 'e', 'f'); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig1 d e f} sl@0: do_test alter-3.3.4 { sl@0: execsql [subst { sl@0: CREATE $::temp TRIGGER trig2 AFTER UPDATE ON tbl2 BEGIN sl@0: SELECT trigfunc('trig2', new.a, new.b, new.c); sl@0: END; sl@0: }] sl@0: } {} sl@0: do_test alter-3.3.5 { sl@0: execsql { sl@0: ALTER TABLE tbl2 RENAME TO tbl3; sl@0: INSERT INTO tbl3 VALUES('g', 'h', 'i'); sl@0: } sl@0: set ::TRIGGER sl@0: } {trig1 g h i} sl@0: do_test alter-3.3.6 { sl@0: execsql { sl@0: UPDATE tbl3 SET a = 'G' where a = 'g'; sl@0: } sl@0: set ::TRIGGER sl@0: } {trig2 G h i} sl@0: do_test alter-3.3.7 { sl@0: execsql { sl@0: DROP TABLE tbl3; sl@0: } sl@0: } {} sl@0: ifcapable tempdb { sl@0: do_test alter-3.3.8 { sl@0: execsql { sl@0: SELECT * FROM sqlite_temp_master WHERE type = 'trigger'; sl@0: } sl@0: } {} sl@0: } sl@0: sl@0: } ;# ifcapable trigger sl@0: sl@0: # If the build does not include AUTOINCREMENT fields, omit alter-4.*. sl@0: ifcapable autoinc { sl@0: sl@0: do_test alter-4.1 { sl@0: execsql { sl@0: CREATE TABLE tbl1(a INTEGER PRIMARY KEY AUTOINCREMENT); sl@0: INSERT INTO tbl1 VALUES(10); sl@0: } sl@0: } {} sl@0: do_test alter-4.2 { sl@0: execsql { sl@0: INSERT INTO tbl1 VALUES(NULL); sl@0: SELECT a FROM tbl1; sl@0: } sl@0: } {10 11} sl@0: do_test alter-4.3 { sl@0: execsql { sl@0: ALTER TABLE tbl1 RENAME TO tbl2; sl@0: DELETE FROM tbl2; sl@0: INSERT INTO tbl2 VALUES(NULL); sl@0: SELECT a FROM tbl2; sl@0: } sl@0: } {12} sl@0: do_test alter-4.4 { sl@0: execsql { sl@0: DROP TABLE tbl2; sl@0: } sl@0: } {} sl@0: sl@0: } ;# ifcapable autoinc sl@0: sl@0: # Test that it is Ok to execute an ALTER TABLE immediately after sl@0: # opening a database. sl@0: do_test alter-5.1 { sl@0: execsql { sl@0: CREATE TABLE tbl1(a, b, c); sl@0: INSERT INTO tbl1 VALUES('x', 'y', 'z'); sl@0: } sl@0: } {} sl@0: do_test alter-5.2 { sl@0: sqlite3 db2 test.db sl@0: execsql { sl@0: ALTER TABLE tbl1 RENAME TO tbl2; sl@0: SELECT * FROM tbl2; sl@0: } db2 sl@0: } {x y z} sl@0: do_test alter-5.3 { sl@0: db2 close sl@0: } {} sl@0: sl@0: foreach tblname [execsql { sl@0: SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite%' sl@0: }] { sl@0: execsql "DROP TABLE \"$tblname\"" sl@0: } sl@0: sl@0: set ::tbl_name "abc\uABCDdef" sl@0: do_test alter-6.1 { sl@0: string length $::tbl_name sl@0: } {7} sl@0: do_test alter-6.2 { sl@0: execsql " sl@0: CREATE TABLE ${tbl_name}(a, b, c); sl@0: " sl@0: set ::oid [execsql {SELECT max(oid) FROM sqlite_master}] sl@0: execsql " sl@0: SELECT sql FROM sqlite_master WHERE oid = $::oid; sl@0: " sl@0: } "{CREATE TABLE ${::tbl_name}(a, b, c)}" sl@0: execsql " sl@0: SELECT * FROM ${::tbl_name} sl@0: " sl@0: set ::tbl_name2 "abcXdef" sl@0: do_test alter-6.3 { sl@0: execsql " sl@0: ALTER TABLE $::tbl_name RENAME TO $::tbl_name2 sl@0: " sl@0: execsql " sl@0: SELECT sql FROM sqlite_master WHERE oid = $::oid sl@0: " sl@0: } "{CREATE TABLE \"${::tbl_name2}\"(a, b, c)}" sl@0: do_test alter-6.4 { sl@0: execsql " sl@0: ALTER TABLE $::tbl_name2 RENAME TO $::tbl_name sl@0: " sl@0: execsql " sl@0: SELECT sql FROM sqlite_master WHERE oid = $::oid sl@0: " sl@0: } "{CREATE TABLE \"${::tbl_name}\"(a, b, c)}" sl@0: set ::col_name ghi\1234\jkl sl@0: do_test alter-6.5 { sl@0: execsql " sl@0: ALTER TABLE $::tbl_name ADD COLUMN $::col_name VARCHAR sl@0: " sl@0: execsql " sl@0: SELECT sql FROM sqlite_master WHERE oid = $::oid sl@0: " sl@0: } "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR)}" sl@0: set ::col_name2 B\3421\A sl@0: do_test alter-6.6 { sl@0: db close sl@0: sqlite3 db test.db sl@0: execsql " sl@0: ALTER TABLE $::tbl_name ADD COLUMN $::col_name2 sl@0: " sl@0: execsql " sl@0: SELECT sql FROM sqlite_master WHERE oid = $::oid sl@0: " sl@0: } "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR, $::col_name2)}" sl@0: do_test alter-6.7 { sl@0: execsql " sl@0: INSERT INTO ${::tbl_name} VALUES(1, 2, 3, 4, 5); sl@0: SELECT $::col_name, $::col_name2 FROM $::tbl_name; sl@0: " sl@0: } {4 5} sl@0: sl@0: # Ticket #1665: Make sure ALTER TABLE ADD COLUMN works on a table sl@0: # that includes a COLLATE clause. sl@0: # sl@0: do_test alter-7.1 { sl@0: execsql { sl@0: CREATE TABLE t1(a TEXT COLLATE BINARY); sl@0: ALTER TABLE t1 ADD COLUMN b INTEGER COLLATE NOCASE; sl@0: INSERT INTO t1 VALUES(1,'-2'); sl@0: INSERT INTO t1 VALUES(5.4e-08,'5.4e-08'); sl@0: SELECT typeof(a), a, typeof(b), b FROM t1; sl@0: } sl@0: } {text 1 integer -2 text 5.4e-08 real 5.4e-08} sl@0: sl@0: # Make sure that when a column is added by ALTER TABLE ADD COLUMN and has sl@0: # a default value that the default value is used by aggregate functions. sl@0: # sl@0: do_test alter-8.1 { sl@0: execsql { sl@0: CREATE TABLE t2(a INTEGER); sl@0: INSERT INTO t2 VALUES(1); sl@0: INSERT INTO t2 VALUES(1); sl@0: INSERT INTO t2 VALUES(2); sl@0: ALTER TABLE t2 ADD COLUMN b INTEGER DEFAULT 9; sl@0: SELECT sum(b) FROM t2; sl@0: } sl@0: } {27} sl@0: do_test alter-8.2 { sl@0: execsql { sl@0: SELECT a, sum(b) FROM t2 GROUP BY a; sl@0: } sl@0: } {1 18 2 9} sl@0: sl@0: #-------------------------------------------------------------------------- sl@0: # alter-9.X - Special test: Make sure the sqlite_rename_trigger() and sl@0: # rename_table() functions do not crash when handed bad input. sl@0: # sl@0: ifcapable trigger { sl@0: do_test alter-9.1 { sl@0: execsql {SELECT SQLITE_RENAME_TRIGGER(0,0)} sl@0: } {{}} sl@0: } sl@0: do_test alter-9.2 { sl@0: execsql { sl@0: SELECT SQLITE_RENAME_TABLE(0,0); sl@0: SELECT SQLITE_RENAME_TABLE(10,20); sl@0: SELECT SQLITE_RENAME_TABLE("foo", "foo"); sl@0: } sl@0: } {{} {} {}} sl@0: sl@0: #------------------------------------------------------------------------ sl@0: # alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters sl@0: # in the names. sl@0: # sl@0: do_test alter-10.1 { sl@0: execsql "CREATE TABLE xyz(x UNIQUE)" sl@0: execsql "ALTER TABLE xyz RENAME TO xyz\u1234abc" sl@0: execsql {SELECT name FROM sqlite_master WHERE name LIKE 'xyz%'} sl@0: } [list xyz\u1234abc] sl@0: do_test alter-10.2 { sl@0: execsql {SELECT name FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'} sl@0: } [list sqlite_autoindex_xyz\u1234abc_1] sl@0: do_test alter-10.3 { sl@0: execsql "ALTER TABLE xyz\u1234abc RENAME TO xyzabc" sl@0: execsql {SELECT name FROM sqlite_master WHERE name LIKE 'xyz%'} sl@0: } [list xyzabc] sl@0: do_test alter-10.4 { sl@0: execsql {SELECT name FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'} sl@0: } [list sqlite_autoindex_xyzabc_1] sl@0: sl@0: do_test alter-11.1 { sl@0: sqlite3_exec db {CREATE TABLE t11(%c6%c6)} sl@0: execsql { sl@0: ALTER TABLE t11 ADD COLUMN abc; sl@0: } sl@0: catchsql { sl@0: ALTER TABLE t11 ADD COLUMN abc; sl@0: } sl@0: } {1 {duplicate column name: abc}} sl@0: set isutf16 [regexp 16 [db one {PRAGMA encoding}]] sl@0: if {!$isutf16} { sl@0: do_test alter-11.2 { sl@0: execsql {INSERT INTO t11 VALUES(1,2)} sl@0: sqlite3_exec db {SELECT %c6%c6 AS xyz, abc FROM t11} sl@0: } {0 {xyz abc 1 2}} sl@0: } sl@0: do_test alter-11.3 { sl@0: sqlite3_exec db {CREATE TABLE t11b("%81%82%83" text)} sl@0: execsql { sl@0: ALTER TABLE t11b ADD COLUMN abc; sl@0: } sl@0: catchsql { sl@0: ALTER TABLE t11b ADD COLUMN abc; sl@0: } sl@0: } {1 {duplicate column name: abc}} sl@0: if {!$isutf16} { sl@0: do_test alter-11.4 { sl@0: execsql {INSERT INTO t11b VALUES(3,4)} sl@0: sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11b} sl@0: } {0 {xyz abc 3 4}} sl@0: do_test alter-11.5 { sl@0: sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11b} sl@0: } {0 {xyz abc 3 4}} sl@0: do_test alter-11.6 { sl@0: sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11b} sl@0: } {0 {xyz abc 3 4}} sl@0: } sl@0: do_test alter-11.7 { sl@0: sqlite3_exec db {CREATE TABLE t11c(%81%82%83 text)} sl@0: execsql { sl@0: ALTER TABLE t11c ADD COLUMN abc; sl@0: } sl@0: catchsql { sl@0: ALTER TABLE t11c ADD COLUMN abc; sl@0: } sl@0: } {1 {duplicate column name: abc}} sl@0: if {!$isutf16} { sl@0: do_test alter-11.8 { sl@0: execsql {INSERT INTO t11c VALUES(5,6)} sl@0: sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11c} sl@0: } {0 {xyz abc 5 6}} sl@0: do_test alter-11.9 { sl@0: sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11c} sl@0: } {0 {xyz abc 5 6}} sl@0: do_test alter-11.10 { sl@0: sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11c} sl@0: } {0 {xyz abc 5 6}} sl@0: } sl@0: sl@0: do_test alter-12.1 { sl@0: execsql { sl@0: CREATE TABLE t12(a, b, c); sl@0: CREATE VIEW v1 AS SELECT * FROM t12; sl@0: } sl@0: } {} sl@0: do_test alter-12.2 { sl@0: catchsql { sl@0: ALTER TABLE v1 RENAME TO v2; sl@0: } sl@0: } {1 {view v1 may not be altered}} sl@0: do_test alter-12.3 { sl@0: execsql { SELECT * FROM v1; } sl@0: } {} sl@0: do_test alter-12.4 { sl@0: db close sl@0: sqlite3 db test.db sl@0: execsql { SELECT * FROM v1; } sl@0: } {} sl@0: do_test alter-12.5 { sl@0: catchsql { sl@0: ALTER TABLE v1 ADD COLUMN new_column; sl@0: } sl@0: } {1 {Cannot add a column to a view}} sl@0: sl@0: # Ticket #3102: sl@0: # Verify that comments do not interfere with the table rename sl@0: # algorithm. sl@0: # sl@0: do_test alter-13.1 { sl@0: execsql { sl@0: CREATE TABLE /* hi */ t3102a(x); sl@0: CREATE TABLE t3102b -- comment sl@0: (y); sl@0: CREATE INDEX t3102c ON t3102a(x); sl@0: SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1; sl@0: } sl@0: } {t3102a t3102b t3102c} sl@0: do_test alter-13.2 { sl@0: execsql { sl@0: ALTER TABLE t3102a RENAME TO t3102a_rename; sl@0: SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1; sl@0: } sl@0: } {t3102a_rename t3102b t3102c} sl@0: do_test alter-13.3 { sl@0: execsql { sl@0: ALTER TABLE t3102b RENAME TO t3102b_rename; sl@0: SELECT name FROM sqlite_master WHERE name LIKE 't3102%' ORDER BY 1; sl@0: } sl@0: } {t3102a_rename t3102b_rename t3102c} sl@0: sl@0: finish_test