From a69d91681d1fc36f40aa6602413d7db673c20992 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 17 Apr 2003 22:57:53 +0000 Subject: [PATCH] Fix triggers to work in an ATTACHed database. Ticket #295. (CVS 915) FossilOrigin-Name: 1e5e00fb73c308378efd034cb291caf338c9fe84 --- manifest | 30 +++++++++++++------------- manifest.uuid | 2 +- src/build.c | 56 +++++++++++++++++++++++++++++++++++++++++++++--- src/delete.c | 46 ++++++++++++--------------------------- src/main.c | 5 ++++- src/parse.y | 6 ++++-- src/select.c | 6 ++---- src/shell.c | 5 +++-- src/sqliteInt.h | 9 +++++--- src/trigger.c | 10 ++++++++- src/update.c | 43 ++++++++++++------------------------- test/attach.test | 39 ++++++++++++++++++++++++++++++++- 12 files changed, 163 insertions(+), 94 deletions(-) diff --git a/manifest b/manifest index 53008cbe40..acf67aaab5 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/auth.c 2dd558dba4d8ffbed25fe1644e9af242f389f3e9 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/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700 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/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be 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 a54a60ce48..cd41037e1b 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 6db409278d..44c4827328 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 @@ -109,7 +109,10 @@ void sqliteExec(Parse *pParse){ /* ** 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 @@ Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){ /* ** 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->useDbnDb ); + 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 @@ void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){ 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; inDb; 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 c2c6d5b490..9d88de4af5 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 @@ Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){ for(i=0; inSrc; 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 @@ void sqliteDeleteFrom( ){ 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 @@ void sqliteDeleteFrom( 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 c38e3d58db..d2900b1016 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 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){ 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 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){ 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 @@ static int sqliteMain( 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 605cc6cd72..c3e14fa68f 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 @@ when_clause(A) ::= WHEN expr(X). { A = X; } %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 013db33398..3aef002cce 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 @@ static int fillInColumnList(Parse *pParse, Select *p){ }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 00601df47b..4873d35df4 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 #include @@ -285,7 +285,8 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){ } if( p->cnt++>0 ) fprintf(p->out,"\n"); for(i=0; iout,"%*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 473492d338..74a8752c49 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 @@ typedef struct Db Db; ** 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 @@ struct Parse { ** 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 Trigger { 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 sqliteExprCode(Parse*, Expr*); 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 71a784aaef..ca6dfa6182 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -65,7 +65,7 @@ void sqliteCreateTrigger( 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 @@ void sqliteCreateTrigger( 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 @@ static int codeTriggerProgram( 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 @@ static int codeTriggerProgram( assert(0); } pParse->nTab = saveNTab; + pParse->useDb = saveUseDb; pTriggerStep = pTriggerStep->pNext; } diff --git a/src/update.c b/src/update.c index 721dbaa871..53dd62ebf3 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 @@ void sqliteUpdate( 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 @@ void sqliteUpdate( 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 6133b8c905..e132f771de 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 @@ do_test attach-1.29 { } } {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}