Handle triggers correctly in ALTER TABLE. (CVS 2111)
FossilOrigin-Name: c61b7de107cea76b561d0d6cd90c752b62c5df95
This commit is contained in:
parent
20fc0887f5
commit
d641d646ff
18
manifest
18
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
|
||||
|
@ -1 +1 @@
|
||||
85d56beb7494ce63e70ab1ffc3797c2ee4c36749
|
||||
c61b7de107cea76b561d0d6cd90c752b62c5df95
|
18
src/build.c
18
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 <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);
|
||||
|
77
src/func.c
77
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 <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},
|
||||
|
134
test/alter.test
134
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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user