Handle triggers correctly in ALTER TABLE. (CVS 2111)

FossilOrigin-Name: c61b7de107cea76b561d0d6cd90c752b62c5df95
This commit is contained in:
danielk1977 2004-11-18 15:44:29 +00:00
parent 20fc0887f5
commit d641d646ff
5 changed files with 234 additions and 15 deletions

View File

@ -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

View File

@ -1 +1 @@
85d56beb7494ce63e70ab1ffc3797c2ee4c36749
c61b7de107cea76b561d0d6cd90c752b62c5df95

View 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);

View 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},

View 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