-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
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
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
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
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
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
-6661bb5f9c1692f94b8b7d900b6be07f027e6324
\ No newline at end of file
+4060a37d0baaa60c50f2dde4a1ab344133fcabbb
\ No newline at end of file
** 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>
return -1;
}
-static int resolveSchemaName(
+int sqlite3TwoPartName(
Parse *pParse,
Token *pName1,
Token *pName2,
** 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 */
sqlite3SelectDelete(pSelect);
return;
}
- resolveSchemaName(pParse, pName1, pName2, &pName);
+ sqlite3TwoPartName(pParse, pName1, pName2, &pName);
if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName)
&& sqlite3FixSelect(&sFix, pSelect)
){
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 */
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;
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;
** 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}
///////////////////////////// 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}
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}
*************************************************************************
** 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"
/*
** 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
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*);
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);
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 **);
*************************************************************************
** 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 */
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.
}
#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
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?");
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;
}
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);
/* 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);
}
Tcl_DecrRefCount(pName);
}else{
- if( !pRet ){
- pRet = Tcl_NewObj();
- Tcl_IncrRefCount(pRet);
- }
Tcl_ListObjAppendElement(interp, pRet, pVal);
}
}
}
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;
}
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
*/
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 */
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.
}
/* 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);
/* 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);
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 );
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;
{ 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);
** 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"
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;
}
# 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]
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);
# 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 $
#
} {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);
}
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';
} {}
# 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;
} {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;
} {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
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
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 {
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 {
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
#