diff --git a/manifest b/manifest index 782e88b5d1..b6087f7cb4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Handle\striggers\scorrectly\sin\sALTER\sTABLE.\s(CVS\s2111) -D 2004-11-18T15:44:29 +C Fix\sbugs\sin\sALTER\sTABLE\srelated\sto\s(a)\swhitespace\sin\stable\sdefn,\s(b)\stemp\striggers.\s(CVS\s2112) +D 2004-11-19T05:14:55 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 f295d8326f72252681b5376dbd0c33b11f8133cc +F src/build.c 467e940702206e950d810e765586a787ec06dd5b F src/date.c 65536e7ea04fdde6e0551264fca15966966e171f F src/delete.c be9d039b819f4a5d0fdfaeceace139ba189ef819 F src/expr.c 4ee3e47358c92a919062255b14057a7a8f641e01 -F src/func.c 4f16eba1a7a0d0b3c617acf781607a03719490a2 +F src/func.c b668e5ad043176049454c95a6a780367a0e8f6bb 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 45d30922605898392d1619265b8a157a43ba010d +F test/alter.test 627f1c24c80b842b02b94ce7416e9b2498a790eb 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 85d56beb7494ce63e70ab1ffc3797c2ee4c36749 -R 4f62fbea632b577ecd5796892a048619 +P c61b7de107cea76b561d0d6cd90c752b62c5df95 +R 2dccc389e14622418d8f9c23b5390305 U danielk1977 -Z 1e8c5296540a3d2d7fecb9700f01ff06 +Z dfa16d84296b817116f68efa4f87c5bd diff --git a/manifest.uuid b/manifest.uuid index ae5e63d160..83e2ddd389 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c61b7de107cea76b561d0d6cd90c752b62c5df95 \ No newline at end of file +1fd8e835a3656799c23f4ef6ea1311fecf5a15cb \ No newline at end of file diff --git a/src/build.c b/src/build.c index a3996ca193..4e035a734f 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.280 2004/11/18 15:44:29 danielk1977 Exp $ +** $Id: build.c,v 1.281 2004/11/19 05:14:55 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -2931,6 +2931,9 @@ void sqlite3AlterRenameTable( char *zWhere = 0; /* Where clause of schema elements to reparse */ sqlite3 *db = pParse->db; /* Database connection */ Vdbe *v; +#ifndef SQLITE_OMIT_TRIGGER + char *zTempTrig = 0; /* Where clause to locate temp triggers */ +#endif assert( pSrc->nSrc==1 ); @@ -2994,6 +2997,39 @@ void sqlite3AlterRenameTable( zName, strlen(pTab->zName), pTab->zName ); +#ifndef SQLITE_OMIT_TRIGGER + /* If there are TEMP triggers on this table, modify the sqlite_temp_master + ** table. Don't do this if the table being ALTERed is itself located in + ** the temp database. + */ + if( iDb!=1 ){ + Trigger *pTrig; + char *tmp = 0; + for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ + if( pTrig->iDb==1 ){ + if( !zTempTrig ){ + zTempTrig = + sqlite3MPrintf("type = 'trigger' AND name IN(%Q", pTrig->name); + }else{ + tmp = zTempTrig; + zTempTrig = sqlite3MPrintf("%s, %Q", zTempTrig, pTrig->name); + sqliteFree(tmp); + } + } + } + if( zTempTrig ){ + tmp = zTempTrig; + zTempTrig = sqlite3MPrintf("%s)", zTempTrig); + sqliteFree(tmp); + sqlite3NestedParse(pParse, + "UPDATE sqlite_temp_master SET " + "sql = sqlite_alter_trigger(sql, %Q), " + "tbl_name = %Q " + "WHERE %s;", zName, zName, zTempTrig); + } + } +#endif + /* Drop the elements of the in-memory schema that refered to the table ** renamed and load the new versions from the database. */ @@ -3001,12 +3037,20 @@ void sqlite3AlterRenameTable( #ifndef SQLITE_OMIT_TRIGGER Trigger *pTrig; for( pTrig=pTab->pTrigger; pTrig; pTrig=pTrig->pNext ){ - sqlite3VdbeOp3(v, OP_DropTrigger, iDb, 0, pTrig->name, 0); + assert( pTrig->iDb==iDb || pTrig->iDb==1 ); + sqlite3VdbeOp3(v, OP_DropTrigger, pTrig->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); +#ifndef SQLITE_OMIT_TRIGGER + if( zTempTrig ){ + sqlite3VdbeOp3(v, OP_ParseSchema, 1, 0, zTempTrig, P3_DYNAMIC); + } + }else{ + sqliteFree(zTempTrig); +#endif } sqliteFree(zName); diff --git a/src/func.c b/src/func.c index c6ec755ba3..e0b85cad64 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.90 2004/11/18 15:44:29 danielk1977 Exp $ +** $Id: func.c,v 1.91 2004/11/19 05:14:55 danielk1977 Exp $ */ #include #include @@ -552,26 +552,38 @@ static void altertableFunc( int argc, sqlite3_value **argv ){ - char const *zSql = sqlite3_value_text(argv[0]); - char const *zTableName = sqlite3_value_text(argv[1]); + unsigned char const *zSql = sqlite3_value_text(argv[0]); + unsigned char const *zTableName = sqlite3_value_text(argv[1]); + int token; + Token tname; char const *zCsr = zSql; - char const *zPrev; - char *zRet = 0; - int tokenType = 0; - int len; + int len = 0; + char *zRet; - assert( argc==2 ); + /* The principle used to locate the table name in the CREATE TABLE + ** statement is that the table name is the first token that is immediatedly + ** followed by a left parenthesis - TK_LP. + */ if( zSql ){ - while( tokenType!=TK_LP ){ - zPrev = zCsr-len; - len = sqlite3GetToken(zCsr, &tokenType); - zCsr += len; - } + do { + /* Store the token that zCsr points to in tname. */ + tname.z = zCsr; + tname.n = len; - zRet = sqlite3MPrintf("%.*s%Q(%s", zPrev-zSql, zSql, zTableName, zCsr); - sqlite3_result_text(context, zRet, -1, SQLITE_TRANSIENT); - sqliteFree(zRet); + /* 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 ); + } while( token!=TK_LP ); + + zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql, + zTableName, tname.z+tname.n); + sqlite3_result_text(context, zRet, -1, sqlite3FreeX); } } #endif @@ -605,7 +617,6 @@ static void altertriggerFunc( ** 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. */ @@ -641,8 +652,7 @@ static void altertriggerFunc( */ zRet = sqlite3MPrintf("%.*s%Q%s", tname.z - zSql, zSql, zTableName, tname.z+tname.n); - sqlite3_result_text(context, zRet, -1, SQLITE_TRANSIENT); - sqliteFree(zRet); + sqlite3_result_text(context, zRet, -1, sqlite3FreeX); } } #endif /* !SQLITE_OMIT_TRIGGER */ diff --git a/test/alter.test b/test/alter.test index fdf1ba5429..b716f6392c 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.3 2004/11/18 15:44:30 danielk1977 Exp $ +# $Id: alter.test,v 1.4 2004/11/19 05:14:56 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -20,6 +20,21 @@ ifcapable !altertable { 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. +# + # Create some tables to rename. Be sure to include some TEMP tables # and some tables with odd names. # @@ -196,6 +211,29 @@ do_test alter-1.8.7 { } } {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 { @@ -224,6 +262,17 @@ ifcapable !trigger { return } +#----------------------------------------------------------------------- +# 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} { @@ -349,5 +398,58 @@ do_test alter-3.2.9 { CREATE TRIGGER 'on'.trig4 AFTER INSERT ON 'ON' BEGIN SELECT 1; END; } } {0 {}} -finish_test +do_test alter-3.2.10 { + execsql { + DROP TABLE t10; + } +} {} +do_test alter-3.3.1 { + execsql { + 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 { + 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; + SELECT * FROM sqlite_temp_master WHERE type = 'trigger'; + } +} {} + +finish_test