mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Fix triggers to work in an ATTACHed database. Ticket #295. (CVS 915)
FossilOrigin-Name: 1e5e00fb73c308378efd034cb291caf338c9fe84
This commit is contained in:
56
src/build.c
56
src/build.c
@ -23,7 +23,7 @@
|
||||
** ROLLBACK
|
||||
** PRAGMA
|
||||
**
|
||||
** $Id: build.c,v 1.145 2003/04/15 01:19:48 drh Exp $
|
||||
** $Id: build.c,v 1.146 2003/04/17 22:57:53 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@ -109,7 +109,10 @@ void sqliteExec(Parse *pParse){
|
||||
/*
|
||||
** Locate the in-memory structure that describes
|
||||
** a particular database table given the name
|
||||
** of that table. Return NULL if not found.
|
||||
** of that table and (optionally) the name of the database
|
||||
** containing the table. Return NULL if not found.
|
||||
**
|
||||
** See also sqliteLocateTable().
|
||||
*/
|
||||
Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
|
||||
Table *p = 0;
|
||||
@ -125,7 +128,48 @@ Table *sqliteFindTable(sqlite *db, const char *zName, const char *zDatabase){
|
||||
|
||||
/*
|
||||
** Locate the in-memory structure that describes
|
||||
** a particular index given the name of that index.
|
||||
** 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.
|
||||
**
|
||||
** 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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
if( p==0 ){
|
||||
if( zDbase ){
|
||||
sqliteErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
|
||||
}else{
|
||||
sqliteErrorMsg(pParse, "no such table: %s", zName);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Locate the in-memory structure that describes
|
||||
** a particular index given the name of that index
|
||||
** and the name of the database that contains the index.
|
||||
** Return NULL if not found.
|
||||
*/
|
||||
Index *sqliteFindIndex(sqlite *db, const char *zName, const char *zDb){
|
||||
@ -2078,7 +2122,13 @@ void sqliteBeginWriteOperation(Parse *pParse, int setCheckpoint, int tempOnly){
|
||||
if( (pParse->db->flags & SQLITE_InTrans)==0 ){
|
||||
sqliteVdbeAddOp(v, OP_Transaction, 1, 0);
|
||||
if( !tempOnly ){
|
||||
int i;
|
||||
sqlite *db = pParse->db;
|
||||
sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
|
||||
for(i=2; i<db->nDb; i++){
|
||||
if( db->aDb[i].pBt==0 ) continue;
|
||||
sqliteVdbeAddOp(v, OP_Transaction, i, 0);
|
||||
}
|
||||
sqliteCodeVerifySchema(pParse);
|
||||
}
|
||||
}else if( setCheckpoint ){
|
||||
|
46
src/delete.c
46
src/delete.c
@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.51 2003/04/15 19:22:23 drh Exp $
|
||||
** $Id: delete.c,v 1.52 2003/04/17 22:57:53 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -27,11 +27,7 @@ Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){
|
||||
for(i=0; i<pSrc->nSrc; i++){
|
||||
const char *zTab = pSrc->a[i].zName;
|
||||
const char *zDb = pSrc->a[i].zDatabase;
|
||||
pTab = sqliteFindTable(pParse->db, zTab, zDb);
|
||||
if( pTab==0 ){
|
||||
sqliteErrorMsg(pParse, "no such table: %S", pSrc, 0);
|
||||
break;
|
||||
}
|
||||
pTab = sqliteLocateTable(pParse, zTab, zDb);
|
||||
pSrc->a[i].pTab = pTab;
|
||||
}
|
||||
return pTab;
|
||||
@ -62,8 +58,6 @@ void sqliteDeleteFrom(
|
||||
){
|
||||
Vdbe *v; /* The virtual database engine */
|
||||
Table *pTab; /* The table from which records will be deleted */
|
||||
char *zTab; /* Name of the table from which we are deleting */
|
||||
char *zDb; /* Name of database containing table zTab */
|
||||
int end, addr; /* A couple addresses of generated code */
|
||||
int i; /* Loop counter */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
@ -83,37 +77,25 @@ void sqliteDeleteFrom(
|
||||
db = pParse->db;
|
||||
assert( pTabList->nSrc==1 );
|
||||
|
||||
/* Check for the special case of a VIEW with one or more ON DELETE triggers
|
||||
** defined
|
||||
*/
|
||||
zTab = pTabList->a[0].zName;
|
||||
zDb = pTabList->a[0].zDatabase;
|
||||
if( zTab != 0 ){
|
||||
pTab = sqliteFindTable(pParse->db, zTab, zDb);
|
||||
if( pTab ){
|
||||
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_DELETE, TK_BEFORE, TK_ROW, 0);
|
||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_DELETE, TK_AFTER, TK_ROW, 0);
|
||||
row_triggers_exist = before_triggers || after_triggers;
|
||||
}
|
||||
if( row_triggers_exist && pTab->pSelect ){
|
||||
/* Just fire VIEW triggers */
|
||||
sqliteSrcListDelete(pTabList);
|
||||
sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Locate the table which we want to delete. This table has to be
|
||||
** put in an SrcList structure because some of the subroutines we
|
||||
** will be calling are designed to work with multiple tables and expect
|
||||
** an SrcList* parameter instead of just a Table* parameter.
|
||||
*/
|
||||
pTab = sqliteSrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 || sqliteIsReadOnly(pParse, pTab) ){
|
||||
goto delete_from_cleanup;
|
||||
if( pTab==0 ) goto delete_from_cleanup;
|
||||
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_DELETE, TK_BEFORE, TK_ROW, 0);
|
||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_DELETE, TK_AFTER, TK_ROW, 0);
|
||||
row_triggers_exist = before_triggers || after_triggers;
|
||||
if( row_triggers_exist && pTab->pSelect ){
|
||||
/* Just fire VIEW triggers */
|
||||
sqliteSrcListDelete(pTabList);
|
||||
sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
|
||||
return;
|
||||
}
|
||||
if( sqliteIsReadOnly(pParse, pTab) ) goto delete_from_cleanup;
|
||||
assert( pTab->pSelect==0 ); /* This table is not a view */
|
||||
if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0) ){
|
||||
goto delete_from_cleanup;
|
||||
|
@ -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.125 2003/04/16 20:24:52 drh Exp $
|
||||
** $Id: main.c,v 1.126 2003/04/17 22:57:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@ -69,6 +69,7 @@ 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);
|
||||
@ -320,6 +321,7 @@ 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,
|
||||
@ -719,6 +721,7 @@ static int sqliteMain(
|
||||
sParse.db = db;
|
||||
sParse.xCallback = xCallback;
|
||||
sParse.pArg = pArg;
|
||||
sParse.useDb = -1;
|
||||
sParse.useCallback = ppVm==0;
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
|
||||
|
@ -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.94 2003/03/31 00:30:48 drh Exp $
|
||||
** @(#) $Id: parse.y,v 1.95 2003/04/17 22:57:54 drh Exp $
|
||||
*/
|
||||
%token_prefix TK_
|
||||
%token_type {Token}
|
||||
@ -794,7 +794,9 @@ when_clause(A) ::= WHEN expr(X). { A = X; }
|
||||
|
||||
%type trigger_cmd_list {TriggerStep *}
|
||||
trigger_cmd_list(A) ::= trigger_cmd(X) SEMI trigger_cmd_list(Y). {
|
||||
X->pNext = Y ; A = X; }
|
||||
X->pNext = Y;
|
||||
A = X;
|
||||
}
|
||||
trigger_cmd_list(A) ::= . { A = 0; }
|
||||
|
||||
%type trigger_cmd {TriggerStep *}
|
||||
|
@ -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.131 2003/04/17 12:44:24 drh Exp $
|
||||
** $Id: select.c,v 1.132 2003/04/17 22:57:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -908,10 +908,8 @@ static int fillInColumnList(Parse *pParse, Select *p){
|
||||
}else{
|
||||
/* An ordinary table or view name in the FROM clause */
|
||||
pTabList->a[i].pTab = pTab =
|
||||
sqliteFindTable(pParse->db, pTabList->a[i].zName,
|
||||
pTabList->a[i].zDatabase);
|
||||
sqliteLocateTable(pParse,pTabList->a[i].zName,pTabList->a[i].zDatabase);
|
||||
if( pTab==0 ){
|
||||
sqliteErrorMsg(pParse, "no such table: %S", pTabList, i);
|
||||
return 1;
|
||||
}
|
||||
if( pTab->pSelect ){
|
||||
|
@ -12,7 +12,7 @@
|
||||
** This file contains code to implement the "sqlite" command line
|
||||
** utility for accessing SQLite databases.
|
||||
**
|
||||
** $Id: shell.c,v 1.69 2003/04/17 02:54:14 drh Exp $
|
||||
** $Id: shell.c,v 1.70 2003/04/17 22:57:54 drh Exp $
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -285,7 +285,8 @@ static int callback(void *pArg, int nArg, char **azArg, char **azCol){
|
||||
}
|
||||
if( p->cnt++>0 ) fprintf(p->out,"\n");
|
||||
for(i=0; i<nArg; i++){
|
||||
fprintf(p->out,"%*s = %s\n", w, azCol[i], azArg[i] ? azArg[i] : p->nullvalue);
|
||||
fprintf(p->out,"%*s = %s\n", w, azCol[i],
|
||||
azArg[i] ? azArg[i] : p->nullvalue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.174 2003/04/16 02:17:35 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.175 2003/04/17 22:57:54 drh Exp $
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "sqlite.h"
|
||||
@ -225,8 +225,8 @@ typedef struct Db Db;
|
||||
** Each database file to be accessed by the system is an instance
|
||||
** of the following structure. There are normally two of these structures
|
||||
** in the sqlite.aDb[] array. aDb[0] is the main database file and
|
||||
** aDb[1] is the database file used to hold temporary tables. But
|
||||
** additional databases may be attached to the engine.
|
||||
** aDb[1] is the database file used to hold temporary tables. Additional
|
||||
** databases may be attached.
|
||||
*/
|
||||
struct Db {
|
||||
char *zName; /* Name of this database */
|
||||
@ -825,6 +825,7 @@ struct Parse {
|
||||
** other than after an OP_Transaction */
|
||||
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 */
|
||||
@ -918,6 +919,7 @@ struct Trigger {
|
||||
struct TriggerStep {
|
||||
int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
|
||||
int orconf; /* OE_Rollback etc. */
|
||||
Trigger *pTrig; /* The trigger that this step is a part of */
|
||||
|
||||
Select *pSelect; /* Valid for SELECT and sometimes
|
||||
INSERT steps (when pExprList == 0) */
|
||||
@ -1062,6 +1064,7 @@ void sqliteExprCode(Parse*, Expr*);
|
||||
void sqliteExprIfTrue(Parse*, Expr*, int, int);
|
||||
void sqliteExprIfFalse(Parse*, Expr*, int, int);
|
||||
Table *sqliteFindTable(sqlite*,const char*, const char*);
|
||||
Table *sqliteLocateTable(Parse*,const char*, const char*);
|
||||
Index *sqliteFindIndex(sqlite*,const char*, const char*);
|
||||
void sqliteUnlinkAndDeleteIndex(sqlite*,Index*);
|
||||
void sqliteCopy(Parse*, SrcList*, Token*, Token*, int);
|
||||
|
@ -65,7 +65,7 @@ void sqliteCreateTrigger(
|
||||
if( !tab ){
|
||||
goto trigger_cleanup;
|
||||
}
|
||||
if( tab->iDb>=2 ){
|
||||
if( tab->iDb>=2 && !pParse->initFlag ){
|
||||
sqliteErrorMsg(pParse, "triggers may not be added to auxiliary "
|
||||
"database %s", db->aDb[tab->iDb].zName);
|
||||
goto trigger_cleanup;
|
||||
@ -124,6 +124,11 @@ void sqliteCreateTrigger(
|
||||
sqliteIdListDelete(pColumns);
|
||||
nt->foreach = foreach;
|
||||
nt->step_list = pStepList;
|
||||
while( pStepList ){
|
||||
pStepList->pTrig = nt;
|
||||
pStepList = pStepList->pNext;
|
||||
}
|
||||
pStepList = nt->step_list;
|
||||
|
||||
/* if we are not initializing, and this trigger is not on a TEMP table,
|
||||
** build the sqlite_master entry
|
||||
@ -512,8 +517,10 @@ 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;
|
||||
switch( pTriggerStep->op ){
|
||||
case TK_SELECT: {
|
||||
Select * ss = sqliteSelectDup(pTriggerStep->pSelect);
|
||||
@ -554,6 +561,7 @@ static int codeTriggerProgram(
|
||||
assert(0);
|
||||
}
|
||||
pParse->nTab = saveNTab;
|
||||
pParse->useDb = saveUseDb;
|
||||
pTriggerStep = pTriggerStep->pNext;
|
||||
}
|
||||
|
||||
|
43
src/update.c
43
src/update.c
@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle UPDATE statements.
|
||||
**
|
||||
** $Id: update.c,v 1.59 2003/04/15 19:22:24 drh Exp $
|
||||
** $Id: update.c,v 1.60 2003/04/17 22:57:54 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -27,8 +27,6 @@ void sqliteUpdate(
|
||||
int onError /* How to handle constraint errors */
|
||||
){
|
||||
int i, j; /* Loop counters */
|
||||
char *zTab; /* Name of the table to be updated */
|
||||
char *zDb; /* Name of the database holding zTab */
|
||||
Table *pTab; /* The table to be updated */
|
||||
int addr; /* VDBE instruction address of the start of the loop */
|
||||
WhereInfo *pWInfo; /* Information about the WHERE clause */
|
||||
@ -58,38 +56,25 @@ void sqliteUpdate(
|
||||
db = pParse->db;
|
||||
assert( pTabList->nSrc==1 );
|
||||
|
||||
/* Check for the special case of a VIEW with one or more ON UPDATE triggers
|
||||
* defined
|
||||
*/
|
||||
zTab = pTabList->a[0].zName;
|
||||
zDb = pTabList->a[0].zDatabase;
|
||||
if( zTab != 0 ){
|
||||
pTab = sqliteFindTable(pParse->db, zTab, zDb);
|
||||
if( pTab ){
|
||||
before_triggers =
|
||||
sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_UPDATE, TK_BEFORE, TK_ROW, pChanges);
|
||||
after_triggers =
|
||||
sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
|
||||
row_triggers_exist = before_triggers || after_triggers;
|
||||
}
|
||||
|
||||
if( row_triggers_exist && pTab->pSelect ){
|
||||
/* Just fire VIEW triggers */
|
||||
sqliteSrcListDelete(pTabList);
|
||||
sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Locate the table which we want to update. This table has to be
|
||||
** put in an SrcList structure because some of the subroutines we
|
||||
** will be calling are designed to work with multiple tables and expect
|
||||
** an SrcList* parameter instead of just a Table* parameter.
|
||||
*/
|
||||
pTab = sqliteSrcListLookup(pParse, pTabList);
|
||||
if( pTab==0 || sqliteIsReadOnly(pParse, pTab) ) goto update_cleanup;
|
||||
if( pTab==0 ) goto update_cleanup;
|
||||
before_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_UPDATE, TK_BEFORE, TK_ROW, pChanges);
|
||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
|
||||
row_triggers_exist = before_triggers || after_triggers;
|
||||
if( row_triggers_exist && pTab->pSelect ){
|
||||
/* Just fire VIEW triggers */
|
||||
sqliteSrcListDelete(pTabList);
|
||||
sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
|
||||
return;
|
||||
}
|
||||
if( sqliteIsReadOnly(pParse, pTab) ) goto update_cleanup;
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
|
||||
if( aXRef==0 ) goto update_cleanup;
|
||||
|
Reference in New Issue
Block a user