/ Check-in [c8c823b0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Prevent an infinite loop when deleting a table that has a TEMP trigger. (CVS 984)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: c8c823b068916711857fa67db10fb479999b55c2
User & Date: drh 2003-05-17 19:04:04
Context
2003-05-17
19:23
Additional attach testing. I now believe that ticket #317 was fixed by check-in (981). (CVS 985) check-in: 24191373 user: drh tags: trunk
19:04
Prevent an infinite loop when deleting a table that has a TEMP trigger. (CVS 984) check-in: c8c823b0 user: drh tags: trunk
17:52
Version 2.8.2 (CVS 983) check-in: f542e5fc user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/build.c.

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
....
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.153 2003/05/17 17:35:11 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
................................................................................
    Index *pIdx;
    Trigger *pTrigger;
    sqliteBeginWriteOperation(pParse, 0, pTable->iDb);

    /* Drop all triggers associated with the table being dropped */
    pTrigger = pTable->pTrigger;
    while( pTrigger ){
      SrcList *pNm;
      assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 );
      pNm = sqliteSrcListAppend(0, 0, 0);
      pNm->a[0].zName = sqliteStrDup(pTrigger->name);
      pNm->a[0].zDatabase = sqliteStrDup(db->aDb[pTable->iDb].zName);
      sqliteDropTrigger(pParse, pNm, 1);
      if( pParse->explain ){
        pTrigger = pTrigger->pNext;
      }else{
        pTrigger = pTable->pTrigger;
      }
    }








|







 







<

<
<
<
|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
....
1293
1294
1295
1296
1297
1298
1299

1300



1301
1302
1303
1304
1305
1306
1307
1308
**     DROP INDEX
**     creating ID lists
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.154 2003/05/17 19:04:04 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called when a new SQL statement is beginning to
** be parsed.  Check to see if the schema for the database needs
................................................................................
    Index *pIdx;
    Trigger *pTrigger;
    sqliteBeginWriteOperation(pParse, 0, pTable->iDb);

    /* Drop all triggers associated with the table being dropped */
    pTrigger = pTable->pTrigger;
    while( pTrigger ){

      assert( pTrigger->iDb==pTable->iDb || pTrigger->iDb==1 );



      sqliteDropTriggerPtr(pParse, pTrigger, 1);
      if( pParse->explain ){
        pTrigger = pTrigger->pNext;
      }else{
        pTrigger = pTable->pTrigger;
      }
    }

Changes to src/parse.y.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
**
*************************************************************************
** This file contains SQLite's grammar for SQL.  Process this file
** using the lemon parser generator to generate C code that runs
** the parser.  Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.97 2003/04/29 17:19:18 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
%default_type {Token}
%extra_argument {Parse *pParse}
%syntax_error {
  if( pParse->zErrMsg==0 ){
................................................................................
  A = sqliteExpr(TK_RAISE, 0, 0, &Z); 
  A->iColumn = OE_Fail;
  sqliteExprSpan(A, &X, &Y);
}

////////////////////////  DROP TRIGGER statement //////////////////////////////
cmd ::= DROP TRIGGER nm(X) dbnm(D). {
  sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&X,&D),0);
}

//////////////////////// ATTACH DATABASE file AS name /////////////////////////
cmd ::= ATTACH database_kw_opt ids(F) AS nm(D). {
  sqliteAttach(pParse, &F, &D);
}








|







 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
**
*************************************************************************
** This file contains SQLite's grammar for SQL.  Process this file
** using the lemon parser generator to generate C code that runs
** the parser.  Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
** @(#) $Id: parse.y,v 1.98 2003/05/17 19:04:04 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
%default_type {Token}
%extra_argument {Parse *pParse}
%syntax_error {
  if( pParse->zErrMsg==0 ){
................................................................................
  A = sqliteExpr(TK_RAISE, 0, 0, &Z); 
  A->iColumn = OE_Fail;
  sqliteExprSpan(A, &X, &Y);
}

////////////////////////  DROP TRIGGER statement //////////////////////////////
cmd ::= DROP TRIGGER nm(X) dbnm(D). {
  sqliteDropTrigger(pParse,sqliteSrcListAppend(0,&X,&D));
}

//////////////////////// ATTACH DATABASE file AS name /////////////////////////
cmd ::= ATTACH database_kw_opt ids(F) AS nm(D). {
  sqliteAttach(pParse, &F, &D);
}

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
889
890
891
892
893
894
895

896
897
898
899
900
901
902
....
1131
1132
1133
1134
1135
1136
1137
1138

1139
1140
1141
1142
1143
1144
1145
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.185 2003/05/17 17:35:12 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
 * The "step_list" member points to the first element of a linked list
 * containing the SQL statements specified as the trigger program.
 */
struct Trigger {
  char *name;             /* The name of the trigger                        */
  char *table;            /* The table or view to which the trigger applies */
  u8 iDb;                 /* Database containing this trigger               */

  u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  u8 tr_tm;               /* One of TK_BEFORE, TK_AFTER */
  Expr *pWhen;            /* The WHEN clause of the expresion (may be NULL) */
  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 */

................................................................................
void sqliteRegisterBuiltinFunctions(sqlite*);
int sqliteSafetyOn(sqlite*);
int sqliteSafetyOff(sqlite*);
int sqliteSafetyCheck(sqlite*);
void sqliteChangeCookie(sqlite*, Vdbe*);
void sqliteBeginTrigger(Parse*, Token*,int,int,IdList*,SrcList*,int,Expr*,int);
void sqliteFinishTrigger(Parse*, TriggerStep*, Token*);
void sqliteDropTrigger(Parse*, SrcList*, int);

int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, 
                         int, int);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
void sqliteDeleteTriggerStep(TriggerStep*);
TriggerStep *sqliteTriggerSelectStep(Select*);
TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);







|







 







>







 







|
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
....
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.186 2003/05/17 19:04:04 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
................................................................................
 * The "step_list" member points to the first element of a linked list
 * containing the SQL statements specified as the trigger program.
 */
struct Trigger {
  char *name;             /* The name of the trigger                        */
  char *table;            /* The table or view to which the trigger applies */
  u8 iDb;                 /* Database containing this trigger               */
  u8 iTabDb;              /* Database containing Trigger.table              */
  u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  u8 tr_tm;               /* One of TK_BEFORE, TK_AFTER */
  Expr *pWhen;            /* The WHEN clause of the expresion (may be NULL) */
  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 */

................................................................................
void sqliteRegisterBuiltinFunctions(sqlite*);
int sqliteSafetyOn(sqlite*);
int sqliteSafetyOff(sqlite*);
int sqliteSafetyCheck(sqlite*);
void sqliteChangeCookie(sqlite*, Vdbe*);
void sqliteBeginTrigger(Parse*, Token*,int,int,IdList*,SrcList*,int,Expr*,int);
void sqliteFinishTrigger(Parse*, TriggerStep*, Token*);
void sqliteDropTrigger(Parse*, SrcList*);
void sqliteDropTriggerPtr(Parse*, Trigger*, int);
int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, 
                         int, int);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
void sqliteDeleteTriggerStep(TriggerStep*);
TriggerStep *sqliteTriggerSelectStep(Select*);
TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);

Changes to src/trigger.c.

123
124
125
126
127
128
129

130
131
132
133
134
135
136
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
355
356
357
358
359
360
361
362
363


364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
388
389
390
391
392
393
394
















395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446


447
448
449
450
451
452
453
...
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
  nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
  if( nt==0 ) goto trigger_cleanup;
  nt->name = zName;
  zName = 0;
  nt->table = sqliteStrDup(pTableName->a[0].zName);
  if( sqlite_malloc_failed ) goto trigger_cleanup;
  nt->iDb = iDb;

  nt->op = op;
  nt->tr_tm = tr_tm;
  nt->pWhen = sqliteExprDup(pWhen);
  nt->pColumns = sqliteIdListDup(pColumns);
  nt->foreach = foreach;
  assert( pParse->pNewTrigger==0 );
  pParse->pNewTrigger = nt;
................................................................................
    int addr;
    Vdbe *v;

    /* Make an entry in the sqlite_master table */
    v = sqliteGetVdbe(pParse);
    if( v==0 ) goto triggerfinish_cleanup;
    sqliteBeginWriteOperation(pParse, 0, 0);
    sqliteOpenMasterTable(v, nt->iDb==1);
    addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
    sqliteVdbeChangeP3(v, addr+2, nt->name, 0); 
    sqliteVdbeChangeP3(v, addr+3, nt->table, 0); 
    sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
    if( nt->iDb==0 ){
      sqliteChangeCookie(db, v);
    }
................................................................................
  sqliteIdListDelete(pTrigger->pColumns);
  sqliteFree(pTrigger);
}

/*
 * This function is called to drop a trigger from the database schema. 
 *
 * This may be called directly from the parser, or from within 
 * sqliteDropTable(). In the latter case the "nested" argument is true.


 *
 * Note that this function does not delete the trigger entirely. Instead it
 * removes it from the internal schema and places it in the trigDrop hash 
 * table. This is so that the trigger can be restored into the database schema
 * if the transaction is rolled back.
 */
void sqliteDropTrigger(Parse *pParse, SrcList *pName, int nested){
  Trigger *pTrigger;
  Table   *pTable;
  Vdbe *v;
  int i;
  const char *zDb;
  const char *zName;
  int nName;
  sqlite *db = pParse->db;

  if( sqlite_malloc_failed ) goto drop_trigger_cleanup;
................................................................................
    pTrigger = sqliteHashFind(&(db->aDb[j].trigHash), zName, nName+1);
    if( pTrigger ) break;
  }
  if( !pTrigger ){
    sqliteErrorMsg(pParse, "no such trigger: %S", pName, 0);
    goto drop_trigger_cleanup;
  }
















  assert( pTrigger->iDb<db->nDb );
  if( pTrigger->iDb>=2 ){
    sqliteErrorMsg(pParse, "triggers may not be removed from "
       "auxiliary database %s", db->aDb[pTrigger->iDb].zName);
    goto drop_trigger_cleanup;
  }
  pTable = sqliteFindTable(db, pTrigger->table, db->aDb[pTrigger->iDb].zName);
  assert(pTable);
  assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
  {
    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( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
      sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
      goto drop_trigger_cleanup;
    }
  }
#endif

  /* Generate code to destroy the database record of the trigger.
  */
  if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
................................................................................
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };

    sqliteBeginWriteOperation(pParse, 0, 0);
    sqliteOpenMasterTable(v, pTrigger->iDb);
    base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
    sqliteVdbeChangeP3(v, base+1, zName, 0);
    if( pTrigger->iDb==0 ){
      sqliteChangeCookie(db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
    sqliteEndWriteOperation(pParse);
  }

  /*
   * If this is not an "explain", then delete the trigger structure.
   */
  if( !pParse->explain ){


    if( pTable->pTrigger == pTrigger ){
      pTable->pTrigger = pTrigger->pNext;
    }else{
      Trigger *cc = pTable->pTrigger;
      while( cc ){ 
        if( cc->pNext == pTrigger ){
          cc->pNext = cc->pNext->pNext;
................................................................................
        cc = cc->pNext;
      }
      assert(cc);
    }
    sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0);
    sqliteDeleteTrigger(pTrigger);
  }

drop_trigger_cleanup:
  sqliteSrcListDelete(pName);
}

/*
** pEList is the SET clause of an UPDATE statement.  Each entry
** in pEList is of the format <id>=<expr>.  If any of the entries
** in pEList have an <id> which matches an identifier in pIdList,
** then return TRUE.  If pIdList==NULL, then it is considered a







>







 







|







 







|
|
>
>






|

<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|

|










|







 







|











>
>







 







<
<
<







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374


375
376
377
378
379
380
381
...
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
...
475
476
477
478
479
480
481



482
483
484
485
486
487
488
  nt = (Trigger*)sqliteMalloc(sizeof(Trigger));
  if( nt==0 ) goto trigger_cleanup;
  nt->name = zName;
  zName = 0;
  nt->table = sqliteStrDup(pTableName->a[0].zName);
  if( sqlite_malloc_failed ) goto trigger_cleanup;
  nt->iDb = iDb;
  nt->iTabDb = tab->iDb;
  nt->op = op;
  nt->tr_tm = tr_tm;
  nt->pWhen = sqliteExprDup(pWhen);
  nt->pColumns = sqliteIdListDup(pColumns);
  nt->foreach = foreach;
  assert( pParse->pNewTrigger==0 );
  pParse->pNewTrigger = nt;
................................................................................
    int addr;
    Vdbe *v;

    /* Make an entry in the sqlite_master table */
    v = sqliteGetVdbe(pParse);
    if( v==0 ) goto triggerfinish_cleanup;
    sqliteBeginWriteOperation(pParse, 0, 0);
    sqliteOpenMasterTable(v, nt->iDb);
    addr = sqliteVdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
    sqliteVdbeChangeP3(v, addr+2, nt->name, 0); 
    sqliteVdbeChangeP3(v, addr+3, nt->table, 0); 
    sqliteVdbeChangeP3(v, addr+5, pAll->z, pAll->n);
    if( nt->iDb==0 ){
      sqliteChangeCookie(db, v);
    }
................................................................................
  sqliteIdListDelete(pTrigger->pColumns);
  sqliteFree(pTrigger);
}

/*
 * This function is called to drop a trigger from the database schema. 
 *
 * This may be called directly from the parser and therefore identifies
 * the trigger by name.  The sqliteDropTriggerPtr() routine does the
 * same job as this routine except it take a spointer to the trigger
 * instead of the trigger name.
 *
 * Note that this function does not delete the trigger entirely. Instead it
 * removes it from the internal schema and places it in the trigDrop hash 
 * table. This is so that the trigger can be restored into the database schema
 * if the transaction is rolled back.
 */
void sqliteDropTrigger(Parse *pParse, SrcList *pName){
  Trigger *pTrigger;


  int i;
  const char *zDb;
  const char *zName;
  int nName;
  sqlite *db = pParse->db;

  if( sqlite_malloc_failed ) goto drop_trigger_cleanup;
................................................................................
    pTrigger = sqliteHashFind(&(db->aDb[j].trigHash), zName, nName+1);
    if( pTrigger ) break;
  }
  if( !pTrigger ){
    sqliteErrorMsg(pParse, "no such trigger: %S", pName, 0);
    goto drop_trigger_cleanup;
  }
  sqliteDropTriggerPtr(pParse, pTrigger, 0);

drop_trigger_cleanup:
  sqliteSrcListDelete(pName);
}

/*
** Drop a trigger given a pointer to that trigger.  If nested is false,
** then also generate code to remove the trigger from the SQLITE_MASTER
** table.
*/
void sqliteDropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
  Table   *pTable;
  Vdbe *v;
  sqlite *db = pParse->db;

  assert( pTrigger->iDb<db->nDb );
  if( pTrigger->iDb>=2 ){
    sqliteErrorMsg(pParse, "triggers may not be removed from "
       "auxiliary database %s", db->aDb[pTrigger->iDb].zName);
    return;
  }
  pTable = sqliteFindTable(db, pTrigger->table,db->aDb[pTrigger->iTabDb].zName);
  assert(pTable);
  assert( pTable->iDb==pTrigger->iDb || pTrigger->iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
  {
    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( sqliteAuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
      sqliteAuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
      return;
    }
  }
#endif

  /* Generate code to destroy the database record of the trigger.
  */
  if( pTable!=0 && !nested && (v = sqliteGetVdbe(pParse))!=0 ){
................................................................................
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(3),  0}, /* 7 */
    };

    sqliteBeginWriteOperation(pParse, 0, 0);
    sqliteOpenMasterTable(v, pTrigger->iDb);
    base = sqliteVdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
    sqliteVdbeChangeP3(v, base+1, pTrigger->name, 0);
    if( pTrigger->iDb==0 ){
      sqliteChangeCookie(db, v);
    }
    sqliteVdbeAddOp(v, OP_Close, 0, 0);
    sqliteEndWriteOperation(pParse);
  }

  /*
   * If this is not an "explain", then delete the trigger structure.
   */
  if( !pParse->explain ){
    const char *zName = pTrigger->name;
    int nName = strlen(zName);
    if( pTable->pTrigger == pTrigger ){
      pTable->pTrigger = pTrigger->pNext;
    }else{
      Trigger *cc = pTable->pTrigger;
      while( cc ){ 
        if( cc->pNext == pTrigger ){
          cc->pNext = cc->pNext->pNext;
................................................................................
        cc = cc->pNext;
      }
      assert(cc);
    }
    sqliteHashInsert(&(db->aDb[pTrigger->iDb].trigHash), zName, nName+1, 0);
    sqliteDeleteTrigger(pTrigger);
  }



}

/*
** pEList is the SET clause of an UPDATE statement.  Each entry
** in pEList is of the format <id>=<expr>.  If any of the entries
** in pEList have an <id> which matches an identifier in pIdList,
** then return TRUE.  If pIdList==NULL, then it is considered a

Changes to test/trigger1.test.

26
27
28
29
30
31
32
33
34
35
36
37







38
39
40
41
42
43
44
...
262
263
264
265
266
267
268
269


































270
# trig-1.13: Ensure that AFTER triggers cannot be created on views
# trig-1.14: Ensure that BEFORE triggers cannot be created on views
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test trigger1-1.1 {
   catchsql {
     CREATE TRIGGER trig UPDATE ON no_such_table BEGIN
       SELECT * from sqlite_master;
     END;







   } 
} {1 {no such table: no_such_table}}

execsql {
    CREATE TABLE t1(a);
}
execsql {
................................................................................
  db close
  sqlite db test.db
  execsql {
    INSERT INTO t1 VALUES(5,6);
    SELECT * FROM t1 UNION ALL SELECT * FROM t2;
  }
} {1 2 3 4 5 6 3 4}



































finish_test







|




>
>
>
>
>
>
>







 








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# trig-1.13: Ensure that AFTER triggers cannot be created on views
# trig-1.14: Ensure that BEFORE triggers cannot be created on views
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test trigger1-1.1.2 {
   catchsql {
     CREATE TRIGGER trig UPDATE ON no_such_table BEGIN
       SELECT * from sqlite_master;
     END;
   } 
} {1 {no such table: no_such_table}}
do_test trigger1-1.1.2 {
   catchsql {
     CREATE TEMP TRIGGER trig UPDATE ON no_such_table BEGIN
       SELECT * from sqlite_master;
     END;
   } 
} {1 {no such table: no_such_table}}

execsql {
    CREATE TABLE t1(a);
}
execsql {
................................................................................
  db close
  sqlite db test.db
  execsql {
    INSERT INTO t1 VALUES(5,6);
    SELECT * FROM t1 UNION ALL SELECT * FROM t2;
  }
} {1 2 3 4 5 6 3 4}

do_test trigger-4.1 {
  execsql {
    CREATE TEMP TRIGGER r1 BEFORE INSERT ON t1 BEGIN
      INSERT INTO t2 VALUES(NEW.a,NEW.b);
    END;
    INSERT INTO t1 VALUES(7,8);
    SELECT * FROM t2;
  }
} {3 4 7 8}
do_test trigger-4.2 {
  sqlite db2 test.db
  execsql {
    INSERT INTO t1 VALUES(9,10);
  } db2;
  db2 close
  execsql {
    SELECT * FROM t2;
  }
} {3 4 7 8}
do_test trigger-4.3 {
  execsql {
    DROP TABLE t1;
    SELECT * FROM t2;
  };
} {3 4 7 8}
do_test trigger-4.4 {
  db close
  sqlite db test.db
  execsql {
    SELECT * FROM t2;
  };
} {3 4 7 8}


finish_test