diff --git a/manifest b/manifest index 859bac8c00..17f8128100 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Style\sfixes\sto\striggers\scode\sin\svarious\s*.c\sfiles\s(partial\sfix\sto\sticket\s#39)\s(CVS\s571) -D 2002-05-19T23:43:13 +C Additional\scode\scleanup\sresulting\sfrom\sa\sreview\sof\sthe\snew\strigger\scode.\s(CVS\s572) +D 2002-05-21T11:38:11 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -20,28 +20,28 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/btree.c c01b404b9373ae1c0daf7d1f9211c72ead67638e F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 -F src/build.c f6d2f428801e636c802fa63e951772569fa133a5 -F src/delete.c f9ab9738c5a6fed434ec9e7721a178f58f65f57a +F src/build.c f5aa02e4553ca2db941d6e7f025a180ddc4cea49 +F src/delete.c c2eae01b76d5418d4ff1768659dfb199c38f0641 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760 F src/expr.c 2c7535ba013b731ed73f12b6cc1cd5b466efb3e2 F src/func.c a31dcba85bc2ecb9b752980289cf7e6cd0cafbce F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 -F src/insert.c 5233701a402cf5f4329525a654928c5de8d9a4e8 +F src/insert.c bc98bfe7f508f7a3027ea93ef9bbdc87bbce1e93 F src/main.c 3f0db74a3e8385322a3b69f51bea6ce19caeff19 F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10 F src/pager.c ba5740104cc27b342cd43eebfdc44d60f64a3ded F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e -F src/parse.y 12d6f6c0d12c868b366e3758e62578e686623394 +F src/parse.y 964a7cc954e3b84e9de54692648fb70bee1c0ba8 F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe F src/select.c 1b623a7d826ec7c245bc542b665d61724da2a62d F src/shell.c 5acbe59e137d60d8efd975c683dbea74ab626530 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b -F src/sqliteInt.h 66bf5c2d48ed35efd5e663501b6c5f830bc87c37 +F src/sqliteInt.h aa18969cf0426fa6bb42c2f2c5baf1179710376e F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1 F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e @@ -49,8 +49,8 @@ F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730 F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f F src/tokenize.c f12f78c58b2a79ea4eee880efad63a328e103c62 -F src/trigger.c 43dbeb68ba379049a78d270db0db27fa01deec12 -F src/update.c 6b1d80b5058e686c2e3197d72c567ea27d35f42e +F src/trigger.c 143d3d2bd1318b88b095b4081dcbb10c842bc1b7 +F src/update.c 46c4b3e86c066cd8a0dcf0b75e4580bcf113aeb0 F src/util.c 707c30f8c13cddace7c08556ac450c0b786660b3 F src/vdbe.c 5fd717e6a42e98e4f244a2149267004f71f23679 F src/vdbe.h 126a651ba26f05de075dcc6da5466244a31af6b8 @@ -77,7 +77,7 @@ F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85 F test/malloc.test 70fdd0812e2a57eb746aaf015350f58bb8eee0b1 F test/minmax.test fb6ab400271ae1f5bc88617c2882f2f081ea8e6d F test/misc1.test a03214118429b40ca5548bc1fae0ebd5c34dabe6 -F test/misuse.test 2c401721fbbe01897a80cef12e2fbbad4ca8a807 +F test/misuse.test a3aa2b18a97e4c409a1fcaff5151a4dd804a0162 F test/notnull.test b1f3e42fc475b0b5827b27b2e9b562081995ff30 F test/pager.test b0c0d00cd5dce0ce21f16926956b195c0ab5044c F test/pragma.test 0b9675ef1f5ba5b43abfa337744445fc5b01a34a @@ -134,7 +134,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P b1d72cb5847a9f5f08e40b36ad117b5493232ea7 -R 6a41d52fabcd64a48785213ecafaf488 -U danielk1977 -Z f416062573015e51faa69ab13000e10e +P 8a4195c7466962291a296e8f53034ea8cb25005f +R 361b70ca0dd473638df412c87f908919 +U drh +Z 5451ccaf091b36f9a5e5679c783b7dab diff --git a/manifest.uuid b/manifest.uuid index eee2cfcd98..46913b5766 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8a4195c7466962291a296e8f53034ea8cb25005f \ No newline at end of file +37dbdd551e88440933066133ec9cc1e10b03fc1a \ No newline at end of file diff --git a/src/build.c b/src/build.c index 5f89a717f7..23f55a6269 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.91 2002/05/19 23:43:14 danielk1977 Exp $ +** $Id: build.c,v 1.92 2002/05/21 11:38:11 drh Exp $ */ #include "sqliteInt.h" #include @@ -478,7 +478,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){ ** now. */ if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){ - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); if( !isTemp ){ sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0); sqliteVdbeAddOp(v, OP_SetCookie, 0, 1); @@ -1092,7 +1092,7 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){ { OP_Close, 0, 0, 0}, }; Index *pIdx; - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); /* Drop all triggers associated with the table being dropped */ while( pTable->pTrigger ){ Token tt; @@ -1355,7 +1355,7 @@ void sqliteCreateIndex( v = sqliteGetVdbe(pParse); if( v==0 ) goto exit_create_index; if( pTable!=0 ){ - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); if( !isTemp ){ sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC); @@ -1467,7 +1467,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){ int base; Table *pTab = pIndex->pTable; - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); if( !pTab->isTemp ){ base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); sqliteVdbeChangeP3(v, base+2, pIndex->zName, P3_STATIC); @@ -1588,7 +1588,7 @@ void sqliteCopy( v = sqliteGetVdbe(pParse); if( v ){ int openOp; - sqliteBeginMultiWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 1); addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeDequoteP3(v, addr); @@ -1669,7 +1669,7 @@ void sqliteBeginTransaction(Parse *pParse, int onError){ if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return; if( pParse->nErr || sqlite_malloc_failed ) return; if( db->flags & SQLITE_InTrans ) return; - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); db->flags |= SQLITE_InTrans; db->onError = onError; } @@ -1708,32 +1708,18 @@ void sqliteRollbackTransaction(Parse *pParse){ /* ** Generate VDBE code that prepares for doing an operation that -** might change the database. The operation will be atomic in the -** sense that it will either do its changes completely or not at -** all. So there is no need to set a checkpoint is a transaction -** is already in effect. +** might change the database. +** +** This routine starts a new transaction if we are not already within +** a transaction. If we are already within a transaction, then a checkpoint +** is set if the setCheckpoint parameter is true. A checkpoint should +** be set for operations that might fail (due to a constraint) part of +** the way through and which will need to undo some writes without having to +** rollback the whole transaction. For operations where all constraints +** can be checked before any changes are made to the database, it is never +** necessary to undo a write and the checkpoint should not be set. */ -void sqliteBeginWriteOperation(Parse *pParse){ - Vdbe *v; - v = sqliteGetVdbe(pParse); - if( v==0 ) return; - if( pParse->trigStack ) return; /* if this is in a trigger */ - if( (pParse->db->flags & SQLITE_InTrans)==0 ){ - sqliteVdbeAddOp(v, OP_Transaction, 0, 0); - sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); - pParse->schemaVerified = 1; - } -} - -/* -** Generate VDBE code that prepares for doing an operation that -** might change the database. The operation might not be atomic in -** the sense that an error may be discovered and the operation might -** abort after some changes have been made. If we are in the middle -** of a transaction, then this sets a checkpoint. If we are not in -** a transaction, then start a transaction. -*/ -void sqliteBeginMultiWriteOperation(Parse *pParse){ +void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint){ Vdbe *v; v = sqliteGetVdbe(pParse); if( v==0 ) return; @@ -1742,7 +1728,7 @@ void sqliteBeginMultiWriteOperation(Parse *pParse){ sqliteVdbeAddOp(v, OP_Transaction, 0, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); pParse->schemaVerified = 1; - }else{ + }else if( setCheckpoint ){ sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0); } } @@ -1844,7 +1830,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ int addr; int size = atoi(zRight); if( size<0 ) size = -size; - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); sqliteVdbeAddOp(v, OP_Integer, size, 0); sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); @@ -1935,7 +1921,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ int addr; int size = db->cache_size; if( size<0 ) size = -size; - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); sqliteVdbeAddOp(v, OP_Dup, 0, 0); addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); diff --git a/src/delete.c b/src/delete.c index 417ed2fd66..bcbe5ea810 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.33 2002/05/19 23:43:14 danielk1977 Exp $ +** $Id: delete.c,v 1.34 2002/05/21 11:38:11 drh Exp $ */ #include "sqliteInt.h" @@ -75,6 +75,7 @@ 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 */ IdList *pTabList; /* An ID list holding pTab and nothing else */ int end, addr; /* A couple addresses of generated code */ int i; /* Loop counter */ @@ -96,32 +97,28 @@ void sqliteDeleteFrom( /* Check for the special case of a VIEW with one or more ON DELETE triggers ** defined */ - { - Table *pTab; - char *zTab = sqliteTableNameFromToken(pTableName); - - if( zTab != 0 ){ - pTab = sqliteFindTable(pParse->db, zTab); - if( pTab ){ - row_triggers_exist = - sqliteTriggersExist(pParse, pTab->pTrigger, - TK_DELETE, TK_BEFORE, TK_ROW, 0) || - sqliteTriggersExist(pParse, pTab->pTrigger, - TK_DELETE, TK_AFTER, TK_ROW, 0); - } - sqliteFree(zTab); - if( row_triggers_exist && pTab->pSelect ){ - /* Just fire VIEW triggers */ - sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0); - return; - } + zTab = sqliteTableNameFromToken(pTableName); + if( zTab != 0 ){ + pTab = sqliteFindTable(pParse->db, zTab); + if( pTab ){ + row_triggers_exist = + sqliteTriggersExist(pParse, pTab->pTrigger, + TK_DELETE, TK_BEFORE, TK_ROW, 0) || + sqliteTriggersExist(pParse, pTab->pTrigger, + TK_DELETE, TK_AFTER, TK_ROW, 0); + } + sqliteFree(zTab); + if( row_triggers_exist && pTab->pSelect ){ + /* Just fire VIEW triggers */ + sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0); + return; } } /* Locate the table which we want to delete. This table has to be ** put in an IdList structure because some of the subroutines we ** will be calling are designed to work with multiple tables and expect - ** an IdList* parameter instead of just a Table* parameger. + ** an IdList* parameter instead of just a Table* parameter. */ pTabList = sqliteTableTokenToIdList(pParse, pTableName); if( pTabList==0 ) goto delete_from_cleanup; @@ -129,6 +126,8 @@ void sqliteDeleteFrom( pTab = pTabList->a[0].pTab; assert( pTab->pSelect==0 ); /* This table is not a view */ + /* Allocate a cursor used to store the old.* data for a trigger. + */ if( row_triggers_exist ){ oldIdx = pParse->nTab++; } @@ -151,11 +150,7 @@ void sqliteDeleteFrom( if( v==0 ){ goto delete_from_cleanup; } - if( row_triggers_exist ){ - sqliteBeginMultiWriteOperation(pParse); - } else { - sqliteBeginWriteOperation(pParse); - } + sqliteBeginWriteOperation(pParse, row_triggers_exist); /* Initialize the counter of the number of rows deleted, if ** we are counting rows. @@ -165,7 +160,8 @@ void sqliteDeleteFrom( } /* Special case: A DELETE without a WHERE clause deletes everything. - ** It is easier just to erase the whole table. + ** It is easier just to erase the whole table. Note, however, that + ** this means that the row change count will be incorrect. */ if( pWhere==0 && !row_triggers_exist ){ if( db->flags & SQLITE_CountRows ){ @@ -215,8 +211,10 @@ void sqliteDeleteFrom( sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); end = sqliteVdbeMakeLabel(v); + /* This is the beginning of the delete loop when there are + ** row triggers. + */ if( row_triggers_exist ){ - int ii; addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); sqliteVdbeAddOp(v, OP_Dup, 0, 0); @@ -226,11 +224,11 @@ void sqliteDeleteFrom( sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0); sqliteVdbeAddOp(v, OP_Integer, 13, 0); - for(ii = 0; iinCol; ii++){ - if( ii==pTab->iPKey ){ + for(i = 0; inCol; i++){ + if( i==pTab->iPKey ){ sqliteVdbeAddOp(v, OP_Recno, base, 0); } else { - sqliteVdbeAddOp(v, OP_Column, base, ii); + sqliteVdbeAddOp(v, OP_Column, base, i); } } sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); @@ -242,6 +240,12 @@ void sqliteDeleteFrom( oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default); } + /* Open cursors for the table we are deleting from and all its + ** indices. If there are row triggers, this happens inside the + ** OP_ListRead loop because the cursor have to all be closed + ** before the trigger fires. If there are no row triggers, the + ** cursors are opened only once on the outside the loop. + */ pParse->nTab = base + 1; openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; sqliteVdbeAddOp(v, openOp, base, pTab->tnum); @@ -249,12 +253,18 @@ void sqliteDeleteFrom( sqliteVdbeAddOp(v, openOp, pParse->nTab++, pIdx->tnum); } + /* This is the beginning of the delete loop when there are no + ** row triggers */ if( !row_triggers_exist ){ addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); } - sqliteGenerateRowDelete(v, pTab, base, pParse->trigStack?0:1); + /* Delete the row */ + sqliteGenerateRowDelete(v, pTab, base, pParse->trigStack==0); + /* If there are row triggers, close all cursors then invoke + ** the AFTER triggers + */ if( row_triggers_exist ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum); @@ -264,10 +274,12 @@ void sqliteDeleteFrom( oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default); } + /* End of the delete loop */ sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeResolveLabel(v, end); sqliteVdbeAddOp(v, OP_ListReset, 0, 0); + /* Close the cursors after the loop if there are no row triggers */ if( !row_triggers_exist ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum); diff --git a/src/insert.c b/src/insert.c index fab85eddb6..091ed75a91 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.55 2002/05/19 23:43:14 danielk1977 Exp $ +** $Id: insert.c,v 1.56 2002/05/21 11:38:11 drh Exp $ */ #include "sqliteInt.h" @@ -95,11 +95,7 @@ void sqliteInsert( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto insert_cleanup; - if( pSelect || row_triggers_exist ){ - sqliteBeginMultiWriteOperation(pParse); - }else{ - sqliteBeginWriteOperation(pParse); - } + sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist); /* if there are row triggers, allocate a temp table for new.* references. */ if( row_triggers_exist ){ diff --git a/src/parse.y b/src/parse.y index bc60a78b65..cffdce119c 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.65 2002/05/15 14:17:45 drh Exp $ +** @(#) $Id: parse.y,v 1.66 2002/05/21 11:38:12 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -98,27 +98,32 @@ columnid ::= ids(X). {sqliteAddColumn(pParse,&X);} // An IDENTIFIER can be a generic identifier, or one of several // keywords. Any non-standard keyword can also be an identifier. -// We also make DESC and identifier since it comes up so often (as -// an abbreviation of "description"). // %type id {Token} id(A) ::= ABORT(X). {A = X;} +id(A) ::= AFTER(X). {A = X;} id(A) ::= ASC(X). {A = X;} +id(A) ::= BEFORE(X). {A = X;} id(A) ::= BEGIN(X). {A = X;} id(A) ::= CLUSTER(X). {A = X;} id(A) ::= CONFLICT(X). {A = X;} id(A) ::= COPY(X). {A = X;} id(A) ::= DELIMITERS(X). {A = X;} id(A) ::= DESC(X). {A = X;} +id(A) ::= EACH(X). {A = X;} id(A) ::= END(X). {A = X;} id(A) ::= EXPLAIN(X). {A = X;} id(A) ::= FAIL(X). {A = X;} +id(A) ::= FOR(X). {A = X;} id(A) ::= ID(X). {A = X;} id(A) ::= IGNORE(X). {A = X;} +id(A) ::= INSTEAD(X). {A = X;} id(A) ::= KEY(X). {A = X;} +id(A) ::= OF(X). {A = X;} id(A) ::= OFFSET(X). {A = X;} id(A) ::= PRAGMA(X). {A = X;} id(A) ::= REPLACE(X). {A = X;} +id(A) ::= ROW(X). {A = X;} id(A) ::= TEMP(X). {A = X;} id(A) ::= TRIGGER(X). {A = X;} id(A) ::= VACUUM(X). {A = X;} diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 9fd9b879ee..92716c63f8 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.110 2002/05/16 00:13:12 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.111 2002/05/21 11:38:12 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -798,8 +798,7 @@ void sqliteGenerateRowDelete(Vdbe*, Table*, int, int); void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int); -void sqliteBeginWriteOperation(Parse*); -void sqliteBeginMultiWriteOperation(Parse*); +void sqliteBeginWriteOperation(Parse*, int); void sqliteEndWriteOperation(Parse*); void sqliteExprMoveStrings(Expr*, int); void sqliteExprListMoveStrings(ExprList*, int); diff --git a/src/trigger.c b/src/trigger.c index d0141b0eb1..d26ddb9eaa 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -95,32 +95,32 @@ void sqliteCreateTrigger( ** build the sqlite_master entry */ if( !pParse->initFlag && !tab->isTemp ){ + static VdbeOp insertTrig[] = { + { OP_OpenWrite, 0, 2, MASTER_NAME}, + { OP_NewRecno, 0, 0, 0 }, + { OP_String, 0, 0, "trigger" }, + { OP_String, 0, 0, 0 }, /* 3: trigger name */ + { OP_String, 0, 0, 0 }, /* 4: table name */ + { OP_Integer, 0, 0, 0 }, + { OP_String, 0, 0, 0 }, /* 6: SQL */ + { OP_MakeRecord, 5, 0, 0 }, + { OP_PutIntKey, 0, 0, 0 }, + { OP_Integer, 0, 0, 0 }, /* 9: Next cookie */ + { OP_SetCookie, 0, 0, 0 }, + { OP_Close, 0, 0, 0 }, + }; + int addr; + Vdbe *v; /* Make an entry in the sqlite_master table */ - sqliteBeginWriteOperation(pParse); - - sqliteVdbeAddOp(pParse->pVdbe, OP_OpenWrite, 0, 2); - sqliteVdbeChangeP3(pParse->pVdbe, -1, MASTER_NAME, P3_STATIC); - sqliteVdbeAddOp(pParse->pVdbe, OP_NewRecno, 0, 0); - sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0); - sqliteVdbeChangeP3(pParse->pVdbe, -1, "trigger", P3_STATIC); - sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0); - sqliteVdbeChangeP3(pParse->pVdbe, -1, nt->name, 0); - sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0); - sqliteVdbeChangeP3(pParse->pVdbe, -1, nt->table, 0); - sqliteVdbeAddOp(pParse->pVdbe, OP_Integer, 0, 0); - sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0); - sqliteVdbeChangeP3(pParse->pVdbe, -1, nt->strings, 0); - sqliteVdbeAddOp(pParse->pVdbe, OP_MakeRecord, 5, 0); - sqliteVdbeAddOp(pParse->pVdbe, OP_PutIntKey, 0, 1); - - /* Change the cookie, since the schema is changed */ + v = sqliteGetVdbe(pParse); + sqliteBeginWriteOperation(pParse, 0); + addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig); + sqliteVdbeChangeP3(v, addr+3, nt->name, 0); + sqliteVdbeChangeP3(v, addr+4, nt->table, 0); + sqliteVdbeChangeP3(v, addr+6, nt->strings, 0); sqliteChangeCookie(pParse->db); - sqliteVdbeAddOp(pParse->pVdbe, OP_Integer, pParse->db->next_cookie, 0); - sqliteVdbeAddOp(pParse->pVdbe, OP_SetCookie, 0, 0); - - sqliteVdbeAddOp(pParse->pVdbe, OP_Close, 0, 0); - + sqliteVdbeChangeP1(v, addr+9, pParse->db->next_cookie); sqliteEndWriteOperation(pParse); } @@ -160,8 +160,14 @@ trigger_cleanup: } } -TriggerStep *sqliteTriggerSelectStep(Select * pSelect) -{ +/* +** Turn a SELECT statement (that the pSelect parameter points to) into +** a trigger step. Return a pointer to a TriggerStep structure. +** +** The parser calls this routine when it finds a SELECT statement in +** body of a TRIGGER. +*/ +TriggerStep *sqliteTriggerSelectStep(Select *pSelect){ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); pTriggerStep->op = TK_SELECT; @@ -171,12 +177,19 @@ TriggerStep *sqliteTriggerSelectStep(Select * pSelect) return pTriggerStep; } +/* +** Build a trigger step out of an INSERT statement. Return a pointer +** to the new trigger step. +** +** The parser calls this routine when it sees an INSERT inside the +** body of a trigger. +*/ TriggerStep *sqliteTriggerInsertStep( - Token *pTableName, - IdList *pColumn, - ExprList *pEList, - Select *pSelect, - int orconf + Token *pTableName, /* Name of the table into which we insert */ + IdList *pColumn, /* List of columns in pTableName to insert into */ + ExprList *pEList, /* The VALUE clause: a list of values to be inserted */ + Select *pSelect, /* A SELECT statement that supplies values */ + int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ ){ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); @@ -193,12 +206,17 @@ TriggerStep *sqliteTriggerInsertStep( return pTriggerStep; } +/* +** Construct a trigger step that implements an UPDATE statement and return +** a pointer to that trigger step. The parser calls this routine when it +** sees an UPDATE statement inside the body of a CREATE TRIGGER. +*/ TriggerStep *sqliteTriggerUpdateStep( - Token *pTableName, - ExprList *pEList, - Expr *pWhere, - int orconf) -{ + Token *pTableName, /* Name of the table to be updated */ + ExprList *pEList, /* The SET clause: list of column and new values */ + Expr *pWhere, /* The WHERE clause */ + int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ +){ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); pTriggerStep->op = TK_UPDATE; @@ -210,8 +228,12 @@ TriggerStep *sqliteTriggerUpdateStep( return pTriggerStep; } -TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere) -{ +/* +** Construct a trigger step that implements a DELETE statement and return +** a pointer to that trigger step. The parser calls this routine when it +** sees a DELETE statement inside the body of a CREATE TRIGGER. +*/ +TriggerStep *sqliteTriggerDeleteStep(Token *pTableName, Expr *pWhere){ TriggerStep * pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); pTriggerStep->op = TK_DELETE; @@ -328,7 +350,7 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested) }; if( !nested ){ - sqliteBeginWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 0); } base = sqliteVdbeAddOpList(pParse->pVdbe, ArraySize(dropTrigger), dropTrigger); @@ -345,8 +367,16 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested) sqliteFree(zName); } -static int checkColumnOverLap(IdList * pIdList, ExprList * pEList) -{ +/* +** pEList is the SET clause of an UPDATE statement. Each entry +** in pEList is of the format =. If any of the entries +** in pEList have an which matches an identifier in pIdList, +** then return TRUE. If pIdList==NULL, then it is considered a +** wildcard that matches anything. Likewise if pEList==NULL then +** it matches anything so always return true. Return false only +** if there is no match. +*/ +static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){ int i, e; if( !pIdList )return 1; if( !pEList )return 1; @@ -378,13 +408,13 @@ int always_code_trigger_setup = 0; * found in the list specified as pTrigger. */ int sqliteTriggersExist( - Parse *pParse, - Trigger *pTrigger, + Parse *pParse, /* Used to check for recursive triggers */ + Trigger *pTrigger, /* A list of triggers associated with a table */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ int tr_tm, /* one of TK_BEFORE, TK_AFTER */ int foreach, /* one of TK_ROW or TK_STATEMENT */ - ExprList *pChanges) -{ + ExprList *pChanges /* Columns that change in an UPDATE statement */ +){ Trigger * pTriggerCursor; if( always_code_trigger_setup ){ @@ -410,10 +440,14 @@ int sqliteTriggersExist( return 0; } +/* +** Generate VDBE code for zero or more statements inside the body of a +** trigger. +*/ static int codeTriggerProgram( - Parse *pParse, - TriggerStep *pStepList, - int orconfin + Parse *pParse, /* The parser context */ + TriggerStep *pStepList, /* List of statements inside the trigger body */ + int orconfin /* Conflict algorithm. (OE_Abort, etc) */ ){ TriggerStep * pTriggerStep = pStepList; int orconf; @@ -470,11 +504,15 @@ static int codeTriggerProgram( ** ** When the code that this function generates is executed, the following ** must be true: -** 1. NO vdbe cursors must be open. +** +** 1. No cursors may be open in the main database. (But newIdx and oldIdx +** can be indices of cursors in temporary tables. See below.) +** ** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then ** a temporary vdbe cursor (index newIdx) must be open and pointing at ** a row containing values to be substituted for new.* expressions in the ** trigger program(s). +** ** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then ** a temporary vdbe cursor (index oldIdx) must be open and pointing at ** a row containing values to be substituted for old.* expressions in the @@ -489,8 +527,8 @@ int sqliteCodeRowTrigger( Table *pTab, /* The table to code triggers from */ int newIdx, /* The indice of the "new" row to access */ int oldIdx, /* The indice of the "old" row to access */ - int orconf) /* ON CONFLICT policy */ -{ + int orconf /* ON CONFLICT policy */ +){ Trigger * pTrigger; TriggerStack * pTriggerStack; @@ -611,7 +649,7 @@ void sqliteViewTriggers( v = sqliteGetVdbe(pParse); assert(v); - sqliteBeginMultiWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 1); /* Allocate temp tables */ oldIdx = pParse->nTab++; diff --git a/src/update.c b/src/update.c index 48ea1b98a4..95923ec189 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.39 2002/05/19 23:43:14 danielk1977 Exp $ +** $Id: update.c,v 1.40 2002/05/21 11:38:12 drh Exp $ */ #include "sqliteInt.h" @@ -177,7 +177,7 @@ void sqliteUpdate( */ v = sqliteGetVdbe(pParse); if( v==0 ) goto update_cleanup; - sqliteBeginMultiWriteOperation(pParse); + sqliteBeginWriteOperation(pParse, 1); /* Begin the database scan */ diff --git a/test/misuse.test b/test/misuse.test index 6ae60262e4..062b359fda 100644 --- a/test/misuse.test +++ b/test/misuse.test @@ -13,7 +13,7 @@ # This file implements tests for the SQLITE_MISUSE detection logic. # This test file leaks memory and file descriptors. # -# $Id: misuse.test,v 1.2 2002/05/10 14:37:31 drh Exp $ +# $Id: misuse.test,v 1.3 2002/05/21 11:38:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -22,6 +22,7 @@ source $testdir/tester.tcl # do_test misuse-1.1 { db close + catch {file delete -force test2.db} set ::DB [sqlite db test2.db] execsql { CREATE TABLE t1(a,b);