/ Check-in [b1034681]
Login

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

Overview
Comment:Beginning to clean up the trigger code. Still lots of work to do. (CVS 566)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b10346818b25940c6dc85e94de8e36d20954161c
User & Date: drh 2002-05-15 12:45:43
Context
2002-05-15
14:17
Fix for ticket #41: Better handling of CREATE TRIGGER in the sqlite_complete() function. (CVS 567) check-in: f45c4b76 user: drh tags: trunk
12:45
Beginning to clean up the trigger code. Still lots of work to do. (CVS 566) check-in: b1034681 user: drh tags: trunk
11:44
Remove all tabs from the beginning of source code lines. Replace tabs with the appropriate number of spaces. (CVS 565) check-in: 690f9a16 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Makefile.template.

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
142
143
144
145
146
147
148

149
150
151
152
153
154
155
...
240
241
242
243
244
245
246



247
248
249
250
251
252
253
#
TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src

# Object files for the SQLite library.
#
LIBOBJ = btree.o build.o delete.o expr.o func.o hash.o insert.o \
         main.o os.o pager.o parse.o printf.o random.o select.o table.o \
         tokenize.o update.o util.o vdbe.o where.o tclsqlite.o

# All of the source code files.
#
SRC = \
  $(TOP)/src/btree.c \
  $(TOP)/src/btree.h \
  $(TOP)/src/build.c \
................................................................................
  $(TOP)/src/select.c \
  $(TOP)/src/shell.c \
  $(TOP)/src/sqlite.h.in \
  $(TOP)/src/sqliteInt.h \
  $(TOP)/src/table.c \
  $(TOP)/src/tclsqlite.c \
  $(TOP)/src/tokenize.c \

  $(TOP)/src/update.c \
  $(TOP)/src/util.c \
  $(TOP)/src/vdbe.c \
  $(TOP)/src/vdbe.h \
  $(TOP)/src/where.c

# Source code to the test files.
................................................................................
sqlite.h:	$(TOP)/src/sqlite.h.in 
	sed -e s/--VERS--/`cat ${TOP}/VERSION`/ \
            -e s/--ENCODING--/$(ENCODING)/ \
                 $(TOP)/src/sqlite.h.in >sqlite.h

tokenize.o:	$(TOP)/src/tokenize.c $(HDR)
	$(TCCX) -c $(TOP)/src/tokenize.c




util.o:	$(TOP)/src/util.c $(HDR)
	$(TCCX) -c $(TOP)/src/util.c

vdbe.o:	$(TOP)/src/vdbe.c $(HDR)
	$(TCCX) -c $(TOP)/src/vdbe.c








|







 







>







 







>
>
>







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#
TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src

# Object files for the SQLite library.
#
LIBOBJ = btree.o build.o delete.o expr.o func.o hash.o insert.o \
         main.o os.o pager.o parse.o printf.o random.o select.o table.o \
         tokenize.o trigger.o update.o util.o vdbe.o where.o tclsqlite.o

# All of the source code files.
#
SRC = \
  $(TOP)/src/btree.c \
  $(TOP)/src/btree.h \
  $(TOP)/src/build.c \
................................................................................
  $(TOP)/src/select.c \
  $(TOP)/src/shell.c \
  $(TOP)/src/sqlite.h.in \
  $(TOP)/src/sqliteInt.h \
  $(TOP)/src/table.c \
  $(TOP)/src/tclsqlite.c \
  $(TOP)/src/tokenize.c \
  $(TOP)/src/trigger.c \
  $(TOP)/src/update.c \
  $(TOP)/src/util.c \
  $(TOP)/src/vdbe.c \
  $(TOP)/src/vdbe.h \
  $(TOP)/src/where.c

# Source code to the test files.
................................................................................
sqlite.h:	$(TOP)/src/sqlite.h.in 
	sed -e s/--VERS--/`cat ${TOP}/VERSION`/ \
            -e s/--ENCODING--/$(ENCODING)/ \
                 $(TOP)/src/sqlite.h.in >sqlite.h

tokenize.o:	$(TOP)/src/tokenize.c $(HDR)
	$(TCCX) -c $(TOP)/src/tokenize.c

trigger.o:	$(TOP)/src/trigger.c $(HDR)
	$(TCCX) -c $(TOP)/src/trigger.c

util.o:	$(TOP)/src/util.c $(HDR)
	$(TCCX) -c $(TOP)/src/util.c

vdbe.o:	$(TOP)/src/vdbe.c $(HDR)
	$(TCCX) -c $(TOP)/src/vdbe.c

Changes to src/build.c.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
...
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
....
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
....
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
....
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
....
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
....
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
**     COPY
**     VACUUM
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.89 2002/05/15 11:44:14 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called after a single SQL statement has been
** parsed and we want to execute the VDBE code to implement 
................................................................................
  /* Any triggers that were dropped - put 'em back in place */
  for(pElem = sqliteHashFirst(&db->trigDrop); pElem; 
      pElem = sqliteHashNext(pElem)) {
    Trigger * pTrigger = sqliteHashData(pElem);
    Table * tab = sqliteFindTable(db, pTrigger->table);
    sqliteHashInsert(&db->trigHash, pTrigger->name, 
        strlen(pTrigger->name) + 1, pTrigger);

    pTrigger->pNext = tab->pTrigger;
    tab->pTrigger = pTrigger;
  }

  sqliteHashClear(&db->trigDrop);
  db->flags &= ~SQLITE_InternChanges;
}
................................................................................
**
** This plan is not completely bullet-proof.  It is possible for
** the schema to change multiple times and for the cookie to be
** set back to prior value.  But schema changes are infrequent
** and the probability of hitting the same cookie value is only
** 1 chance in 2^32.  So we're safe enough.
*/
void changeCookie(sqlite *db){
  if( db->next_cookie==db->schema_cookie ){
    db->next_cookie = db->schema_cookie + sqliteRandomByte() + 1;
    db->flags |= SQLITE_InternChanges;
  }
}

/*
................................................................................
      }else{
        assert( pEnd!=0 );
        n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1;
        sqliteVdbeChangeP3(v, -1, pParse->sFirstToken.z, n);
      }
      sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
      sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
      changeCookie(db);
      sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
      sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
      sqliteVdbeAddOp(v, OP_Close, 0, 0);
    }
    if( pSelect ){
      int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite;
      sqliteVdbeAddOp(v, op, 1, 0);
................................................................................
      { OP_Integer,    0, 0,        0}, /* 9 */
      { OP_SetCookie,  0, 0,        0},
      { OP_Close,      0, 0,        0},
    };
    Index *pIdx;
    sqliteBeginWriteOperation(pParse);
    /* Drop all triggers associated with the table being dropped */
    while (pTable->pTrigger) {
      Token tt;
      tt.z = pTable->pTrigger->name;
      tt.n = strlen(pTable->pTrigger->name);
      sqliteDropTrigger(pParse, &tt, 1);
    }
    if( !pTable->isTemp ){
      base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
      sqliteVdbeChangeP3(v, base+2, pTable->zName, 0);
      changeCookie(db);
      sqliteVdbeChangeP1(v, base+9, db->next_cookie);
    }
    if( !isView ){
      sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->isTemp);
      for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
        sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->isTemp);
      }
................................................................................
      sqliteVdbeAddOp(v, OP_Next, 2, lbl1);
      sqliteVdbeResolveLabel(v, lbl2);
      sqliteVdbeAddOp(v, OP_Close, 2, 0);
      sqliteVdbeAddOp(v, OP_Close, 1, 0);
    }
    if( pTable!=0 ){
      if( !isTemp ){
        changeCookie(db);
        sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
        sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
        sqliteVdbeAddOp(v, OP_Close, 0, 0);
      }
      sqliteEndWriteOperation(pParse);
    }
  }
................................................................................
    int base;
    Table *pTab = pIndex->pTable;

    sqliteBeginWriteOperation(pParse);
    if( !pTab->isTemp ){
      base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
      sqliteVdbeChangeP3(v, base+2, pIndex->zName, P3_STATIC);
      changeCookie(db);
      sqliteVdbeChangeP1(v, base+10, db->next_cookie);
    }
    sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pTab->isTemp);
    sqliteEndWriteOperation(pParse);
  }

  /* Move the index onto the pending DROP queue.  Or, if the index was
................................................................................
  db->onError = OE_Default;
}

/*
** Generate VDBE code that prepares for doing an operation that
** might change the database.  The operation will be atomic in the
** sense that it will either do its changes completely or not at
** all.  So there is not need to set a checkpoint is a transaction
** is already in effect.
*/
void sqliteBeginWriteOperation(Parse *pParse){
  Vdbe *v;
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if (pParse->trigStack) return; /* if this is in a trigger */
  if( (pParse->db->flags & SQLITE_InTrans)==0  ){
    sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
    sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
    pParse->schemaVerified = 1;
  }
}

................................................................................
** of a transaction, then this sets a checkpoint.  If we are not in
** a transaction, then start a transaction.
*/
void sqliteBeginMultiWriteOperation(Parse *pParse){
  Vdbe *v;
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if (pParse->trigStack) return; /* if this is in a trigger */
  if( (pParse->db->flags & SQLITE_InTrans)==0 ){
    sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
    sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
    pParse->schemaVerified = 1;
  }else{
    sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
  }







|







 







<







 







|







 







|







 







|








|







 







|







 







|







 







|






|







 







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
...
352
353
354
355
356
357
358

359
360
361
362
363
364
365
...
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
...
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
....
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
....
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
....
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
....
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
....
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
**     COPY
**     VACUUM
**     BEGIN TRANSACTION
**     COMMIT
**     ROLLBACK
**     PRAGMA
**
** $Id: build.c,v 1.90 2002/05/15 12:45:43 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** This routine is called after a single SQL statement has been
** parsed and we want to execute the VDBE code to implement 
................................................................................
  /* Any triggers that were dropped - put 'em back in place */
  for(pElem = sqliteHashFirst(&db->trigDrop); pElem; 
      pElem = sqliteHashNext(pElem)) {
    Trigger * pTrigger = sqliteHashData(pElem);
    Table * tab = sqliteFindTable(db, pTrigger->table);
    sqliteHashInsert(&db->trigHash, pTrigger->name, 
        strlen(pTrigger->name) + 1, pTrigger);

    pTrigger->pNext = tab->pTrigger;
    tab->pTrigger = pTrigger;
  }

  sqliteHashClear(&db->trigDrop);
  db->flags &= ~SQLITE_InternChanges;
}
................................................................................
**
** This plan is not completely bullet-proof.  It is possible for
** the schema to change multiple times and for the cookie to be
** set back to prior value.  But schema changes are infrequent
** and the probability of hitting the same cookie value is only
** 1 chance in 2^32.  So we're safe enough.
*/
void sqliteChangeCookie(sqlite *db){
  if( db->next_cookie==db->schema_cookie ){
    db->next_cookie = db->schema_cookie + sqliteRandomByte() + 1;
    db->flags |= SQLITE_InternChanges;
  }
}

/*
................................................................................
      }else{
        assert( pEnd!=0 );
        n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1;
        sqliteVdbeChangeP3(v, -1, pParse->sFirstToken.z, n);
      }
      sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
      sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
      sqliteChangeCookie(db);
      sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
      sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
      sqliteVdbeAddOp(v, OP_Close, 0, 0);
    }
    if( pSelect ){
      int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite;
      sqliteVdbeAddOp(v, op, 1, 0);
................................................................................
      { OP_Integer,    0, 0,        0}, /* 9 */
      { OP_SetCookie,  0, 0,        0},
      { OP_Close,      0, 0,        0},
    };
    Index *pIdx;
    sqliteBeginWriteOperation(pParse);
    /* Drop all triggers associated with the table being dropped */
    while( pTable->pTrigger ){
      Token tt;
      tt.z = pTable->pTrigger->name;
      tt.n = strlen(pTable->pTrigger->name);
      sqliteDropTrigger(pParse, &tt, 1);
    }
    if( !pTable->isTemp ){
      base = sqliteVdbeAddOpList(v, ArraySize(dropTable), dropTable);
      sqliteVdbeChangeP3(v, base+2, pTable->zName, 0);
      sqliteChangeCookie(db);
      sqliteVdbeChangeP1(v, base+9, db->next_cookie);
    }
    if( !isView ){
      sqliteVdbeAddOp(v, OP_Destroy, pTable->tnum, pTable->isTemp);
      for(pIdx=pTable->pIndex; pIdx; pIdx=pIdx->pNext){
        sqliteVdbeAddOp(v, OP_Destroy, pIdx->tnum, pTable->isTemp);
      }
................................................................................
      sqliteVdbeAddOp(v, OP_Next, 2, lbl1);
      sqliteVdbeResolveLabel(v, lbl2);
      sqliteVdbeAddOp(v, OP_Close, 2, 0);
      sqliteVdbeAddOp(v, OP_Close, 1, 0);
    }
    if( pTable!=0 ){
      if( !isTemp ){
        sqliteChangeCookie(db);
        sqliteVdbeAddOp(v, OP_Integer, db->next_cookie, 0);
        sqliteVdbeAddOp(v, OP_SetCookie, 0, 0);
        sqliteVdbeAddOp(v, OP_Close, 0, 0);
      }
      sqliteEndWriteOperation(pParse);
    }
  }
................................................................................
    int base;
    Table *pTab = pIndex->pTable;

    sqliteBeginWriteOperation(pParse);
    if( !pTab->isTemp ){
      base = sqliteVdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
      sqliteVdbeChangeP3(v, base+2, pIndex->zName, P3_STATIC);
      sqliteChangeCookie(db);
      sqliteVdbeChangeP1(v, base+10, db->next_cookie);
    }
    sqliteVdbeAddOp(v, OP_Destroy, pIndex->tnum, pTab->isTemp);
    sqliteEndWriteOperation(pParse);
  }

  /* Move the index onto the pending DROP queue.  Or, if the index was
................................................................................
  db->onError = OE_Default;
}

/*
** Generate VDBE code that prepares for doing an operation that
** might change the database.  The operation will be atomic in the
** sense that it will either do its changes completely or not at
** all.  So there is no need to set a checkpoint is a transaction
** is already in effect.
*/
void sqliteBeginWriteOperation(Parse *pParse){
  Vdbe *v;
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if( pParse->trigStack ) return; /* if this is in a trigger */
  if( (pParse->db->flags & SQLITE_InTrans)==0  ){
    sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
    sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
    pParse->schemaVerified = 1;
  }
}

................................................................................
** of a transaction, then this sets a checkpoint.  If we are not in
** a transaction, then start a transaction.
*/
void sqliteBeginMultiWriteOperation(Parse *pParse){
  Vdbe *v;
  v = sqliteGetVdbe(pParse);
  if( v==0 ) return;
  if( pParse->trigStack ) return; /* if this is in a trigger */
  if( (pParse->db->flags & SQLITE_InTrans)==0 ){
    sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
    sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
    pParse->schemaVerified = 1;
  }else{
    sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
  }

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
...
722
723
724
725
726
727
728
729
730










**    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.108 2002/05/15 08:30:14 danielk1977 Exp $
*/
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
#include <stdio.h>
................................................................................
  int nSet;            /* Number of sets used so far */
  int nAgg;            /* Number of aggregate expressions */
  AggExpr *aAgg;       /* An array of aggregate expressions */
  int useAgg;          /* If true, extract field values from the aggregator
                       ** while generating expressions.  Normally false */
  int schemaVerified;  /* True if an OP_VerifySchema has been coded someplace
                       ** other than after an OP_Transaction */

  TriggerStack * trigStack;
};

struct TriggerStack {
  Trigger * pTrigger;
  Table *   pTab;         /* Table that triggers are currently being coded as */
  int       newIdx;       /* Index of "new" temp table */
  int       oldIdx;       /* Index of "old" temp table */
  int       orconf;       /* Current orconf policy */
  struct TriggerStack * pNext;
};
struct TriggerStep {
  int op;               /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
  int orconf;

  Select * pSelect;     /* Valid for SELECT and sometimes 
			   INSERT steps (when pExprList == 0) */
  Token target;         /* Valid for DELETE, UPDATE, INSERT steps */
  Expr * pWhere;        /* Valid for DELETE, UPDATE steps */
  ExprList * pExprList; /* Valid for UPDATE statements and sometimes 
			   INSERT steps (when pSelect == 0)         */
  IdList *pIdList;      /* Valid for INSERT statements only */

  TriggerStep * pNext;  /* Next in the link-list */
};
struct Trigger {
  char * name;             /* The name of the trigger                        */
  char * table;            /* The table or view to which the trigger applies */
  int    op;               /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  int    tr_tm;            /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD         */
  Expr * pWhen;            /* The WHEN clause of the expresion (may be NULL) */
  IdList * pColumns;       /* If this is an UPDATE OF <column-list> trigger,
			      the column names are stored in this list       */
  int foreach;             /* One of TK_ROW or TK_STATEMENT */

  TriggerStep * step_list; /* Link list of trigger program steps             */

  char * strings;  /* pointer to the allocation of Token strings */
  Trigger * pNext; /* Next trigger associated with the table */
  int isCommit;
};

TriggerStep * sqliteTriggerSelectStep(Select *);
TriggerStep * sqliteTriggerInsertStep(Token *, IdList *, ExprList *, 
    Select *, int);
TriggerStep * sqliteTriggerUpdateStep(Token *, ExprList *, Expr *, int);
TriggerStep * sqliteTriggerDeleteStep(Token *, Expr *);

extern int always_code_trigger_setup;

void sqliteCreateTrigger(Parse * ,Token *, int, int, IdList *, Token *, int, Expr *, TriggerStep *, char const *,int);
void sqliteDropTrigger(Parse *, Token *, int);
int sqliteTriggersExist( Parse * , Trigger * , int , int , int, ExprList * );
int sqliteCodeRowTrigger( Parse * pParse, int op, ExprList *, int tr_tm,   Table * tbl, int newTable, int oldTable, int onError);

void sqliteViewTriggers(Parse *, Table *, Expr *, int, ExprList *);

/*
** Internal function prototypes
*/
int sqliteStrICmp(const char *, const char *);
int sqliteStrNICmp(const char *, const char *, int);
int sqliteHashNoCase(const char *, int);
................................................................................
IdList *sqliteIdListDup(IdList*);
Select *sqliteSelectDup(Select*);
FuncDef *sqliteFindFunction(sqlite*,const char*,int,int,int);
void sqliteRegisterBuildinFunctions(sqlite*);
int sqliteSafetyOn(sqlite*);
int sqliteSafetyOff(sqlite*);
int sqliteSafetyCheck(sqlite*);

void changeCookie(sqlite *);

















|







 







<
|



|
|
|
|
|
|





|


|
|






|
|
|
|
|
|
|
|

|
<
|
|



<
<
<
<
<
<


<
<
<
<
<
<







 







<
|
>
>
>
>
>
>
>
>
>
>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
554
555
556
557
558
559
560

561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596

597
598
599
600
601






602
603






604
605
606
607
608
609
610
...
708
709
710
711
712
713
714

715
716
717
718
719
720
721
722
723
724
725
**    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.109 2002/05/15 12:45:43 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
#include "vdbe.h"
#include "parse.h"
#include "btree.h"
#include <stdio.h>
................................................................................
  int nSet;            /* Number of sets used so far */
  int nAgg;            /* Number of aggregate expressions */
  AggExpr *aAgg;       /* An array of aggregate expressions */
  int useAgg;          /* If true, extract field values from the aggregator
                       ** while generating expressions.  Normally false */
  int schemaVerified;  /* True if an OP_VerifySchema has been coded someplace
                       ** other than after an OP_Transaction */

  TriggerStack *trigStack;
};

struct TriggerStack {
  Trigger *pTrigger;
  Table *pTab;         /* Table that triggers are currently being coded as */
  int newIdx;          /* Index of "new" temp table */
  int oldIdx;          /* Index of "old" temp table */
  int orconf;          /* Current orconf policy */
  TriggerStack *pNext;
};
struct TriggerStep {
  int op;               /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
  int orconf;

  Select *pSelect;     /* Valid for SELECT and sometimes 
			   INSERT steps (when pExprList == 0) */
  Token target;         /* Valid for DELETE, UPDATE, INSERT steps */
  Expr *pWhere;        /* Valid for DELETE, UPDATE steps */
  ExprList *pExprList; /* Valid for UPDATE statements and sometimes 
			   INSERT steps (when pSelect == 0)         */
  IdList *pIdList;      /* Valid for INSERT statements only */

  TriggerStep * pNext;  /* Next in the link-list */
};
struct Trigger {
  char *name;             /* The name of the trigger                        */
  char *table;            /* The table or view to which the trigger applies */
  int op;                 /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
  int tr_tm;              /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD         */
  Expr *pWhen;            /* The WHEN clause of the expresion (may be NULL) */
  IdList *pColumns;       /* If this is an UPDATE OF <column-list> trigger,
                             the column names are stored in this list       */
  int foreach;            /* One of TK_ROW or TK_STATEMENT */

  TriggerStep *step_list; /* Link list of trigger program steps             */

  char *strings;          /* pointer to allocation of Token strings */
  Trigger *pNext;         /* Next trigger associated with the table */
  int isCommit;
};







extern int always_code_trigger_setup;








/*
** Internal function prototypes
*/
int sqliteStrICmp(const char *, const char *);
int sqliteStrNICmp(const char *, const char *, int);
int sqliteHashNoCase(const char *, int);
................................................................................
IdList *sqliteIdListDup(IdList*);
Select *sqliteSelectDup(Select*);
FuncDef *sqliteFindFunction(sqlite*,const char*,int,int,int);
void sqliteRegisterBuildinFunctions(sqlite*);
int sqliteSafetyOn(sqlite*);
int sqliteSafetyOff(sqlite*);
int sqliteSafetyCheck(sqlite*);

void sqliteChangeCookie(sqlite *);
void sqliteCreateTrigger(Parse*, Token*, int, int, IdList*, Token*, 
                         int, Expr*, TriggerStep*, char const*,int);
void sqliteDropTrigger(Parse*, Token*, int);
int sqliteTriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
int sqliteCodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int, int);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
TriggerStep *sqliteTriggerSelectStep(Select*);
TriggerStep *sqliteTriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
TriggerStep *sqliteTriggerUpdateStep(Token*, ExprList*, Expr*, int);
TriggerStep *sqliteTriggerDeleteStep(Token*, Expr*);

Changes to src/trigger.c.

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316

317
318
319
320
321
322
323
    sqliteVdbeAddOp(pParse->pVdbe,        OP_Integer,    0, 0);
    sqliteVdbeAddOp(pParse->pVdbe,        OP_String,    0, 0);
    sqliteVdbeChangeP3(pParse->pVdbe, -1, nt->strings,     0);
    sqliteVdbeAddOp(pParse->pVdbe,        OP_MakeRecord, 5, 0);
    sqliteVdbeAddOp(pParse->pVdbe,        OP_PutIntKey, 0, 1);

    /* Change the cookie, since the schema is changed */
    changeCookie(pParse->db);
    sqliteVdbeAddOp(pParse->pVdbe, OP_Integer, pParse->db->next_cookie, 0);
    sqliteVdbeAddOp(pParse->pVdbe, OP_SetCookie, 0, 0);

    sqliteVdbeAddOp(pParse->pVdbe,        OP_Close,     0, 0);

    sqliteEndWriteOperation(pParse);
  }
................................................................................
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(4),  0}, /* 8 */
      { OP_Integer,    0, 0,        0}, /* 9 */
      { OP_SetCookie,  0, 0,        0},
      { OP_Close,      0, 0,        0},
    };

    if (!nested) 
      sqliteBeginWriteOperation(pParse);

    base = sqliteVdbeAddOpList(pParse->pVdbe, 
        ArraySize(dropTrigger), dropTrigger);
    sqliteVdbeChangeP3(pParse->pVdbe, base+2, tmp_name, 0);

    if (!nested)
      changeCookie(pParse->db);

    sqliteVdbeChangeP1(pParse->pVdbe, base+9, pParse->db->next_cookie);

    if (!nested)
      sqliteEndWriteOperation(pParse);

  }

  sqliteFree(tmp_name);
}

static int checkColumnOverLap(IdList * ii, ExprList * ee)
{







|







 







|

|



<
|
|
|

<
|

>







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
296
297
298
299
300
301
302
303
304
305
306
307
308

309
310
311
312

313
314
315
316
317
318
319
320
321
322
    sqliteVdbeAddOp(pParse->pVdbe,        OP_Integer,    0, 0);
    sqliteVdbeAddOp(pParse->pVdbe,        OP_String,    0, 0);
    sqliteVdbeChangeP3(pParse->pVdbe, -1, nt->strings,     0);
    sqliteVdbeAddOp(pParse->pVdbe,        OP_MakeRecord, 5, 0);
    sqliteVdbeAddOp(pParse->pVdbe,        OP_PutIntKey, 0, 1);

    /* Change the cookie, since the schema is changed */
    sqliteChangeCookie(pParse->db);
    sqliteVdbeAddOp(pParse->pVdbe, OP_Integer, pParse->db->next_cookie, 0);
    sqliteVdbeAddOp(pParse->pVdbe, OP_SetCookie, 0, 0);

    sqliteVdbeAddOp(pParse->pVdbe,        OP_Close,     0, 0);

    sqliteEndWriteOperation(pParse);
  }
................................................................................
      { OP_Delete,     0, 0,        0},
      { OP_Next,       0, ADDR(4),  0}, /* 8 */
      { OP_Integer,    0, 0,        0}, /* 9 */
      { OP_SetCookie,  0, 0,        0},
      { OP_Close,      0, 0,        0},
    };

    if( !nested ){
      sqliteBeginWriteOperation(pParse);
    }
    base = sqliteVdbeAddOpList(pParse->pVdbe, 
        ArraySize(dropTrigger), dropTrigger);
    sqliteVdbeChangeP3(pParse->pVdbe, base+2, tmp_name, 0);

    if( !nested ){
      sqliteChangeCookie(pParse->db);
    }
    sqliteVdbeChangeP1(pParse->pVdbe, base+9, pParse->db->next_cookie);

    if( !nested ){
      sqliteEndWriteOperation(pParse);
    }
  }

  sqliteFree(tmp_name);
}

static int checkColumnOverLap(IdList * ii, ExprList * ee)
{