From ef2cb63e9e15a749c7ae0b0a3ce3dd6fd6cd9029 Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Sat, 29 May 2004 02:37:19 +0000 Subject: [PATCH] Allow CREATE and DROP TRIGGER on attached databases. (CVS 1488) FossilOrigin-Name: 4060a37d0baaa60c50f2dde4a1ab344133fcabbb --- manifest | 30 ++--- manifest.uuid | 2 +- src/build.c | 60 ++++------ src/parse.y | 11 +- src/sqliteInt.h | 11 +- src/tclsqlite.c | 290 +++++---------------------------------------- src/trigger.c | 122 +++++++++++-------- src/vdbe.c | 6 +- test/attach.test | 4 +- test/attach3.test | 65 +++++++--- test/trigger1.test | 8 +- 11 files changed, 211 insertions(+), 398 deletions(-) diff --git a/manifest b/manifest index 925dcad8ce..b62be5461d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Factor\scommon\scode\sfor\sgenerating\sindex\skeys\sinto\sa\sprocedure.\s\sOther\nspeed\simprovements\sand\sbug\sfixes.\s(CVS\s1487) -D 2004-05-28T16:00:22 +C Allow\sCREATE\sand\sDROP\sTRIGGER\son\sattached\sdatabases.\s(CVS\s1488) +D 2004-05-29T02:37:19 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -26,7 +26,7 @@ F src/attach.c c315c58cb16fd6e913b3bfa6412aedecb4567fa5 F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79 F src/btree.c 6db76fbf63efd6008c5e6cb038ea40f94abffcf7 F src/btree.h b65140b5ae891f30d2a39e64b9f0343225553545 -F src/build.c 60ec4b38c0f158c9f2e4778ef6af13d19af7bfcd +F src/build.c ed09cd54a48ef2ef700c7e3a63b5e35224bde9cc F src/date.c 0eb922af5c5f5e2455f8dc2f98023ed3e04a857e F src/delete.c 72f8febf6170cda830f509c8f9dffbed3df3596c F src/encode.c a876af473d1d636faa3dca51c7571f2e007eea37 @@ -48,28 +48,28 @@ F src/os_win.c 92b51a38437b98d8aa3ac05b57c71e1d1092e5be F src/os_win.h 5d41af24caaef6c13a2d8e2399caa1c57d45c84d F src/pager.c 6ff6b906427d4824099140776cb8768f922f3dc5 F src/pager.h 78a00ac280899bcba1a89dc51585dcae6b7b3253 -F src/parse.y 9d3be712abc9005495701efbec741c58408f1343 +F src/parse.y fbb2378795cad3f6141836fb2035b97bd5ddad4e F src/pragma.c 0c17b613d719c62a0dbad659b7d8a6e7ce7e9733 F src/printf.c ef750e8e2398ca7e8b58be991075f08c6a7f0e53 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 F src/select.c ee54bf2faa76f5e30832fd589a1f10c485047469 F src/shell.c ed4d237b3e52a0a42512bfcc53530e46de20c28f F src/sqlite.h.in edc6408c7f53c2104f781a76b926036e17018ec9 -F src/sqliteInt.h d95d08442d19e2ee592ce1ec7865cbbcf23640bd +F src/sqliteInt.h 01f9250ee3a1ab681b7ed91ad2b3748c2f230521 F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2 -F src/tclsqlite.c 877d0b96013a25b03ed6bd2d32917c42e84403bc +F src/tclsqlite.c b314f12760547e4ef090e055f1298f70627450d3 F src/test1.c 32934478366531503d634968db414df17cb38238 F src/test2.c 6195a1ca2c8d0d2d93644e86da3289b403486872 F src/test3.c 5e4a6d596f982f6f47a5f9f75ede9b4a3b739968 F src/test4.c 34848a9fd31aa65857b20a8bfc03aff77d8c3426 F src/test5.c 9a1f15133f6955f067c5246e564723b5f23ff221 F src/tokenize.c 50a87c7414de54a008427c9fed22e4e86efb6844 -F src/trigger.c 9ab75040aec65b593b54a7c1d4546f2f9ca058ef +F src/trigger.c 9040e5dd7e5586e863c20acdca6808e8f7bb9727 F src/update.c 96461bcf4e946697e83c09c77c7e61b545a2f66e F src/utf.c d87fffc1ea7e52d73014ccea06afe1382bbb28b2 F src/util.c 4df9d9b0d930d81ec581bcb68748e7c48bdc8c7d F src/vacuum.c 8734f89742f246abd91dbd3e087fc153bddbfbad -F src/vdbe.c 68f3583ba2cb13ef0121be99e0edccfff133281a +F src/vdbe.c ea010d63dfdf84b7d23781144fe2cd11add2c1bd F src/vdbe.h e73f890e0f2a6c42b183d7d6937947930fe4fdeb F src/vdbeInt.h c2bcd6e5a6e6a3753e4c5a368629c3a625719bfc F src/vdbeapi.c 0c5d64c81871cb4fe5407e639604ee95738b6942 @@ -77,9 +77,9 @@ F src/vdbeaux.c bbcf1bb953526130495b01b23751cf756cfed2fb F src/vdbemem.c c97c145ff6d9fc5b4236704c04a65849117e6214 F src/where.c efe5d25fe18cd7381722457898cd863e84097a0c F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 -F test/attach.test cb9b884344e6cfa5e165965d5b1adea679a24c83 +F test/attach.test e872e1cf3e97949727d1a2c9582efeaf04b192a3 F test/attach2.test 5472d442bb2ef1ee587e0ae7472bb68b52509a38 -F test/attach3.test 8c55071e4629fe781f3b1955454db2b7f33c943b +F test/attach3.test 65c52f1e5f435518db06a877eed6afe2cac652c9 F test/auth.test 95809b8f6a9bec18b94d28cafd03fe27d2f8a9e9 F test/bigfile.test ea904b853ce2d703b16c5ce90e2b54951bc1ae81 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 @@ -151,7 +151,7 @@ F test/thread1.test 53f050d5be6932d9430df7756edd379366508ff6 F test/threadtest1.c f7f896e62ed46feae1dc411114a48c15a0f82ee2 F test/threadtest2.c d94ca4114fd1504f7e0ae724bcd83d4b40931d86 F test/trans.test 0cb8256daff1ae0da75321e00125338c6681158d -F test/trigger1.test 4538c1c7d6bbca5dfe619ea6e1682b07ece95b21 +F test/trigger1.test 99b7cd9a568ac60aa04bbc3b9db9575ffa97709a F test/trigger2.test 0767ab30cb5a2c8402c8524f3d566b410b6f5263 F test/trigger3.test a95ccace88291449f5eae7139ec438a42f90654d F test/trigger4.test 542afce45774e8f8e1130b96b8675f414d6e4bd8 @@ -204,7 +204,7 @@ F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P c8a40218c20cf5d0abad330e8fa59ca4c36e7608 -R 3143f6cfe246debfe7547a873f48094e -U drh -Z 33a80435a29a583c43c0f69454c7f7ae +P 6661bb5f9c1692f94b8b7d900b6be07f027e6324 +R fde9d916f771cc8e55efbaa7078f8e6a +U danielk1977 +Z b5bca915020b46ffea1984f7a4d9cf29 diff --git a/manifest.uuid b/manifest.uuid index c1e422d992..f022fb6e2b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6661bb5f9c1692f94b8b7d900b6be07f027e6324 \ No newline at end of file +4060a37d0baaa60c50f2dde4a1ab344133fcabbb \ No newline at end of file diff --git a/src/build.c b/src/build.c index bf208acd22..8710897e88 100644 --- a/src/build.c +++ b/src/build.c @@ -23,7 +23,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.197 2004/05/28 16:00:22 drh Exp $ +** $Id: build.c,v 1.198 2004/05/29 02:37:19 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -425,7 +425,7 @@ int findDb(sqlite3 *db, Token *pName){ return -1; } -static int resolveSchemaName( +int sqlite3TwoPartName( Parse *pParse, Token *pName1, Token *pName2, @@ -501,7 +501,7 @@ void sqlite3StartTable( ** set to the index of the database that the table or view is to be ** created in. */ - iDb = resolveSchemaName(pParse, pName1, pName2, &pName); + iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) return; if( isTemp && iDb>1 ){ /* If creating a temp table, the name may not be qualified */ @@ -1166,7 +1166,7 @@ void sqlite3CreateView( sqlite3SelectDelete(pSelect); return; } - resolveSchemaName(pParse, pName1, pName2, &pName); + sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName) && sqlite3FixSelect(&sFix, pSelect) ){ @@ -1606,7 +1606,7 @@ void sqlite3CreateIndex( Parse *pParse, /* All information about this parse */ Token *pName1, /* First part of index name. May be NULL */ Token *pName2, /* Second part of index name. May be NULL */ - Token *pTblName, /* Name of the table to index. Use pParse->pNewTable if 0 */ + SrcList *pTblName, /* Name of the table to index. Use pParse->pNewTable if 0 */ IdList *pList, /* A list of columns to be indexed */ int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */ @@ -1624,50 +1624,38 @@ void sqlite3CreateIndex( int iDb; /* Index of the database that is being written */ Token *pName = 0; /* Unqualified name of the index to create */ -/* if( pParse->nErr || sqlite3_malloc_failed ) goto exit_create_index; - if( db->init.busy - && sqlite3FixInit(&sFix, pParse, db->init.iDb, "index", pName) - && sqlite3FixSrcList(&sFix, pTable) - ){ - goto exit_create_index; - } -*/ /* ** Find the table that is to be indexed. Return early if not found. */ if( pTblName!=0 ){ - char *zTblName; /* Use the two-part index name to determine the database - ** to search for the table. If no database name is specified, - ** iDb is set to 0. In this case search both the temp and main - ** databases for the named table. + ** to search for the table. 'Fix' the table name to this db + ** before looking up the table. */ assert( pName1 && pName2 ); - iDb = resolveSchemaName(pParse, pName1, pName2, &pName); + iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) goto exit_create_index; - /* Now search for the table in the database iDb. If iDb is - ** zero, then search both the "main" and "temp" databases. + /* If the index name was unqualified, check if the the table + ** is a temp table. If so, set the database to 1. */ - zTblName = sqlite3TableNameFromToken(pTblName); - if( !zTblName ){ - pParse->nErr++; - pParse->rc = SQLITE_NOMEM; + pTab = sqlite3SrcListLookup(pParse, pTblName); + if( pName2 && pName2->n==0 && pTab && pTab->iDb==1 ){ + iDb = 1; + } + + if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) && + sqlite3FixSrcList(&sFix, pTblName) + ){ goto exit_create_index; } - assert( pName1!=0 ); - if( iDb==0 ){ - pTab = sqlite3FindTable(db, zTblName, "temp"); - } - if( !pTab ){ - pTab = sqlite3LocateTable(pParse, zTblName, db->aDb[iDb].zName); - } - sqliteFree( zTblName ); + pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName, + pTblName->a[0].zDatabase); if( !pTab ) goto exit_create_index; - iDb = pTab->iDb; + assert( iDb==pTab->iDb ); }else{ assert( pName==0 ); pTab = pParse->pNewTable; @@ -1679,12 +1667,6 @@ void sqlite3CreateIndex( sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); goto exit_create_index; } -/* - if( pTab->iDb>=2 && db->init.busy==0 ){ - sqlite3ErrorMsg(pParse, "table %s may not have indices added", pTab->zName); - goto exit_create_index; - } -*/ if( pTab->pSelect ){ sqlite3ErrorMsg(pParse, "views may not be indexed"); goto exit_create_index; diff --git a/src/parse.y b/src/parse.y index 6f3a34e27a..4b117c2335 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.122 2004/05/28 12:33:31 danielk1977 Exp $ +** @(#) $Id: parse.y,v 1.123 2004/05/29 02:37:19 danielk1977 Exp $ */ %token_prefix TK_ %token_type {Token} @@ -730,10 +730,11 @@ expritem(A) ::= . {A = 0;} ///////////////////////////// The CREATE INDEX command /////////////////////// // cmd ::= CREATE(S) uniqueflag(U) INDEX nm(X) dbnm(D) - ON nm(Y) LP idxlist(Z) RP(E) onconf(R). { + ON nm(Y) dbnm(C) LP idxlist(Z) RP(E) onconf(R). { if( U!=OE_None ) U = R; if( U==OE_Default) U = OE_Abort; - sqlite3CreateIndex(pParse, &X, &D, &Y, Z, U, &S, &E); + sqlite3CreateIndex(pParse, &X, &D, sqlite3SrcListAppend(0,&Y,&C), + Z, U, &S, &E); } %type uniqueflag {int} @@ -788,10 +789,10 @@ cmd ::= CREATE(A) trigger_decl BEGIN trigger_cmd_list(S) END(Z). { sqlite3FinishTrigger(pParse, S, &all); } -trigger_decl ::= temp(T) TRIGGER nm(B) trigger_time(C) trigger_event(D) +trigger_decl ::= temp(T) TRIGGER nm(B) dbnm(Z) trigger_time(C) trigger_event(D) ON nm(E) dbnm(DB) foreach_clause(F) when_clause(G). { SrcList *pTab = sqlite3SrcListAppend(0, &E, &DB); - sqlite3BeginTrigger(pParse, &B, C, D.a, D.b, pTab, F, G, T); + sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, pTab, F, G, T); } %type trigger_time {int} diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c19a6cc354..f1d13b52f4 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.260 2004/05/28 16:00:22 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.261 2004/05/29 02:37:19 danielk1977 Exp $ */ #include "config.h" #include "sqlite.h" @@ -229,7 +229,7 @@ extern int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls * /* ** The name of the schema table. */ -#define SCHEMA_TABLE(x) (x?TEMP_MASTER_NAME:MASTER_NAME) +#define SCHEMA_TABLE(x) (x==1?TEMP_MASTER_NAME:MASTER_NAME) /* ** A convenience macro that returns the number of elements in @@ -1221,7 +1221,8 @@ void sqlite3SrcListAddAlias(SrcList*, Token*); void sqlite3SrcListAssignCursors(Parse*, SrcList*); void sqlite3IdListDelete(IdList*); void sqlite3SrcListDelete(SrcList*); -void sqlite3CreateIndex(Parse*,Token*,Token*,Token*,IdList*,int,Token*,Token*); +void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,IdList*,int,Token*, + Token*); void sqlite3DropIndex(Parse*, SrcList*); void sqlite3AddKeyType(Vdbe*, ExprList*); void sqlite3AddIdxKeyType(Vdbe*, Index*); @@ -1286,7 +1287,8 @@ int sqlite3SafetyOn(sqlite*); int sqlite3SafetyOff(sqlite*); int sqlite3SafetyCheck(sqlite*); void sqlite3ChangeCookie(sqlite*, Vdbe*, int); -void sqlite3BeginTrigger(Parse*, Token*,int,int,IdList*,SrcList*,int,Expr*,int); +void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*, + int,Expr*,int); void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*); void sqlite3DropTrigger(Parse*, SrcList*); void sqlite3DropTriggerPtr(Parse*, Trigger*, int); @@ -1352,3 +1354,4 @@ void sqlite3Error(sqlite *, int, const char*,...); int sqlite3utfTranslate(const void *, int , u8 , void **, int *, u8); u8 sqlite3UtfReadBom(const void *zData, int nData); void *sqlite3HexToBlob(const char *z); +int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 29e6093037..c446710a73 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.75 2004/05/27 13:35:20 danielk1977 Exp $ +** $Id: tclsqlite.c,v 1.76 2004/05/29 02:37:19 danielk1977 Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -75,182 +75,6 @@ struct CallbackData { char **azColName; /* Column names translated to UTF-8 */ }; -#ifdef UTF_TRANSLATION_NEEDED -/* -** Called for each row of the result. -** -** This version is used when TCL expects UTF-8 data but the database -** uses the ISO8859 format. A translation must occur from ISO8859 into -** UTF-8. -*/ -static int DbEvalCallback( - void *clientData, /* An instance of CallbackData */ - int nCol, /* Number of columns in the result */ - char ** azCol, /* Data for each column */ - char ** azN /* Name for each column */ -){ - CallbackData *cbData = (CallbackData*)clientData; - int i, rc; - Tcl_DString dCol; - Tcl_DStringInit(&dCol); - if( cbData->azColName==0 ){ - assert( cbData->once ); - cbData->once = 0; - if( cbData->zArray[0] ){ - Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0); - } - cbData->azColName = malloc( nCol*sizeof(char*) ); - if( cbData->azColName==0 ){ return 1; } - cbData->nColName = nCol; - for(i=0; iazColName[i] = malloc( Tcl_DStringLength(&dCol) + 1 ); - if( cbData->azColName[i] ){ - strcpy(cbData->azColName[i], Tcl_DStringValue(&dCol)); - }else{ - return 1; - } - if( cbData->zArray[0] ){ - Tcl_SetVar2(cbData->interp, cbData->zArray, "*", - Tcl_DStringValue(&dCol), TCL_LIST_ELEMENT|TCL_APPEND_VALUE); - if( azN[nCol]!=0 ){ - Tcl_DString dType; - Tcl_DStringInit(&dType); - Tcl_DStringAppend(&dType, "typeof:", -1); - Tcl_DStringAppend(&dType, Tcl_DStringValue(&dCol), -1); - Tcl_DStringFree(&dCol); - Tcl_ExternalToUtfDString(NULL, azN[i+nCol], -1, &dCol); - Tcl_SetVar2(cbData->interp, cbData->zArray, - Tcl_DStringValue(&dType), Tcl_DStringValue(&dCol), - TCL_LIST_ELEMENT|TCL_APPEND_VALUE); - Tcl_DStringFree(&dType); - } - } - - Tcl_DStringFree(&dCol); - } - } - if( azCol!=0 ){ - if( cbData->zArray[0] ){ - for(i=0; iinterp, cbData->zArray, cbData->azColName[i], - Tcl_DStringValue(&dCol), 0); - Tcl_DStringFree(&dCol); - } - }else{ - for(i=0; iinterp, cbData->azColName[i], - Tcl_DStringValue(&dCol), 0); - Tcl_DStringFree(&dCol); - } - } - } - rc = Tcl_EvalObj(cbData->interp, cbData->pCode); - if( rc==TCL_CONTINUE ) rc = TCL_OK; - cbData->tcl_rc = rc; - return rc!=TCL_OK; -} -#endif /* UTF_TRANSLATION_NEEDED */ - -#ifndef UTF_TRANSLATION_NEEDED -/* -** Called for each row of the result. -** -** This version is used when either of the following is true: -** -** (1) This version of TCL uses UTF-8 and the data in the -** SQLite database is already in the UTF-8 format. -** -** (2) This version of TCL uses ISO8859 and the data in the -** SQLite database is already in the ISO8859 format. -*/ -static int DbEvalCallback( - void *clientData, /* An instance of CallbackData */ - int nCol, /* Number of columns in the result */ - char ** azCol, /* Data for each column */ - char ** azN /* Name for each column */ -){ - CallbackData *cbData = (CallbackData*)clientData; - int i, rc; - if( azCol==0 || (cbData->once && cbData->zArray[0]) ){ - Tcl_SetVar2(cbData->interp, cbData->zArray, "*", "", 0); - for(i=0; iinterp, cbData->zArray, "*", azN[i], - TCL_LIST_ELEMENT|TCL_APPEND_VALUE); - if( azN[nCol] ){ - char *z = sqlite3_mprintf("typeof:%s", azN[i]); - Tcl_SetVar2(cbData->interp, cbData->zArray, z, azN[i+nCol], - TCL_LIST_ELEMENT|TCL_APPEND_VALUE); - sqlite3_freemem(z); - } - } - cbData->once = 0; - } - if( azCol!=0 ){ - if( cbData->zArray[0] ){ - for(i=0; iinterp, cbData->zArray, azN[i], z, 0); - } - }else{ - for(i=0; iinterp, azN[i], z, 0); - } - } - } - rc = Tcl_EvalObj(cbData->interp, cbData->pCode); - if( rc==TCL_CONTINUE ) rc = TCL_OK; - cbData->tcl_rc = rc; - return rc!=TCL_OK; -} -#endif - -/* -** This is an alternative callback for database queries. Instead -** of invoking a TCL script to handle the result, this callback just -** appends each column of the result to a list. After the query -** is complete, the list is returned. -*/ -static int DbEvalCallback2( - void *clientData, /* An instance of CallbackData */ - int nCol, /* Number of columns in the result */ - char ** azCol, /* Data for each column */ - char ** azN /* Name for each column */ -){ - Tcl_Obj *pList = (Tcl_Obj*)clientData; - int i; - if( azCol==0 ) return 0; - for(i=0; idb, zSql, -1, &pStmt, &zLeft) ){ - Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_STATIC); + Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); rc = TCL_ERROR; break; } @@ -801,7 +647,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ for(i=0; idb) ){ - Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_STATIC); + Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db))); rc = TCL_ERROR; break; } @@ -857,83 +699,13 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ zSql = zLeft; } - if( rc==TCL_OK && pRet ){ + if( rc==TCL_OK ){ Tcl_SetObjResult(interp, pRet); - Tcl_DecrRefCount(pRet); } + Tcl_DecrRefCount(pRet); break; } -#if 0 - case DB_EVAL: { - CallbackData cbData; - char *zErrMsg; - char *zSql; -#ifdef UTF_TRANSLATION_NEEDED - Tcl_DString dSql; - int i; -#endif - - if( objc!=5 && objc!=3 ){ - Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?"); - return TCL_ERROR; - } - pDb->interp = interp; - zSql = Tcl_GetStringFromObj(objv[2], 0); -#ifdef UTF_TRANSLATION_NEEDED - Tcl_DStringInit(&dSql); - Tcl_UtfToExternalDString(NULL, zSql, -1, &dSql); - zSql = Tcl_DStringValue(&dSql); -#endif - Tcl_IncrRefCount(objv[2]); - if( objc==5 ){ - cbData.interp = interp; - cbData.once = 1; - cbData.zArray = Tcl_GetStringFromObj(objv[3], 0); - cbData.pCode = objv[4]; - cbData.tcl_rc = TCL_OK; - cbData.nColName = 0; - cbData.azColName = 0; - zErrMsg = 0; - Tcl_IncrRefCount(objv[3]); - Tcl_IncrRefCount(objv[4]); - rc = sqlite3_exec(pDb->db, zSql, DbEvalCallback, &cbData, &zErrMsg); - Tcl_DecrRefCount(objv[4]); - Tcl_DecrRefCount(objv[3]); - if( cbData.tcl_rc==TCL_BREAK ){ cbData.tcl_rc = TCL_OK; } - }else{ - Tcl_Obj *pList = Tcl_NewObj(); - cbData.tcl_rc = TCL_OK; - rc = sqlite3_exec(pDb->db, zSql, DbEvalCallback2, pList, &zErrMsg); - Tcl_SetObjResult(interp, pList); - } - pDb->rc = rc; - if( rc==SQLITE_ABORT ){ - if( zErrMsg ) free(zErrMsg); - rc = cbData.tcl_rc; - }else if( zErrMsg ){ - Tcl_SetResult(interp, zErrMsg, TCL_VOLATILE); - free(zErrMsg); - rc = TCL_ERROR; - }else if( rc!=SQLITE_OK ){ - Tcl_AppendResult(interp, sqlite3_error_string(rc), 0); - rc = TCL_ERROR; - }else{ - } - Tcl_DecrRefCount(objv[2]); -#ifdef UTF_TRANSLATION_NEEDED - Tcl_DStringFree(&dSql); - if( objc==5 && cbData.azColName ){ - for(i=0; idb; - int iDb; /* When database to store the trigger in */ + int iDb; /* The database to store the trigger in */ + Token *pName; /* The unqualified db name */ DbFixer sFix; - /* Check that: - ** 1. the trigger name does not already exist. - ** 2. the table (or view) does exist in the same database as the trigger. - ** 3. that we are not trying to create a trigger on the sqlite_master table - ** 4. That we are not trying to create an INSTEAD OF trigger on a table. - ** 5. That we are not trying to create a BEFORE or AFTER trigger on a view. + if( isTemp ){ + /* If TEMP was specified, then the trigger name may not be qualified. */ + if( pName2 && pName2->n>0 ){ + sqlite3ErrorMsg(pParse, "temporary trigger may not have qualified name"); + goto trigger_cleanup; + } + iDb = 1; + pName = pName1; + }else{ + /* Figure out the db that the the trigger will be created in */ + iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); + if( iDb<0 ){ + goto trigger_cleanup; + } + } + + /* If the trigger name was unqualified, and the table is a temp table, + ** then set iDb to 1 to create the trigger in the temporary database. + ** If sqlite3SrcListLookup() returns 0, indicating the table does not + ** exist, the error is caught by the block below. */ + pTab = sqlite3SrcListLookup(pParse, pTableName); + if( pName2->n==0 && pTab && pTab->iDb==1 ){ + iDb = 1; + } + + /* Ensure the table name matches database name and that the table exists */ if( sqlite3_malloc_failed ) goto trigger_cleanup; assert( pTableName->nSrc==1 ); - if( db->init.busy - && sqlite3FixInit(&sFix, pParse, db->init.iDb, "trigger", pName) - && sqlite3FixSrcList(&sFix, pTableName) - ){ + if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) && + sqlite3FixSrcList(&sFix, pTableName) ){ goto trigger_cleanup; } - tab = sqlite3SrcListLookup(pParse, pTableName); - if( !tab ){ - goto trigger_cleanup; - } - iDb = isTemp ? 1 : tab->iDb; - if( iDb>=2 && !db->init.busy ){ - sqlite3ErrorMsg(pParse, "triggers may not be added to auxiliary " - "database %s", db->aDb[tab->iDb].zName); + pTab = sqlite3SrcListLookup(pParse, pTableName); + if( !pTab ){ + /* The table does not exist. */ goto trigger_cleanup; } + /* Check that no trigger of the specified name exists */ zName = sqliteStrNDup(pName->z, pName->n); sqlite3Dequote(zName); if( sqlite3HashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){ sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); goto trigger_cleanup; } - if( sqlite3StrNICmp(tab->zName, "sqlite_", 7)==0 ){ + + /* Do not create a trigger on a system table */ + if( (iDb!=1 && sqlite3StrICmp(pTab->zName, MASTER_NAME)==0) || + (iDb==1 && sqlite3StrICmp(pTab->zName, TEMP_MASTER_NAME)==0) + ){ sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); pParse->nErr++; goto trigger_cleanup; } - if( tab->pSelect && tr_tm != TK_INSTEAD ){ + + /* INSTEAD of triggers are only for views and views only support INSTEAD + ** of triggers. + */ + if( pTab->pSelect && tr_tm!=TK_INSTEAD ){ sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0); goto trigger_cleanup; } - if( !tab->pSelect && tr_tm == TK_INSTEAD ){ + if( !pTab->pSelect && tr_tm==TK_INSTEAD ){ sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" " trigger on table: %S", pTableName, 0); goto trigger_cleanup; } + #ifndef SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_CREATE_TRIGGER; - const char *zDb = db->aDb[tab->iDb].zName; + const char *zDb = db->aDb[pTab->iDb].zName; const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb; - if( tab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; - if( sqlite3AuthCheck(pParse, code, zName, tab->zName, zDbTrig) ){ + if( pTab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; + if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){ goto trigger_cleanup; } - if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(tab->iDb), 0, zDb)){ + if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->iDb), 0, zDb)){ goto trigger_cleanup; } } #endif - /* INSTEAD OF triggers can only appear on views and BEGIN triggers + /* INSTEAD OF triggers can only appear on views and BEFORE triggers ** cannot appear on views. So we might as well translate every ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code ** elsewhere. @@ -128,22 +153,22 @@ void sqlite3BeginTrigger( } /* Build the Trigger object */ - nt = (Trigger*)sqliteMalloc(sizeof(Trigger)); - if( nt==0 ) goto trigger_cleanup; - nt->name = zName; + pTrigger = (Trigger*)sqliteMalloc(sizeof(Trigger)); + if( pTrigger==0 ) goto trigger_cleanup; + pTrigger->name = zName; zName = 0; - nt->table = sqliteStrDup(pTableName->a[0].zName); + pTrigger->table = sqliteStrDup(pTableName->a[0].zName); if( sqlite3_malloc_failed ) goto trigger_cleanup; - nt->iDb = iDb; - nt->iTabDb = tab->iDb; - nt->op = op; - nt->tr_tm = tr_tm; - nt->pWhen = sqlite3ExprDup(pWhen); - nt->pColumns = sqlite3IdListDup(pColumns); - nt->foreach = foreach; - sqlite3TokenCopy(&nt->nameToken,pName); + pTrigger->iDb = iDb; + pTrigger->iTabDb = pTab->iDb; + pTrigger->op = op; + pTrigger->tr_tm = tr_tm; + pTrigger->pWhen = sqlite3ExprDup(pWhen); + pTrigger->pColumns = sqlite3IdListDup(pColumns); + pTrigger->foreach = foreach; + sqlite3TokenCopy(&pTrigger->nameToken,pName); assert( pParse->pNewTrigger==0 ); - pParse->pNewTrigger = nt; + pParse->pNewTrigger = pTrigger; trigger_cleanup: sqliteFree(zName); @@ -198,7 +223,7 @@ void sqlite3FinishTrigger( /* Make an entry in the sqlite_master table */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto triggerfinish_cleanup; - sqlite3BeginWriteOperation(pParse, 0, 0); + sqlite3BeginWriteOperation(pParse, 0, nt->iDb); sqlite3OpenMasterTable(v, nt->iDb); addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig); sqlite3VdbeChangeP3(v, addr+2, nt->name, 0); @@ -425,11 +450,6 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){ sqlite *db = pParse->db; assert( pTrigger->iDbnDb ); - if( pTrigger->iDb>=2 ){ - sqlite3ErrorMsg(pParse, "triggers may not be removed from " - "auxiliary database %s", db->aDb[pTrigger->iDb].zName); - return; - } pTable = sqlite3FindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName); assert(pTable); assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 ); @@ -438,7 +458,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){ int code = SQLITE_DROP_TRIGGER; const char *zDb = db->aDb[pTrigger->iDb].zName; const char *zTab = SCHEMA_TABLE(pTrigger->iDb); - if( pTrigger->iDb ) code = SQLITE_DROP_TEMP_TRIGGER; + if( pTrigger->iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER; if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) || sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ return; @@ -462,7 +482,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){ { OP_Next, 0, ADDR(1), 0}, /* 8 */ }; - sqlite3BeginWriteOperation(pParse, 0, 0); + sqlite3BeginWriteOperation(pParse, 0, pTrigger->iDb); sqlite3OpenMasterTable(v, pTrigger->iDb); base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0); diff --git a/src/vdbe.c b/src/vdbe.c index 22b61be7f8..6199d02dcc 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.345 2004/05/28 16:00:22 drh Exp $ +** $Id: vdbe.c,v 1.346 2004/05/29 02:37:19 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -2142,12 +2142,12 @@ case OP_MakeRecord: { pTos++; pTos->n = nByte; if( nByte<=sizeof(zTemp) ){ - assert( zNewRecord==zTemp ); + assert( zNewRecord==(unsigned char *)zTemp ); pTos->z = pTos->zShort; memcpy(pTos->zShort, zTemp, nByte); pTos->flags = MEM_Blob | MEM_Short; }else{ - assert( zNewRecord!=zTemp ); + assert( zNewRecord!=(unsigned char *)zTemp ); pTos->z = zNewRecord; pTos->flags = MEM_Blob | MEM_Dyn; } diff --git a/test/attach.test b/test/attach.test index 817a34dbda..85957d8de8 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.16 2004/05/11 09:57:35 drh Exp $ +# $Id: attach.test,v 1.17 2004/05/29 02:37:20 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -488,7 +488,7 @@ do_test attach-5.1 { SELECT 'no-op'; END; } db2 -} {1 {triggers may not be added to auxiliary database orig}} +} {1 {trigger r1 cannot reference objects in database orig}} do_test attach-5.2 { catchsql { CREATE TABLE t5(x,y); diff --git a/test/attach3.test b/test/attach3.test index a3c8352f85..c75caa7d35 100644 --- a/test/attach3.test +++ b/test/attach3.test @@ -12,7 +12,7 @@ # focus of this script is testing the ATTACH and DETACH commands # and schema changes to attached databases. # -# $Id: attach3.test,v 1.3 2004/05/28 12:33:32 danielk1977 Exp $ +# $Id: attach3.test,v 1.4 2004/05/29 02:37:20 danielk1977 Exp $ # @@ -63,7 +63,7 @@ do_test attach3-1.5 { } {1 2} # Create an index on the auxilary database table. -do_test attach4-2.1 { +do_test attach3-2.1 { execsql { CREATE INDEX aux.i1 on t3(e); } @@ -71,31 +71,31 @@ do_test attach4-2.1 { execsql { pragma vdbe_trace = off; } -do_test attach4-2.2 { +do_test attach3-2.2 { execsql { SELECT * FROM sqlite_master WHERE name = 'i1'; } } {} -do_test attach4-2.3 { +do_test attach3-2.3 { execsql { SELECT * FROM aux.sqlite_master WHERE name = 'i1'; } } {index i1 t3 5 {CREATE INDEX i1 on t3(e)}} # Drop the index on the aux database table. -do_test attach4-3.1 { +do_test attach3-3.1 { execsql { DROP INDEX aux.i1; SELECT * FROM aux.sqlite_master WHERE name = 'i1'; } } {} -do_test attach4-3.2 { +do_test attach3-3.2 { execsql { CREATE INDEX aux.i1 on t3(e); SELECT * FROM aux.sqlite_master WHERE name = 'i1'; } } {index i1 t3 5 {CREATE INDEX i1 on t3(e)}} -do_test attach4-3.3 { +do_test attach3-3.3 { execsql { DROP INDEX i1; SELECT * FROM aux.sqlite_master WHERE name = 'i1'; @@ -103,20 +103,20 @@ do_test attach4-3.3 { } {} # Drop tables t1 and t2 in the auxilary database. -do_test attach4-4.1 { +do_test attach3-4.1 { execsql { DROP TABLE aux.t1; SELECT name FROM aux.sqlite_master; } } {t2 t3} -do_test attach4-4.2 { +do_test attach3-4.2 { # This will drop main.t2 execsql { DROP TABLE t2; SELECT name FROM aux.sqlite_master; } } {t2 t3} -do_test attach4-4.3 { +do_test attach3-4.3 { execsql { DROP TABLE t2; SELECT name FROM aux.sqlite_master; @@ -124,17 +124,17 @@ do_test attach4-4.3 { } {t3} # Create a view in the auxilary database. -do_test attach4-5.1 { +do_test attach3-5.1 { execsql { CREATE VIEW aux.v1 AS SELECT * FROM t3; } } {} -do_test attach4-5.2 { +do_test attach3-5.2 { execsql { SELECT * FROM aux.sqlite_master WHERE name = 'v1'; } } {view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t3}} -do_test attach4-5.3 { +do_test attach3-5.3 { execsql { INSERT INTO aux.t3 VALUES('hello', 'world'); SELECT * FROM v1; @@ -142,17 +142,52 @@ do_test attach4-5.3 { } {1 2 hello world} # Drop the view -do_test attach4-6.1 { +do_test attach3-6.1 { execsql { DROP VIEW aux.v1; } } {} -do_test attach4-5.2 { +do_test attach3-6.2 { execsql { SELECT * FROM aux.sqlite_master WHERE name = 'v1'; } } {} +# Create a trigger in the auxilary database. +do_test attach3-7.1 { + execsql { + CREATE TRIGGER aux.tr1 AFTER INSERT ON t3 BEGIN + INSERT INTO t3 VALUES(new.e*2, new.f*2); + END; + } +} {} +do_test attach3-7.2 { + execsql { + DELETE FROM t3; + INSERT INTO t3 VALUES(10, 20); + SELECT * FROM t3; + } +} {10 20 20 40} +do_test attach3-5.3 { + execsql { + SELECT * FROM aux.sqlite_master WHERE name = 'tr1'; + } +} {trigger tr1 t3 0 {CREATE TRIGGER aux.tr1 AFTER INSERT ON t3 BEGIN + INSERT INTO t3 VALUES(new.e*2, new.f*2); + END}} + +# Drop the trigger +do_test attach3-8.1 { + execsql { + DROP TRIGGER aux.tr1; + } +} {} +do_test attach3-8.2 { + execsql { + SELECT * FROM aux.sqlite_master WHERE name = 'tr1'; + } +} {} + finish_test diff --git a/test/trigger1.test b/test/trigger1.test index b55ca12378..7c23f874dd 100644 --- a/test/trigger1.test +++ b/test/trigger1.test @@ -36,7 +36,7 @@ do_test trigger1-1.1.2 { SELECT * from sqlite_master; END; } -} {1 {no such table: no_such_table}} +} {1 {no such table: main.no_such_table}} do_test trigger1-1.1.2 { catchsql { CREATE TEMP TRIGGER trig UPDATE ON no_such_table BEGIN @@ -166,7 +166,7 @@ do_test trigger1-1.12 { delete from t1 WHERE a=old.a+2; end; } -} {1 {cannot create INSTEAD OF trigger on table: t1}} +} {1 {cannot create INSTEAD OF trigger on table: main.t1}} # Ensure that we cannot create BEFORE triggers on views do_test trigger1-1.13 { catchsql { @@ -175,7 +175,7 @@ do_test trigger1-1.13 { delete from t1 WHERE a=old.a+2; end; } -} {1 {cannot create BEFORE trigger on view: v1}} +} {1 {cannot create BEFORE trigger on view: main.v1}} # Ensure that we cannot create AFTER triggers on views do_test trigger1-1.14 { catchsql { @@ -185,7 +185,7 @@ do_test trigger1-1.14 { delete from t1 WHERE a=old.a+2; end; } -} {1 {cannot create AFTER trigger on view: v1}} +} {1 {cannot create AFTER trigger on view: main.v1}} # Check for memory leaks in the trigger parser #