1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-05 15:55:57 +03:00

Additional code cleanup resulting from a review of the new trigger code. (CVS 572)

FossilOrigin-Name: 37dbdd551e88440933066133ec9cc1e10b03fc1a
This commit is contained in:
drh
2002-05-21 11:38:11 +00:00
parent f29ce55958
commit c977f7f596
10 changed files with 185 additions and 148 deletions

View File

@@ -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) C Additional\scode\scleanup\sresulting\sfrom\sa\sreview\sof\sthe\snew\strigger\scode.\s(CVS\s572)
D 2002-05-19T23:43:13 D 2002-05-21T11:38:11
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495 F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -20,28 +20,28 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
F src/btree.c c01b404b9373ae1c0daf7d1f9211c72ead67638e F src/btree.c c01b404b9373ae1c0daf7d1f9211c72ead67638e
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
F src/build.c f6d2f428801e636c802fa63e951772569fa133a5 F src/build.c f5aa02e4553ca2db941d6e7f025a180ddc4cea49
F src/delete.c f9ab9738c5a6fed434ec9e7721a178f58f65f57a F src/delete.c c2eae01b76d5418d4ff1768659dfb199c38f0641
F src/encode.c 346b12b46148506c32038524b95c4631ab46d760 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
F src/expr.c 2c7535ba013b731ed73f12b6cc1cd5b466efb3e2 F src/expr.c 2c7535ba013b731ed73f12b6cc1cd5b466efb3e2
F src/func.c a31dcba85bc2ecb9b752980289cf7e6cd0cafbce F src/func.c a31dcba85bc2ecb9b752980289cf7e6cd0cafbce
F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892 F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
F src/insert.c 5233701a402cf5f4329525a654928c5de8d9a4e8 F src/insert.c bc98bfe7f508f7a3027ea93ef9bbdc87bbce1e93
F src/main.c 3f0db74a3e8385322a3b69f51bea6ce19caeff19 F src/main.c 3f0db74a3e8385322a3b69f51bea6ce19caeff19
F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc F src/os.c 5ab8b6b4590d0c1ab8e96c67996c170e4462e0fc
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10 F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
F src/pager.c ba5740104cc27b342cd43eebfdc44d60f64a3ded F src/pager.c ba5740104cc27b342cd43eebfdc44d60f64a3ded
F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e
F src/parse.y 12d6f6c0d12c868b366e3758e62578e686623394 F src/parse.y 964a7cc954e3b84e9de54692648fb70bee1c0ba8
F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c 1b623a7d826ec7c245bc542b665d61724da2a62d F src/select.c 1b623a7d826ec7c245bc542b665d61724da2a62d
F src/shell.c 5acbe59e137d60d8efd975c683dbea74ab626530 F src/shell.c 5acbe59e137d60d8efd975c683dbea74ab626530
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
F src/sqliteInt.h 66bf5c2d48ed35efd5e663501b6c5f830bc87c37 F src/sqliteInt.h aa18969cf0426fa6bb42c2f2c5baf1179710376e
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1 F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e
@@ -49,8 +49,8 @@ F src/test2.c 669cc22781c6461a273416ec1a7414d25c081730
F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
F src/tokenize.c f12f78c58b2a79ea4eee880efad63a328e103c62 F src/tokenize.c f12f78c58b2a79ea4eee880efad63a328e103c62
F src/trigger.c 43dbeb68ba379049a78d270db0db27fa01deec12 F src/trigger.c 143d3d2bd1318b88b095b4081dcbb10c842bc1b7
F src/update.c 6b1d80b5058e686c2e3197d72c567ea27d35f42e F src/update.c 46c4b3e86c066cd8a0dcf0b75e4580bcf113aeb0
F src/util.c 707c30f8c13cddace7c08556ac450c0b786660b3 F src/util.c 707c30f8c13cddace7c08556ac450c0b786660b3
F src/vdbe.c 5fd717e6a42e98e4f244a2149267004f71f23679 F src/vdbe.c 5fd717e6a42e98e4f244a2149267004f71f23679
F src/vdbe.h 126a651ba26f05de075dcc6da5466244a31af6b8 F src/vdbe.h 126a651ba26f05de075dcc6da5466244a31af6b8
@@ -77,7 +77,7 @@ F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85
F test/malloc.test 70fdd0812e2a57eb746aaf015350f58bb8eee0b1 F test/malloc.test 70fdd0812e2a57eb746aaf015350f58bb8eee0b1
F test/minmax.test fb6ab400271ae1f5bc88617c2882f2f081ea8e6d F test/minmax.test fb6ab400271ae1f5bc88617c2882f2f081ea8e6d
F test/misc1.test a03214118429b40ca5548bc1fae0ebd5c34dabe6 F test/misc1.test a03214118429b40ca5548bc1fae0ebd5c34dabe6
F test/misuse.test 2c401721fbbe01897a80cef12e2fbbad4ca8a807 F test/misuse.test a3aa2b18a97e4c409a1fcaff5151a4dd804a0162
F test/notnull.test b1f3e42fc475b0b5827b27b2e9b562081995ff30 F test/notnull.test b1f3e42fc475b0b5827b27b2e9b562081995ff30
F test/pager.test b0c0d00cd5dce0ce21f16926956b195c0ab5044c F test/pager.test b0c0d00cd5dce0ce21f16926956b195c0ab5044c
F test/pragma.test 0b9675ef1f5ba5b43abfa337744445fc5b01a34a F test/pragma.test 0b9675ef1f5ba5b43abfa337744445fc5b01a34a
@@ -134,7 +134,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
P b1d72cb5847a9f5f08e40b36ad117b5493232ea7 P 8a4195c7466962291a296e8f53034ea8cb25005f
R 6a41d52fabcd64a48785213ecafaf488 R 361b70ca0dd473638df412c87f908919
U danielk1977 U drh
Z f416062573015e51faa69ab13000e10e Z 5451ccaf091b36f9a5e5679c783b7dab

View File

@@ -1 +1 @@
8a4195c7466962291a296e8f53034ea8cb25005f 37dbdd551e88440933066133ec9cc1e10b03fc1a

View File

@@ -25,7 +25,7 @@
** ROLLBACK ** ROLLBACK
** PRAGMA ** 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 "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@@ -478,7 +478,7 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName, int isTemp){
** now. ** now.
*/ */
if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){ if( !pParse->initFlag && (v = sqliteGetVdbe(pParse))!=0 ){
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
if( !isTemp ){ if( !isTemp ){
sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0); sqliteVdbeAddOp(v, OP_Integer, db->file_format, 0);
sqliteVdbeAddOp(v, OP_SetCookie, 0, 1); sqliteVdbeAddOp(v, OP_SetCookie, 0, 1);
@@ -1092,7 +1092,7 @@ void sqliteDropTable(Parse *pParse, Token *pName, int isView){
{ OP_Close, 0, 0, 0}, { OP_Close, 0, 0, 0},
}; };
Index *pIdx; Index *pIdx;
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
/* Drop all triggers associated with the table being dropped */ /* Drop all triggers associated with the table being dropped */
while( pTable->pTrigger ){ while( pTable->pTrigger ){
Token tt; Token tt;
@@ -1355,7 +1355,7 @@ void sqliteCreateIndex(
v = sqliteGetVdbe(pParse); v = sqliteGetVdbe(pParse);
if( v==0 ) goto exit_create_index; if( v==0 ) goto exit_create_index;
if( pTable!=0 ){ if( pTable!=0 ){
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
if( !isTemp ){ if( !isTemp ){
sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2); sqliteVdbeAddOp(v, OP_OpenWrite, 0, 2);
sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC); sqliteVdbeChangeP3(v, -1, MASTER_NAME, P3_STATIC);
@@ -1467,7 +1467,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
int base; int base;
Table *pTab = pIndex->pTable; Table *pTab = pIndex->pTable;
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
if( !pTab->isTemp ){ if( !pTab->isTemp ){
base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex); base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
sqliteVdbeChangeP3(v, base+2, pIndex->zName, P3_STATIC); sqliteVdbeChangeP3(v, base+2, pIndex->zName, P3_STATIC);
@@ -1588,7 +1588,7 @@ void sqliteCopy(
v = sqliteGetVdbe(pParse); v = sqliteGetVdbe(pParse);
if( v ){ if( v ){
int openOp; int openOp;
sqliteBeginMultiWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 1);
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0); addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n); sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
sqliteVdbeDequoteP3(v, addr); 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==0 || (db=pParse->db)==0 || db->pBe==0 ) return;
if( pParse->nErr || sqlite_malloc_failed ) return; if( pParse->nErr || sqlite_malloc_failed ) return;
if( db->flags & SQLITE_InTrans ) return; if( db->flags & SQLITE_InTrans ) return;
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
db->flags |= SQLITE_InTrans; db->flags |= SQLITE_InTrans;
db->onError = onError; db->onError = onError;
} }
@@ -1708,32 +1708,18 @@ void sqliteRollbackTransaction(Parse *pParse){
/* /*
** Generate VDBE code that prepares for doing an operation that ** Generate VDBE code that prepares for doing an operation that
** might change the database. The operation will be atomic in the ** might change the database.
** 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 ** This routine starts a new transaction if we are not already within
** is already in effect. ** 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){ void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint){
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){
Vdbe *v; Vdbe *v;
v = sqliteGetVdbe(pParse); v = sqliteGetVdbe(pParse);
if( v==0 ) return; if( v==0 ) return;
@@ -1742,7 +1728,7 @@ void sqliteBeginMultiWriteOperation(Parse *pParse){
sqliteVdbeAddOp(v, OP_Transaction, 0, 0); sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0); sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
pParse->schemaVerified = 1; pParse->schemaVerified = 1;
}else{ }else if( setCheckpoint ){
sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0); sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
} }
} }
@@ -1844,7 +1830,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
int addr; int addr;
int size = atoi(zRight); int size = atoi(zRight);
if( size<0 ) size = -size; if( size<0 ) size = -size;
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
sqliteVdbeAddOp(v, OP_Integer, size, 0); sqliteVdbeAddOp(v, OP_Integer, size, 0);
sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);
@@ -1935,7 +1921,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
int addr; int addr;
int size = db->cache_size; int size = db->cache_size;
if( size<0 ) size = -size; if( size<0 ) size = -size;
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2); sqliteVdbeAddOp(v, OP_ReadCookie, 0, 2);
sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_Dup, 0, 0);
addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0); addr = sqliteVdbeAddOp(v, OP_Integer, 0, 0);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements. ** 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" #include "sqliteInt.h"
@@ -75,6 +75,7 @@ void sqliteDeleteFrom(
){ ){
Vdbe *v; /* The virtual database engine */ Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */ 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 */ IdList *pTabList; /* An ID list holding pTab and nothing else */
int end, addr; /* A couple addresses of generated code */ int end, addr; /* A couple addresses of generated code */
int i; /* Loop counter */ 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 /* Check for the special case of a VIEW with one or more ON DELETE triggers
** defined ** defined
*/ */
{ zTab = sqliteTableNameFromToken(pTableName);
Table *pTab; if( zTab != 0 ){
char *zTab = sqliteTableNameFromToken(pTableName); pTab = sqliteFindTable(pParse->db, zTab);
if( pTab ){
if( zTab != 0 ){ row_triggers_exist =
pTab = sqliteFindTable(pParse->db, zTab); sqliteTriggersExist(pParse, pTab->pTrigger,
if( pTab ){ TK_DELETE, TK_BEFORE, TK_ROW, 0) ||
row_triggers_exist = sqliteTriggersExist(pParse, pTab->pTrigger,
sqliteTriggersExist(pParse, pTab->pTrigger, TK_DELETE, TK_AFTER, TK_ROW, 0);
TK_DELETE, TK_BEFORE, TK_ROW, 0) || }
sqliteTriggersExist(pParse, pTab->pTrigger, sqliteFree(zTab);
TK_DELETE, TK_AFTER, TK_ROW, 0); if( row_triggers_exist && pTab->pSelect ){
} /* Just fire VIEW triggers */
sqliteFree(zTab); sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
if( row_triggers_exist && pTab->pSelect ){ return;
/* 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 /* Locate the table which we want to delete. This table has to be
** put in an IdList structure because some of the subroutines we ** put in an IdList structure because some of the subroutines we
** will be calling are designed to work with multiple tables and expect ** 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); pTabList = sqliteTableTokenToIdList(pParse, pTableName);
if( pTabList==0 ) goto delete_from_cleanup; if( pTabList==0 ) goto delete_from_cleanup;
@@ -129,6 +126,8 @@ void sqliteDeleteFrom(
pTab = pTabList->a[0].pTab; pTab = pTabList->a[0].pTab;
assert( pTab->pSelect==0 ); /* This table is not a view */ 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 ){ if( row_triggers_exist ){
oldIdx = pParse->nTab++; oldIdx = pParse->nTab++;
} }
@@ -151,11 +150,7 @@ void sqliteDeleteFrom(
if( v==0 ){ if( v==0 ){
goto delete_from_cleanup; goto delete_from_cleanup;
} }
if( row_triggers_exist ){ sqliteBeginWriteOperation(pParse, row_triggers_exist);
sqliteBeginMultiWriteOperation(pParse);
} else {
sqliteBeginWriteOperation(pParse);
}
/* Initialize the counter of the number of rows deleted, if /* Initialize the counter of the number of rows deleted, if
** we are counting rows. ** we are counting rows.
@@ -165,7 +160,8 @@ void sqliteDeleteFrom(
} }
/* Special case: A DELETE without a WHERE clause deletes everything. /* 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( pWhere==0 && !row_triggers_exist ){
if( db->flags & SQLITE_CountRows ){ if( db->flags & SQLITE_CountRows ){
@@ -215,8 +211,10 @@ void sqliteDeleteFrom(
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
end = sqliteVdbeMakeLabel(v); end = sqliteVdbeMakeLabel(v);
/* This is the beginning of the delete loop when there are
** row triggers.
*/
if( row_triggers_exist ){ if( row_triggers_exist ){
int ii;
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
sqliteVdbeAddOp(v, OP_Dup, 0, 0); sqliteVdbeAddOp(v, OP_Dup, 0, 0);
@@ -226,11 +224,11 @@ void sqliteDeleteFrom(
sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0); sqliteVdbeAddOp(v, OP_OpenTemp, oldIdx, 0);
sqliteVdbeAddOp(v, OP_Integer, 13, 0); sqliteVdbeAddOp(v, OP_Integer, 13, 0);
for(ii = 0; ii<pTab->nCol; ii++){ for(i = 0; i<pTab->nCol; i++){
if( ii==pTab->iPKey ){ if( i==pTab->iPKey ){
sqliteVdbeAddOp(v, OP_Recno, base, 0); sqliteVdbeAddOp(v, OP_Recno, base, 0);
} else { } else {
sqliteVdbeAddOp(v, OP_Column, base, ii); sqliteVdbeAddOp(v, OP_Column, base, i);
} }
} }
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0); sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
@@ -242,6 +240,12 @@ void sqliteDeleteFrom(
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default); 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; pParse->nTab = base + 1;
openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
sqliteVdbeAddOp(v, openOp, base, pTab->tnum); sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
@@ -249,12 +253,18 @@ void sqliteDeleteFrom(
sqliteVdbeAddOp(v, openOp, pParse->nTab++, pIdx->tnum); 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 ){ if( !row_triggers_exist ){
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end); 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 ){ if( row_triggers_exist ){
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum); sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);
@@ -264,10 +274,12 @@ void sqliteDeleteFrom(
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default); oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
} }
/* End of the delete loop */
sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeAddOp(v, OP_Goto, 0, addr);
sqliteVdbeResolveLabel(v, end); sqliteVdbeResolveLabel(v, end);
sqliteVdbeAddOp(v, OP_ListReset, 0, 0); sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
/* Close the cursors after the loop if there are no row triggers */
if( !row_triggers_exist ){ if( !row_triggers_exist ){
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum); sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite. ** 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" #include "sqliteInt.h"
@@ -95,11 +95,7 @@ void sqliteInsert(
*/ */
v = sqliteGetVdbe(pParse); v = sqliteGetVdbe(pParse);
if( v==0 ) goto insert_cleanup; if( v==0 ) goto insert_cleanup;
if( pSelect || row_triggers_exist ){ sqliteBeginWriteOperation(pParse, pSelect || row_triggers_exist);
sqliteBeginMultiWriteOperation(pParse);
}else{
sqliteBeginWriteOperation(pParse);
}
/* if there are row triggers, allocate a temp table for new.* references. */ /* if there are row triggers, allocate a temp table for new.* references. */
if( row_triggers_exist ){ if( row_triggers_exist ){

View File

@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing ** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens. ** 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_prefix TK_
%token_type {Token} %token_type {Token}
@@ -98,27 +98,32 @@ columnid ::= ids(X). {sqliteAddColumn(pParse,&X);}
// An IDENTIFIER can be a generic identifier, or one of several // An IDENTIFIER can be a generic identifier, or one of several
// keywords. Any non-standard keyword can also be an identifier. // 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} %type id {Token}
id(A) ::= ABORT(X). {A = X;} id(A) ::= ABORT(X). {A = X;}
id(A) ::= AFTER(X). {A = X;}
id(A) ::= ASC(X). {A = X;} id(A) ::= ASC(X). {A = X;}
id(A) ::= BEFORE(X). {A = X;}
id(A) ::= BEGIN(X). {A = X;} id(A) ::= BEGIN(X). {A = X;}
id(A) ::= CLUSTER(X). {A = X;} id(A) ::= CLUSTER(X). {A = X;}
id(A) ::= CONFLICT(X). {A = X;} id(A) ::= CONFLICT(X). {A = X;}
id(A) ::= COPY(X). {A = X;} id(A) ::= COPY(X). {A = X;}
id(A) ::= DELIMITERS(X). {A = X;} id(A) ::= DELIMITERS(X). {A = X;}
id(A) ::= DESC(X). {A = X;} id(A) ::= DESC(X). {A = X;}
id(A) ::= EACH(X). {A = X;}
id(A) ::= END(X). {A = X;} id(A) ::= END(X). {A = X;}
id(A) ::= EXPLAIN(X). {A = X;} id(A) ::= EXPLAIN(X). {A = X;}
id(A) ::= FAIL(X). {A = X;} id(A) ::= FAIL(X). {A = X;}
id(A) ::= FOR(X). {A = X;}
id(A) ::= ID(X). {A = X;} id(A) ::= ID(X). {A = X;}
id(A) ::= IGNORE(X). {A = X;} id(A) ::= IGNORE(X). {A = X;}
id(A) ::= INSTEAD(X). {A = X;}
id(A) ::= KEY(X). {A = X;} id(A) ::= KEY(X). {A = X;}
id(A) ::= OF(X). {A = X;}
id(A) ::= OFFSET(X). {A = X;} id(A) ::= OFFSET(X). {A = X;}
id(A) ::= PRAGMA(X). {A = X;} id(A) ::= PRAGMA(X). {A = X;}
id(A) ::= REPLACE(X). {A = X;} id(A) ::= REPLACE(X). {A = X;}
id(A) ::= ROW(X). {A = X;}
id(A) ::= TEMP(X). {A = X;} id(A) ::= TEMP(X). {A = X;}
id(A) ::= TRIGGER(X). {A = X;} id(A) ::= TRIGGER(X). {A = X;}
id(A) ::= VACUUM(X). {A = X;} id(A) ::= VACUUM(X). {A = X;}

View File

@@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** 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 "sqlite.h"
#include "hash.h" #include "hash.h"
@@ -798,8 +798,7 @@ void sqliteGenerateRowDelete(Vdbe*, Table*, int, int);
void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*); void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*);
void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int); void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
void sqliteBeginWriteOperation(Parse*); void sqliteBeginWriteOperation(Parse*, int);
void sqliteBeginMultiWriteOperation(Parse*);
void sqliteEndWriteOperation(Parse*); void sqliteEndWriteOperation(Parse*);
void sqliteExprMoveStrings(Expr*, int); void sqliteExprMoveStrings(Expr*, int);
void sqliteExprListMoveStrings(ExprList*, int); void sqliteExprListMoveStrings(ExprList*, int);

View File

@@ -95,32 +95,32 @@ void sqliteCreateTrigger(
** build the sqlite_master entry ** build the sqlite_master entry
*/ */
if( !pParse->initFlag && !tab->isTemp ){ 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 */ /* Make an entry in the sqlite_master table */
sqliteBeginWriteOperation(pParse); v = sqliteGetVdbe(pParse);
sqliteBeginWriteOperation(pParse, 0);
sqliteVdbeAddOp(pParse->pVdbe, OP_OpenWrite, 0, 2); addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
sqliteVdbeChangeP3(pParse->pVdbe, -1, MASTER_NAME, P3_STATIC); sqliteVdbeChangeP3(v, addr+3, nt->name, 0);
sqliteVdbeAddOp(pParse->pVdbe, OP_NewRecno, 0, 0); sqliteVdbeChangeP3(v, addr+4, nt->table, 0);
sqliteVdbeAddOp(pParse->pVdbe, OP_String, 0, 0); sqliteVdbeChangeP3(v, addr+6, nt->strings, 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 */
sqliteChangeCookie(pParse->db); sqliteChangeCookie(pParse->db);
sqliteVdbeAddOp(pParse->pVdbe, OP_Integer, pParse->db->next_cookie, 0); sqliteVdbeChangeP1(v, addr+9, pParse->db->next_cookie);
sqliteVdbeAddOp(pParse->pVdbe, OP_SetCookie, 0, 0);
sqliteVdbeAddOp(pParse->pVdbe, OP_Close, 0, 0);
sqliteEndWriteOperation(pParse); 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)); TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
pTriggerStep->op = TK_SELECT; pTriggerStep->op = TK_SELECT;
@@ -171,12 +177,19 @@ TriggerStep *sqliteTriggerSelectStep(Select * pSelect)
return pTriggerStep; 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( TriggerStep *sqliteTriggerInsertStep(
Token *pTableName, Token *pTableName, /* Name of the table into which we insert */
IdList *pColumn, IdList *pColumn, /* List of columns in pTableName to insert into */
ExprList *pEList, ExprList *pEList, /* The VALUE clause: a list of values to be inserted */
Select *pSelect, Select *pSelect, /* A SELECT statement that supplies values */
int orconf int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
){ ){
TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
@@ -193,12 +206,17 @@ TriggerStep *sqliteTriggerInsertStep(
return pTriggerStep; 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( TriggerStep *sqliteTriggerUpdateStep(
Token *pTableName, Token *pTableName, /* Name of the table to be updated */
ExprList *pEList, ExprList *pEList, /* The SET clause: list of column and new values */
Expr *pWhere, Expr *pWhere, /* The WHERE clause */
int orconf) int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
{ ){
TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep)); TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
pTriggerStep->op = TK_UPDATE; pTriggerStep->op = TK_UPDATE;
@@ -210,8 +228,12 @@ TriggerStep *sqliteTriggerUpdateStep(
return pTriggerStep; 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)); TriggerStep * pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
pTriggerStep->op = TK_DELETE; pTriggerStep->op = TK_DELETE;
@@ -328,7 +350,7 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested)
}; };
if( !nested ){ if( !nested ){
sqliteBeginWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 0);
} }
base = sqliteVdbeAddOpList(pParse->pVdbe, base = sqliteVdbeAddOpList(pParse->pVdbe,
ArraySize(dropTrigger), dropTrigger); ArraySize(dropTrigger), dropTrigger);
@@ -345,8 +367,16 @@ void sqliteDropTrigger(Parse *pParse, Token *pName, int nested)
sqliteFree(zName); 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 <id>=<expr>. If any of the entries
** in pEList have an <id> 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; int i, e;
if( !pIdList )return 1; if( !pIdList )return 1;
if( !pEList )return 1; if( !pEList )return 1;
@@ -378,13 +408,13 @@ int always_code_trigger_setup = 0;
* found in the list specified as pTrigger. * found in the list specified as pTrigger.
*/ */
int sqliteTriggersExist( int sqliteTriggersExist(
Parse *pParse, Parse *pParse, /* Used to check for recursive triggers */
Trigger *pTrigger, Trigger *pTrigger, /* A list of triggers associated with a table */
int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
int tr_tm, /* one of TK_BEFORE, TK_AFTER */ int tr_tm, /* one of TK_BEFORE, TK_AFTER */
int foreach, /* one of TK_ROW or TK_STATEMENT */ int foreach, /* one of TK_ROW or TK_STATEMENT */
ExprList *pChanges) ExprList *pChanges /* Columns that change in an UPDATE statement */
{ ){
Trigger * pTriggerCursor; Trigger * pTriggerCursor;
if( always_code_trigger_setup ){ if( always_code_trigger_setup ){
@@ -410,10 +440,14 @@ int sqliteTriggersExist(
return 0; return 0;
} }
/*
** Generate VDBE code for zero or more statements inside the body of a
** trigger.
*/
static int codeTriggerProgram( static int codeTriggerProgram(
Parse *pParse, Parse *pParse, /* The parser context */
TriggerStep *pStepList, TriggerStep *pStepList, /* List of statements inside the trigger body */
int orconfin int orconfin /* Conflict algorithm. (OE_Abort, etc) */
){ ){
TriggerStep * pTriggerStep = pStepList; TriggerStep * pTriggerStep = pStepList;
int orconf; int orconf;
@@ -470,11 +504,15 @@ static int codeTriggerProgram(
** **
** When the code that this function generates is executed, the following ** When the code that this function generates is executed, the following
** must be true: ** 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 ** 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 temporary vdbe cursor (index newIdx) must be open and pointing at
** a row containing values to be substituted for new.* expressions in the ** a row containing values to be substituted for new.* expressions in the
** trigger program(s). ** trigger program(s).
**
** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then ** 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 temporary vdbe cursor (index oldIdx) must be open and pointing at
** a row containing values to be substituted for old.* expressions in the ** 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 */ Table *pTab, /* The table to code triggers from */
int newIdx, /* The indice of the "new" row to access */ int newIdx, /* The indice of the "new" row to access */
int oldIdx, /* The indice of the "old" 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; Trigger * pTrigger;
TriggerStack * pTriggerStack; TriggerStack * pTriggerStack;
@@ -611,7 +649,7 @@ void sqliteViewTriggers(
v = sqliteGetVdbe(pParse); v = sqliteGetVdbe(pParse);
assert(v); assert(v);
sqliteBeginMultiWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 1);
/* Allocate temp tables */ /* Allocate temp tables */
oldIdx = pParse->nTab++; oldIdx = pParse->nTab++;

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle UPDATE statements. ** 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" #include "sqliteInt.h"
@@ -177,7 +177,7 @@ void sqliteUpdate(
*/ */
v = sqliteGetVdbe(pParse); v = sqliteGetVdbe(pParse);
if( v==0 ) goto update_cleanup; if( v==0 ) goto update_cleanup;
sqliteBeginMultiWriteOperation(pParse); sqliteBeginWriteOperation(pParse, 1);
/* Begin the database scan /* Begin the database scan
*/ */

View File

@@ -13,7 +13,7 @@
# This file implements tests for the SQLITE_MISUSE detection logic. # This file implements tests for the SQLITE_MISUSE detection logic.
# This test file leaks memory and file descriptors. # 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] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
@@ -22,6 +22,7 @@ source $testdir/tester.tcl
# #
do_test misuse-1.1 { do_test misuse-1.1 {
db close db close
catch {file delete -force test2.db}
set ::DB [sqlite db test2.db] set ::DB [sqlite db test2.db]
execsql { execsql {
CREATE TABLE t1(a,b); CREATE TABLE t1(a,b);