aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordanielk1977 <danielk1977@noemail.net>2004-05-29 02:37:19 +0000
committerdanielk1977 <danielk1977@noemail.net>2004-05-29 02:37:19 +0000
commitef2cb63e9e15a749c7ae0b0a3ce3dd6fd6cd9029 (patch)
tree7cc9e572a27b78381fc2c51636c88a2d0d1d02a0 /src
parent51846b56edaba72e986cd7128f38547690c48671 (diff)
downloadsqlite-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.c60
-rw-r--r--src/parse.y11
-rw-r--r--src/sqliteInt.h11
-rw-r--r--src/tclsqlite.c290
-rw-r--r--src/trigger.c122
-rw-r--r--src/vdbe.c6
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;
}