Fix triggers to work in an ATTACHed database. Ticket #295. (CVS 915)
FossilOrigin-Name: 1e5e00fb73c308378efd034cb291caf338c9fe84
diff --git a/manifest b/manifest
index 53008cb..acf67aa 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Make\ssure\sthe\smin()\sand\smax()\soptimizations\swork\son\sempty\sindexed\stables.\nTicket\s#296.\s(CVS\s914)
-D 2003-04-17T12:44:24
+C Fix\striggers\sto\swork\sin\san\sATTACHed\sdatabase.\s\sTicket\s#295.\s(CVS\s915)
+D 2003-04-17T22:57:53
F Makefile.in df3a4db41a7450468b5fe934d9dd8f723b631249
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -24,30 +24,30 @@
F src/btree.c b9487cceb9ea78af9cbae9def34114902f511736
F src/btree.h 529c98cb0715c62214544fbbe50b946f99a85540
F src/btree_rb.c 7fa4901a65de66522ce31985833f20b98f7baad4
-F src/build.c daed1dacdb70e5d4def9df2e34a1cabeeb8467c9
+F src/build.c 6694013c86c4c480754f515ddab561302c6e732a
F src/copy.c 8699e571994934c78f70761a1458d7b9e9e75073
-F src/delete.c 6021fd293a78ebeb35e8177bd811d752fe090f89
+F src/delete.c af65b26d9d13abbf63fdc4e97b88d26c700b04bb
F src/encode.c faf03741efe921755ec371cf4a6984536de00042
F src/expr.c 942f535c8906ef81df00bac62223868feb78424b
F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605
F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c 45d27e3e8447bff4025db2f0dc3bb4e318e602f4
-F src/main.c e48b3b019cf34503655e9737bcb859443ab6718c
+F src/main.c d6a7f78ec5269c7ced3380908a7ff04508aa2f8e
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
F src/os.c 7274951ed6894f383cb889342267ded07caf339b
F src/os.h aa52f0c9da321ff6134d19f2ca959e18e33615d0
F src/pager.c df4c81350cbd80c1ab48341ae0768ba78d99ad49
F src/pager.h e3702f7d384921f6cd5ce0b3ed589185433e9f6c
-F src/parse.y 3be47fa18323aa2e3364fc42bf7a6ba5b3cc0a81
+F src/parse.y ad40843ae5462ba32606d55c53c2c8a9e56dd1ce
F src/pragma.c aef327bd597e15f0d31f45b042bd2797cca65039
F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 84168d6ef8deac8436179a0219393572e0b84517
-F src/shell.c 6980eadda7506f741ab42fd9d32613e2fdabafa9
+F src/select.c 07140aaf5f2e209dd7bf8a681401a412ce16dc04
+F src/shell.c a0b7043713713ff45f666ce6b3c03a64109a8bb5
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in f49c2cdec7d24cb03e496a1ca519e16306495ee1
-F src/sqliteInt.h b3d4e485ab646970e8b0d268771486683aceab12
+F src/sqliteInt.h 757c82342dbcf90867471e59a1f4e34ed4d34290
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c 7a072c3c8ba9796edc25e5ffa62b68558134e192
F src/test1.c 7ad4e6308dde0bf5a0f0775ce20cb2ec37a328f8
@@ -55,15 +55,15 @@
F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
F src/tokenize.c a88cfb6f698d047e14d5064fa6c4ecb709bf8fa4
-F src/trigger.c 4ca4499d367548385b8e9fc67eb360cd1ca95b8a
-F src/update.c a60470d07cdd4ff3c11c5418f8055f2f41b3d751
+F src/trigger.c 45b67f6c4338245288e4662c6a5b802ae3a66e5d
+F src/update.c 7f1aa8912876a682a692676f8adb215ddffad295
F src/util.c 13c338a7d0e1e6290ca227edb0d6d7be6a7c7127
F src/vacuum.c ac65e9578506a0cdf70ece2668e5b22f4895477c
F src/vdbe.c d453e8c95c9fac5a5e067c5c58243b3ae75699fc
F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
F src/where.c e5733f7d5e9cc4ed3590dc3401f779e7b7bb8127
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
-F test/attach.test 4912f001bf807b898083cfdef0f6d24f1533e4d7
+F test/attach.test b311c83e370e6b22b79a8279317039440ce64862
F test/auth.test 8128cd750830cba01b7fd0fba8ddfa1722ea6291
F test/bigfile.test 1cd8256d4619c39bea48147d344f348823e78678
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
@@ -162,7 +162,7 @@
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 35caefe31750fd103b5f0231ad36f375771063eb
-R 863a74a305f0c2f82dcca28addb978eb
+P 98ef6110068e5ed3cd77a14b004f890b79b731f7
+R 8d0229a2d7c6c86f6fe4145a683ab8e2
U drh
-Z ff7113cb2286f12309cb08ea1daacc5c
+Z a65dc41f80624ed136df0a6b0a99456f
diff --git a/manifest.uuid b/manifest.uuid
index a54a60c..cd41037 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-98ef6110068e5ed3cd77a14b004f890b79b731f7
\ No newline at end of file
+1e5e00fb73c308378efd034cb291caf338c9fe84
\ No newline at end of file
diff --git a/src/build.c b/src/build.c
index 6db4092..44c4827 100644
--- a/src/build.c
+++ b/src/build.c
@@ -23,7 +23,7 @@
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.145 2003/04/15 01:19:48 drh Exp $
+** $Id: build.c,v 1.146 2003/04/17 22:57:53 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -109,7 +109,10 @@
/*
** Locate the in-memory structure that describes
** a particular database table given the name
-** of that table. Return NULL if not found.
+** of that table and (optionally) the name of the database
+** containing the table. Return NULL if not found.
+**
+** See also sqliteLocateTable().
*/
Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
Table *p = 0;
@@ -125,7 +128,48 @@
/*
** Locate the in-memory structure that describes
-** a particular index given the name of that index.
+** a particular database table given the name
+** of that table and (optionally) the name of the database
+** containing the table. Return NULL if not found.
+**
+** If pParse->useDb is not negative, then the table must be
+** located in that database. If a different database is specified,
+** an error message is generated into pParse->zErrMsg.
+*/
+Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){
+ sqlite *db;
+ const char *zUse;
+ Table *p;
+ db = pParse->db;
+ if( pParse->useDb<0 ){
+ p = sqliteFindTable(db, zName, zDbase);
+ }else {
+ assert( pParse->useDb<db->nDb );
+ assert( db->aDb[pParse->useDb].pBt!=0 );
+ zUse = db->aDb[pParse->useDb].zName;
+ if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){
+ sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase);
+ return 0;
+ }
+ p = sqliteFindTable(db, zName, zUse);
+ if( p==0 && pParse->useDb==1 && zDbase==0 ){
+ p = sqliteFindTable(db, zName, 0);
+ }
+ }
+ if( p==0 ){
+ if( zDbase ){
+ sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
+ }else{
+ sqliteErrorMsg(pParse, "no such table: %s", zName);
+ }
+ }
+ return p;
+}
+
+/*
+** Locate the in-memory structure that describes
+** a particular index given the name of that index
+** and the name of the database that contains the index.
** Return NULL if not found.
*/
Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){
@@ -2078,7 +2122,13 @@
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
sqliteVdbeAddOp(v, OP_Transaction, 1, 0);
if( !tempOnly ){
+ int i;
+ sqlite *db = pParse->db;
sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
+ for(i=2; i<db->nDb; i++){
+ if( db->aDb[i].pBt==0 ) continue;
+ sqliteVdbeAddOp(v, OP_Transaction, i, 0);
+ }
sqliteCodeVerifySchema(pParse);
}
}else if( setCheckpoint ){
diff --git a/src/delete.c b/src/delete.c
index c2c6d5b..9d88de4 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
-** $Id: delete.c,v 1.51 2003/04/15 19:22:23 drh Exp $
+** $Id: delete.c,v 1.52 2003/04/17 22:57:53 drh Exp $
*/
#include "sqliteInt.h"
@@ -27,11 +27,7 @@
for(i=0; i<pSrc->nSrc; i++){
const char *zTab = pSrc->a[i].zName;
const char *zDb = pSrc->a[i].zDatabase;
- pTab = sqliteFindTable(pParse->db, zTab, zDb);
- if( pTab==0 ){
- sqliteErrorMsg(pParse, "no such table: %S", pSrc, 0);
- break;
- }
+ pTab = sqliteLocateTable(pParse, zTab, zDb);
pSrc->a[i].pTab = pTab;
}
return pTab;
@@ -62,8 +58,6 @@
){
Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */
- char *zTab; /* Name of the table from which we are deleting */
- char *zDb; /* Name of database containing table zTab */
int end, addr; /* A couple addresses of generated code */
int i; /* Loop counter */
WhereInfo *pWInfo; /* Information about the WHERE clause */
@@ -83,37 +77,25 @@
db = pParse->db;
assert( pTabList->nSrc==1 );
- /* Check for the special case of a VIEW with one or more ON DELETE triggers
- ** defined
- */
- zTab = pTabList->a[0].zName;
- zDb = pTabList->a[0].zDatabase;
- if( zTab != 0 ){
- pTab = sqliteFindTable(pParse->db, zTab, zDb);
- if( pTab ){
- before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
- TK_DELETE, TK_BEFORE, TK_ROW, 0);
- after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
- TK_DELETE, TK_AFTER, TK_ROW, 0);
- row_triggers_exist = before_triggers || after_triggers;
- }
- if( row_triggers_exist && pTab->pSelect ){
- /* Just fire VIEW triggers */
- sqliteSrcListDelete(pTabList);
- sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
- return;
- }
- }
-
/* Locate the table which we want to delete. This table has to be
** put in an SrcList structure because some of the subroutines we
** will be calling are designed to work with multiple tables and expect
** an SrcList* parameter instead of just a Table* parameter.
*/
pTab = sqliteSrcListLookup(pParse, pTabList);
- if( pTab==0 || sqliteIsReadOnly(pParse, pTab) ){
- goto delete_from_cleanup;
+ if( pTab==0 ) goto delete_from_cleanup;
+ before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
+ TK_DELETE, TK_BEFORE, TK_ROW, 0);
+ after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
+ TK_DELETE, TK_AFTER, TK_ROW, 0);
+ row_triggers_exist = before_triggers || after_triggers;
+ if( row_triggers_exist && pTab->pSelect ){
+ /* Just fire VIEW triggers */
+ sqliteSrcListDelete(pTabList);
+ sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
+ return;
}
+ if( sqliteIsReadOnly(pParse, pTab) ) goto delete_from_cleanup;
assert( pTab->pSelect==0 ); /* This table is not a view */
if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0) ){
goto delete_from_cleanup;
diff --git a/src/main.c b/src/main.c
index c38e3d5..d2900b1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.125 2003/04/16 20:24:52 drh Exp $
+** $Id: main.c,v 1.126 2003/04/17 22:57:54 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -69,6 +69,7 @@
sParse.db = pData->db;
sParse.initFlag = 1;
sParse.iDb = atoi(argv[4]);
+ sParse.useDb = -1;
sParse.newTnum = atoi(argv[2]);
sParse.useCallback = 1;
sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
@@ -320,6 +321,7 @@
sParse.xCallback = sqliteInitCallback;
sParse.pArg = (void*)&initData;
sParse.initFlag = 1;
+ sParse.useDb = -1;
sParse.useCallback = 1;
if( iDb==0 ){
sqliteRunParser(&sParse,
@@ -719,6 +721,7 @@
sParse.db = db;
sParse.xCallback = xCallback;
sParse.pArg = pArg;
+ sParse.useDb = -1;
sParse.useCallback = ppVm==0;
#ifndef SQLITE_OMIT_TRACE
if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
diff --git a/src/parse.y b/src/parse.y
index 605cc6c..c3e14fa 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.94 2003/03/31 00:30:48 drh Exp $
+** @(#) $Id: parse.y,v 1.95 2003/04/17 22:57:54 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -794,7 +794,9 @@
%type trigger_cmd_list {TriggerStep *}
trigger_cmd_list(A) ::= trigger_cmd(X) SEMI trigger_cmd_list(Y). {
- X->pNext = Y ; A = X; }
+ X->pNext = Y;
+ A = X;
+}
trigger_cmd_list(A) ::= . { A = 0; }
%type trigger_cmd {TriggerStep *}
diff --git a/src/select.c b/src/select.c
index 013db33..3aef002 100644
--- a/src/select.c
+++ b/src/select.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
-** $Id: select.c,v 1.131 2003/04/17 12:44:24 drh Exp $
+** $Id: select.c,v 1.132 2003/04/17 22:57:54 drh Exp $
*/
#include "sqliteInt.h"
@@ -908,10 +908,8 @@
}else{
/* An ordinary table or view name in the FROM clause */
pTabList->a[i].pTab = pTab =
- sqliteFindTable(pParse->db, pTabList->a[i].zName,
- pTabList->a[i].zDatabase);
+ sqliteLocateTable(pParse,pTabList->a[i].zName,pTabList->a[i].zDatabase);
if( pTab==0 ){
- sqliteErrorMsg(pParse, "no such table: %S", pTabList, i);
return 1;
}
if( pTab->pSelect ){
diff --git a/src/shell.c b/src/shell.c
index 00601df..4873d35 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -12,7 +12,7 @@
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
-** $Id: shell.c,v 1.69 2003/04/17 02:54:14 drh Exp $
+** $Id: shell.c,v 1.70 2003/04/17 22:57:54 drh Exp $
*/
#include <stdlib.h>
#include <string.h>
@@ -285,7 +285,8 @@
}
if( p->cnt++>0 ) fprintf(p->out,"\n");
for(i=0; i<nArg; i++){
- fprintf(p->out,"%*s = %s\n", w, azCol[i], azArg[i] ? azArg[i] : p->nullvalue);
+ fprintf(p->out,"%*s = %s\n", w, azCol[i],
+ azArg[i] ? azArg[i] : p->nullvalue);
}
break;
}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 473492d..74a8752 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.174 2003/04/16 02:17:35 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.175 2003/04/17 22:57:54 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
@@ -225,8 +225,8 @@
** Each database file to be accessed by the system is an instance
** of the following structure. There are normally two of these structures
** in the sqlite.aDb[] array. aDb[0] is the main database file and
-** aDb[1] is the database file used to hold temporary tables. But
-** additional databases may be attached to the engine.
+** aDb[1] is the database file used to hold temporary tables. Additional
+** databases may be attached.
*/
struct Db {
char *zName; /* Name of this database */
@@ -825,6 +825,7 @@
** other than after an OP_Transaction */
u8 iDb; /* Index of database whose schema is being parsed */
u8 useCallback; /* True if callbacks should be used to report results */
+ int useDb; /* Restrict references to tables in this database */
int newTnum; /* Table number to use when reparsing CREATE TABLEs */
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated VDBE cursors */
@@ -918,6 +919,7 @@
struct TriggerStep {
int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
int orconf; /* OE_Rollback etc. */
+ Trigger *pTrig; /* The trigger that this step is a part of */
Select *pSelect; /* Valid for SELECT and sometimes
INSERT steps (when pExprList == 0) */
@@ -1062,6 +1064,7 @@
void sqliteExprIfTrue(Parse*, Expr*, int, int);
void sqliteExprIfFalse(Parse*, Expr*, int, int);
Table *sqliteFindTable(sqlite*,const char*, const char*);
+Table *sqliteLocateTable(Parse*,const char*, const char*);
Index *sqliteFindIndex(sqlite*,const char*, const char*);
void sqliteUnlinkAndDeleteIndex(sqlite*,Index*);
void sqliteCopy(Parse*, SrcList*, Token*, Token*, int);
diff --git a/src/trigger.c b/src/trigger.c
index 71a784a..ca6dfa6 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -65,7 +65,7 @@
if( !tab ){
goto trigger_cleanup;
}
- if( tab->iDb>=2 ){
+ if( tab->iDb>=2 && !pParse->initFlag ){
sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
"database %s", db->aDb[tab->iDb].zName);
goto trigger_cleanup;
@@ -124,6 +124,11 @@
sqliteIdListDelete(pColumns);
nt->foreach = foreach;
nt->step_list = pStepList;
+ while( pStepList ){
+ pStepList->pTrig = nt;
+ pStepList = pStepList->pNext;
+ }
+ pStepList = nt->step_list;
/* if we are not initializing, and this trigger is not on a TEMP table,
** build the sqlite_master entry
@@ -512,8 +517,10 @@
while( pTriggerStep ){
int saveNTab = pParse->nTab;
+ int saveUseDb = pParse->useDb;
orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
pParse->trigStack->orconf = orconf;
+ pParse->useDb = pTriggerStep->pTrig->iDb;
switch( pTriggerStep->op ){
case TK_SELECT: {
Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
@@ -554,6 +561,7 @@
assert(0);
}
pParse->nTab = saveNTab;
+ pParse->useDb = saveUseDb;
pTriggerStep = pTriggerStep->pNext;
}
diff --git a/src/update.c b/src/update.c
index 721dbaa..53dd62e 100644
--- a/src/update.c
+++ b/src/update.c
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
-** $Id: update.c,v 1.59 2003/04/15 19:22:24 drh Exp $
+** $Id: update.c,v 1.60 2003/04/17 22:57:54 drh Exp $
*/
#include "sqliteInt.h"
@@ -27,8 +27,6 @@
int onError /* How to handle constraint errors */
){
int i, j; /* Loop counters */
- char *zTab; /* Name of the table to be updated */
- char *zDb; /* Name of the database holding zTab */
Table *pTab; /* The table to be updated */
int addr; /* VDBE instruction address of the start of the loop */
WhereInfo *pWInfo; /* Information about the WHERE clause */
@@ -58,38 +56,25 @@
db = pParse->db;
assert( pTabList->nSrc==1 );
- /* Check for the special case of a VIEW with one or more ON UPDATE triggers
- * defined
- */
- zTab = pTabList->a[0].zName;
- zDb = pTabList->a[0].zDatabase;
- if( zTab != 0 ){
- pTab = sqliteFindTable(pParse->db, zTab, zDb);
- if( pTab ){
- before_triggers =
- sqliteTriggersExist(pParse, pTab->pTrigger,
- TK_UPDATE, TK_BEFORE, TK_ROW, pChanges);
- after_triggers =
- sqliteTriggersExist(pParse, pTab->pTrigger,
- TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
- row_triggers_exist = before_triggers || after_triggers;
- }
-
- if( row_triggers_exist && pTab->pSelect ){
- /* Just fire VIEW triggers */
- sqliteSrcListDelete(pTabList);
- sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
- return;
- }
- }
-
/* Locate the table which we want to update. This table has to be
** put in an SrcList structure because some of the subroutines we
** will be calling are designed to work with multiple tables and expect
** an SrcList* parameter instead of just a Table* parameter.
*/
pTab = sqliteSrcListLookup(pParse, pTabList);
- if( pTab==0 || sqliteIsReadOnly(pParse, pTab) ) goto update_cleanup;
+ if( pTab==0 ) goto update_cleanup;
+ before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
+ TK_UPDATE, TK_BEFORE, TK_ROW, pChanges);
+ after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
+ TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
+ row_triggers_exist = before_triggers || after_triggers;
+ if( row_triggers_exist && pTab->pSelect ){
+ /* Just fire VIEW triggers */
+ sqliteSrcListDelete(pTabList);
+ sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
+ return;
+ }
+ if( sqliteIsReadOnly(pParse, pTab) ) goto update_cleanup;
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
if( aXRef==0 ) goto update_cleanup;
diff --git a/test/attach.test b/test/attach.test
index 6133b8c..e132f77 100644
--- a/test/attach.test
+++ b/test/attach.test
@@ -12,7 +12,7 @@
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
-# $Id: attach.test,v 1.2 2003/04/05 16:56:30 drh Exp $
+# $Id: attach.test,v 1.3 2003/04/17 22:57:55 drh Exp $
#
set testdir [file dirname $argv0]
@@ -200,6 +200,43 @@
}
} {0 main 1 temp}
+do_test attach-2.1 {
+ execsql {
+ CREATE TABLE tx(x1,x2,y1,y2);
+ CREATE TRIGGER r1 AFTER UPDATE ON t2 FOR EACH ROW BEGIN
+ INSERT INTO tx(x1,x2,y1,y2) VALUES(OLD.x,NEW.x,OLD.y,NEW.y);
+ END;
+ SELECT * FROM tx;
+ } db2;
+} {}
+do_test attach-2.2 {
+ execsql {
+ UPDATE t2 SET x=x+10;
+ SELECT * FROM tx;
+ } db2;
+} {1 11 x x 2 12 y y}
+do_test attach-2.3 {
+ execsql {
+ CREATE TABLE tx(x1,x2,y1,y2);
+ SELECT * FROM tx;
+ }
+} {}
+do_test attach-2.4 {
+ execsql {
+ ATTACH 'test2.db' AS db2;
+ }
+} {}
+do_test attach-2.5 {
+ execsql {
+ UPDATE db2.t2 SET x=x+10;
+ SELECT * FROM db2.tx;
+ }
+} {1 11 x x 2 12 y y 11 21 x x 12 22 y y}
+do_test attach-2.6 {
+ execsql {
+ SELECT * FROM main.tx;
+ }
+} {}
for {set i 2} {$i<=15} {incr i} {
catch {db$i close}