From: danielk1977 Date: Thu, 18 Nov 2004 15:44:29 +0000 (+0000) Subject: Handle triggers correctly in ALTER TABLE. (CVS 2111) X-Git-Tag: version-3.6.10~4039 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d641d646ff6e428980130f64487f72c3c0b8f3ed;p=thirdparty%2Fsqlite.git Handle triggers correctly in ALTER TABLE. (CVS 2111) FossilOrigin-Name: c61b7de107cea76b561d0d6cd90c752b62c5df95 --- diff --git a/manifest b/manifest index f8d6392253..782e88b5d1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Speed\simprovement\son\sthe\sLIKE\scommand.\s(CVS\s2110) -D 2004-11-18T13:49:26 +C Handle\striggers\scorrectly\sin\sALTER\sTABLE.\s(CVS\s2111) +D 2004-11-18T15:44:29 F Makefile.in e747bb5ba34ccbdd81f79dcf1b2b33c02817c21d F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -31,11 +31,11 @@ F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea F src/btree.c 49b09718cd988d1c7c981b03e94679bc10b5f711 F src/btree.h 861e40b759a195ba63819740e484390012cf81ab -F src/build.c a95eb1181247368b0ffe2eed121a43735976a964 +F src/build.c f295d8326f72252681b5376dbd0c33b11f8133cc F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f F src/delete.c be9d039b819f4a5d0fdfaeceace139ba189ef819 F src/expr.c 4ee3e47358c92a919062255b14057a7a8f641e01 -F src/func.c 1a5e23e206ecb81b73e4b97ef1a9654775b53f1e +F src/func.c 4f16eba1a7a0d0b3c617acf781607a03719490a2 F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 F src/insert.c 9524a6c3e86cbdbae3313f6a083bb9a3e7a2462b @@ -83,7 +83,7 @@ F src/vdbeaux.c c6da55e0096e141211f918837eca98e0be6400b4 F src/vdbemem.c 5876c8abf4374fef671f4fd8dc333ef3fc95a2f0 F src/where.c 4d28167e450255372b45abf1bc8cd5f0e9264d7b F test/all.test 929bfa932b55e75c96fe2203f7650ba451c1862c -F test/alter.test ea6b104fa83da6970b1ce61885827817bdaced3a +F test/alter.test 45d30922605898392d1619265b8a157a43ba010d F test/attach.test e305dd59a375e37c658c6d401f19f8a95880bf9a F test/attach2.test 399128a7b3b209a339a8dbf53ca2ed42eb982d1a F test/attach3.test 8a0309e284cf9aa1d7d6cc444989031881f7a21c @@ -259,7 +259,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c -P c93a9e18d2bf53690f577a901e0defd8a449d56a -R 41f63229808e62aecab58ec2f7d5cad7 -U drh -Z 1e586546872d49a1225e64744cd9ac4a +P 85d56beb7494ce63e70ab1ffc3797c2ee4c36749 +R 4f62fbea632b577ecd5796892a048619 +U danielk1977 +Z 1e8c5296540a3d2d7fecb9700f01ff06 diff --git a/manifest.uuid b/manifest.uuid index 18436f4358..ae5e63d160 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -85d56beb7494ce63e70ab1ffc3797c2ee4c36749 \ No newline at end of file +c61b7de107cea76b561d0d6cd90c752b62c5df95 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 1016145571..a3996ca193 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.279 2004/11/13 03:48:07 drh Exp $ +** $Id: build.c,v 1.280 2004/11/18 15:44:29 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -2976,15 +2976,21 @@ void sqlite3AlterRenameTable( /* Modify the sqlite_master table to use the new table name. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET " +#ifdef SQLITE_OMIT_TRIGGER "sql = sqlite_alter_table(sql, %Q), " +#else + "sql = CASE " + "WHEN type = 'trigger' THEN sqlite_alter_trigger(sql, %Q)" + "ELSE sqlite_alter_table(sql, %Q) END, " +#endif "tbl_name = %Q, " "name = CASE " "WHEN type='table' THEN %Q " "WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN " "'sqlite_autoindex_' || %Q || substr(name, %d+18,10) " "ELSE name END " - "WHERE tbl_name=%Q AND type IN ('table', 'index');", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName, zName, zName, + "WHERE tbl_name=%Q AND type IN ('table', 'index', 'trigger');", + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName, zName, zName, zName, zName, strlen(pTab->zName), pTab->zName ); @@ -2992,6 +2998,12 @@ void sqlite3AlterRenameTable( ** renamed and load the new versions from the database. */ if( pParse->nErr==0 ){ +#ifndef SQLITE_OMIT_TRIGGER + Trigger *pTrig; + for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ + sqlite3VdbeOp3(v, OP_DropTrigger, iDb, 0, pTrig->name, 0); + } +#endif sqlite3VdbeOp3(v, OP_DropTable, iDb, 0, pTab->zName, 0); zWhere = sqlite3MPrintf("tbl_name=%Q", zName); sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0, zWhere, P3_DYNAMIC); diff --git a/src/func.c b/src/func.c index 287e47eb26..c6ec755ba3 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: func.c,v 1.89 2004/11/18 13:49:26 drh Exp $ +** $Id: func.c,v 1.90 2004/11/18 15:44:29 danielk1977 Exp $ */ #include #include @@ -576,6 +576,78 @@ static void altertableFunc( } #endif +#ifndef SQLITE_OMIT_ALTERTABLE +#ifndef SQLITE_OMIT_TRIGGER +/* This function is used by SQL generated to implement the ALTER TABLE +** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER +** statement. The second is a table name. The table name in the CREATE +** TRIGGER statement is replaced with the second argument and the result +** returned. This is analagous to altertableFunc() above, except for CREATE +** TRIGGER, not CREATE INDEX and CREATE TABLE. +*/ +static void altertriggerFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + unsigned char const *zSql = sqlite3_value_text(argv[0]); + unsigned char const *zTableName = sqlite3_value_text(argv[1]); + + int token; + Token tname; + int dist = 3; + char const *zCsr = zSql; + int len = 0; + char *zRet; + + /* The principle used to locate the table name in the CREATE TRIGGER + ** statement is that the table name is the first token that is immediatedly + ** preceded by either TK_ON or TK_DOT and immediatedly followed by one + ** of TK_WHEN, TK_BEGIN or TK_FOR. + */ + assert( argc==2 ); + if( zSql ){ + do { + /* Store the token that zCsr points to in tname. */ + tname.z = zCsr; + tname.n = len; + + /* Advance zCsr to the next token. Store that token type in 'token', + ** and it's length in 'len' (to be used next iteration of this loop). + */ + do { + zCsr += len; + len = sqlite3GetToken(zCsr, &token); + }while( token==TK_SPACE ); + assert( len>0 ); + + /* Variable 'dist' stores the number of tokens read since the most + ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN + ** token is read and 'dist' equals 2, the condition stated above + ** to be met. + ** + ** Note that ON cannot be a database, table or column name, so + ** there is no need to worry about syntax like + ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc. + */ + dist++; + if( token==TK_DOT || token==TK_ON ){ + dist = 0; + } + } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) ); + + /* Variable tname now contains the token that is the old table-name + ** in the CREATE TRIGGER statement. + */ + zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql, + zTableName, tname.z+tname.n); + sqlite3_result_text(context, zRet, -1, SQLITE_TRANSIENT); + sqliteFree(zRet); + } +} +#endif /* !SQLITE_OMIT_TRIGGER */ +#endif /* !SQLITE_OMIT_ALTERTABLE */ + /* ** EXPERIMENTAL - This is not an official function. The interface may ** change. This function may disappear. Do not write code that depends @@ -1028,6 +1100,9 @@ void sqlite3RegisterBuiltinFunctions(sqlite3 *db){ { "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes }, #ifndef SQLITE_OMIT_ALTERTABLE { "sqlite_alter_table", 2, 0, SQLITE_UTF8, 0, altertableFunc}, +#ifndef SQLITE_OMIT_TRIGGER + { "sqlite_alter_trigger", 2, 0, SQLITE_UTF8, 0, altertriggerFunc}, +#endif #endif #ifdef SQLITE_SOUNDEX { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc}, diff --git a/test/alter.test b/test/alter.test index a107bd038b..fdf1ba5429 100644 --- a/test/alter.test +++ b/test/alter.test @@ -8,7 +8,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is testing the ALTER TABLE statement. # -# $Id: alter.test,v 1.2 2004/11/12 15:53:37 danielk1977 Exp $ +# $Id: alter.test,v 1.3 2004/11/18 15:44:30 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -217,5 +217,137 @@ do_test alter-2.3 { } } {1 {there is already another table or index with this name: i3}} +# If this compilation does not include triggers, omit the remainder +# of this file. +ifcapable !trigger { + finish_test + return +} + +# 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); + 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. +file delete -force test3.db +file delete -force test3.db-journal +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}} +do_test alter-3.2.9 { + catchsql { + CREATE TRIGGER 'on'.trig4 AFTER INSERT ON 'ON' BEGIN SELECT 1; END; + } +} {0 {}} finish_test +