]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Handle triggers correctly in ALTER TABLE. (CVS 2111)
authordanielk1977 <danielk1977@noemail.net>
Thu, 18 Nov 2004 15:44:29 +0000 (15:44 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 18 Nov 2004 15:44:29 +0000 (15:44 +0000)
FossilOrigin-Name: c61b7de107cea76b561d0d6cd90c752b62c5df95

manifest
manifest.uuid
src/build.c
src/func.c
test/alter.test

index f8d63922534b6e1b1f86405a85198fca6fa74385..782e88b5d1654307ecbc12bb40d95803d466d7e4 100644 (file)
--- 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
index 18436f4358d95bbee0f4cf35de326280a37666df..ae5e63d1609e1a4f9178fc397370a211137d1406 100644 (file)
@@ -1 +1 @@
-85d56beb7494ce63e70ab1ffc3797c2ee4c36749
\ No newline at end of file
+c61b7de107cea76b561d0d6cd90c752b62c5df95
\ No newline at end of file
index 10161455711a775c431ca9f8cceffa299bb440c1..a3996ca1932ac22be8c2da7f0586e76526862991 100644 (file)
@@ -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 <ctype.h>
@@ -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);
index 287e47eb269a98601c5b0aec3387ce7571d14617..c6ec755ba3305bdf21bb579d42872c6e0de425b0 100644 (file)
@@ -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 <ctype.h>
 #include <math.h>
@@ -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},
index a107bd038bd3371517df687e297b67d771e34106..fdf1ba5429bfef12458ac7eceff6f7dd1c3023db 100644 (file)
@@ -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
+