]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Changes to support recursive triggers.
authordan <dan@noemail.net>
Fri, 28 Aug 2009 18:53:45 +0000 (18:53 +0000)
committerdan <dan@noemail.net>
Fri, 28 Aug 2009 18:53:45 +0000 (18:53 +0000)
FossilOrigin-Name: 9b9c19211593d5ff7b39254a29c284560a8bcedb

25 files changed:
manifest
manifest.uuid
src/alter.c
src/build.c
src/delete.c
src/expr.c
src/insert.c
src/prepare.c
src/resolve.c
src/select.c
src/sqliteInt.h
src/trigger.c
src/update.c
src/vdbe.c
src/vdbe.h
src/vdbeInt.h
src/vdbeapi.c
src/vdbeaux.c
src/vdbeblob.c
src/vdbemem.c
src/vtab.c
test/attach3.test
test/autoinc.test
test/trigger1.test
test/trigger8.test

index edf9fdd5a38c52faa2f80d73548f7635856d91bd..d5c980e157bf5c5d017506b6007a7e6fbb0e5168 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,8 +1,5 @@
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-C Run\sthe\scolumn\scache\sin\sa\snew\scontext\swhen\sgenerating\scode\sfor\strigger\nprograms.\s\sFix\sfor\sticket\s[efc02f9779].
-D 2009-08-24T01:35:25
+C Changes\sto\ssupport\srecursive\striggers.
+D 2009-08-28T18:53:45
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 73ddeec9dd10b85876c5c2ce1fdce627e1dcc7f8
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -101,7 +98,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
 F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
 F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
 F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
-F src/alter.c 8b42cace4f8e312de596807ba2685179da64fec4
+F src/alter.c 55b601857d924e07c91cfa69e9b2cb5093498c93
 F src/analyze.c 4584556d374fe3ab791be9be32ada7713fdced52
 F src/attach.c 13995348fc5a26cdd136a50806faf292aabc173f
 F src/auth.c 802a9439dfa0b8c208b10055cba400e82ef18025
@@ -111,19 +108,19 @@ F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7
 F src/btree.c 49212ddaee8d7d12b4f1e17b9de62f7ea91ca59d
 F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe
 F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705
-F src/build.c a43c959c5953c25989207c929f99ef808d5336a6
+F src/build.c 212476dc971756e7f7429e677059fafc678afbd5
 F src/callback.c 9bc0ae998f15207d3115224979331c0aafc9bcc3
 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
 F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3
-F src/delete.c dcf07632d8ca3d4086df8b65ea907a47278e6382
-F src/expr.c adb61b7de6bf9f51080195ed7dab9282a697ba24
+F src/delete.c 0fa2c14bb3520590ef48bf214d4925cfb6e95e8c
+F src/expr.c 24a8f8088d2af202852bac0a364614d20d5e663c
 F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
 F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606
 F src/global.c 271952d199a8cc59d4ce840b3bbbfd2f30c8ba32
 F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7
 F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1
 F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb
-F src/insert.c 95625f99f377a9ef264c289407173b722c7af6e8
+F src/insert.c 5bddd3d65fceb671f56b000c202d07d17bed2775
 F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
 F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6
 F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d
@@ -156,16 +153,16 @@ F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa
 F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36
 F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c
 F src/pragma.c 9eb44ac1d3dc1ac3ea4f444abe1a10ae8acaa16c
-F src/prepare.c 0b966d20979237121ec5fec449c9db45f6b9789a
+F src/prepare.c 49739b385c4cd0667cfa9941c41bf6d4f8edc157
 F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1
 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
-F src/resolve.c 4a61d03e49b15440878096e6030863fc628828f0
+F src/resolve.c 92ef8a85d53b305a7de9faef27d652b96c2b4db6
 F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f
-F src/select.c 67b0778c9585905c8aa75aaa469e76ef3c1d315a
+F src/select.c 56ecb073e6f6696173ad80493aa14355225b6e53
 F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb
 F src/sqlite.h.in 3ccf717d82101f19548d0b1243f0a6f4854d51ee
 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
-F src/sqliteInt.h 20ab1da1a9a652ea673e5bc586382143914381c0
+F src/sqliteInt.h ff4ae0702b0d816395030f9170151954f26830a7
 F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d
 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
@@ -202,19 +199,19 @@ F src/test_tclvar.c 9e42fa59d3d2f064b7ab8628e7ab2dc8a9fe93d4
 F src/test_thread.c b8a1ab7ca1a632f18e8a361880d5d65eeea08eac
 F src/test_wsd.c 3ae5101de6cbfda2720152ab659ea84079719241
 F src/tokenize.c af8a56e6a50c5042fc305bfa796275e9bf26ff2b
-F src/trigger.c 545f3b8354001577a82c44fc6c4beac598b45b86
-F src/update.c 4da327f706c0d0dfedf4d92154b1b5688bdea0ac
+F src/trigger.c 135c1fc78ada9d5a5d8ffee412263547b1a93ca9
+F src/update.c cddbcabb2fccc146b8712f5b97e636c413c34906
 F src/utf.c 3ca2c9461b8e942c68da28bfccd448663f536a6f
 F src/util.c efb5f8e533d4beef545cf765cab5f7920b4c75f9
 F src/vacuum.c 3fe0eebea6d2311c1c2ab2962887d11f7a4dcfb0
-F src/vdbe.c 464e2e30b1287554a23cdaa0b6b010a9dcb5eb29
-F src/vdbe.h 457b6c70f02885cec1f5225b5e6441d067b55d3f
-F src/vdbeInt.h 831c254a6eef237ef4664c8381a0137586567007
-F src/vdbeapi.c 0ab8ada7260b32031ca97f338caecf0812460624
-F src/vdbeaux.c 4956536a636468fd07284028c39aab65ea99777e
-F src/vdbeblob.c a3f3e0e877fc64ea50165eec2855f5ada4477611
-F src/vdbemem.c c4a5188ff43692f2ca78d3539ad4877e14b70712
-F src/vtab.c aedd76e8670d5a5379f93804398d3ba960125547
+F src/vdbe.c e45309524c11866f2d158b3578aecfe6d9af0676
+F src/vdbe.h 080fe6bc1264438becb8bf9b9f3c84074c336b78
+F src/vdbeInt.h 9b62f7053dc1255e9c5dec16f35b5fd079ba7ed4
+F src/vdbeapi.c 8d5013ab6104be757c208a70ffb191cc27d2b688
+F src/vdbeaux.c cf440244489accee8ddeb85c24b239659c31de1b
+F src/vdbeblob.c f93cb60ac388633ed3bde8a94ef161ad2dbfb689
+F src/vdbemem.c dc551981833756ea34a3e0b238f759479e7cf526
+F src/vtab.c 10df5c77cea34a49f2ad4e5de763f820d6223eb4
 F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
 F src/where.c b9ad2d2db4a7d1cda7bed8a7299eb73fde63b5b1
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -234,12 +231,12 @@ F test/async4.test aafa6328c559d3e4bb587de770cbdecfca06f0da
 F test/async5.test f3592d79c84d6e83a5f50d3fd500445f7d97dfdf
 F test/attach.test 1d1be27b9e4c654f9bb14d011a4a87753c0b197a
 F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437
-F test/attach3.test 7b92dc8e40c1ebca9732ca6f2d3fefbd46f196df
+F test/attach3.test 512521ba94372dfd0efb07d04b2cdefc8c929e66
 F test/attachmalloc.test cf8cf17d183de357b1147a9baacbdfc85b940b61
 F test/auth.test 8e9a21d7321c9ad20d26f630acc02e15f2f2a3b6
 F test/auth2.test ee3ba272e2b975e913afc9b041ee75706e190005
 F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
-F test/autoinc.test 71bc5183c93ed5e2b8b3a71c218d777b55e4fffc
+F test/autoinc.test f1959df99bca67066db8ff816ae89b49f1f42e00
 F test/autovacuum.test 25f891bc343a8bf5d9229e2e9ddab9f31a9ab5ec
 F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6
 F test/avtrans.test 1e901d8102706b63534dbd2bdd4d8f16c4082650
@@ -672,14 +669,14 @@ F test/trace.test 19ffbc09885c3321d56358a5738feae8587fb377
 F test/trans.test d887cb07630dc39879a322d958ad8b006137485c
 F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
 F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97
-F test/trigger1.test 4ecf469e6f46610e0864f52b29d0ea162a2767c1
+F test/trigger1.test 2e18561f85e448bb633c9c9de792e9bbf7b2dd3e
 F test/trigger2.test 53cf2209f68eb5bbb0ea0a07d694007fa702f1ed
 F test/trigger3.test 501b8489eb6b9cb5b005f60b071583c01a3c3041
 F test/trigger4.test 8e90ee98cba940cd5f96493f82e55083806ab8a0
 F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
 F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9
 F test/trigger7.test 72feaf8dbc52cea84de0c3e6ce7559ff19c479af
-F test/trigger8.test 83d92c212f36442d26527d6f7701575905a52ae1
+F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
 F test/trigger9.test e6e8dbab673666b3c0a63f0fefcff2329fe6bba8
 F test/triggerA.test 0718ad2d9bfef27c7af00e636df79bee6b988da7
 F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
@@ -750,14 +747,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
-P 9b4d9ab62d687289837b13b07885e72cc3abe8a9
-R 8c0a86d1dda3ad47818cafa2a01a76cc
-U drh
-Z a8886aa9c2f39a29c2eeac74183b7a19
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.6 (GNU/Linux)
-
-iD8DBQFKke5hoxKgR168RlERAlsiAJ99uio4dN8iQWmrdPv34d66O3Nb/wCdEt6E
-0TzPUVqtgGFOj3LSxkTm7fQ=
-=7r/8
------END PGP SIGNATURE-----
+P dee1b8eb402f47c6d5ee60aac28f8e3dcf98167f
+R ef22ff6ef84d7c63fbbf7ca259ee2aa2
+U dan
+Z 871f833b67d466cf35f851400879eb8b
index c6f1f2e6c98c88e9166c7fd2f3c4a0c63ff6bf77..5f887b3ebc97d930252d37b14e91f7727b27a8ae 100644 (file)
@@ -1 +1 @@
-dee1b8eb402f47c6d5ee60aac28f8e3dcf98167f
\ No newline at end of file
+9b9c19211593d5ff7b39254a29c284560a8bcedb
\ No newline at end of file
index be4a2e086277b8d81eebaad265292cc9ae2fe394..14b58df336dd39cf3078825bdbf175868ff2abf1 100644 (file)
@@ -196,10 +196,10 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
     for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
       if( pTrig->pSchema==pTempSchema ){
         if( !zWhere ){
-          zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name);
+          zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->zName);
         }else{
           tmp = zWhere;
-          zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->name);
+          zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->zName);
           sqlite3DbFree(db, tmp);
         }
       }
@@ -235,7 +235,7 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){
   for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
     int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
     assert( iTrigDb==iDb || iTrigDb==1 );
-    sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0);
+    sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0);
   }
 #endif
 
index 730caf97e190dff55ebbf1bc662527b40de28c0f..06e5dd56372654426c9939eda248553fd9c8f918 100644 (file)
@@ -69,6 +69,10 @@ void sqlite3TableLock(
   TableLock *p;
 
   assert( iDb>=0 );
+
+  if( pParse->pRoot ){
+    pParse = pParse->pRoot;
+  }
   for(i=0; i<pParse->nTableLock; i++){
     p = &pParse->aTableLock[i];
     if( p->iDb==iDb && p->iTab==iTab ){
@@ -78,7 +82,7 @@ void sqlite3TableLock(
   }
 
   nBytes = sizeof(TableLock) * (pParse->nTableLock+1);
-  pParse->aTableLock = 
+  pParse->aTableLock =
       sqlite3DbReallocOrFree(pParse->db, pParse->aTableLock, nBytes);
   if( pParse->aTableLock ){
     p = &pParse->aTableLock[pParse->nTableLock++];
@@ -194,7 +198,7 @@ void sqlite3FinishCoding(Parse *pParse){
 #endif
     assert( pParse->iCacheLevel==0 );  /* Disables and re-enables match */
     sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
-                         pParse->nTab, pParse->explain);
+                         pParse->nTab, pParse->nArg, pParse->explain);
     pParse->rc = SQLITE_DONE;
     pParse->colNamesSet = 0;
   }else if( pParse->rc==SQLITE_OK ){
@@ -3425,23 +3429,27 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
   sqlite3 *db;
   Vdbe *v;
   int mask;
+  Parse *pRoot = pParse->pRoot;        /* Root parse structure */
 
   v = sqlite3GetVdbe(pParse);
   if( v==0 ) return;  /* This only happens if there was a prior error */
   db = pParse->db;
-  if( pParse->cookieGoto==0 ){
+  if( pParse->cookieGoto==0 && pRoot==0 ){
     pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1;
   }
   if( iDb>=0 ){
+    if( pRoot==0 ){
+      pRoot = pParse;
+    }
     assert( iDb<db->nDb );
     assert( db->aDb[iDb].pBt!=0 || iDb==1 );
     assert( iDb<SQLITE_MAX_ATTACHED+2 );
     mask = 1<<iDb;
-    if( (pParse->cookieMask & mask)==0 ){
-      pParse->cookieMask |= mask;
-      pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
+    if( (pRoot->cookieMask & mask)==0 ){
+      pRoot->cookieMask |= mask;
+      pRoot->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
       if( !OMIT_TEMPDB && iDb==1 ){
-        sqlite3OpenTempDatabase(pParse);
+        sqlite3OpenTempDatabase(pRoot);
       }
     }
   }
@@ -3461,9 +3469,13 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
 ** necessary to undo a write and the checkpoint should not be set.
 */
 void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
+  Parse *pRoot = pParse->pRoot;
   sqlite3CodeVerifySchema(pParse, iDb);
-  pParse->writeMask |= 1<<iDb;
-  if( setStatement && pParse->nested==0 ){
+  if( pRoot==0 ){
+    pRoot = pParse;
+  }
+  pRoot->writeMask |= 1<<iDb;
+  if( setStatement && pParse->nested==0 && pParse->pRoot==0 ){
     /* Every place where this routine is called with setStatement!=0 has
     ** already successfully created a VDBE. */
     assert( pParse->pVdbe );
index abf7e0c745a064b7c2363bfe7d2fdc87e1fabbb5..1425fea839a236eeb16d36f33100ba3c65fcde72 100644 (file)
@@ -228,7 +228,6 @@ void sqlite3DeleteFrom(
   int iCur;              /* VDBE Cursor number for pTab */
   sqlite3 *db;           /* Main database structure */
   AuthContext sContext;  /* Authorization context */
-  int oldIdx = -1;       /* Cursor for the OLD table of AFTER triggers */
   NameContext sNC;       /* Name context to resolve expressions in */
   int iDb;               /* Database number */
   int memCnt = -1;       /* Memory cell used for change counting */
@@ -238,11 +237,6 @@ void sqlite3DeleteFrom(
   int isView;                  /* True if attempting to delete from a view */
   Trigger *pTrigger;           /* List of table triggers, if required */
 #endif
-  int iBeginAfterTrigger = 0;  /* Address of after trigger program */
-  int iEndAfterTrigger = 0;    /* Exit of after trigger program */
-  int iBeginBeforeTrigger = 0; /* Address of before trigger program */
-  int iEndBeforeTrigger = 0;   /* Exit of before trigger program */
-  u32 old_col_mask = 0;        /* Mask of OLD.* columns in use */
 
   memset(&sContext, 0, sizeof(sContext));
   db = pParse->db;
@@ -293,12 +287,6 @@ void sqlite3DeleteFrom(
   }
   assert(!isView || pTrigger);
 
-  /* Allocate a cursor used to store the old.* data for a trigger.
-  */
-  if( pTrigger ){ 
-    oldIdx = pParse->nTab++;
-  }
-
   /* Assign  cursor number to the table and all its indices.
   */
   assert( pTabList->nSrc==1 );
@@ -322,24 +310,6 @@ void sqlite3DeleteFrom(
   if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
   sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb);
 
-  if( pTrigger ){
-    int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default);
-    int iGoto = sqlite3VdbeAddOp0(v, OP_Goto);
-    addr = sqlite3VdbeMakeLabel(v);
-
-    iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
-    (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, 
-        TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
-    iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
-
-    iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
-    (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, 
-        TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0);
-    iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto);
-
-    sqlite3VdbeJumpHere(v, iGoto);
-  }
-
   /* If we are trying to delete from a view, realize that view into
   ** a ephemeral table.
   */
@@ -385,8 +355,9 @@ void sqlite3DeleteFrom(
   ** the table and pick which records to delete.
   */
   {
-    int iRowid = ++pParse->nMem;    /* Used for storing rowid values. */
     int iRowSet = ++pParse->nMem;   /* Register for rowset of rows to delete */
+    int iRowid = ++pParse->nMem;    /* Used for storing rowid values. */
+    int regOld = pParse->nMem + 1;  /* Start of array for old.* (if triggers) */
     int regRowid;                   /* Actual register containing rowids */
 
     /* Collect rowids of every row to be deleted.
@@ -401,35 +372,31 @@ void sqlite3DeleteFrom(
     }
     sqlite3WhereEnd(pWInfo);
 
-    /* Open the pseudo-table used to store OLD if there are triggers.
-    */
-    if( pTrigger ){
-      sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
-    }
-
     /* Delete every item whose key was written to the list during the
     ** database scan.  We have to delete items after the scan is complete
-    ** because deleting an item can change the scan order.
-    */
+    ** because deleting an item can change the scan order.  */
     end = sqlite3VdbeMakeLabel(v);
 
+    /* Unless this is a view, open cursors for the table we are 
+    ** deleting from and all its indices. If this is a view, then the
+    ** only effect this statement has is to fire the INSTEAD OF 
+    ** triggers.  */
     if( !isView ){
-      /* Open cursors for the table we are deleting from and 
-      ** all its indices.
-      */
       sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
     }
 
-    /* This is the beginning of the delete loop. If a trigger encounters
-    ** an IGNORE constraint, it jumps back to here.
-    */
-    if( pTrigger ){
-      sqlite3VdbeResolveLabel(v, addr);
-    }
     addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
 
+    /* If there are triggers, populate an array of registers with the
+    ** data required by the old.* references in the trigger bodies.  */
     if( pTrigger ){
-      int iData = ++pParse->nMem;   /* For storing row data of OLD table */
+      u32 mask = 0;         /* Mask of OLD.* columns in use */
+      u32 dummy = 0;        /* Unused. Initialized to prevent valgrind error. */
+      pParse->nMem += pTab->nCol;
+
+      /* Open the pseudo-table used to store OLD if there are triggers. */
+      sqlite3TriggerUses(
+          pParse, pTrigger, TK_DELETE, 0, pTab, OE_Default, &mask, &dummy);
 
       /* If the record is no longer present in the table, jump to the
       ** next iteration of the loop through the contents of the fifo.
@@ -437,16 +404,16 @@ void sqlite3DeleteFrom(
       sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid);
 
       /* Populate the OLD.* pseudo-table */
-      if( old_col_mask ){
-        sqlite3VdbeAddOp2(v, OP_RowData, iCur, iData);
-      }else{
-        sqlite3VdbeAddOp2(v, OP_Null, 0, iData);
+      assert( regOld==iRowid+1 );
+      for(i=0; i<pTab->nCol; i++){
+        if( mask==0xffffffff || mask&(1<<i) ){
+          sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
+        }
       }
-      sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, iData, iRowid);
 
-      /* Jump back and run the BEFORE triggers */
-      sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger);
-      sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
+      sqlite3CodeRowTrigger(pParse, pTrigger, 
+          TK_DELETE, 0, TRIGGER_BEFORE, pTab, -1, regOld, OE_Default, addr
+      );
     }
 
     if( !isView ){
@@ -463,21 +430,17 @@ void sqlite3DeleteFrom(
       }
     }
 
-    /* If there are row triggers, close all cursors then invoke
-    ** the AFTER triggers
-    */
-    if( pTrigger ){
-      /* Jump back and run the AFTER triggers */
-      sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
-      sqlite3VdbeJumpHere(v, iEndAfterTrigger);
-    }
+    /* Code the AFTER triggers. This is a no-op if there are no triggers. */
+    sqlite3CodeRowTrigger(pParse, 
+      pTrigger, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, regOld, OE_Default, addr
+    );
 
     /* End of the delete loop */
     sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
     sqlite3VdbeResolveLabel(v, end);
 
-    /* Close the cursors after the loop if there are no row triggers */
-    if( !isView  && !IsVirtual(pTab) ){
+    /* Close the cursors open on the table and its indexes. */
+    if( !isView && !IsVirtual(pTab) ){
       for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
         sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
       }
@@ -489,7 +452,7 @@ void sqlite3DeleteFrom(
   ** maximum rowid counter values recorded while inserting into
   ** autoincrement tables.
   */
-  if( pParse->nested==0 && pParse->trigStack==0 ){
+  if( pParse->nested==0 && pParse->pTriggerTab==0 ){
     sqlite3AutoincrementEnd(pParse);
   }
 
@@ -498,7 +461,7 @@ void sqlite3DeleteFrom(
   ** generating code because of a call to sqlite3NestedParse(), do not
   ** invoke the callback function.
   */
-  if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
+  if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
     sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
     sqlite3VdbeSetNumCols(v, 1);
     sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
index 0ff234ee94a24d25492c379cee567d0a831194f3..62616199c67b5f151dd3ca3e3bd9e3fdec37046e 100644 (file)
@@ -1521,7 +1521,7 @@ void sqlite3CodeSubselect(
   ** If all of the above are false, then we can run this code just once
   ** save the results, and reuse the same result on subsequent invocations.
   */
-  if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->trigStack ){
+  if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
     int mem = ++pParse->nMem;
     sqlite3VdbeAddOp1(v, OP_If, mem);
     testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem);
@@ -2556,6 +2556,17 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
       break;
     }
 
+    case TK_TRIGGER: {
+      sqlite3VdbeAddOp3(v, OP_TriggerVal, pExpr->iColumn, target,pExpr->iTable);
+      assert( pExpr->pTab );
+      VdbeComment((v, "%s.%s", 
+        (pExpr->iTable ? "new" : "old"), 
+        (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName)
+      ));
+      break;
+    }
+
+
     /*
     ** Form A:
     **   CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END
@@ -2640,24 +2651,20 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
     }
 #ifndef SQLITE_OMIT_TRIGGER
     case TK_RAISE: {
-      if( !pParse->trigStack ){
+      int vrc;
+      if( !pParse->pTriggerTab ){
         sqlite3ErrorMsg(pParse,
                        "RAISE() may only be used within a trigger-program");
         return 0;
       }
-      if( pExpr->affinity!=OE_Ignore ){
-         assert( pExpr->affinity==OE_Rollback ||
-                 pExpr->affinity == OE_Abort ||
-                 pExpr->affinity == OE_Fail );
-         assert( !ExprHasProperty(pExpr, EP_IntValue) );
-         sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0,
-                           pExpr->u.zToken, 0);
-      } else {
-         assert( pExpr->affinity == OE_Ignore );
-         sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
-         sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
-         VdbeComment((v, "raise(IGNORE)"));
-      }
+      assert( pExpr->affinity==OE_Rollback 
+           || pExpr->affinity==OE_Abort
+           || pExpr->affinity==OE_Fail
+           || pExpr->affinity==OE_Ignore
+      );
+      assert( !ExprHasProperty(pExpr, EP_IntValue) );
+      vrc = (pExpr->affinity==OE_Ignore ? SQLITE_OK : SQLITE_CONSTRAINT);
+      sqlite3VdbeAddOp4(v, OP_Halt, vrc, pExpr->affinity, 0, pExpr->u.zToken,0);
       break;
     }
 #endif
index 728c06ed924c5ee7ce0d86c6f87f9b3a58dc6c4c..94a59a3afa0ef3f4e501bb872b330c41c9904be3 100644 (file)
@@ -536,11 +536,6 @@ void sqlite3Insert(
   if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
   sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb);
 
-  /* if there are row triggers, allocate a temp table for new.* references. */
-  if( pTrigger ){
-    newIdx = pParse->nTab++;
-  }
-
 #ifndef SQLITE_OMIT_XFER_OPT
   /* If the statement is of the form
   **
@@ -744,12 +739,6 @@ void sqlite3Insert(
   if( pColumn==0 && nColumn>0 ){
     keyColumn = pTab->iPKey;
   }
-
-  /* Open the temp table for FOR EACH ROW triggers
-  */
-  if( pTrigger ){
-    sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
-  }
     
   /* Initialize the count of rows to be inserted
   */
@@ -818,7 +807,6 @@ void sqlite3Insert(
   if( tmask & TRIGGER_BEFORE ){
     int regTrigRowid;
     int regCols;
-    int regRec;
 
     /* build the NEW.* reference row.  Note that if there is an INTEGER
     ** PRIMARY KEY into which a NULL is being inserted, that NULL will be
@@ -846,7 +834,7 @@ void sqlite3Insert(
     /* Cannot have triggers on a virtual table. If it were possible,
     ** this block would have to account for hidden column.
     */
-    assert(!IsVirtual(pTab));
+    assert( !IsVirtual(pTab) );
 
     /* Create the new column data
     */
@@ -868,8 +856,6 @@ void sqlite3Insert(
         sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i);
       }
     }
-    regRec = sqlite3GetTempReg(pParse);
-    sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRec);
 
     /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
     ** do not attempt any conversions before assembling the record.
@@ -877,18 +863,16 @@ void sqlite3Insert(
     ** table column affinities.
     */
     if( !isView ){
+      sqlite3VdbeAddOp2(v, OP_Affinity, regCols, pTab->nCol);
       sqlite3TableAffinityStr(v, pTab);
     }
-    sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regTrigRowid);
-    sqlite3ReleaseTempReg(pParse, regRec);
-    sqlite3ReleaseTempReg(pParse, regTrigRowid);
-    sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
 
     /* Fire BEFORE or INSTEAD OF triggers */
-    if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, 
-        pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
-      goto insert_cleanup;
-    }
+    sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, 
+        pTab, regCols, -1, onError, endOfLoop);
+
+    sqlite3ReleaseTempReg(pParse, regTrigRowid);
+    sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
   }
 
   /* Push the record number for the new entry onto the stack.  The
@@ -1009,10 +993,8 @@ void sqlite3Insert(
 
   if( pTrigger ){
     /* Code AFTER triggers */
-    if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, 
-          pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){
-      goto insert_cleanup;
-    }
+    sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, 
+          pTab, regData, -1, onError, endOfLoop);
   }
 
   /* The bottom of the main insertion loop, if the data source
@@ -1041,7 +1023,7 @@ insert_end:
   ** maximum rowid counter values recorded while inserting into
   ** autoincrement tables.
   */
-  if( pParse->nested==0 && pParse->trigStack==0 ){
+  if( pParse->nested==0 && pParse->pTriggerTab==0 ){
     sqlite3AutoincrementEnd(pParse);
   }
 
@@ -1050,7 +1032,7 @@ insert_end:
   ** generating code because of a call to sqlite3NestedParse(), do not
   ** invoke the callback function.
   */
-  if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){
+  if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
     sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
     sqlite3VdbeSetNumCols(v, 1);
     sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
@@ -1423,11 +1405,6 @@ void sqlite3CompleteInsertion(
   sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
   sqlite3TableAffinityStr(v, pTab);
   sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
-#ifndef SQLITE_OMIT_TRIGGER
-  if( newIdx>=0 ){
-    sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid);
-  }
-#endif
   if( pParse->nested ){
     pik_flags = 0;
   }else{
index e692a28bf1115566932f83fba5e533dbf37b9254..cd25d5a1f9dba74fd3c80176d5ea64d1382a6b33 100644 (file)
@@ -676,6 +676,13 @@ static int sqlite3Prepare(
     sqlite3Error(db, rc, 0);
   }
 
+  while( pParse->pCodedTrigger ){
+    CodedTrigger *pT = pParse->pCodedTrigger;
+    pParse->pCodedTrigger = pT->pNext;
+    sqlite3VdbeProgramDelete(db, pT->pProgram, 0);
+    sqlite3DbFree(db, pT);
+  }
+
 end_prepare:
 
   sqlite3StackFree(db, pParse);
index 9ba75676595205c8aba7f363c1ec8797446337be..1fe885f51edc0cafb05155c48834e03982e2544c 100644 (file)
@@ -137,6 +137,7 @@ static int lookupName(
   struct SrcList_item *pMatch = 0;  /* The matching pSrcList item */
   NameContext *pTopNC = pNC;        /* First namecontext in the list */
   Schema *pSchema = 0;              /* Schema of the expression */
+  int isTrigger = 0;                /* True if a new.* or old.* reference. */
 
   assert( pNC );     /* the name context cannot be NULL. */
   assert( zCol );    /* The Z in X.Y.Z cannot be NULL */
@@ -222,33 +223,29 @@ static int lookupName(
     /* If we have not already resolved the name, then maybe 
     ** it is a new.* or old.* trigger argument reference
     */
-    if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){
-      TriggerStack *pTriggerStack = pParse->trigStack;
+    if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
       Table *pTab = 0;
       u32 *piColMask = 0;
-      if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){
-        pExpr->iTable = pTriggerStack->newIdx;
-        assert( pTriggerStack->pTab );
-        pTab = pTriggerStack->pTab;
-        piColMask = &(pTriggerStack->newColMask);
-      }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){
-        pExpr->iTable = pTriggerStack->oldIdx;
-        assert( pTriggerStack->pTab );
-        pTab = pTriggerStack->pTab;
-        piColMask = &(pTriggerStack->oldColMask);
+      if( pParse->triggerOp!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
+        pExpr->iTable = 1;
+        pTab = pParse->pTriggerTab;
+        piColMask = &(pParse->newmask);
+      }else if( pParse->triggerOp!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
+        pExpr->iTable = 0;
+        pTab = pParse->pTriggerTab;
+        piColMask = &(pParse->oldmask);
       }
 
       if( pTab ){ 
         int iCol;
-        Column *pCol = pTab->aCol;
-
         pSchema = pTab->pSchema;
         cntTab++;
-        for(iCol=0; iCol < pTab->nCol; iCol++, pCol++) {
+        isTrigger = 1;
+        for(iCol=0; iCol<pTab->nCol; iCol++){
+          Column *pCol = &pTab->aCol[iCol];
           if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
             cnt++;
             pExpr->iColumn = iCol==pTab->iPKey ? -1 : (i16)iCol;
-            pExpr->pTab = pTab;
             testcase( iCol==31 );
             testcase( iCol==32 );
             if( iCol>=32 ){
@@ -382,6 +379,10 @@ lookupname_end:
       if( pTopNC==pNC ) break;
       pTopNC = pTopNC->pNext;
     }
+    if( isTrigger ){
+      pExpr->pTab = pParse->pTriggerTab;
+      pExpr->op = TK_TRIGGER;
+    }
     return WRC_Prune;
   } else {
     return WRC_Abort;
index 90fab7bb819dfb096d8cb29b3b5ee2b50e53c163..c6940d70447f42cf47a3df1a4ce00ba1f3776d8f 100644 (file)
@@ -2733,9 +2733,10 @@ static int flattenSubquery(
   */
   if( ALWAYS(pSubitem->pTab!=0) ){
     Table *pTabToDel = pSubitem->pTab;
+    Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse);
     if( pTabToDel->nRef==1 ){
-      pTabToDel->pNextZombie = pParse->pZombieTab;
-      pParse->pZombieTab = pTabToDel;
+      pTabToDel->pNextZombie = pRoot->pZombieTab;
+      pRoot->pZombieTab = pTabToDel;
     }else{
       pTabToDel->nRef--;
     }
index 4eda0119b4fdbd1f751c1f82bcd2d2d52c76d192..5482eee9de80acc87759613a699057c2d120039b 100644 (file)
@@ -2015,6 +2015,16 @@ struct AutoincInfo {
 # define SQLITE_N_COLCACHE 10
 #endif
 
+typedef struct CodedTrigger CodedTrigger;
+struct CodedTrigger {
+  SubProgram *pProgram;
+  Trigger *pTrigger;
+  u32 oldmask;            /* Mask of old.* columns accessed */
+  u32 newmask;            /* Mask of new.* columns accessed */
+  int orconf;             /* Default ON CONFLICT policy */
+  CodedTrigger *pNext;
+};
+
 /*
 ** An SQL parser context.  A copy of this structure is passed through
 ** the parser and down into all the parser action routine in order to
@@ -2076,6 +2086,15 @@ struct Parse {
   int regRoot;         /* Register holding root page number for new objects */
   AutoincInfo *pAinc;  /* Information about AUTOINCREMENT counters */
 
+  /* Information used while coding trigger programs. */
+  Parse *pRoot;        /* Root Parse structure */
+  Table *pTriggerTab;  /* Table triggers are being coded for */
+  u32 oldmask; 
+  u32 newmask; 
+  int triggerOp;       /* TK_UPDATE, TK_INSERT or TK_DELETE */
+  int nArg;
+  int orconf;          /* Default ON CONFLICT policy for trigger steps */
+
   /* Above is constant between recursions.  Below is reset before and after
   ** each recursion */
 
@@ -2092,7 +2111,9 @@ struct Parse {
   const char *zTail;   /* All SQL text past the last semicolon parsed */
   Table *pNewTable;    /* A table being constructed by CREATE TABLE */
   Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
+#if 0
   TriggerStack *trigStack;  /* Trigger actions being coded */
+#endif
   const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   Token sArg;                /* Complete text of a module argument */
@@ -2102,6 +2123,7 @@ struct Parse {
 #endif
   int nHeight;            /* Expression tree height of current sub-select */
   Table *pZombieTab;      /* List of Table objects to delete after code gen */
+  CodedTrigger *pCodedTrigger;    /* Linked list of coded triggers */
 };
 
 #ifdef SQLITE_OMIT_VIRTUALTABLE
@@ -2144,7 +2166,7 @@ struct AuthContext {
  * containing the SQL statements specified as the trigger program.
  */
 struct Trigger {
-  char *name;             /* The name of the trigger                        */
+  char *zName;            /* The name of the trigger                        */
   char *table;            /* The table or view to which the trigger applies */
   u8 op;                  /* One of TK_DELETE, TK_UPDATE, TK_INSERT         */
   u8 tr_tm;               /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
@@ -2687,8 +2709,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
   void sqlite3DropTriggerPtr(Parse*, Trigger*);
   Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask);
   Trigger *sqlite3TriggerList(Parse *, Table *);
-  int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
-                            int, int, int, int, u32*, u32*);
+  void sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
+                            int, int, int, int);
   void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
   void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
   TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
@@ -2698,12 +2720,13 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
   TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
   void sqlite3DeleteTrigger(sqlite3*, Trigger*);
   void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
+  void sqlite3TriggerUses(Parse*,Trigger*,int,ExprList*,Table*,int,u32*,u32*);
 #else
 # define sqlite3TriggersExist(B,C,D,E,F) 0
 # define sqlite3DeleteTrigger(A,B)
 # define sqlite3DropTriggerPtr(A,B)
 # define sqlite3UnlinkAndDeleteTrigger(A,B,C)
-# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K,L) 0
+# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J)
 # define sqlite3TriggerList(X, Y) 0
 #endif
 
index eb84d9b4335f09779bc8a7a25e395dc7c262bd4a..e59c53bb05529596dafa422ea02e14ec4cf665db 100644 (file)
@@ -219,7 +219,7 @@ void sqlite3BeginTrigger(
   /* Build the Trigger object */
   pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
   if( pTrigger==0 ) goto trigger_cleanup;
-  pTrigger->name = zName;
+  pTrigger->zName = zName;
   zName = 0;
   pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
   pTrigger->pSchema = db->aDb[iDb].pSchema;
@@ -262,14 +262,14 @@ void sqlite3FinishTrigger(
   pTrig = pParse->pNewTrigger;
   pParse->pNewTrigger = 0;
   if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup;
-  zName = pTrig->name;
+  zName = pTrig->zName;
   iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
   pTrig->step_list = pStepList;
   while( pStepList ){
     pStepList->pTrig = pTrig;
     pStepList = pStepList->pNext;
   }
-  nameToken.z = pTrig->name;
+  nameToken.z = pTrig->zName;
   nameToken.n = sqlite3Strlen30(nameToken.z);
   if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken) 
           && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
@@ -451,7 +451,7 @@ TriggerStep *sqlite3TriggerDeleteStep(
 void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
   if( pTrigger==0 ) return;
   sqlite3DeleteTriggerStep(db, pTrigger->step_list);
-  sqlite3DbFree(db, pTrigger->name);
+  sqlite3DbFree(db, pTrigger->zName);
   sqlite3DbFree(db, pTrigger->table);
   sqlite3ExprDelete(db, pTrigger->pWhen);
   sqlite3IdListDelete(db, pTrigger->pColumns);
@@ -558,11 +558,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
     sqlite3BeginWriteOperation(pParse, 0, iDb);
     sqlite3OpenMasterTable(pParse, iDb);
     base = sqlite3VdbeAddOpList(v,  ArraySize(dropTrigger), dropTrigger);
-    sqlite3VdbeChangeP4(v, base+1, pTrigger->name, 0);
+    sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, 0);
     sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
     sqlite3ChangeCookie(pParse, iDb);
     sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
-    sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0);
+    sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0);
     if( pParse->nMem<3 ){
       pParse->nMem = 3;
     }
@@ -674,70 +674,226 @@ static int codeTriggerProgram(
   TriggerStep *pStepList,   /* List of statements inside the trigger body */
   int orconfin              /* Conflict algorithm. (OE_Abort, etc) */  
 ){
-  TriggerStep * pTriggerStep = pStepList;
-  int orconf;
+  TriggerStep * pStep = pStepList;
   Vdbe *v = pParse->pVdbe;
   sqlite3 *db = pParse->db;
 
-  assert( pTriggerStep!=0 );
+  assert( pParse->pRoot );
+  assert( pStep!=0 );
   assert( v!=0 );
-  sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0);
-  VdbeComment((v, "begin trigger %s", pStepList->pTrig->name));
-  while( pTriggerStep ){
-    sqlite3ExprCacheClear(pParse);
-    orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
-    pParse->trigStack->orconf = orconf;
-    switch( pTriggerStep->op ){
+/* sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0); */
+  while( pStep ){
+    /* Figure out the ON CONFLICT policy that will be used for this step
+    ** of the trigger program. If the statement that caused this trigger
+    ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use
+    ** the ON CONFLICT policy that was specified as part of the trigger
+    ** step statement. Example:
+    **
+    **   CREATE TRIGGER AFTER INSERT ON t1 BEGIN;
+    **     INSERT OR REPLACE INTO t2 VALUES(new.a, new.b);
+    **   END;
+    **
+    **   INSERT INTO t1 ... ;            -- insert into t2 uses REPLACE policy
+    **   INSERT OR IGNORE INTO t1 ... ;  -- insert into t2 uses IGNORE policy
+    */
+    pParse->orconf = (orconfin==OE_Default)?pStep->orconf:orconfin;
+
+    switch( pStep->op ){
       case TK_UPDATE: {
-        SrcList *pSrc;
-        pSrc = targetSrcList(pParse, pTriggerStep);
-        sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
-        sqlite3Update(pParse, pSrc,
-                sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), 
-                sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf);
-        sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
+        sqlite3Update(pParse, 
+          targetSrcList(pParse, pStep),
+          sqlite3ExprListDup(db, pStep->pExprList, 0), 
+          sqlite3ExprDup(db, pStep->pWhere, 0), 
+          pParse->orconf
+        );
         break;
       }
       case TK_INSERT: {
-        SrcList *pSrc;
-        pSrc = targetSrcList(pParse, pTriggerStep);
-        sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
-        sqlite3Insert(pParse, pSrc,
-          sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), 
-          sqlite3SelectDup(db, pTriggerStep->pSelect, 0), 
-          sqlite3IdListDup(db, pTriggerStep->pIdList), orconf);
-        sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
+        sqlite3Insert(pParse, 
+          targetSrcList(pParse, pStep),
+          sqlite3ExprListDup(db, pStep->pExprList, 0), 
+          sqlite3SelectDup(db, pStep->pSelect, 0), 
+          sqlite3IdListDup(db, pStep->pIdList), 
+          pParse->orconf
+        );
         break;
       }
       case TK_DELETE: {
-        SrcList *pSrc;
-        sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0);
-        pSrc = targetSrcList(pParse, pTriggerStep);
-        sqlite3DeleteFrom(pParse, pSrc, 
-                          sqlite3ExprDup(db, pTriggerStep->pWhere, 0));
-        sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
+        sqlite3DeleteFrom(pParse, 
+          targetSrcList(pParse, pStep),
+          sqlite3ExprDup(db, pStep->pWhere, 0)
+        );
         break;
       }
-      default: assert( pTriggerStep->op==TK_SELECT ); {
-        Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0);
-        if( ss ){
-          SelectDest dest;
-
-          sqlite3SelectDestInit(&dest, SRT_Discard, 0);
-          sqlite3Select(pParse, ss, &dest);
-          sqlite3SelectDelete(db, ss);
-        }
+      default: assert( pStep->op==TK_SELECT ); {
+        SelectDest sDest;
+        Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0);
+        sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
+        sqlite3Select(pParse, pSelect, &sDest);
+        sqlite3SelectDelete(db, pSelect);
         break;
       }
     } 
-    pTriggerStep = pTriggerStep->pNext;
+    pStep = pStep->pNext;
   }
-  sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0);
-  VdbeComment((v, "end trigger %s", pStepList->pTrig->name));
+/* sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); */
 
   return 0;
 }
 
+#ifdef SQLITE_DEBUG
+/*
+** This function is used to add VdbeComment() annotations to a VDBE
+** program. It is not used in production code, only for debugging.
+*/
+static const char *onErrorText(int onError){
+  switch( onError ){
+    case OE_Abort:    return "abort";
+    case OE_Rollback: return "rollback";
+    case OE_Fail:     return "fail";
+    case OE_Replace:  return "replace";
+    case OE_Ignore:   return "ignore";
+    case OE_Default:  return "default";
+  }
+  return "n/a";
+}
+#endif
+
+/*
+** Parse context structure pFrom has just been used to create a sub-vdbe
+** (trigger program). If an error has occurred, transfer error information
+** from pFrom to pTo.
+*/
+static void transferParseError(Parse *pTo, Parse *pFrom){
+  assert( pFrom->zErrMsg==0 || pFrom->nErr );
+  assert( pTo->zErrMsg==0 || pTo->nErr );
+  if( pTo->nErr==0 ){
+    pTo->zErrMsg = pFrom->zErrMsg;
+    pTo->nErr = pFrom->nErr;
+  }else{
+    sqlite3DbFree(pFrom->db, pFrom->zErrMsg);
+  }
+}
+
+static CodedTrigger *codeRowTrigger(
+  Parse *pRoot,        /* Root parse context */
+  Parse *pParse,       /* Current parse context */
+  Trigger *pTrigger,   /* Trigger to code */
+  int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
+  Table *pTab,         /* The table to code triggers from */
+  int orconf
+){
+  sqlite3 *db = pParse->db;
+  CodedTrigger *pC;
+  Expr *pWhen = 0;            /* Duplicate of trigger WHEN expression */
+  Vdbe *v;                    /* Temporary VM */
+  AuthContext sContext;       /* Auth context for sub-vdbe */
+  NameContext sNC;            /* Name context for sub-vdbe */
+  SubProgram *pProgram = 0;   /* Sub-vdbe for trigger program */
+  Parse *pSubParse;           /* Parse context for sub-vdbe */
+  int iEndTrigger = 0;        /* Label to jump to if WHEN is false */
+
+  pC = sqlite3DbMallocZero(db, sizeof(CodedTrigger));
+  if( !pC ) return 0;
+  pC->pNext = pRoot->pCodedTrigger;
+  pRoot->pCodedTrigger = pC;
+  pC->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram));
+  if( !pProgram ) return 0;
+  pProgram->nRef = 1;
+  pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
+  if( !pSubParse ) return 0;
+
+  pC->pProgram = pProgram;
+  pC->pTrigger = pTrigger;
+  pC->orconf = orconf;
+
+  memset(&sNC, 0, sizeof(sNC));
+  sNC.pParse = pSubParse;
+  pSubParse->db = db;
+  pSubParse->pTriggerTab = pTab;
+  pSubParse->pRoot = pRoot;
+
+  /* Push an entry on to the auth context stack */
+  sqlite3AuthContextPush(pParse, &sContext, pTrigger->name);
+
+  v = sqlite3GetVdbe(pSubParse);
+  if( v ){
+    VdbeComment((v, "Trigger: %s (%s %s%s%s ON %s) (%s)", pTrigger->zName,
+      (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"),
+        (op==TK_UPDATE ? "UPDATE" : ""),
+        (op==TK_INSERT ? "INSERT" : ""),
+        (op==TK_DELETE ? "DELETE" : ""),
+      pTab->zName, onErrorText(orconf)
+    ));
+
+    if( pTrigger->pWhen ){
+      /* Code the WHEN clause. If it evaluates to false (or NULL) the 
+      ** sub-vdbe is immediately halted.  */
+      pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0);
+      if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) ){
+        iEndTrigger = sqlite3VdbeMakeLabel(v);
+        sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL);
+      }
+      sqlite3ExprDelete(db, pWhen);
+    }
+
+    /* Code the trigger program into the sub-vdbe. */
+    codeTriggerProgram(pSubParse, pTrigger->step_list, OE_Default);
+    if( iEndTrigger ){
+      sqlite3VdbeResolveLabel(v, iEndTrigger);
+    }
+    sqlite3VdbeAddOp0(v, OP_Halt);
+    transferParseError(pParse, pSubParse);
+    pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pParse->nArg);
+    pProgram->nMem = pSubParse->nMem;
+    pProgram->nCsr = pSubParse->nTab;
+    pProgram->token = (void *)pTrigger;
+    pC->oldmask = pSubParse->oldmask;
+    pC->newmask = pSubParse->newmask;
+    sqlite3VdbeDelete(v);
+
+    while( pSubParse->pAinc ){
+      AutoincInfo *p = pSubParse->pAinc;
+      pSubParse->pAinc = p->pNext;
+      sqlite3DbFree(db, p);
+    }
+  }
+  sqlite3StackFree(db, pSubParse);
+
+  /* Pop the entry off the authorization stack */
+  sqlite3AuthContextPop(&sContext);
+  return pC;
+}
+    
+static CodedTrigger *getRowTrigger(
+  Parse *pParse,
+  Trigger *pTrigger,   /* Trigger to code */
+  int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
+  Table *pTab,         /* The table to code triggers from */
+  int orconf
+){
+  CodedTrigger *pC;
+  Parse *pRoot = pParse;
+
+  /* It may be that this trigger has already been coded (or is in the
+  ** process of being coded). If this is the case, then an entry with
+  ** a matching CodedTrigger.pTrigger field will be present somewhere
+  ** in the Parse.pCodedTrigger list. Search for such an entry.  */
+  if( pParse->pRoot ){
+    pRoot = pParse->pRoot;
+  }
+  for(pC=pRoot->pCodedTrigger; 
+      pC && (pC->pTrigger!=pTrigger || pC->orconf!=orconf); 
+      pC=pC->pNext
+  );
+
+  if( !pC ){
+    pC = codeRowTrigger(pRoot, pParse, pTrigger, op, pTab, orconf);
+  }
+
+  return pC;
+}
+
 /*
 ** This is called to code FOR EACH ROW triggers.
 **
@@ -765,7 +921,7 @@ static int codeTriggerProgram(
 ** output mask is set to the special value 0xffffffff.
 **
 */
-int sqlite3CodeRowTrigger(
+void sqlite3CodeRowTrigger(
   Parse *pParse,       /* Parse context */
   Trigger *pTrigger,   /* List of triggers on table pTab */
   int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
@@ -775,100 +931,68 @@ int sqlite3CodeRowTrigger(
   int newIdx,          /* The indice of the "new" row to access */
   int oldIdx,          /* The indice of the "old" row to access */
   int orconf,          /* ON CONFLICT policy */
-  int ignoreJump,      /* Instruction to jump to for RAISE(IGNORE) */
-  u32 *piOldColMask,   /* OUT: Mask of columns used from the OLD.* table */
-  u32 *piNewColMask    /* OUT: Mask of columns used from the NEW.* table */
+  int ignoreJump       /* Instruction to jump to for RAISE(IGNORE) */
 ){
   Trigger *p;
-  sqlite3 *db = pParse->db;
-  TriggerStack trigStackEntry;
-
-  trigStackEntry.oldColMask = 0;
-  trigStackEntry.newColMask = 0;
 
   assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
   assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER );
 
-  assert(newIdx != -1 || oldIdx != -1);
-
   for(p=pTrigger; p; p=p->pNext){
-    int fire_this = 0;
 
     /* Sanity checking:  The schema for the trigger and for the table are
     ** always defined.  The trigger must be in the same schema as the table
     ** or else it must be a TEMP trigger. */
     assert( p->pSchema!=0 );
     assert( p->pTabSchema!=0 );
-    assert( p->pSchema==p->pTabSchema || p->pSchema==db->aDb[1].pSchema );
+    assert( p->pSchema==p->pTabSchema 
+         || p->pSchema==pParse->db->aDb[1].pSchema );
 
     /* Determine whether we should code this trigger */
-    if( 
-      p->op==op && 
-      p->tr_tm==tr_tm && 
-      checkColumnOverlap(p->pColumns,pChanges)
+    if( p->op==op 
+     && p->tr_tm==tr_tm 
+     && checkColumnOverlap(p->pColumns,pChanges)
     ){
-      TriggerStack *pS;      /* Pointer to trigger-stack entry */
-      for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){}
-      if( !pS ){
-        fire_this = 1;
+      Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */
+      CodedTrigger *pC;
+      pC = getRowTrigger(pParse, p, op, pTab, orconf);
+      assert( pC || pParse->nErr || pParse->db->mallocFailed );
+
+      /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program 
+      ** is a pointer to the sub-vdbe containing the trigger program.  */
+      if( pC ){
+        sqlite3VdbeAddOp3(v, OP_Program, oldIdx, newIdx, ++pParse->nMem);
+        pC->pProgram->nRef++;
+        sqlite3VdbeChangeP4(v, -1, (const char *)pC->pProgram, P4_SUBPROGRAM);
+        VdbeComment((v, "Call trigger: %s (%s)", p->zName,onErrorText(orconf)));
       }
-#if 0    /* Give no warning for recursive triggers.  Just do not do them */
-      else{
-        sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)",
-            p->name);
-        return SQLITE_ERROR;
-      }
-#endif
     }
-    if( fire_this ){
-      int endTrigger;
-      Expr * whenExpr;
-      AuthContext sContext;
-      NameContext sNC;
-
-#ifndef SQLITE_OMIT_TRACE
-      sqlite3VdbeAddOp4(pParse->pVdbe, OP_Trace, 0, 0, 0,
-                        sqlite3MPrintf(db, "-- TRIGGER %s", p->name),
-                        P4_DYNAMIC);
-#endif
-      memset(&sNC, 0, sizeof(sNC));
-      sNC.pParse = pParse;
-
-      /* Push an entry on to the trigger stack */
-      trigStackEntry.pTrigger = p;
-      trigStackEntry.newIdx = newIdx;
-      trigStackEntry.oldIdx = oldIdx;
-      trigStackEntry.pTab = pTab;
-      trigStackEntry.pNext = pParse->trigStack;
-      trigStackEntry.ignoreJump = ignoreJump;
-      pParse->trigStack = &trigStackEntry;
-      sqlite3AuthContextPush(pParse, &sContext, p->name);
-
-      /* code the WHEN clause */
-      endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
-      whenExpr = sqlite3ExprDup(db, p->pWhen, 0);
-      if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){
-        pParse->trigStack = trigStackEntry.pNext;
-        sqlite3ExprDelete(db, whenExpr);
-        return 1;
-      }
-      sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL);
-      sqlite3ExprDelete(db, whenExpr);
-
-      sqlite3ExprCachePush(pParse);
-      codeTriggerProgram(pParse, p->step_list, orconf); 
-      sqlite3ExprCachePop(pParse, 1);
+  }
+}
 
-      /* Pop the entry off the trigger stack */
-      pParse->trigStack = trigStackEntry.pNext;
-      sqlite3AuthContextPop(&sContext);
+void sqlite3TriggerUses(
+  Parse *pParse,       /* Parse context */
+  Trigger *pTrigger,   /* List of triggers on table pTab */
+  int op,              /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
+  ExprList *pChanges,  /* Changes list for any UPDATE OF triggers */
+  Table *pTab,         /* The table to code triggers from */
+  int orconf,          /* Default ON CONFLICT policy for trigger steps */
+  u32 *piOldColMask,   /* OUT: Mask of columns used from the OLD.* table */
+  u32 *piNewColMask    /* OUT: Mask of columns used from the NEW.* table */
+){
+  Trigger *p;
+  assert(op==TK_UPDATE || op==TK_INSERT || op==TK_DELETE);
 
-      sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger);
+  for(p=pTrigger; p; p=p->pNext){
+    if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){
+      CodedTrigger *pC;
+      pC = getRowTrigger(pParse, p, op, pTab, orconf);
+      if( pC ){
+        *piOldColMask |= pC->oldmask;
+        *piNewColMask |= pC->newmask;
+      }
     }
   }
-  if( piOldColMask ) *piOldColMask |= trigStackEntry.oldColMask;
-  if( piNewColMask ) *piNewColMask |= trigStackEntry.newColMask;
-  return 0;
 }
+
 #endif /* !defined(SQLITE_OMIT_TRIGGER) */
index dcf861213226591f23d94dc82aa1e28ae723ba05..1f03e31c29faebd647b7bdccfa6240b5d55d7ffc 100644 (file)
@@ -120,21 +120,15 @@ void sqlite3Update(
   int isView;                  /* Trying to update a view */
   Trigger *pTrigger;           /* List of triggers on pTab, if required */
 #endif
-  int iBeginAfterTrigger = 0;  /* Address of after trigger program */
-  int iEndAfterTrigger = 0;    /* Exit of after trigger program */
-  int iBeginBeforeTrigger = 0; /* Address of before trigger program */
-  int iEndBeforeTrigger = 0;   /* Exit of before trigger program */
   u32 old_col_mask = 0;        /* Mask of OLD.* columns in use */
   u32 new_col_mask = 0;        /* Mask of NEW.* columns in use */
 
-  int newIdx      = -1;  /* index of trigger "new" temp table       */
-  int oldIdx      = -1;  /* index of trigger "old" temp table       */
-
   /* Register Allocations */
   int regRowCount = 0;   /* A count of rows changed */
   int regOldRowid;       /* The old rowid */
   int regNewRowid;       /* The new rowid */
-  int regData;           /* New data for the row */
+  int regNew;
+  int regOld;
   int regRowSet = 0;     /* Rowset of rows to be updated */
 
   memset(&sContext, 0, sizeof(sContext));
@@ -175,14 +169,6 @@ void sqlite3Update(
   if( aXRef==0 ) goto update_cleanup;
   for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
 
-  /* If there are FOR EACH ROW triggers, allocate cursors for the
-  ** special OLD and NEW tables
-  */
-  if( pTrigger ){
-    newIdx = pParse->nTab++;
-    oldIdx = pParse->nTab++;
-  }
-
   /* Allocate a cursors for the main database table and for all indices.
   ** The index cursors might not be used, but if they are used they
   ** need to occur right after the database cursor.  So go ahead and
@@ -268,24 +254,7 @@ void sqlite3Update(
     aRegIdx[j] = reg;
   }
 
-  /* Allocate a block of register used to store the change record
-  ** sent to sqlite3GenerateConstraintChecks().  There are either
-  ** one or two registers for holding the rowid.  One rowid register
-  ** is used if chngRowid is false and two are used if chngRowid is
-  ** true.  Following these are pTab->nCol register holding column
-  ** data.
-  */
-  regOldRowid = regNewRowid = pParse->nMem + 1;
-  pParse->nMem += pTab->nCol + 1;
-  if( chngRowid ){
-    regNewRowid++;
-    pParse->nMem++;
-  }
-  regData = regNewRowid+1;
-
-  /* Begin generating code.
-  */
+  /* Begin generating code. */
   v = sqlite3GetVdbe(pParse);
   if( v==0 ) goto update_cleanup;
   if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
@@ -302,40 +271,27 @@ void sqlite3Update(
   }
 #endif
 
-  /* Start the view context
-  */
+  /* Allocate required registers. */
+  regOldRowid = regNewRowid = ++pParse->nMem;
+  if( pTrigger ){
+    regOld = pParse->nMem + 1;
+    pParse->nMem += pTab->nCol;
+  }
+  if( chngRowid || pTrigger ){
+    regNewRowid = ++pParse->nMem;
+  }
+  regNew = pParse->nMem + 1;
+  pParse->nMem += pTab->nCol;
+
+  /* Start the view context. */
   if( isView ){
     sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
   }
 
-  /* Generate the code for triggers.
-  */
-  if( pTrigger ){
-    int iGoto;
-
-    /* Create pseudo-tables for NEW and OLD
-    */
-    sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol);
-    sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol);
-
-    iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
-    addr = sqlite3VdbeMakeLabel(v);
-    iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
-    if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, 
-          TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr, 
-          &old_col_mask, &new_col_mask) ){
-      goto update_cleanup;
-    }
-    iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
-    iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v);
-    if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, 
-          TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr, 
-          &old_col_mask, &new_col_mask) ){
-      goto update_cleanup;
-    }
-    iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
-    sqlite3VdbeJumpHere(v, iGoto);
-  }
+  /* If there are any triggers, set old_col_mask and new_col_mask. */
+  sqlite3TriggerUses(pParse, 
+      pTrigger, TK_UPDATE, pChanges, pTab, onError, &old_col_mask, &new_col_mask
+  );
 
   /* If we are trying to update a view, realize that view into
   ** a ephemeral table.
@@ -374,7 +330,7 @@ void sqlite3Update(
 
   /* Initialize the count of updated rows
   */
-  if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
+  if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
     regRowCount = ++pParse->nMem;
     sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
   }
@@ -407,11 +363,6 @@ void sqlite3Update(
       }
     }
   }
-  
-  /* Jump back to this point if a trigger encounters an IGNORE constraint. */
-  if( pTrigger ){
-    sqlite3VdbeResolveLabel(v, addr);
-  }
 
   /* Top of the update loop */
   if( okOnePass ){
@@ -422,139 +373,87 @@ void sqlite3Update(
     addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
   }
 
-  if( pTrigger ){
-    int regRowid;
-    int regRow;
-    int regCols;
-
-    /* Make cursor iCur point to the record that is being updated.
-    */
-    sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
+  /* Make cursor iCur point to the record that is being updated. If
+  ** this record does not exist for some reason (deleted by a trigger,
+  ** for example, then jump to the next iteration of the RowSet loop.  */
+  sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
 
-    /* Generate the OLD table
-    */
-    regRowid = sqlite3GetTempReg(pParse);
-    regRow = sqlite3GetTempReg(pParse);
-    sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
-    if( !old_col_mask ){
-      sqlite3VdbeAddOp2(v, OP_Null, 0, regRow);
-    }else{
-      sqlite3VdbeAddOp2(v, OP_RowData, iCur, regRow);
-    }
-    sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, regRow, regRowid);
-
-    /* Generate the NEW table
-    */
-    if( chngRowid ){
-      sqlite3ExprCodeAndCache(pParse, pRowidExpr, regRowid);
-      sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
-    }else{
-      sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid);
-    }
-    regCols = sqlite3GetTempRange(pParse, pTab->nCol);
+  /* If there are triggers on this table, populate an array of registers 
+  ** with the required old.* column data.  */
+  if( pTrigger ){
     for(i=0; i<pTab->nCol; i++){
-      if( i==pTab->iPKey ){
-        sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
-        continue;
-      }
-      j = aXRef[i];
-      if( (i<32 && (new_col_mask&((u32)1<<i))!=0) || new_col_mask==0xffffffff ){
-        if( j<0 ){
-          sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regCols+i);
-          sqlite3ColumnDefault(v, pTab, i, -1);
-        }else{
-          sqlite3ExprCodeAndCache(pParse, pChanges->a[j].pExpr, regCols+i);
-        }
+      if( aXRef[i]<0 || old_col_mask==0xffffffff || (old_col_mask & (1<<i)) ){
+        sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
+        sqlite3ColumnDefault(v, pTab, i, regOld+i);
       }else{
-        sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i);
+        sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
       }
     }
-    sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRow);
-    if( !isView ){
-      sqlite3TableAffinityStr(v, pTab);
-      sqlite3ExprCacheAffinityChange(pParse, regCols, pTab->nCol);
-    }
-    sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
-    /* if( pParse->nErr ) goto update_cleanup; */
-    sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRow, regRowid);
-    sqlite3ReleaseTempReg(pParse, regRowid);
-    sqlite3ReleaseTempReg(pParse, regRow);
-
-    sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger);
-    sqlite3VdbeJumpHere(v, iEndBeforeTrigger);
   }
 
-  if( !isView ){
-    /* Loop over every record that needs updating.  We have to load
-    ** the old data for each record to be updated because some columns
-    ** might not change and we will need to copy the old value.
-    ** Also, the old data is needed to delete the old index entries.
-    ** So make the cursor point at the old record.
-    */
-    sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
-    /* If the record number will change, push the record number as it
-    ** will be after the update. (The old record number is currently
-    ** on top of the stack.)
-    */
-    if( chngRowid ){
-      sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
-      sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
-    }
+  /* If the record number will change, set register regNewRowid to
+  ** contain the new value. If the record number is not being modified,
+  ** then regNewRowid is the same register as regOldRowid, which is
+  ** already populated.  */
+  assert( chngRowid || pTrigger || regOldRowid==regNewRowid );
+  if( chngRowid ){
+    sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
+    sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
+  }else if( pTrigger ){
+    sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
+  }
 
-    /* Compute new data for this record.  
-    */
-    for(i=0; i<pTab->nCol; i++){
-      if( i==pTab->iPKey ){
-        sqlite3VdbeAddOp2(v, OP_Null, 0, regData+i);
-        continue;
-      }
+  /* Populate the array of registers beginning at regNew with the new
+  ** row data. This array is used to check constaints, create the new
+  ** table and index records, and as the values for any new.* references
+  ** made by triggers.  */
+  for(i=0; i<pTab->nCol; i++){
+    if( i==pTab->iPKey ){
+      sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
+    }else{
       j = aXRef[i];
       if( j<0 ){
-        sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regData+i);
-        sqlite3ColumnDefault(v, pTab, i, regData+i);
+        sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
+        sqlite3ColumnDefault(v, pTab, i, regNew+i);
       }else{
-        sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regData+i);
+        sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
       }
     }
+  }
 
-    /* Do constraint checks
-    */
+  /* Fire any BEFORE UPDATE triggers. This happens before constraints are
+  ** verified. One could argue that this is wrong.  */
+  sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, 
+      TRIGGER_BEFORE, pTab, regNew, regOld, onError, addr);
+
+  if( !isView ){
+
+    /* Do constraint checks. */
     sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
-                                    aRegIdx, chngRowid, 1,
-                                    onError, addr, 0);
+        aRegIdx, chngRowid, 1, onError, addr, 0);
 
-    /* Delete the old indices for the current record.
-    */
+    /* Delete the index entries associated with the current record.  */
     j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
     sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
-
-    /* If changing the record number, delete the old record.
-    */
+  
+    /* If changing the record number, delete the old record.  */
     if( chngRowid ){
       sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
     }
     sqlite3VdbeJumpHere(v, j1);
-
-    /* Create the new index entries and the new record.
-    */
-    sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, 
-                             aRegIdx, 1, -1, 0, 0);
+  
+    /* Create the new index entries and the new record.  */
+    sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx,1,-1,0,0);
   }
 
   /* Increment the row counter 
   */
-  if( db->flags & SQLITE_CountRows && !pParse->trigStack){
+  if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){
     sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
   }
 
-  /* If there are triggers, close all the cursors after each iteration
-  ** through the loop.  The fire the after triggers.
-  */
-  if( pTrigger ){
-    sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
-    sqlite3VdbeJumpHere(v, iEndAfterTrigger);
-  }
+  sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, 
+      TRIGGER_AFTER, pTab, regNew, regOld, onError, addr);
 
   /* Repeat the above with the next record to be updated, until
   ** all record selected by the WHERE clause have been updated.
@@ -569,16 +468,12 @@ void sqlite3Update(
     }
   }
   sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
-  if( pTrigger ){
-    sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0);
-    sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0);
-  }
 
   /* Update the sqlite_sequence table by storing the content of the
   ** maximum rowid counter values recorded while inserting into
   ** autoincrement tables.
   */
-  if( pParse->nested==0 && pParse->trigStack==0 ){
+  if( pParse->nested==0 && pParse->pTriggerTab==0 ){
     sqlite3AutoincrementEnd(pParse);
   }
 
@@ -587,7 +482,7 @@ void sqlite3Update(
   ** generating code because of a call to sqlite3NestedParse(), do not
   ** invoke the callback function.
   */
-  if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){
+  if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){
     sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
     sqlite3VdbeSetNumCols(v, 1);
     sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
index 44d12f68d75be388e395ddcea4ae4f3eba6f9c9d..92d61cd43b943cede3ce76c6da2f2402ec2caef2 100644 (file)
@@ -846,9 +846,18 @@ case OP_HaltIfNull: {      /* in3 */
 ** is the same as executing Halt.
 */
 case OP_Halt: {
+  if( pOp->p1==SQLITE_OK && p->pFrame ){
+    VdbeFrame *pFrame = p->pFrame;
+    p->pFrame = pFrame->pParent;
+    p->nFrame--;
+    pc = sqlite3VdbeFrameRestore(pFrame);
+    if( pOp->p2==OE_Ignore ){
+    }
+    break;
+  }
   p->rc = pOp->p1;
-  p->pc = pc;
   p->errorAction = (u8)pOp->p2;
+  p->pc = pc;
   if( pOp->p4.z ){
     sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
   }
@@ -4770,6 +4779,137 @@ case OP_ContextPop: {
   p->nChange = pContext->nChange;
   break;
 }
+
+/* Opcode: Program P1 P2 P3 P4 *
+**
+** Execute a trigger program. P1 contains the address of the memory cell
+** that contains the left-most column of the old.* table (unless the trigger
+** program is firing as a result of an INSERT statement). P2 is the address 
+** of the corresponding column in the new.* table (unless the trigger 
+** program is being fired due to a DELETE).
+**
+** Register P3 contains the address of a memory cell in this (the parent)
+** VM that is used to allocate the memory required by the sub-vdbe at 
+** runtime.
+**
+** P4 is a pointer to the VM containing the trigger program.
+*/
+case OP_Program: {
+  VdbeFrame *pFrame;
+  SubProgram *pProgram = pOp->p4.pProgram;
+  Mem *pRt = &p->aMem[pOp->p3];        /* Register to allocate runtime space */
+  assert( pProgram->nOp>0 );
+  
+  /* If noRecTrigger is true, then recursive invocation of triggers is
+  ** disabled for backwards compatibility. 
+  ** 
+  ** It is recursive invocation of triggers, at the SQL level, that is 
+  ** disabled. In some cases a single trigger may generate more than one 
+  ** SubProgram (if the trigger may be executed with more than one different 
+  ** ON CONFLICT algorithm). SubProgram structures associated with a
+  ** single trigger all have the same value for the SubProgram.token 
+  ** variable.
+  */
+  if( 1 || p->noRecTrigger ){
+    void *t = pProgram->token;
+    for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent);
+    if( pFrame ) break;
+  }
+
+  /* TODO: This constant should be configurable. */
+  if( p->nFrame>1000 ){
+    rc = SQLITE_ERROR;
+    sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion");
+    break;
+  }
+
+  /* Register pRt is used to store the memory required to save the state
+  ** of the current program, and the memory required at runtime to execute
+  ** the trigger program. If this trigger has been fired before, then pRt 
+  ** is already allocated. Otherwise, it must be initialized.  */
+  if( (pRt->flags&MEM_Frame)==0 ){
+    Mem *pMem;
+    Mem *pEnd;
+
+    /* SubProgram.nMem is set to the number of memory cells used by the 
+    ** program stored in SubProgram.aOp. As well as these, one memory
+    ** cell is required for each cursor used by the program. Set local
+    ** variable nMem (and later, VdbeFrame.nChildMem) to this value.
+    */
+    int nMem = pProgram->nMem + pProgram->nCsr;
+    int nByte = ROUND8(sizeof(VdbeFrame))
+              + nMem * sizeof(Mem)
+              + pProgram->nCsr * sizeof(VdbeCursor *);
+    pFrame = sqlite3DbMallocZero(db, nByte);
+    if( !pFrame ){
+      goto no_mem;
+    }
+    sqlite3VdbeMemRelease(pRt);
+    pRt->flags = MEM_Frame;
+    pRt->u.pFrame = pFrame;
+
+    pFrame->v = p;
+    pFrame->nChildMem = nMem;
+    pFrame->nChildCsr = pProgram->nCsr;
+    pFrame->pc = pc;
+    pFrame->aMem = p->aMem;
+    pFrame->nMem = p->nMem;
+    pFrame->apCsr = p->apCsr;
+    pFrame->nCursor = p->nCursor;
+    pFrame->aOp = p->aOp;
+    pFrame->nOp = p->nOp;
+    pFrame->token = pProgram->token;
+
+    pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
+    for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
+      pMem->flags = MEM_Null;
+      pMem->db = db;
+    }
+  }else{
+    pFrame = pRt->u.pFrame;
+    assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem );
+    assert( pProgram->nCsr==pFrame->nChildCsr );
+    assert( pc==pFrame->pc );
+  }
+
+  p->nFrame++;
+  pFrame->pParent = p->pFrame;
+  p->pFrame = pFrame;
+  p->aMem = &VdbeFrameMem(pFrame)[-1];
+  p->nMem = pFrame->nChildMem;
+  p->nCursor = pFrame->nChildCsr;
+  p->apCsr = (VdbeCursor **)&p->aMem[p->nMem+1];
+  p->aOp = pProgram->aOp;
+  p->nOp = pProgram->nOp;
+  pc = -1;
+
+  break;
+}
+
+/* Opcode: TriggerVal P1 P2 P3 * *
+**
+** Copy a value currently stored in a memory cell of the parent VM to
+** a cell in this VMs address space. This is used by trigger programs
+** to access the new.* and old.* values.
+**
+** If parameter P3 is non-zero, then the value read is from the new.*
+** table. If P3 is zero, then the value is read from the old.* table.
+** Parameter P1 is the index of the required new.* or old.* column (or
+** -1 for rowid). 
+**
+** Parameter P2 is the index of the memory cell in this VM to copy the 
+** value to.
+*/
+case OP_TriggerVal: {           /* out2-prerelease */
+  VdbeFrame *pF = p->pFrame;
+  Mem *pIn;
+  int iFrom = pOp->p1;           /* Memory cell in parent frame */
+  iFrom += (pOp->p3 ? pF->aOp[pF->pc].p2 : pF->aOp[pF->pc].p1);
+  pIn = &pF->aMem[iFrom];
+  sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem);
+  break;
+}
+
 #endif /* #ifndef SQLITE_OMIT_TRIGGER */
 
 #ifndef SQLITE_OMIT_AUTOINCREMENT
index c72ebe1c8e5874bcc78502e358e81118b29d9842..3fef873827a90344e09e6cad8a273a45496fac43 100644 (file)
@@ -34,6 +34,7 @@ typedef struct Vdbe Vdbe;
 */
 typedef struct VdbeFunc VdbeFunc;
 typedef struct Mem Mem;
+typedef struct SubProgram SubProgram;
 
 /*
 ** A single instruction of the virtual machine has an opcode
@@ -48,7 +49,7 @@ struct VdbeOp {
   int p1;             /* First operand */
   int p2;             /* Second parameter (often the jump destination) */
   int p3;             /* The third parameter */
-  union {             /* forth parameter */
+  union {             /* fourth parameter */
     int i;                 /* Integer value if p4type==P4_INT32 */
     void *p;               /* Generic pointer */
     char *z;               /* Pointer to data for string (char array) types */
@@ -61,6 +62,7 @@ struct VdbeOp {
     VTable *pVtab;         /* Used when p4type is P4_VTAB */
     KeyInfo *pKeyInfo;     /* Used when p4type is P4_KEYINFO */
     int *ai;               /* Used when p4type is P4_INTARRAY */
+    SubProgram *pProgram;  /* Used when p4type is P4_SUBPROGRAM */
   } p4;
 #ifdef SQLITE_DEBUG
   char *zComment;          /* Comment to improve readability */
@@ -72,6 +74,19 @@ struct VdbeOp {
 };
 typedef struct VdbeOp VdbeOp;
 
+
+/*
+** A sub-routine used to implement a trigger program.
+*/
+struct SubProgram {
+  VdbeOp *aOp;                  /* Array of opcodes for sub-program */
+  int nOp;                      /* Elements in aOp[] */
+  int nMem;                     /* Number of memory cells required */
+  int nCsr;                     /* Number of cursors required */
+  int nRef;                     /* Number of pointers to this structure */
+  void *token;                  /* id that may be used to recursive triggers */
+};
+
 /*
 ** A smaller version of VdbeOp used for the VdbeAddOpList() function because
 ** it takes up less space.
@@ -85,7 +100,7 @@ struct VdbeOpList {
 typedef struct VdbeOpList VdbeOpList;
 
 /*
-** Allowed values of VdbeOp.p3type
+** Allowed values of VdbeOp.p4type
 */
 #define P4_NOTUSED    0   /* The P4 parameter is not used */
 #define P4_DYNAMIC  (-1)  /* Pointer to a string obtained from sqliteMalloc() */
@@ -102,6 +117,7 @@ typedef struct VdbeOpList VdbeOpList;
 #define P4_INT64    (-13) /* P4 is a 64-bit signed integer */
 #define P4_INT32    (-14) /* P4 is a 32-bit signed integer */
 #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
+#define P4_SUBPROGRAM  (-18) /* P4 is a pointer to a SubProgram structure */
 
 /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
 ** is made.  That copy is freed when the Vdbe is finalized.  But if the
@@ -168,7 +184,7 @@ void sqlite3VdbeUsesBtree(Vdbe*, int);
 VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
 int sqlite3VdbeMakeLabel(Vdbe*);
 void sqlite3VdbeDelete(Vdbe*);
-void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int);
+void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int);
 int sqlite3VdbeFinalize(Vdbe*);
 void sqlite3VdbeResolveLabel(Vdbe*, int);
 int sqlite3VdbeCurrentAddr(Vdbe*);
@@ -183,6 +199,8 @@ void sqlite3VdbeCountChanges(Vdbe*);
 sqlite3 *sqlite3VdbeDb(Vdbe*);
 void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
 void sqlite3VdbeSwap(Vdbe*,Vdbe*);
+VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
+void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int);
 
 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
 int sqlite3VdbeReleaseMemory(int);
index e44e03a9994aad9ac1459a14f0e35f67aab10c05..fae1f09c5a68484a1c20da9441de33221c19518f 100644 (file)
@@ -89,6 +89,24 @@ struct VdbeCursor {
 };
 typedef struct VdbeCursor VdbeCursor;
 
+typedef struct VdbeFrame VdbeFrame;
+struct VdbeFrame {
+  Vdbe *v;                /* VM this frame belongs to */
+  int pc;                 /* Program Counter */
+  Op *aOp;                /* Program instructions */
+  int nOp;                /* Size of aOp array */
+  Mem *aMem;              /* Array of memory cells */
+  int nMem;               /* Number of entries in aMem */
+  VdbeCursor **apCsr;     /* Element of Vdbe cursors */
+  u16 nCursor;            /* Number of entries in apCsr */
+  VdbeFrame *pParent;     /* Parent of this frame */
+  void *token;            /* Copy of SubProgram.token */
+  int nChildMem;          /* Number of memory cells for child frame */
+  int nChildCsr;          /* Number of cursors for child frame */
+};
+
+#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
+
 /*
 ** A value for VdbeCursor.cacheValid that means the cache is always invalid.
 */
@@ -111,6 +129,7 @@ struct Mem {
     int nZero;          /* Used when bit MEM_Zero is set in flags */
     FuncDef *pDef;      /* Used only when flags==MEM_Agg */
     RowSet *pRowSet;    /* Used only when flags==MEM_RowSet */
+    VdbeFrame *pFrame;  /* Used when flags==MEM_Frame */
   } u;
   double r;           /* Real value */
   sqlite3 *db;        /* The associated database connection */
@@ -144,6 +163,7 @@ struct Mem {
 #define MEM_Real      0x0008   /* Value is a real number */
 #define MEM_Blob      0x0010   /* Value is a BLOB */
 #define MEM_RowSet    0x0020   /* Value is a RowSet object */
+#define MEM_Frame     0x0040   /* Value is a VdbeFrame object */
 #define MEM_TypeMask  0x00ff   /* Mask of type bits */
 
 /* Whenever Mem contains a valid string or blob representation, one of
@@ -302,6 +322,9 @@ struct Vdbe {
 #ifdef SQLITE_DEBUG
   FILE *trace;            /* Write an execution trace here, if not NULL */
 #endif
+  VdbeFrame *pFrame;      /* Parent frame */
+  int nFrame;             /* Number of frames in pFrame list */
+  u8 noRecTrigger;        /* True to disable recursive triggers */
 };
 
 /*
@@ -362,6 +385,8 @@ const char *sqlite3OpcodeName(int);
 int sqlite3VdbeOpcodeHasProperty(int, int);
 int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
 int sqlite3VdbeCloseStatement(Vdbe *, int);
+void sqlite3VdbeFrameDelete(VdbeFrame*);
+int sqlite3VdbeFrameRestore(VdbeFrame *);
 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
 int sqlite3VdbeReleaseBuffers(Vdbe *p);
 #endif
index caaebd8160d223b658e5268e542ca81c2379816b..fc3af40040467d8c03da8d7ca53e3c055bb34e15 100644 (file)
@@ -76,7 +76,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
     Vdbe *v = (Vdbe*)pStmt;
     sqlite3_mutex_enter(v->db->mutex);
     rc = sqlite3VdbeReset(v);
-    sqlite3VdbeMakeReady(v, -1, 0, 0, 0);
+    sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0);
     assert( (rc & (v->db->errMask))==rc );
     rc = sqlite3ApiExit(v->db, rc);
     sqlite3_mutex_leave(v->db->mutex);
index ce413dc8f3c3e7e91cf31397d55546d9339eda50..46835e51ae4954c2170ab8378a5bfc5b3d703cd2 100644 (file)
@@ -266,7 +266,7 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){
 */
 static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
   int i;
-  int nMaxArgs = 0;
+  int nMaxArgs = *pMaxFuncArgs;
   Op *pOp;
   int *aLabel = p->aLabel;
   int doesStatementRollback = 0;
@@ -339,6 +339,14 @@ int sqlite3VdbeCurrentAddr(Vdbe *p){
   return p->nOp;
 }
 
+VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
+  VdbeOp *aOp = p->aOp;
+  resolveP2Values(p, pnMaxArg);
+  *pnOp = p->nOp;
+  p->aOp = 0;
+  return aOp;
+}
+
 /*
 ** Add a whole list of operations to the operation stack.  Return the
 ** address of the first operation added.
@@ -482,8 +490,39 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
         sqlite3VtabUnlock((VTable *)p4);
         break;
       }
+      case P4_SUBPROGRAM : {
+        sqlite3VdbeProgramDelete(db, (SubProgram *)p4, 1);
+        break;
+      }
+    }
+  }
+}
+
+static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
+  if( aOp ){
+    Op *pOp;
+    for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
+      freeP4(db, pOp->p4type, pOp->p4.p);
+#ifdef SQLITE_DEBUG
+      sqlite3DbFree(db, pOp->zComment);
+#endif     
     }
   }
+  sqlite3DbFree(db, aOp);
+}
+
+void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){
+  assert( p->nRef>0 );
+  if( freeop || p->nRef==1 ){
+    Op *aOp = p->aOp;
+    p->aOp = 0;
+    vdbeFreeOpArray(db, aOp, p->nOp);
+    p->nOp = 0;
+  }
+  p->nRef--;
+  if( p->nRef==0 ){
+    sqlite3DbFree(db, p);
+  }
 }
 
 
@@ -604,6 +643,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
 */
 void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
   va_list ap;
+  if( !p ) return;
   assert( p->nOp>0 || p->aOp==0 );
   assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
   if( p->nOp ){
@@ -616,6 +656,7 @@ void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
 }
 void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
   va_list ap;
+  if( !p ) return;
   sqlite3VdbeAddOp0(p, OP_Noop);
   assert( p->nOp>0 || p->aOp==0 );
   assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
@@ -749,6 +790,10 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
       sqlite3_snprintf(nTemp, zTemp, "intarray");
       break;
     }
+    case P4_SUBPROGRAM: {
+      sqlite3_snprintf(nTemp, zTemp, "program");
+      break;
+    }
     default: {
       zP4 = pOp->p4.z;
       if( zP4==0 ){
@@ -823,7 +868,7 @@ static void releaseMemArray(Mem *p, int N){
       ** with no indexes using a single prepared INSERT statement, bind() 
       ** and reset(). Inserts are grouped into a transaction.
       */
-      if( p->flags&(MEM_Agg|MEM_Dyn) ){
+      if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){
         sqlite3VdbeMemRelease(p);
       }else if( p->zMalloc ){
         sqlite3DbFree(db, p->zMalloc);
@@ -836,6 +881,18 @@ static void releaseMemArray(Mem *p, int N){
   }
 }
 
+void sqlite3VdbeFrameDelete(VdbeFrame *p){
+  int i;
+  Mem *aMem = VdbeFrameMem(p);
+  VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem];
+  for(i=0; i<p->nChildCsr; i++){
+    sqlite3VdbeFreeCursor(p->v, apCsr[i]);
+  }
+  releaseMemArray(aMem, p->nChildMem);
+  sqlite3DbFree(p->v->db, p);
+}
+
+
 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
 int sqlite3VdbeReleaseBuffers(Vdbe *p){
   int ii;
@@ -872,6 +929,10 @@ int sqlite3VdbeReleaseBuffers(Vdbe *p){
 int sqlite3VdbeList(
   Vdbe *p                   /* The VDBE */
 ){
+  int nRow;                            /* Total number of rows to return */
+  int nSub = 0;                        /* Number of sub-vdbes seen so far */
+  SubProgram **apSub = 0;              /* Array of sub-vdbes */
+  Mem *pSub = 0;
   sqlite3 *db = p->db;
   int i;
   int rc = SQLITE_OK;
@@ -886,7 +947,7 @@ int sqlite3VdbeList(
   ** the result, result columns may become dynamic if the user calls
   ** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
   */
-  releaseMemArray(pMem, p->nMem);
+  releaseMemArray(pMem, 8);
 
   if( p->rc==SQLITE_NOMEM ){
     /* This happens if a malloc() inside a call to sqlite3_column_text() or
@@ -895,10 +956,24 @@ int sqlite3VdbeList(
     return SQLITE_ERROR;
   }
 
+  /* Figure out total number of rows that will be returned by this 
+  ** EXPLAIN program.  */
+  nRow = p->nOp;
+  if( p->explain==1 ){
+    pSub = &p->aMem[9];
+    if( pSub->flags&MEM_Blob ){
+      nSub = pSub->n/sizeof(Vdbe*);
+      apSub = (SubProgram **)pSub->z;
+    }
+    for(i=0; i<nSub; i++){
+      nRow += apSub[i]->nOp;
+    }
+  }
+
   do{
     i = p->pc++;
-  }while( i<p->nOp && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
-  if( i>=p->nOp ){
+  }while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
+  if( i>=nRow ){
     p->rc = SQLITE_OK;
     rc = SQLITE_DONE;
   }else if( db->u1.isInterrupted ){
@@ -907,7 +982,17 @@ int sqlite3VdbeList(
     sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
   }else{
     char *z;
-    Op *pOp = &p->aOp[i];
+    Op *pOp;
+    if( i<p->nOp ){
+      pOp = &p->aOp[i];
+    }else{
+      int j;
+      i -= p->nOp;
+      for(j=0; i>=apSub[j]->nOp; j++){
+        i -= apSub[j]->nOp;
+      }
+      pOp = &apSub[j]->aOp[i];
+    }
     if( p->explain==1 ){
       pMem->flags = MEM_Int;
       pMem->type = SQLITE_INTEGER;
@@ -921,6 +1006,20 @@ int sqlite3VdbeList(
       pMem->type = SQLITE_TEXT;
       pMem->enc = SQLITE_UTF8;
       pMem++;
+
+      if( pOp->p4type==P4_SUBPROGRAM ){
+        int nByte = (nSub+1)*sizeof(SubProgram*);
+        int j;
+        for(j=0; j<nSub; j++){
+          if( apSub[j]==pOp->p4.pProgram ) break;
+        }
+        if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, 1) ){
+          apSub = (SubProgram **)pSub->z;
+          apSub[nSub++] = pOp->p4.pProgram;
+          pSub->flags |= MEM_Blob;
+          pSub->n = nSub*sizeof(SubProgram*);
+        }
+      }
     }
 
     pMem->flags = MEM_Int;
@@ -1095,6 +1194,7 @@ void sqlite3VdbeMakeReady(
   int nVar,                      /* Number of '?' see in the SQL statement */
   int nMem,                      /* Number of memory cells to allocate */
   int nCursor,                   /* Number of cursors to allocate */
+  int nArg,                      /* Maximum number of args in SubPrograms */
   int isExplain                  /* True if the EXPLAIN keywords is present */
 ){
   int n;
@@ -1130,7 +1230,6 @@ void sqlite3VdbeMakeReady(
     u8 *zCsr = (u8 *)&p->aOp[p->nOp];
     u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc];
     int nByte;
-    int nArg;       /* Maximum number of args passed to a user function. */
     resolveP2Values(p, &nArg);
     if( isExplain && nMem<10 ){
       nMem = 10;
@@ -1229,12 +1328,37 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
   }
 }
 
+int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
+  Vdbe *v = pFrame->v;
+  v->aOp = pFrame->aOp;
+  v->nOp = pFrame->nOp;
+  v->aMem = pFrame->aMem;
+  v->nMem = pFrame->nMem;
+  v->apCsr = pFrame->apCsr;
+  v->nCursor = pFrame->nCursor;
+  return pFrame->pc;
+}
+
 /*
-** Close all cursors.
+** Close all cursors. 
+**
+** Also release any dynamic memory held by the VM in the Vdbe.aMem memory 
+** cell array. This is necessary as the memory cell array may contain
+** pointers to VdbeFrame objects, which may in turn contain pointers to
+** open cursors.
 */
 static void closeAllCursors(Vdbe *p){
   int i;
-  if( p->apCsr==0 ) return;
+  /* if( p->apCsr==0 ) return; */
+
+  if( p->pFrame ){
+    VdbeFrame *pFrame = p->pFrame;
+    for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
+    sqlite3VdbeFrameRestore(pFrame);
+  }
+  p->pFrame = 0;
+  p->nFrame = 0;
+
   for(i=0; i<p->nCursor; i++){
     VdbeCursor *pC = p->apCsr[i];
     if( pC ){
@@ -1242,6 +1366,7 @@ static void closeAllCursors(Vdbe *p){
       p->apCsr[i] = 0;
     }
   }
+  releaseMemArray(&p->aMem[1], p->nMem);
 }
 
 /*
@@ -1252,17 +1377,16 @@ static void closeAllCursors(Vdbe *p){
 ** variables in the aVar[] array.
 */
 static void Cleanup(Vdbe *p){
-  int i;
   sqlite3 *db = p->db;
-  Mem *pMem;
-  closeAllCursors(p);
-  for(pMem=&p->aMem[1], i=1; i<=p->nMem; i++, pMem++){
-    if( pMem->flags & MEM_RowSet ){
-      sqlite3RowSetClear(pMem->u.pRowSet);
-    }
-    MemSetTypeFlag(pMem, MEM_Null);
-  }
-  releaseMemArray(&p->aMem[1], p->nMem);
+
+#ifdef SQLITE_DEBUG
+  /* Execute assert() statements to ensure that the Vdbe.apCsr[] and 
+  ** Vdbe.aMem[] arrays have already been cleaned up.  */
+  int i;
+  for(i=0; i<p->nCursor; i++){ assert( p->apCsr[i]==0 ); }
+  for(i=1; i<=p->nMem; i++){ assert( p->aMem[i].flags==MEM_Null ); }
+#endif
+
   if( p->contextStack ){
     sqlite3DbFree(db, p->contextStack);
   }
@@ -1993,7 +2117,6 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
 ** Delete an entire VDBE.
 */
 void sqlite3VdbeDelete(Vdbe *p){
-  int i;
   sqlite3 *db;
 
   if( NEVER(p==0) ) return;
@@ -2007,22 +2130,13 @@ void sqlite3VdbeDelete(Vdbe *p){
   if( p->pNext ){
     p->pNext->pPrev = p->pPrev;
   }
-  if( p->aOp ){
-    Op *pOp = p->aOp;
-    for(i=0; i<p->nOp; i++, pOp++){
-      freeP4(db, pOp->p4type, pOp->p4.p);
-#ifdef SQLITE_DEBUG
-      sqlite3DbFree(db, pOp->zComment);
-#endif     
-    }
-  }
   releaseMemArray(p->aVar, p->nVar);
-  sqlite3DbFree(db, p->aLabel);
   releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
+  vdbeFreeOpArray(db, p->aOp, p->nOp);
+  sqlite3DbFree(db, p->aLabel);
   sqlite3DbFree(db, p->aColName);
   sqlite3DbFree(db, p->zSql);
   p->magic = VDBE_MAGIC_DEAD;
-  sqlite3DbFree(db, p->aOp);
   sqlite3DbFree(db, p->pFree);
   sqlite3DbFree(db, p);
 }
index 71ce228c5e51ad129b5d3ae8515083a2fd3405d5..b17af8acf78c4b6aeaa6167b9715b930be53b7c9 100644 (file)
@@ -204,7 +204,7 @@ int sqlite3_blob_open(
       sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
       sqlite3VdbeChangeP2(v, 7, pTab->nCol);
       if( !db->mallocFailed ){
-        sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
+        sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0);
       }
     }
    
index 3daeebf2c60b38ae72343866c9d4417f0e0c99a0..0995a3b68b51e006cfde1b22e65c3997edc53cc2 100644 (file)
@@ -270,7 +270,7 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
 */
 void sqlite3VdbeMemReleaseExternal(Mem *p){
   assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
-  if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet) ){
+  if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){
     if( p->flags&MEM_Agg ){
       sqlite3VdbeMemFinalize(p, p->u.pDef);
       assert( (p->flags & MEM_Agg)==0 );
@@ -281,6 +281,9 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
       p->xDel = 0;
     }else if( p->flags&MEM_RowSet ){
       sqlite3RowSetClear(p->u.pRowSet);
+    }else if( p->flags&MEM_Frame ){
+      sqlite3VdbeFrameDelete(p->u.pFrame);
+      p->flags &= ~MEM_Frame;
     }
   }
 }
@@ -482,6 +485,9 @@ int sqlite3VdbeMemNumerify(Mem *pMem){
 ** Delete any previous value and set the value stored in *pMem to NULL.
 */
 void sqlite3VdbeMemSetNull(Mem *pMem){
+  if( pMem->flags & MEM_Frame ){
+    sqlite3VdbeFrameDelete(pMem->u.pFrame);
+  }
   if( pMem->flags & MEM_RowSet ){
     sqlite3RowSetClear(pMem->u.pRowSet);
   }
index e47495152d2e475d7b60cd6cd1b64a33ed9a2c47..12fa2f046272333c015d524fc4746b13466a4b0c 100644 (file)
@@ -943,18 +943,19 @@ FuncDef *sqlite3VtabOverloadFunction(
 void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
   int i, n;
   Table **apVtabLock;
+  Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse);
 
   assert( IsVirtual(pTab) );
-  for(i=0; i<pParse->nVtabLock; i++){
-    if( pTab==pParse->apVtabLock[i] ) return;
+  for(i=0; i<pRoot->nVtabLock; i++){
+    if( pTab==pRoot->apVtabLock[i] ) return;
   }
-  n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]);
-  apVtabLock = sqlite3_realloc(pParse->apVtabLock, n);
+  n = (pRoot->nVtabLock+1)*sizeof(pRoot->apVtabLock[0]);
+  apVtabLock = sqlite3_realloc(pRoot->apVtabLock, n);
   if( apVtabLock ){
-    pParse->apVtabLock = apVtabLock;
-    pParse->apVtabLock[pParse->nVtabLock++] = pTab;
+    pRoot->apVtabLock = apVtabLock;
+    pRoot->apVtabLock[pRoot->nVtabLock++] = pTab;
   }else{
-    pParse->db->mallocFailed = 1;
+    pRoot->db->mallocFailed = 1;
   }
 }
 
index 98ad34700284aa48bd5a93ca082742c269433151..cb31143c3f0689118041a55dcfcf0a302568a9fa 100644 (file)
@@ -317,6 +317,7 @@ do_test attach3-12.9 {
   db_list
 } {main temp {}}
 do_test attach3-12.10 {
+breakpoint
   execsql {
     DETACH ?
   }
index 9080a3997d1e0c43556017434169a0d7946f3b45..0db385c733e27ed7289970b7ec4e68082a195d80 100644 (file)
@@ -25,6 +25,8 @@ ifcapable {!autoinc} {
   return
 }
 
+sqlite3_db_config_lookaside db 0 0 0
+
 # The database is initially empty.
 #
 do_test autoinc-1.1 {
index b344beeccebf8acdd56659d1d65fd747808f06f4..622aff2de7e67c7ec57f5ae617efaaf30505af86 100644 (file)
@@ -63,84 +63,84 @@ do_test trigger1-1.1.3 {
   }
 } {1 {near "STATEMENT": syntax error}}
 execsql {
-       CREATE TRIGGER tr1 INSERT ON t1 BEGIN
-         INSERT INTO t1 values(1);
-       END;
+        CREATE TRIGGER tr1 INSERT ON t1 BEGIN
+          INSERT INTO t1 values(1);
+         END;
 }
 do_test trigger1-1.2.0 {
     catchsql {
-       CREATE TRIGGER IF NOT EXISTS tr1 DELETE ON t1 BEGIN
-           SELECT * FROM sqlite_master;
-       END
+        CREATE TRIGGER IF NOT EXISTS tr1 DELETE ON t1 BEGIN
+            SELECT * FROM sqlite_master;
+         END
      }
 } {0 {}}
 do_test trigger1-1.2.1 {
     catchsql {
-       CREATE TRIGGER tr1 DELETE ON t1 BEGIN
-           SELECT * FROM sqlite_master;
-       END
+        CREATE TRIGGER tr1 DELETE ON t1 BEGIN
+            SELECT * FROM sqlite_master;
+         END
      }
 } {1 {trigger tr1 already exists}}
 do_test trigger1-1.2.2 {
     catchsql {
-       CREATE TRIGGER "tr1" DELETE ON t1 BEGIN
-           SELECT * FROM sqlite_master;
-       END
+        CREATE TRIGGER "tr1" DELETE ON t1 BEGIN
+            SELECT * FROM sqlite_master;
+         END
      }
 } {1 {trigger "tr1" already exists}}
 do_test trigger1-1.2.3 {
     catchsql {
-       CREATE TRIGGER [tr1] DELETE ON t1 BEGIN
-           SELECT * FROM sqlite_master;
-       END
+        CREATE TRIGGER [tr1] DELETE ON t1 BEGIN
+            SELECT * FROM sqlite_master;
+         END
      }
 } {1 {trigger [tr1] already exists}}
 
 do_test trigger1-1.3 {
     catchsql {
-       BEGIN;
-       CREATE TRIGGER tr2 INSERT ON t1 BEGIN
-           SELECT * from sqlite_master; END;
+        BEGIN;
+        CREATE TRIGGER tr2 INSERT ON t1 BEGIN
+            SELECT * from sqlite_master; END;
         ROLLBACK;
-       CREATE TRIGGER tr2 INSERT ON t1 BEGIN
-           SELECT * from sqlite_master; END;
+        CREATE TRIGGER tr2 INSERT ON t1 BEGIN
+            SELECT * from sqlite_master; END;
     }
 } {0 {}}
 
 do_test trigger1-1.4 {
     catchsql {
-       DROP TRIGGER IF EXISTS tr1;
-       CREATE TRIGGER tr1 DELETE ON t1 BEGIN
-           SELECT * FROM sqlite_master;
-       END
+        DROP TRIGGER IF EXISTS tr1;
+        CREATE TRIGGER tr1 DELETE ON t1 BEGIN
+            SELECT * FROM sqlite_master;
+        END
     }
 } {0 {}}
 
 do_test trigger1-1.5 {
     execsql {
-       BEGIN;
-       DROP TRIGGER tr2;
-       ROLLBACK;
-       DROP TRIGGER tr2;
+        BEGIN;
+        DROP TRIGGER tr2;
+        ROLLBACK;
+        DROP TRIGGER tr2;
     }
 } {}
 
 do_test trigger1-1.6.1 {
     catchsql {
-       DROP TRIGGER IF EXISTS biggles;
+        DROP TRIGGER IF EXISTS biggles;
     }
 } {0 {}}
 
 do_test trigger1-1.6.2 {
     catchsql {
-       DROP TRIGGER biggles;
+        DROP TRIGGER biggles;
     }
 } {1 {no such trigger: biggles}}
 
 do_test trigger1-1.7 {
     catchsql {
-       DROP TABLE t1;
-       DROP TRIGGER tr1;
+        DROP TABLE t1;
+        DROP TRIGGER tr1;
     }
 } {1 {no such trigger: tr1}}
 
@@ -150,10 +150,10 @@ ifcapable tempdb {
   }
   do_test trigger1-1.8 {
     execsql {
-       CREATE TRIGGER temp_trig UPDATE ON temp_table BEGIN
-           SELECT * from sqlite_master;
-       END;
-       SELECT count(*) FROM sqlite_master WHERE name = 'temp_trig';
+          CREATE TRIGGER temp_trig UPDATE ON temp_table BEGIN
+              SELECT * from sqlite_master;
+          END;
+          SELECT count(*) FROM sqlite_master WHERE name = 'temp_trig';
     } 
   } {0}
 }
@@ -403,14 +403,14 @@ do_test trigger1-6.1 {
 do_test trigger1-6.2 {
   execsql {
     CREATE TRIGGER t2 BEFORE DELETE ON t2 BEGIN
-      SELECT RAISE(ABORT,'deletes are not allows');
+      SELECT RAISE(ABORT,'deletes are not permitted');
     END;
     SELECT type, name FROM sqlite_master;
   }
 } [concat $view_v1 {table t2 trigger t2}]
 do_test trigger1-6.3 {
   catchsql {DELETE FROM t2}
-} {1 {deletes are not allows}}
+} {1 {deletes are not permitted}}
 do_test trigger1-6.4 {
   execsql {SELECT * FROM t2}
 } {3 4 7 8}
index da292024bd0373fadad42e92526fabf2c16fdc43..3064ea601489cc50f32965e3b286a0fc2c52b5ce 100644 (file)
@@ -32,6 +32,7 @@ if {$tcl_platform(platform) == "symbian"} {
   set nStatement 1000
 }
 
+set nStatement 5
 do_test trigger8-1.1 {
   execsql {
     CREATE TABLE t1(x);