diff options
author | danielk1977 <danielk1977@noemail.net> | 2004-05-29 02:37:19 +0000 |
---|---|---|
committer | danielk1977 <danielk1977@noemail.net> | 2004-05-29 02:37:19 +0000 |
commit | ef2cb63e9e15a749c7ae0b0a3ce3dd6fd6cd9029 (patch) | |
tree | 7cc9e572a27b78381fc2c51636c88a2d0d1d02a0 /src | |
parent | 51846b56edaba72e986cd7128f38547690c48671 (diff) | |
download | sqlite-ef2cb63e9e15a749c7ae0b0a3ce3dd6fd6cd9029.tar.gz sqlite-ef2cb63e9e15a749c7ae0b0a3ce3dd6fd6cd9029.zip |
Allow CREATE and DROP TRIGGER on attached databases. (CVS 1488)
FossilOrigin-Name: 4060a37d0baaa60c50f2dde4a1ab344133fcabbb
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 60 | ||||
-rw-r--r-- | src/parse.y | 11 | ||||
-rw-r--r-- | src/sqliteInt.h | 11 | ||||
-rw-r--r-- | src/tclsqlite.c | 290 | ||||
-rw-r--r-- | src/trigger.c | 122 | ||||
-rw-r--r-- | src/vdbe.c | 6 |
6 files changed, 139 insertions, 361 deletions
diff --git a/src/build.c b/src/build.c index bf208acd2..8710897e8 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 <ctype.h> @@ -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; - goto exit_create_index; - } - assert( pName1!=0 ); - if( iDb==0 ){ - pTab = sqlite3FindTable(db, zTblName, "temp"); + pTab = sqlite3SrcListLookup(pParse, pTblName); + if( pName2 && pName2->n==0 && pTab && pTab->iDb==1 ){ + iDb = 1; } - if( !pTab ){ - pTab = sqlite3LocateTable(pParse, zTblName, db->aDb[iDb].zName); + + if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) && + sqlite3FixSrcList(&sFix, pTblName) + ){ + goto exit_create_index; } - 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 6f3a34e27..4b117c233 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 c19a6cc35..f1d13b52f 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 29e609303..c446710a7 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; i<nCol; i++){ - Tcl_ExternalToUtfDString(NULL, azN[i], -1, &dCol); - cbData->azColName[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; i<nCol; i++){ - char *z = azCol[i]; - if( z==0 ) z = ""; - Tcl_DStringInit(&dCol); - Tcl_ExternalToUtfDString(NULL, z, -1, &dCol); - Tcl_SetVar2(cbData->interp, cbData->zArray, cbData->azColName[i], - Tcl_DStringValue(&dCol), 0); - Tcl_DStringFree(&dCol); - } - }else{ - for(i=0; i<nCol; i++){ - char *z = azCol[i]; - if( z==0 ) z = ""; - Tcl_DStringInit(&dCol); - Tcl_ExternalToUtfDString(NULL, z, -1, &dCol); - Tcl_SetVar(cbData->interp, 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; i<nCol; i++){ - Tcl_SetVar2(cbData->interp, 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; i<nCol; i++){ - char *z = azCol[i]; - if( z==0 ) z = ""; - Tcl_SetVar2(cbData->interp, cbData->zArray, azN[i], z, 0); - } - }else{ - for(i=0; i<nCol; i++){ - char *z = azCol[i]; - if( z==0 ) z = ""; - Tcl_SetVar(cbData->interp, 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; i<nCol; i++){ - Tcl_Obj *pElem; - if( azCol[i] && *azCol[i] ){ -#ifdef UTF_TRANSLATION_NEEDED - Tcl_DString dCol; - Tcl_DStringInit(&dCol); - Tcl_ExternalToUtfDString(NULL, azCol[i], -1, &dCol); - pElem = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1); - Tcl_DStringFree(&dCol); -#else - pElem = Tcl_NewStringObj(azCol[i], -1); -#endif - }else{ - pElem = Tcl_NewObj(); - } - Tcl_ListObjAppendElement(0, pList, pElem); - } - return 0; -} - /* ** This is a second alternative callback for database queries. A the ** first column of the first row of the result is made the TCL result. @@ -476,6 +300,26 @@ static int auth_callback( #endif /* SQLITE_OMIT_AUTHORIZATION */ /* +** zText is a pointer to text obtained via an sqlite3_result_text() +** or similar interface. This routine returns a Tcl string object, +** reference count set to 0, containing the text. If a translation +** between iso8859 and UTF-8 is required, it is preformed. +*/ +static Tcl_Obj *dbTextToObj(char const *zText){ + Tcl_Obj *pVal; +#ifdef UTF_TRANSLATION_NEEDED + Tcl_DString dCol; + Tcl_DStringInit(&dCol); + Tcl_ExternalToUtfDString(NULL, zText, -1, &dCol); + pVal = Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1); + Tcl_DStringFree(&dCol); +#else + pVal = Tcl_NewStringObj(zText, -1); +#endif + return pVal; +} + +/* ** The "sqlite" command below creates a new Tcl command for each ** connection it opens to an SQLite database. This routine is invoked ** whenever one of those connection-specific commands is executed @@ -777,7 +621,9 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ char const *zSql; char const *zLeft; sqlite3_stmt *pStmt; - Tcl_Obj *pRet = 0; + + Tcl_Obj *pRet = Tcl_NewObj(); + Tcl_IncrRefCount(pRet); if( objc!=5 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SQL ?ARRAY-NAME CODE?"); @@ -790,7 +636,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ int i; if( SQLITE_OK!=sqlite3_prepare(pDb->db, 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; i<sqlite3_column_count(pStmt); i++){ Tcl_ListObjAppendElement(interp, pColList, - Tcl_NewStringObj(sqlite3_column_name(pStmt, i), -1) + dbTextToObj(sqlite3_column_name(pStmt, i)) ); } Tcl_ObjSetVar2(interp,objv[3],Tcl_NewStringObj("*",-1),pColList,0); @@ -813,14 +659,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ /* Set pVal to contain the i'th column of this row. */ if( SQLITE3_BLOB!=sqlite3_column_type(pStmt, i) ){ - pVal = Tcl_NewStringObj(sqlite3_column_text(pStmt, i), -1); + pVal = dbTextToObj(sqlite3_column_text(pStmt, i)); }else{ int bytes = sqlite3_column_bytes(pStmt, i); pVal = Tcl_NewByteArrayObj(sqlite3_column_blob(pStmt, i), bytes); } if( objc==5 ){ - Tcl_Obj *pName = Tcl_NewStringObj(sqlite3_column_name(pStmt, i), -1); + Tcl_Obj *pName = dbTextToObj(sqlite3_column_name(pStmt, i)); Tcl_IncrRefCount(pName); if( !strcmp("", Tcl_GetString(objv[3])) ){ Tcl_ObjSetVar2(interp, pName, 0, pVal, 0); @@ -829,10 +675,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } Tcl_DecrRefCount(pName); }else{ - if( !pRet ){ - pRet = Tcl_NewObj(); - Tcl_IncrRefCount(pRet); - } Tcl_ListObjAppendElement(interp, pRet, pVal); } } @@ -848,7 +690,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ } if( pStmt && SQLITE_OK!=sqlite3_errcode(pDb->db) ){ - 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; i<cbData.nColName; i++){ - if( cbData.azColName[i] ) free(cbData.azColName[i]); - } - free(cbData.azColName); - cbData.azColName = 0; - } -#endif - return rc; - } -#endif /* ** $db function NAME SCRIPT diff --git a/src/trigger.c b/src/trigger.c index e224db6f5..e9e31b2db 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -40,7 +40,8 @@ void sqlite3DeleteTriggerStep(TriggerStep *pTriggerStep){ */ void sqlite3BeginTrigger( Parse *pParse, /* The parse context of the CREATE TRIGGER statement */ - Token *pName, /* The name of the trigger */ + Token *pName1, /* The name of the trigger */ + Token *pName2, /* The name of the trigger */ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList *pColumns, /* column list if this is an UPDATE OF trigger */ @@ -49,76 +50,100 @@ void sqlite3BeginTrigger( Expr *pWhen, /* WHEN clause */ int isTemp /* True if the TEMPORARY keyword is present */ ){ - Trigger *nt; - Table *tab; + Trigger *pTrigger; + Table *pTab; char *zName = 0; /* Name of the trigger */ sqlite *db = pParse->db; - 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) - ){ - goto trigger_cleanup; - } - tab = sqlite3SrcListLookup(pParse, pTableName); - if( !tab ){ + if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) && + sqlite3FixSrcList(&sFix, pTableName) ){ 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->iDb<db->nDb ); - 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 22b61be7f..6199d02dc 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; } |