summaryrefslogtreecommitdiffstats
path: root/test/alter.test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:28:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:28:19 +0000
commit18657a960e125336f704ea058e25c27bd3900dcb (patch)
tree17b438b680ed45a996d7b59951e6aa34023783f2 /test/alter.test
parentInitial commit. (diff)
downloadsqlite3-upstream.tar.xz
sqlite3-upstream.zip
Adding upstream version 3.40.1.upstream/3.40.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/alter.test')
-rw-r--r--test/alter.test938
1 files changed, 938 insertions, 0 deletions
diff --git a/test/alter.test b/test/alter.test
new file mode 100644
index 0000000..0088858
--- /dev/null
+++ b/test/alter.test
@@ -0,0 +1,938 @@
+# 2004 November 10
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing the ALTER TABLE statement.
+#
+# $Id: alter.test,v 1.32 2009/03/24 15:08:10 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
+ifcapable !altertable {
+ finish_test
+ return
+}
+
+#----------------------------------------------------------------------
+# Test organization:
+#
+# alter-1.1.* - alter-1.7.*: Basic tests of ALTER TABLE, including tables
+# with implicit and explicit indices. These tests came from an earlier
+# fork of SQLite that also supported ALTER TABLE.
+# alter-1.8.*: Tests for ALTER TABLE when the table resides in an
+# attached database.
+# alter-1.9.*: Tests for ALTER TABLE when their is whitespace between the
+# table name and left parenthesis token. i.e:
+# "CREATE TABLE abc (a, b, c);"
+# alter-2.*: Test error conditions and messages.
+# alter-3.*: Test ALTER TABLE on tables that have TRIGGERs attached to them.
+# alter-4.*: Test ALTER TABLE on tables that have AUTOINCREMENT fields.
+# ...
+# alter-12.*: Test ALTER TABLE on views.
+#
+
+# Create some tables to rename. Be sure to include some TEMP tables
+# and some tables with odd names.
+#
+do_test alter-1.1 {
+ ifcapable tempdb {
+ set ::temp TEMP
+ } else {
+ set ::temp {}
+ }
+ execsql [subst -nocommands {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE TABLE [t1'x1](c UNIQUE, b PRIMARY KEY);
+ INSERT INTO [t1'x1] VALUES(3,4);
+ CREATE INDEX t1i1 ON T1(B);
+ CREATE INDEX t1i2 ON t1(a,b);
+ CREATE INDEX i3 ON [t1'x1](b,c);
+ CREATE $::temp TABLE "temp table"(e,f,g UNIQUE);
+ CREATE INDEX i2 ON [temp table](f);
+ INSERT INTO [temp table] VALUES(5,6,7);
+ }]
+ execsql {
+ SELECT 't1', * FROM t1;
+ SELECT 't1''x1', * FROM "t1'x1";
+ SELECT * FROM [temp table];
+ }
+} {t1 1 2 t1'x1 3 4 5 6 7}
+do_test alter-1.2 {
+ execsql [subst {
+ CREATE $::temp TABLE objlist(type, name, tbl_name);
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_master WHERE NAME!='objlist';
+ }]
+ ifcapable tempdb {
+ execsql {
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM temp.sqlite_master WHERE NAME!='objlist';
+ }
+ }
+
+ execsql {
+ SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
+ }
+} [list \
+ table t1 t1 \
+ index t1i1 t1 \
+ index t1i2 t1 \
+ table t1'x1 t1'x1 \
+ index i3 t1'x1 \
+ index {sqlite_autoindex_t1'x1_1} t1'x1 \
+ index {sqlite_autoindex_t1'x1_2} t1'x1 \
+ table {temp table} {temp table} \
+ index i2 {temp table} \
+ index {sqlite_autoindex_temp table_1} {temp table} \
+ ]
+
+# Make some changes
+#
+integrity_check alter-1.3.0
+do_test alter-1.3 {
+ execsql {
+ ALTER TABLE [T1] RENAME to [-t1-];
+ ALTER TABLE "t1'x1" RENAME TO T2;
+ ALTER TABLE [temp table] RENAME to TempTab;
+ }
+} {}
+integrity_check alter-1.3.1
+do_test alter-1.4 {
+ execsql {
+ SELECT 't1', * FROM [-t1-];
+ SELECT 't2', * FROM t2;
+ SELECT * FROM temptab;
+ }
+} {t1 1 2 t2 3 4 5 6 7}
+do_test alter-1.5 {
+ execsql {
+ DELETE FROM objlist;
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_master WHERE NAME!='objlist';
+ }
+ catchsql {
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_temp_master WHERE NAME!='objlist';
+ }
+ execsql {
+ SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
+ }
+} [list \
+ table -t1- -t1- \
+ index t1i1 -t1- \
+ index t1i2 -t1- \
+ table T2 T2 \
+ index i3 T2 \
+ index {sqlite_autoindex_T2_1} T2 \
+ index {sqlite_autoindex_T2_2} T2 \
+ table {TempTab} {TempTab} \
+ index i2 {TempTab} \
+ index {sqlite_autoindex_TempTab_1} {TempTab} \
+ ]
+
+# Make sure the changes persist after restarting the database.
+# (The TEMP table will not persist, of course.)
+#
+ifcapable tempdb {
+ do_test alter-1.6 {
+ db close
+ sqlite3 db test.db
+ set DB [sqlite3_connection_pointer db]
+ execsql {
+ CREATE TEMP TABLE objlist(type, name, tbl_name);
+ INSERT INTO objlist SELECT type, name, tbl_name FROM sqlite_master;
+ INSERT INTO objlist
+ SELECT type, name, tbl_name FROM temp.sqlite_master
+ WHERE NAME!='objlist';
+ SELECT type, name, tbl_name FROM objlist
+ ORDER BY tbl_name, type desc, name;
+ }
+ } [list \
+ table -t1- -t1- \
+ index t1i1 -t1- \
+ index t1i2 -t1- \
+ table T2 T2 \
+ index i3 T2 \
+ index {sqlite_autoindex_T2_1} T2 \
+ index {sqlite_autoindex_T2_2} T2 \
+ ]
+} else {
+ execsql {
+ DROP TABLE TempTab;
+ }
+}
+
+# Create bogus application-defined functions for functions used
+# internally by ALTER TABLE, to ensure that ALTER TABLE falls back
+# to the built-in functions.
+#
+proc failing_app_func {args} {error "bad function"}
+do_test alter-1.7-prep {
+ db func substr failing_app_func
+ db func like failing_app_func
+ db func sqlite_rename_table failing_app_func
+ db func sqlite_rename_trigger failing_app_func
+ db func sqlite_rename_parent failing_app_func
+ catchsql {SELECT substr(name,1,3) FROM sqlite_master}
+} {1 {bad function}}
+
+# Make sure the ALTER TABLE statements work with the
+# non-callback API
+#
+do_test alter-1.7 {
+ stepsql $DB {
+ ALTER TABLE [-t1-] RENAME to [*t1*];
+ ALTER TABLE T2 RENAME TO [<t2>];
+ }
+ execsql {
+ DELETE FROM objlist;
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_master WHERE NAME!='objlist';
+ }
+ catchsql {
+ INSERT INTO objlist SELECT type, name, tbl_name
+ FROM sqlite_temp_master WHERE NAME!='objlist';
+ }
+ execsql {
+ SELECT type, name, tbl_name FROM objlist ORDER BY tbl_name, type desc, name;
+ }
+} [list \
+ table *t1* *t1* \
+ index t1i1 *t1* \
+ index t1i2 *t1* \
+ table <t2> <t2> \
+ index i3 <t2> \
+ index {sqlite_autoindex_<t2>_1} <t2> \
+ index {sqlite_autoindex_<t2>_2} <t2> \
+ ]
+
+# Check that ALTER TABLE works on attached databases.
+#
+ifcapable attach {
+ do_test alter-1.8.1 {
+ forcedelete test2.db
+ forcedelete test2.db-journal
+ execsql {
+ ATTACH 'test2.db' AS aux;
+ }
+ } {}
+ do_test alter-1.8.2 {
+ execsql {
+ CREATE TABLE t4(a PRIMARY KEY, b, c);
+ CREATE TABLE aux.t4(a PRIMARY KEY, b, c);
+ CREATE INDEX i4 ON t4(b);
+ CREATE INDEX aux.i4 ON t4(b);
+ }
+ } {}
+ do_test alter-1.8.3 {
+ execsql {
+ INSERT INTO t4 VALUES('main', 'main', 'main');
+ INSERT INTO aux.t4 VALUES('aux', 'aux', 'aux');
+ SELECT * FROM t4 WHERE a = 'main';
+ }
+ } {main main main}
+ do_test alter-1.8.4 {
+ execsql {
+ ALTER TABLE t4 RENAME TO t5;
+ SELECT * FROM t4 WHERE a = 'aux';
+ }
+ } {aux aux aux}
+ do_test alter-1.8.5 {
+ execsql {
+ SELECT * FROM t5;
+ }
+ } {main main main}
+ do_test alter-1.8.6 {
+ execsql {
+ SELECT * FROM t5 WHERE b = 'main';
+ }
+ } {main main main}
+ do_test alter-1.8.7 {
+ execsql {
+ ALTER TABLE aux.t4 RENAME TO t5;
+ SELECT * FROM aux.t5 WHERE b = 'aux';
+ }
+ } {aux aux aux}
+}
+
+do_test alter-1.9.1 {
+ execsql {
+ CREATE TABLE tbl1 (a, b, c);
+ INSERT INTO tbl1 VALUES(1, 2, 3);
+ }
+} {}
+do_test alter-1.9.2 {
+ execsql {
+ SELECT * FROM tbl1;
+ }
+} {1 2 3}
+do_test alter-1.9.3 {
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ SELECT * FROM tbl2;
+ }
+} {1 2 3}
+do_test alter-1.9.4 {
+ execsql {
+ DROP TABLE tbl2;
+ }
+} {}
+
+# Test error messages
+#
+do_test alter-2.1 {
+ catchsql {
+ ALTER TABLE none RENAME TO hi;
+ }
+} {1 {no such table: none}}
+do_test alter-2.2 {
+ execsql {
+ CREATE TABLE t3(p,q,r);
+ }
+ catchsql {
+ ALTER TABLE [<t2>] RENAME TO t3;
+ }
+} {1 {there is already another table or index with this name: t3}}
+do_test alter-2.3 {
+ catchsql {
+ ALTER TABLE [<t2>] RENAME TO i3;
+ }
+} {1 {there is already another table or index with this name: i3}}
+do_test alter-2.4 {
+ catchsql {
+ ALTER TABLE SqLiTe_master RENAME TO master;
+ }
+} {1 {table sqlite_master may not be altered}}
+do_test alter-2.5 {
+ catchsql {
+ ALTER TABLE t3 RENAME TO sqlite_t3;
+ }
+} {1 {object name reserved for internal use: sqlite_t3}}
+do_test alter-2.6 {
+ catchsql {
+ ALTER TABLE t3 ADD COLUMN (ALTER TABLE t3 ADD COLUMN);
+ }
+} {1 {near "(": syntax error}}
+
+# If this compilation does not include triggers, omit the alter-3.* tests.
+ifcapable trigger {
+
+#-----------------------------------------------------------------------
+# Tests alter-3.* test ALTER TABLE on tables that have triggers.
+#
+# alter-3.1.*: ALTER TABLE with triggers.
+# alter-3.2.*: Test that the ON keyword cannot be used as a database,
+# table or column name unquoted. This is done because part of the
+# ALTER TABLE code (specifically the implementation of SQL function
+# "sqlite_alter_trigger") will break in this case.
+# alter-3.3.*: ALTER TABLE with TEMP triggers (todo).
+#
+
+# An SQL user-function for triggers to fire, so that we know they
+# are working.
+proc trigfunc {args} {
+ set ::TRIGGER $args
+}
+db func trigfunc trigfunc
+
+do_test alter-3.1.0 {
+ execsql {
+ CREATE TABLE t6(a, b, c);
+ -- Different case for the table name in the trigger.
+ CREATE TRIGGER trig1 AFTER INSERT ON T6 BEGIN
+ SELECT trigfunc('trig1', new.a, new.b, new.c);
+ END;
+ }
+} {}
+do_test alter-3.1.1 {
+ execsql {
+ INSERT INTO t6 VALUES(1, 2, 3);
+ }
+ set ::TRIGGER
+} {trig1 1 2 3}
+do_test alter-3.1.2 {
+ execsql {
+ ALTER TABLE t6 RENAME TO t7;
+ INSERT INTO t7 VALUES(4, 5, 6);
+ }
+ set ::TRIGGER
+} {trig1 4 5 6}
+do_test alter-3.1.3 {
+ execsql {
+ DROP TRIGGER trig1;
+ }
+} {}
+do_test alter-3.1.4 {
+ execsql {
+ CREATE TRIGGER trig2 AFTER INSERT ON main.t7 BEGIN
+ SELECT trigfunc('trig2', new.a, new.b, new.c);
+ END;
+ INSERT INTO t7 VALUES(1, 2, 3);
+ }
+ set ::TRIGGER
+} {trig2 1 2 3}
+do_test alter-3.1.5 {
+ execsql {
+ ALTER TABLE t7 RENAME TO t8;
+ INSERT INTO t8 VALUES(4, 5, 6);
+ }
+ set ::TRIGGER
+} {trig2 4 5 6}
+do_test alter-3.1.6 {
+ execsql {
+ DROP TRIGGER trig2;
+ }
+} {}
+do_test alter-3.1.7 {
+ execsql {
+ CREATE TRIGGER trig3 AFTER INSERT ON main.'t8'BEGIN
+ SELECT trigfunc('trig3', new.a, new.b, new.c);
+ END;
+ INSERT INTO t8 VALUES(1, 2, 3);
+ }
+ set ::TRIGGER
+} {trig3 1 2 3}
+do_test alter-3.1.8 {
+ execsql {
+ ALTER TABLE t8 RENAME TO t9;
+ INSERT INTO t9 VALUES(4, 5, 6);
+ }
+ set ::TRIGGER
+} {trig3 4 5 6}
+
+# Make sure "ON" cannot be used as a database, table or column name without
+# quoting. Otherwise the sqlite_alter_trigger() function might not work.
+forcedelete test3.db
+forcedelete test3.db-journal
+ifcapable attach {
+ do_test alter-3.2.1 {
+ catchsql {
+ ATTACH 'test3.db' AS ON;
+ }
+ } {1 {near "ON": syntax error}}
+ do_test alter-3.2.2 {
+ catchsql {
+ ATTACH 'test3.db' AS 'ON';
+ }
+ } {0 {}}
+ do_test alter-3.2.3 {
+ catchsql {
+ CREATE TABLE ON.t1(a, b, c);
+ }
+ } {1 {near "ON": syntax error}}
+ do_test alter-3.2.4 {
+ catchsql {
+ CREATE TABLE 'ON'.t1(a, b, c);
+ }
+ } {0 {}}
+ do_test alter-3.2.4 {
+ catchsql {
+ CREATE TABLE 'ON'.ON(a, b, c);
+ }
+ } {1 {near "ON": syntax error}}
+ do_test alter-3.2.5 {
+ catchsql {
+ CREATE TABLE 'ON'.'ON'(a, b, c);
+ }
+ } {0 {}}
+}
+do_test alter-3.2.6 {
+ catchsql {
+ CREATE TABLE t10(a, ON, c);
+ }
+} {1 {near "ON": syntax error}}
+do_test alter-3.2.7 {
+ catchsql {
+ CREATE TABLE t10(a, 'ON', c);
+ }
+} {0 {}}
+do_test alter-3.2.8 {
+ catchsql {
+ CREATE TRIGGER trig4 AFTER INSERT ON ON BEGIN SELECT 1; END;
+ }
+} {1 {near "ON": syntax error}}
+ifcapable attach {
+ do_test alter-3.2.9 {
+ catchsql {
+ CREATE TRIGGER 'on'.trig4 AFTER INSERT ON 'ON' BEGIN SELECT 1; END;
+ }
+ } {0 {}}
+}
+do_test alter-3.2.10 {
+ execsql {
+ DROP TABLE t10;
+ }
+} {}
+
+do_test alter-3.3.1 {
+ execsql [subst {
+ CREATE TABLE tbl1(a, b, c);
+ CREATE $::temp TRIGGER trig1 AFTER INSERT ON tbl1 BEGIN
+ SELECT trigfunc('trig1', new.a, new.b, new.c);
+ END;
+ }]
+} {}
+do_test alter-3.3.2 {
+ execsql {
+ INSERT INTO tbl1 VALUES('a', 'b', 'c');
+ }
+ set ::TRIGGER
+} {trig1 a b c}
+do_test alter-3.3.3 {
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ INSERT INTO tbl2 VALUES('d', 'e', 'f');
+ }
+ set ::TRIGGER
+} {trig1 d e f}
+do_test alter-3.3.4 {
+ execsql [subst {
+ CREATE $::temp TRIGGER trig2 AFTER UPDATE ON tbl2 BEGIN
+ SELECT trigfunc('trig2', new.a, new.b, new.c);
+ END;
+ }]
+} {}
+do_test alter-3.3.5 {
+ execsql {
+ ALTER TABLE tbl2 RENAME TO tbl3;
+ INSERT INTO tbl3 VALUES('g', 'h', 'i');
+ }
+ set ::TRIGGER
+} {trig1 g h i}
+do_test alter-3.3.6 {
+ execsql {
+ UPDATE tbl3 SET a = 'G' where a = 'g';
+ }
+ set ::TRIGGER
+} {trig2 G h i}
+do_test alter-3.3.7 {
+ execsql {
+ DROP TABLE tbl3;
+ }
+} {}
+ifcapable tempdb {
+ do_test alter-3.3.8 {
+ execsql {
+ SELECT * FROM temp.sqlite_master WHERE type = 'trigger';
+ }
+ } {}
+}
+
+} ;# ifcapable trigger
+
+# If the build does not include AUTOINCREMENT fields, omit alter-4.*.
+ifcapable autoinc {
+
+do_test alter-4.1 {
+ execsql {
+ CREATE TABLE tbl1(a INTEGER PRIMARY KEY AUTOINCREMENT);
+ INSERT INTO tbl1 VALUES(10);
+ }
+} {}
+do_test alter-4.2 {
+ execsql {
+ INSERT INTO tbl1 VALUES(NULL);
+ SELECT a FROM tbl1;
+ }
+} {10 11}
+do_test alter-4.3 {
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ DELETE FROM tbl2;
+ INSERT INTO tbl2 VALUES(NULL);
+ SELECT a FROM tbl2;
+ }
+} {12}
+do_test alter-4.4 {
+ execsql {
+ DROP TABLE tbl2;
+ }
+} {}
+
+} ;# ifcapable autoinc
+
+# Test that it is Ok to execute an ALTER TABLE immediately after
+# opening a database.
+do_test alter-5.1 {
+ execsql {
+ CREATE TABLE tbl1(a, b, c);
+ INSERT INTO tbl1 VALUES('x', 'y', 'z');
+ }
+} {}
+do_test alter-5.2 {
+ sqlite3 db2 test.db
+ execsql {
+ ALTER TABLE tbl1 RENAME TO tbl2;
+ SELECT * FROM tbl2;
+ } db2
+} {x y z}
+do_test alter-5.3 {
+ db2 close
+} {}
+
+foreach tblname [execsql {
+ SELECT name FROM sqlite_master
+ WHERE type='table' AND name NOT GLOB 'sqlite*'
+}] {
+ execsql "DROP TABLE \"$tblname\""
+}
+
+set ::tbl_name "abc\uABCDdef"
+do_test alter-6.1 {
+ string length $::tbl_name
+} {7}
+do_test alter-6.2 {
+ execsql "
+ CREATE TABLE ${tbl_name}(a, b, c);
+ "
+ set ::oid [execsql {SELECT max(oid) FROM sqlite_master}]
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid;
+ "
+} "{CREATE TABLE ${::tbl_name}(a, b, c)}"
+execsql "
+ SELECT * FROM ${::tbl_name}
+"
+set ::tbl_name2 "abcXdef"
+do_test alter-6.3 {
+ execsql "
+ ALTER TABLE $::tbl_name RENAME TO $::tbl_name2
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name2}\"(a, b, c)}"
+do_test alter-6.4 {
+ execsql "
+ ALTER TABLE $::tbl_name2 RENAME TO $::tbl_name
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name}\"(a, b, c)}"
+set ::col_name ghi\1234\jkl
+do_test alter-6.5 {
+ execsql "
+ ALTER TABLE $::tbl_name ADD COLUMN $::col_name VARCHAR
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR)}"
+set ::col_name2 B\3421\A
+do_test alter-6.6 {
+ db close
+ sqlite3 db test.db
+ execsql "
+ ALTER TABLE $::tbl_name ADD COLUMN $::col_name2
+ "
+ execsql "
+ SELECT sql FROM sqlite_master WHERE oid = $::oid
+ "
+} "{CREATE TABLE \"${::tbl_name}\"(a, b, c, $::col_name VARCHAR, $::col_name2)}"
+do_test alter-6.7 {
+ execsql "
+ INSERT INTO ${::tbl_name} VALUES(1, 2, 3, 4, 5);
+ SELECT $::col_name, $::col_name2 FROM $::tbl_name;
+ "
+} {4 5}
+
+# Ticket #1665: Make sure ALTER TABLE ADD COLUMN works on a table
+# that includes a COLLATE clause.
+#
+do_realnum_test alter-7.1 {
+ execsql {
+ CREATE TABLE t1(a TEXT COLLATE BINARY);
+ ALTER TABLE t1 ADD COLUMN b INTEGER COLLATE NOCASE;
+ INSERT INTO t1 VALUES(1,'-2');
+ INSERT INTO t1 VALUES(5.4e-08,'5.4e-08');
+ SELECT typeof(a), a, typeof(b), b FROM t1;
+ }
+} {text 1 integer -2 text 5.4e-08 real 5.4e-08}
+
+# Make sure that when a column is added by ALTER TABLE ADD COLUMN and has
+# a default value that the default value is used by aggregate functions.
+#
+do_test alter-8.1 {
+ execsql {
+ CREATE TABLE t2(a INTEGER);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(1);
+ INSERT INTO t2 VALUES(2);
+ ALTER TABLE t2 ADD COLUMN b INTEGER DEFAULT 9;
+ SELECT sum(b) FROM t2;
+ }
+} {27}
+do_test alter-8.2 {
+ execsql {
+ SELECT a, sum(b) FROM t2 GROUP BY a;
+ }
+} {1 18 2 9}
+
+#--------------------------------------------------------------------------
+# alter-9.X - Special test: Make sure the sqlite_rename_column() and
+# rename_table() functions do not crash when handed bad input.
+#
+sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
+do_test alter-9.1 {
+ execsql {SELECT SQLITE_RENAME_COLUMN(0,0,0,0,0,0,0,0,0)}
+} {{}}
+foreach {tn sql} {
+ 1 { SELECT SQLITE_RENAME_TABLE(0,0,0,0,0,0,0) }
+ 2 { SELECT SQLITE_RENAME_TABLE(10,20,30,40,50,60,70) }
+ 3 { SELECT SQLITE_RENAME_TABLE('foo','foo','foo','foo','foo','foo','foo') }
+} {
+ do_test alter-9.2.$tn {
+ catch { execsql $sql }
+ } 1
+}
+sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS db
+
+# If the INTERNAL_FUNCTIONS test-control is disabled (which is the default),
+# then the sqlite_rename_table() SQL function is not accessible to ordinary SQL.
+#
+do_catchsql_test alter-9.3 {
+ SELECT sqlite_rename_table(0,0,0,0,0,0,0);
+} {1 {no such function: sqlite_rename_table}}
+
+#------------------------------------------------------------------------
+# alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters
+# in the names.
+#
+do_test alter-10.1 {
+ execsql "CREATE TABLE xyz(x UNIQUE)"
+ execsql "ALTER TABLE xyz RENAME TO xyz\u1234abc"
+ execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'}
+} [list xyz\u1234abc]
+do_test alter-10.2 {
+ execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'}
+} [list sqlite_autoindex_xyz\u1234abc_1]
+do_test alter-10.3 {
+ execsql "ALTER TABLE xyz\u1234abc RENAME TO xyzabc"
+ execsql {SELECT name FROM sqlite_master WHERE name GLOB 'xyz*'}
+} [list xyzabc]
+do_test alter-10.4 {
+ execsql {SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_autoindex*'}
+} [list sqlite_autoindex_xyzabc_1]
+
+do_test alter-11.1 {
+ sqlite3_exec db {CREATE TABLE t11(%c6%c6)}
+ execsql {
+ ALTER TABLE t11 ADD COLUMN abc;
+ }
+ catchsql {
+ ALTER TABLE t11 ADD COLUMN abc;
+ }
+} {1 {duplicate column name: abc}}
+set isutf16 [regexp 16 [db one {PRAGMA encoding}]]
+if {!$isutf16} {
+ do_test alter-11.2 {
+ execsql {INSERT INTO t11 VALUES(1,2)}
+ sqlite3_exec db {SELECT %c6%c6 AS xyz, abc FROM t11}
+ } {0 {xyz abc 1 2}}
+}
+do_test alter-11.3 {
+ sqlite3_exec db {CREATE TABLE t11b("%81%82%83" text)}
+ execsql {
+ ALTER TABLE t11b ADD COLUMN abc;
+ }
+ catchsql {
+ ALTER TABLE t11b ADD COLUMN abc;
+ }
+} {1 {duplicate column name: abc}}
+if {!$isutf16} {
+ do_test alter-11.4 {
+ execsql {INSERT INTO t11b VALUES(3,4)}
+ sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11b}
+ } {0 {xyz abc 3 4}}
+ do_test alter-11.5 {
+ sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11b}
+ } {0 {xyz abc 3 4}}
+ do_test alter-11.6 {
+ sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11b}
+ } {0 {xyz abc 3 4}}
+}
+do_test alter-11.7 {
+ sqlite3_exec db {CREATE TABLE t11c(%81%82%83 text)}
+ execsql {
+ ALTER TABLE t11c ADD COLUMN abc;
+ }
+ catchsql {
+ ALTER TABLE t11c ADD COLUMN abc;
+ }
+} {1 {duplicate column name: abc}}
+if {!$isutf16} {
+ do_test alter-11.8 {
+ execsql {INSERT INTO t11c VALUES(5,6)}
+ sqlite3_exec db {SELECT %81%82%83 AS xyz, abc FROM t11c}
+ } {0 {xyz abc 5 6}}
+ do_test alter-11.9 {
+ sqlite3_exec db {SELECT [%81%82%83] AS xyz, abc FROM t11c}
+ } {0 {xyz abc 5 6}}
+ do_test alter-11.10 {
+ sqlite3_exec db {SELECT "%81%82%83" AS xyz, abc FROM t11c}
+ } {0 {xyz abc 5 6}}
+}
+
+do_test alter-12.1 {
+ execsql {
+ CREATE TABLE t12(a, b, c);
+ CREATE VIEW v1 AS SELECT * FROM t12;
+ }
+} {}
+do_test alter-12.2 {
+ catchsql {
+ ALTER TABLE v1 RENAME TO v2;
+ }
+} {1 {view v1 may not be altered}}
+do_test alter-12.3 {
+ execsql { SELECT * FROM v1; }
+} {}
+do_test alter-12.4 {
+ db close
+ sqlite3 db test.db
+ execsql { SELECT * FROM v1; }
+} {}
+do_test alter-12.5 {
+ catchsql {
+ ALTER TABLE v1 ADD COLUMN new_column;
+ }
+} {1 {Cannot add a column to a view}}
+
+# Ticket #3102:
+# Verify that comments do not interfere with the table rename
+# algorithm.
+#
+do_test alter-13.1 {
+ execsql {
+ CREATE TABLE /* hi */ t3102a(x);
+ CREATE TABLE t3102b -- comment
+ (y);
+ CREATE INDEX t3102c ON t3102a(x);
+ SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
+ }
+} {t3102a t3102b t3102c}
+do_test alter-13.2 {
+ execsql {
+ ALTER TABLE t3102a RENAME TO t3102a_rename;
+ SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
+ }
+} {t3102a_rename t3102b t3102c}
+do_test alter-13.3 {
+ execsql {
+ ALTER TABLE t3102b RENAME TO t3102b_rename;
+ SELECT name FROM sqlite_master WHERE name GLOB 't3102*' ORDER BY 1;
+ }
+} {t3102a_rename t3102b_rename t3102c}
+
+# Ticket #3651
+do_test alter-14.1 {
+ catchsql {
+ CREATE TABLE t3651(a UNIQUE);
+ INSERT INTO t3651 VALUES(5);
+ ALTER TABLE t3651 ADD COLUMN b UNIQUE;
+ }
+} {1 {Cannot add a UNIQUE column}}
+do_test alter-14.2 {
+ catchsql {
+ ALTER TABLE t3651 ADD COLUMN b PRIMARY KEY;
+ }
+} {1 {Cannot add a PRIMARY KEY column}}
+
+
+#-------------------------------------------------------------------------
+# Test that it is not possible to use ALTER TABLE on any system table.
+#
+set system_table_list {1 sqlite_master}
+catchsql ANALYZE
+ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
+ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 }
+
+foreach {tn tbl} $system_table_list {
+ do_test alter-15.$tn.1 {
+ catchsql "ALTER TABLE $tbl RENAME TO xyz"
+ } [list 1 "table $tbl may not be altered"]
+
+ do_test alter-15.$tn.2 {
+ catchsql "ALTER TABLE $tbl ADD COLUMN xyz"
+ } [list 1 "table $tbl may not be altered"]
+}
+
+#------------------------------------------------------------------------
+# Verify that ALTER TABLE works on tables with the WITHOUT rowid option.
+#
+do_execsql_test alter-16.1 {
+ CREATE TABLE t16a(a TEXT, b REAL, c INT, PRIMARY KEY(a,b)) WITHOUT rowid;
+ INSERT INTO t16a VALUES('abc',1.25,99);
+ ALTER TABLE t16a ADD COLUMN d TEXT DEFAULT 'xyzzy';
+ INSERT INTO t16a VALUES('cba',5.5,98,'fizzle');
+ SELECT * FROM t16a ORDER BY a;
+} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
+do_execsql_test alter-16.2 {
+ ALTER TABLE t16a RENAME TO t16a_rn;
+ SELECT * FROM t16a_rn ORDER BY a;
+} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
+
+# 2018-09-16 ticket b41031ea2b5372378cb3d2d43cf9fe2a4a5c2510
+#
+ifcapable rtree {
+ db close
+ sqlite3 db :memory:
+ do_execsql_test alter-17.100 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ CREATE VIRTUAL TABLE t2 USING rtree(id,x0,x1);
+ INSERT INTO t1 VALUES(1,'apple'),(2,'fig'),(3,'pear');
+ INSERT INTO t2 VALUES(1,1.0,2.0),(2,2.0,3.0),(3,1.5,3.5);
+ CREATE TRIGGER r1 AFTER UPDATE ON t1 BEGIN
+ DELETE FROM t2 WHERE id = OLD.a;
+ END;
+ ALTER TABLE t1 RENAME TO t3;
+ UPDATE t3 SET b='peach' WHERE a=2;
+ SELECT * FROM t2 ORDER BY 1;
+ } {1 1.0 2.0 3 1.5 3.5}
+}
+
+# 2021-03-08 dbsqlfuzz 3f0a7245b69cd08617d7d7781ebaedb0fe765a93
+reset_db
+do_catchsql_test alter-18.1 {
+ CREATE TABLE t1(a,b,c);
+ CREATE TABLE log(a INTEGER PRIMARY KEY,b,c);
+ CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
+ INSERT INTO logx(a,b,c) VALUES(new.a,new.b,new.c)
+ ON CONFLICT(a) DO UPDATE SET c=excluded.c, b=new.b;
+ END;
+ ALTER TABLE log RENAME COLUMN a TO x;
+} {1 {error in trigger tr1: no such table: main.logx}}
+
+# 2021-10-13 dbsqlfuzz e89174cbfad2d904f06b5e24df0a22510b6a1c1e
+reset_db
+do_execsql_test alter-19.1 {
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(c);
+ CREATE TRIGGER r1 AFTER INSERT ON t2 BEGIN
+ UPDATE t2 SET (c)=(
+ EXISTS(SELECT 1 WHERE (WITH cte1(a) AS (SELECT 1 FROM t1 WHERE (SELECT 1 WHERE (WITH cte2(b) AS (VALUES(1))SELECT b FROM cte2)))SELECT a FROM cte1))
+ );
+ END;
+ ALTER TABLE t2 RENAME TO t3;
+} {}
+do_execsql_test alter-19.2 {
+ SELECT name FROM sqlite_schema WHERE sql LIKE '%t2%';
+} {}
+do_execsql_test alter-19.3 {
+ SELECT name FROM sqlite_schema WHERE sql LIKE '%t3%' ORDER BY name;
+} {r1 t3}
+
+
+finish_test