mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Tighter binding of views, triggers, and indices to their respective
databases. Ticket #323. Much more testing needs to be done to the sqliteFix...() routines in attach.c. (CVS 990) FossilOrigin-Name: 7202d4f1a8853368954a967b7ccca9d8a6645a2e
This commit is contained in:
135
src/attach.c
135
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;
|
||||
}
|
||||
|
62
src/build.c
62
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.
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 ){
|
||||
|
@ -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 */
|
||||
@ -1001,6 +1001,19 @@ struct TriggerStack {
|
||||
TriggerStack *pNext; /* Next trigger down on the trigger stack */
|
||||
};
|
||||
|
||||
/*
|
||||
** 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
|
||||
@ -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*);
|
||||
|
@ -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);
|
||||
@ -547,6 +559,36 @@ int sqliteTriggersExist(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user