diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/attach.c | 135 | ||||
-rw-r--r-- | src/build.c | 62 | ||||
-rw-r--r-- | src/expr.c | 3 | ||||
-rw-r--r-- | src/main.c | 5 | ||||
-rw-r--r-- | src/select.c | 3 | ||||
-rw-r--r-- | src/sqliteInt.h | 23 | ||||
-rw-r--r-- | src/trigger.c | 61 |
7 files changed, 246 insertions, 46 deletions
diff --git a/src/attach.c b/src/attach.c index e1c8a3c4d..77be3bcf9 100644 --- a/src/attach.c +++ b/src/attach.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** -** $Id: attach.c,v 1.3 2003/04/15 19:22:23 drh Exp $ +** $Id: attach.c,v 1.4 2003/05/31 16:21:12 drh Exp $ */ #include "sqliteInt.h" @@ -128,3 +128,136 @@ void sqliteDetach(Parse *pParse, Token *pDbname){ sqliteResetInternalSchema(db, i); } } + +/* +** Initialize a DbFixer structure. This routine must be called prior +** to passing the structure to one of the sqliteFixAAAA() routines below. +** +** The return value indicates whether or not fixation is required. TRUE +** means we do need to fix the database references, FALSE means we do not. +*/ +int sqliteFixInit( + DbFixer *pFix, /* The fixer to be initialized */ + Parse *pParse, /* Error messages will be written here */ + int iDb, /* This is the database that must must be used */ + const char *zType, /* "view", "trigger", or "index" */ + const Token *pName /* Name of the view, trigger, or index */ +){ + sqlite *db; + + if( iDb<0 || iDb==1 ) return 0; + db = pParse->db; + assert( db->nDb>iDb ); + pFix->zDb = db->aDb[iDb].zName; + pFix->zType = zType; + pFix->pName = pName; + return 1; +} + +/* +** The following set of routines walk through the parse tree and assign +** a specific database to all table references where the database name +** was left unspecified in the original SQL statement. The pFix structure +** must have been initialized by a prior call to sqliteFixInit(). +** +** These routines are used to make sure that an index, trigger, or +** view in one database does not refer to objects in a different database. +** (Exception: indices, triggers, and views in the TEMP database are +** allowed to refer to anything.) If a reference is explicitly made +** to an object in a different database, an error message is added to +** pParse->zErrMsg and these routines return non-zero. If everything +** checks out, these routines return 0. +*/ +int sqliteFixSrcList( + DbFixer *pFix, /* Context of the fixation */ + SrcList *pList /* The Source list to check and modify */ +){ + int i; + const char *zDb; + + if( pList==0 ) return 0; + zDb = pFix->zDb; + for(i=0; i<pList->nSrc; i++){ + if( pList->a[i].zDatabase==0 ){ + pList->a[i].zDatabase = sqliteStrDup(zDb); + }else if( sqliteStrICmp(pList->a[i].zDatabase,zDb)!=0 ){ + sqliteErrorMsg(pFix->pParse, + "%s %.*s cannot reference objects in database %s", + pFix->zType, pFix->pName->n, pFix->pName->z, pList->a[i].zDatabase); + return 1; + } + if( sqliteFixSelect(pFix, pList->a[i].pSelect) ) return 1; + if( sqliteFixExpr(pFix, pList->a[i].pOn) ) return 1; + } + return 0; +} +int sqliteFixSelect( + DbFixer *pFix, /* Context of the fixation */ + Select *pSelect /* The SELECT statement to be fixed to one database */ +){ + while( pSelect ){ + if( sqliteFixExprList(pFix, pSelect->pEList) ){ + return 1; + } + if( sqliteFixSrcList(pFix, pSelect->pSrc) ){ + return 1; + } + if( sqliteFixExpr(pFix, pSelect->pWhere) ){ + return 1; + } + if( sqliteFixExpr(pFix, pSelect->pHaving) ){ + return 1; + } + pSelect = pSelect->pPrior; + } + return 0; +} +int sqliteFixExpr( + DbFixer *pFix, /* Context of the fixation */ + Expr *pExpr /* The expression to be fixed to one database */ +){ + while( pExpr ){ + if( sqliteFixSelect(pFix, pExpr->pSelect) ){ + return 1; + } + if( sqliteFixExprList(pFix, pExpr->pList) ){ + return 1; + } + if( sqliteFixExpr(pFix, pExpr->pRight) ){ + return 1; + } + pExpr = pExpr->pLeft; + } + return 0; +} +int sqliteFixExprList( + DbFixer *pFix, /* Context of the fixation */ + ExprList *pList /* The expression to be fixed to one database */ +){ + int i; + if( pList==0 ) return 0; + for(i=0; i<pList->nExpr; i++){ + if( sqliteFixExpr(pFix, pList->a[i].pExpr) ){ + return 1; + } + } + return 0; +} +int sqliteFixTriggerStep( + DbFixer *pFix, /* Context of the fixation */ + TriggerStep *pStep /* The trigger step be fixed to one database */ +){ + while( pStep ){ + if( sqliteFixSelect(pFix, pStep->pSelect) ){ + return 1; + } + if( sqliteFixExpr(pFix, pStep->pWhere) ){ + return 1; + } + if( sqliteFixExprList(pFix, pStep->pExprList) ){ + return 1; + } + pStep = pStep->pNext; + } + return 0; +} diff --git a/src/build.c b/src/build.c index 100b701c8..36d083fc3 100644 --- a/src/build.c +++ b/src/build.c @@ -23,7 +23,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.154 2003/05/17 19:04:04 drh Exp $ +** $Id: build.c,v 1.155 2003/05/31 16:21:12 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -118,6 +118,12 @@ void sqliteExec(Parse *pParse){ ** of that table and (optionally) the name of the database ** containing the table. Return NULL if not found. ** +** If zDatabase is 0, all databases are searched for the +** table and the first matching table is returned. (No checking +** for duplicate table names is done.) The search order is +** TEMP first, then MAIN, then any auxiliary databases added +** using the ATTACH command. +** ** See also sqliteLocateTable(). */ Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){ @@ -137,38 +143,22 @@ Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){ ** a particular database table given the name ** of that table and (optionally) the name of the database ** containing the table. Return NULL if not found. +** Also leave an error message in pParse->zErrMsg. ** -** If pParse->useDb is not negative, then the table must be -** located in that database. If a different database is specified, -** an error message is generated into pParse->zErrMsg. +** The difference between this routine and sqliteFindTable() +** is that this routine leaves an error message in pParse->zErrMsg +** where sqliteFindTable() does not. */ Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){ - sqlite *db; - const char *zUse; Table *p; - db = pParse->db; - if( pParse->useDb<0 ){ - p = sqliteFindTable(db, zName, zDbase); - }else { - assert( pParse->useDb<db->nDb ); - assert( db->aDb[pParse->useDb].pBt!=0 ); - zUse = db->aDb[pParse->useDb].zName; - if( zDbase && pParse->useDb!=1 && sqliteStrICmp(zDbase, zUse)!=0 ){ - sqliteErrorMsg(pParse,"cannot use database %s in this context", zDbase); - return 0; - } - p = sqliteFindTable(db, zName, zUse); - if( p==0 && pParse->useDb==1 && zDbase==0 ){ - p = sqliteFindTable(db, zName, 0); - } - } + + p = sqliteFindTable(pParse->db, zName, zDbase); if( p==0 ){ if( zDbase ){ sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName); - }else if( (pParse->useDb==0 || pParse->useDb>=2) - && sqliteFindTable(db, zName, 0)!=0 ){ + }else if( sqliteFindTable(pParse->db, zName, 0)!=0 ){ sqliteErrorMsg(pParse, "table \"%s\" is not in database \"%s\"", - zName, zUse); + zName, zDbase); }else{ sqliteErrorMsg(pParse, "no such table: %s", zName); } @@ -181,6 +171,12 @@ Table *sqliteLocateTable(Parse *pParse, const char *zName, const char *zDbase){ ** a particular index given the name of that index ** and the name of the database that contains the index. ** Return NULL if not found. +** +** If zDatabase is 0, all databases are searched for the +** table and the first matching index is returned. (No checking +** for duplicate index names is done.) The search order is +** TEMP first, then MAIN, then any auxiliary databases added +** using the ATTACH command. */ Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){ Index *p = 0; @@ -1046,6 +1042,7 @@ void sqliteCreateView( int n; const char *z; Token sEnd; + DbFixer sFix; sqliteStartTable(pParse, pBegin, pName, isTemp, 1); p = pParse->pNewTable; @@ -1053,6 +1050,12 @@ void sqliteCreateView( sqliteSelectDelete(pSelect); return; } + if( sqliteFixInit(&sFix, pParse, p->iDb, "view", pName) + && sqliteFixSelect(&sFix, pSelect) + ){ + sqliteSelectDelete(pSelect); + return; + } /* Make a copy of the entire SELECT statement that defines the view. ** This will force all the Expr.token.z values to be dynamically @@ -1534,10 +1537,17 @@ void sqliteCreateIndex( Index *pIndex; /* The index to be created */ char *zName = 0; int i, j; - Token nullId; /* Fake token for an empty ID list */ + Token nullId; /* Fake token for an empty ID list */ + DbFixer sFix; /* For assigning database names to pTable */ sqlite *db = pParse->db; if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index; + if( !isTemp && pParse->initFlag + && sqliteFixInit(&sFix, pParse, pParse->iDb, "index", pName) + && sqliteFixSrcList(&sFix, pTable) + ){ + goto exit_create_index; + } /* ** Find the table that is to be indexed. Return early if not found. diff --git a/src/expr.c b/src/expr.c index fbd15163f..d6b9b1279 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.95 2003/05/02 14:32:13 drh Exp $ +** $Id: expr.c,v 1.96 2003/05/31 16:21:12 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -191,6 +191,7 @@ SrcList *sqliteSrcListDup(SrcList *p){ if( pNew==0 ) return 0; pNew->nSrc = p->nSrc; for(i=0; i<p->nSrc; i++){ + pNew->a[i].zDatabase = sqliteStrDup(p->a[i].zDatabase); pNew->a[i].zName = sqliteStrDup(p->a[i].zName); pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias); pNew->a[i].jointype = p->a[i].jointype; diff --git a/src/main.c b/src/main.c index a4c1b69c3..8a9194e45 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.131 2003/05/17 17:35:12 drh Exp $ +** $Id: main.c,v 1.132 2003/05/31 16:21:12 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -80,7 +80,6 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){ sParse.db = pData->db; sParse.initFlag = 1; sParse.iDb = atoi(argv[4]); - sParse.useDb = -1; sParse.newTnum = atoi(argv[2]); sParse.useCallback = 1; sqliteRunParser(&sParse, argv[3], pData->pzErrMsg); @@ -332,7 +331,6 @@ static int sqliteInitOne(sqlite *db, int iDb, char **pzErrMsg){ sParse.xCallback = sqliteInitCallback; sParse.pArg = (void*)&initData; sParse.initFlag = 1; - sParse.useDb = -1; sParse.useCallback = 1; if( iDb==0 ){ sqliteRunParser(&sParse, @@ -623,7 +621,6 @@ static int sqliteMain( sParse.db = db; sParse.xCallback = xCallback; sParse.pArg = pArg; - sParse.useDb = -1; sParse.useCallback = ppVm==0; if( db->xTrace ) db->xTrace(db->pTraceArg, zSql); sqliteRunParser(&sParse, zSql, pzErrMsg); diff --git a/src/select.c b/src/select.c index a4b300508..1b6d45ef7 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.139 2003/05/17 17:35:12 drh Exp $ +** $Id: select.c,v 1.140 2003/05/31 16:21:13 drh Exp $ */ #include "sqliteInt.h" @@ -1657,6 +1657,7 @@ static int flattenSubquery( if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){ sqliteDeleteTable(0, pSrc->a[iFrom].pTab); } + sqliteFree(pSrc->a[iFrom].zDatabase); sqliteFree(pSrc->a[iFrom].zName); sqliteFree(pSrc->a[iFrom].zAlias); if( nSubSrc>1 ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 7836dbd7d..67d466926 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.188 2003/05/29 17:50:55 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.189 2003/05/31 16:21:13 drh Exp $ */ #include "config.h" #include "sqlite.h" @@ -857,7 +857,6 @@ struct Parse { ** while generating expressions. Normally false */ u8 iDb; /* Index of database whose schema is being parsed */ u8 useCallback; /* True if callbacks should be used to report results */ - int useDb; /* Restrict references to tables in this database */ int newTnum; /* Table number to use when reparsing CREATE TABLEs */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ @@ -905,6 +904,7 @@ struct Trigger { IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger, the <column-list> is stored here */ int foreach; /* One of TK_ROW or TK_STATEMENT */ + Token *pNameToken; /* Token containing zName. Use during parsing only */ TriggerStep *step_list; /* Link list of trigger program steps */ Trigger *pNext; /* Next trigger associated with the table */ @@ -1002,6 +1002,19 @@ struct TriggerStack { }; /* +** The following structure contains information used by the sqliteFix... +** routines as they walk the parse tree to make database references +** explicit. +*/ +typedef struct DbFixer DbFixer; +struct DbFixer { + Parse *pParse; /* The parsing context. Error messages written here */ + const char *zDb; /* Make sure all objects are contained in this database */ + const char *zType; /* Type of the container - used for error messages */ + const Token *pName; /* Name of the container - used for error messages */ +}; + +/* * This global flag is set for performance testing of triggers. When it is set * SQLite will perform the overhead of building new and old trigger references * even when no triggers exist @@ -1171,3 +1184,9 @@ void sqliteAttach(Parse*, Token*, Token*); void sqliteDetach(Parse*, Token*); int sqliteBtreeFactory(const sqlite *db, const char *zFilename, int mode, int nPg, Btree **ppBtree); +int sqliteFixInit(DbFixer*, Parse*, int, const char*, const Token*); +int sqliteFixSrcList(DbFixer*, SrcList*); +int sqliteFixSelect(DbFixer*, Select*); +int sqliteFixExpr(DbFixer*, Expr*); +int sqliteFixExprList(DbFixer*, ExprList*); +int sqliteFixTriggerStep(DbFixer*, TriggerStep*); diff --git a/src/trigger.c b/src/trigger.c index 6ac45fc23..eec48e0ff 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -64,7 +64,12 @@ void sqliteBeginTrigger( */ if( sqlite_malloc_failed ) goto trigger_cleanup; assert( pTableName->nSrc==1 ); + assert( pTableName->a[0].zDatabase==0 ); + if( pParse->initFlag ){ + pTableName->a[0].zDatabase = db->aDb[pParse->iDb].zName; + } tab = sqliteSrcListLookup(pParse, pTableName); + pTableName->a[0].zDatabase = 0; if( !tab ){ goto trigger_cleanup; } @@ -133,6 +138,7 @@ void sqliteBeginTrigger( nt->pWhen = sqliteExprDup(pWhen); nt->pColumns = sqliteIdListDup(pColumns); nt->foreach = foreach; + nt->pNameToken = pName; assert( pParse->pNewTrigger==0 ); pParse->pNewTrigger = nt; @@ -152,8 +158,9 @@ void sqliteFinishTrigger( TriggerStep *pStepList, /* The triggered program */ Token *pAll /* Token that describes the complete CREATE TRIGGER */ ){ - Trigger *nt; /* The trigger whose construction is finishing up */ + Trigger *nt = 0; /* The trigger whose construction is finishing up */ sqlite *db = pParse->db; /* The database */ + DbFixer sFix; if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup; nt = pParse->pNewTrigger; @@ -163,6 +170,11 @@ void sqliteFinishTrigger( pStepList->pTrig = nt; pStepList = pStepList->pNext; } + if( sqliteFixInit(&sFix, pParse, nt->iDb, "trigger", nt->pNameToken) + && sqliteFixTriggerStep(&sFix, nt->step_list) ){ + goto triggerfinish_cleanup; + } + nt->pNameToken = 0; /* if we are not initializing, and this trigger is not on a TEMP table, ** build the sqlite_master entry @@ -201,15 +213,15 @@ void sqliteFinishTrigger( Table *pTab; sqliteHashInsert(&db->aDb[nt->iDb].trigHash, nt->name, strlen(nt->name)+1, nt); - pTab = sqliteLocateTable(pParse, nt->table, 0); + pTab = sqliteLocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName); assert( pTab!=0 ); nt->pNext = pTab->pTrigger; pTab->pTrigger = nt; - }else{ - sqliteDeleteTrigger(nt); + nt = 0; } triggerfinish_cleanup: + sqliteDeleteTrigger(nt); sqliteDeleteTrigger(pParse->pNewTrigger); pParse->pNewTrigger = 0; sqliteDeleteTriggerStep(pStepList); @@ -548,6 +560,36 @@ int sqliteTriggersExist( } /* +** Convert the pStep->target token into a SrcList and return a pointer +** to that SrcList. +** +** This routine adds a specific database name, if needed, to the target when +** forming the SrcList. This prevents a trigger in one database from +** referring to a target in another database. An exception is when the +** trigger is in TEMP in which case it can refer to any other database it +** wants. +*/ +static SrcList *targetSrcList( + Parse *pParse, /* The parsing context */ + TriggerStep *pStep /* The trigger containing the target token */ +){ + Token sDb; /* Dummy database name token */ + int iDb; /* Index of the database to use */ + SrcList *pSrc; /* SrcList to be returned */ + + iDb = pStep->pTrig->iDb; + if( iDb==0 || iDb>=2 ){ + assert( iDb<pParse->db->nDb ); + sDb.z = pParse->db->aDb[iDb].zName; + sDb.n = strlen(sDb.z); + pSrc = sqliteSrcListAppend(0, &sDb, &pStep->target); + } else { + pSrc = sqliteSrcListAppend(0, &pStep->target, 0); + } + return pSrc; +} + +/* ** Generate VDBE code for zero or more statements inside the body of a ** trigger. */ @@ -561,11 +603,9 @@ static int codeTriggerProgram( while( pTriggerStep ){ int saveNTab = pParse->nTab; - int saveUseDb = pParse->useDb; + orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; pParse->trigStack->orconf = orconf; - pParse->useDb = pTriggerStep->pTrig->iDb; - if( pParse->useDb==1 ) pParse->useDb = -1; switch( pTriggerStep->op ){ case TK_SELECT: { Select * ss = sqliteSelectDup(pTriggerStep->pSelect); @@ -577,7 +617,7 @@ static int codeTriggerProgram( } case TK_UPDATE: { SrcList *pSrc; - pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0); + pSrc = targetSrcList(pParse, pTriggerStep); sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0); sqliteUpdate(pParse, pSrc, sqliteExprListDup(pTriggerStep->pExprList), @@ -587,7 +627,7 @@ static int codeTriggerProgram( } case TK_INSERT: { SrcList *pSrc; - pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0); + pSrc = targetSrcList(pParse, pTriggerStep); sqliteInsert(pParse, pSrc, sqliteExprListDup(pTriggerStep->pExprList), sqliteSelectDup(pTriggerStep->pSelect), @@ -597,7 +637,7 @@ static int codeTriggerProgram( case TK_DELETE: { SrcList *pSrc; sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0); - pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0); + pSrc = targetSrcList(pParse, pTriggerStep); sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere)); sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0); break; @@ -606,7 +646,6 @@ static int codeTriggerProgram( assert(0); } pParse->nTab = saveNTab; - pParse->useDb = saveUseDb; pTriggerStep = pTriggerStep->pNext; } |