1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Changes to support recursive triggers.

FossilOrigin-Name: 9b9c19211593d5ff7b39254a29c284560a8bcedb
This commit is contained in:
dan
2009-08-28 18:53:45 +00:00
parent e408edac16
commit 165921a742
25 changed files with 889 additions and 581 deletions

View File

@@ -1,8 +1,5 @@
-----BEGIN PGP SIGNED MESSAGE----- C Changes\sto\ssupport\srecursive\striggers.
Hash: SHA1 D 2009-08-28T18:53:45
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
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in 73ddeec9dd10b85876c5c2ce1fdce627e1dcc7f8 F Makefile.in 73ddeec9dd10b85876c5c2ce1fdce627e1dcc7f8
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -101,7 +98,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c 8b42cace4f8e312de596807ba2685179da64fec4 F src/alter.c 55b601857d924e07c91cfa69e9b2cb5093498c93
F src/analyze.c 4584556d374fe3ab791be9be32ada7713fdced52 F src/analyze.c 4584556d374fe3ab791be9be32ada7713fdced52
F src/attach.c 13995348fc5a26cdd136a50806faf292aabc173f F src/attach.c 13995348fc5a26cdd136a50806faf292aabc173f
F src/auth.c 802a9439dfa0b8c208b10055cba400e82ef18025 F src/auth.c 802a9439dfa0b8c208b10055cba400e82ef18025
@@ -111,19 +108,19 @@ F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7
F src/btree.c 49212ddaee8d7d12b4f1e17b9de62f7ea91ca59d F src/btree.c 49212ddaee8d7d12b4f1e17b9de62f7ea91ca59d
F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe
F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705 F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705
F src/build.c a43c959c5953c25989207c929f99ef808d5336a6 F src/build.c 212476dc971756e7f7429e677059fafc678afbd5
F src/callback.c 9bc0ae998f15207d3115224979331c0aafc9bcc3 F src/callback.c 9bc0ae998f15207d3115224979331c0aafc9bcc3
F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3 F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3
F src/delete.c dcf07632d8ca3d4086df8b65ea907a47278e6382 F src/delete.c 0fa2c14bb3520590ef48bf214d4925cfb6e95e8c
F src/expr.c adb61b7de6bf9f51080195ed7dab9282a697ba24 F src/expr.c 24a8f8088d2af202852bac0a364614d20d5e663c
F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff
F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606 F src/func.c e536218d193b8d326aab91120bc4c6f28aa2b606
F src/global.c 271952d199a8cc59d4ce840b3bbbfd2f30c8ba32 F src/global.c 271952d199a8cc59d4ce840b3bbbfd2f30c8ba32
F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7 F src/hash.c ebcaa921ffd9d86f7ea5ae16a0a29d1c871130a7
F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1 F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1
F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb
F src/insert.c 95625f99f377a9ef264c289407173b722c7af6e8 F src/insert.c 5bddd3d65fceb671f56b000c202d07d17bed2775
F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0 F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6 F src/legacy.c 303b4ffcf1ae652fcf5ef635846c563c254564f6
F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d F src/lempar.c 0c4d1ab0a5ef2b0381eb81a732c54f68f27a574d
@@ -156,16 +153,16 @@ F src/pcache.c c92ffd4f3e1279b3766854c6d18b5bf4aac0d1fa
F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36 F src/pcache.h 435ef324197f79391f9c92b71d7f92b548ad7a36
F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c F src/pcache1.c 211295a9ff6a5b30f1ca50516731a5cf3e9bf82c
F src/pragma.c 9eb44ac1d3dc1ac3ea4f444abe1a10ae8acaa16c F src/pragma.c 9eb44ac1d3dc1ac3ea4f444abe1a10ae8acaa16c
F src/prepare.c 0b966d20979237121ec5fec449c9db45f6b9789a F src/prepare.c 49739b385c4cd0667cfa9941c41bf6d4f8edc157
F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1 F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1
F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
F src/resolve.c 4a61d03e49b15440878096e6030863fc628828f0 F src/resolve.c 92ef8a85d53b305a7de9faef27d652b96c2b4db6
F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f
F src/select.c 67b0778c9585905c8aa75aaa469e76ef3c1d315a F src/select.c 56ecb073e6f6696173ad80493aa14355225b6e53
F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb
F src/sqlite.h.in 3ccf717d82101f19548d0b1243f0a6f4854d51ee F src/sqlite.h.in 3ccf717d82101f19548d0b1243f0a6f4854d51ee
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
F src/sqliteInt.h 20ab1da1a9a652ea673e5bc586382143914381c0 F src/sqliteInt.h ff4ae0702b0d816395030f9170151954f26830a7
F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
@@ -202,19 +199,19 @@ F src/test_tclvar.c 9e42fa59d3d2f064b7ab8628e7ab2dc8a9fe93d4
F src/test_thread.c b8a1ab7ca1a632f18e8a361880d5d65eeea08eac F src/test_thread.c b8a1ab7ca1a632f18e8a361880d5d65eeea08eac
F src/test_wsd.c 3ae5101de6cbfda2720152ab659ea84079719241 F src/test_wsd.c 3ae5101de6cbfda2720152ab659ea84079719241
F src/tokenize.c af8a56e6a50c5042fc305bfa796275e9bf26ff2b F src/tokenize.c af8a56e6a50c5042fc305bfa796275e9bf26ff2b
F src/trigger.c 545f3b8354001577a82c44fc6c4beac598b45b86 F src/trigger.c 135c1fc78ada9d5a5d8ffee412263547b1a93ca9
F src/update.c 4da327f706c0d0dfedf4d92154b1b5688bdea0ac F src/update.c cddbcabb2fccc146b8712f5b97e636c413c34906
F src/utf.c 3ca2c9461b8e942c68da28bfccd448663f536a6f F src/utf.c 3ca2c9461b8e942c68da28bfccd448663f536a6f
F src/util.c efb5f8e533d4beef545cf765cab5f7920b4c75f9 F src/util.c efb5f8e533d4beef545cf765cab5f7920b4c75f9
F src/vacuum.c 3fe0eebea6d2311c1c2ab2962887d11f7a4dcfb0 F src/vacuum.c 3fe0eebea6d2311c1c2ab2962887d11f7a4dcfb0
F src/vdbe.c 464e2e30b1287554a23cdaa0b6b010a9dcb5eb29 F src/vdbe.c e45309524c11866f2d158b3578aecfe6d9af0676
F src/vdbe.h 457b6c70f02885cec1f5225b5e6441d067b55d3f F src/vdbe.h 080fe6bc1264438becb8bf9b9f3c84074c336b78
F src/vdbeInt.h 831c254a6eef237ef4664c8381a0137586567007 F src/vdbeInt.h 9b62f7053dc1255e9c5dec16f35b5fd079ba7ed4
F src/vdbeapi.c 0ab8ada7260b32031ca97f338caecf0812460624 F src/vdbeapi.c 8d5013ab6104be757c208a70ffb191cc27d2b688
F src/vdbeaux.c 4956536a636468fd07284028c39aab65ea99777e F src/vdbeaux.c cf440244489accee8ddeb85c24b239659c31de1b
F src/vdbeblob.c a3f3e0e877fc64ea50165eec2855f5ada4477611 F src/vdbeblob.c f93cb60ac388633ed3bde8a94ef161ad2dbfb689
F src/vdbemem.c c4a5188ff43692f2ca78d3539ad4877e14b70712 F src/vdbemem.c dc551981833756ea34a3e0b238f759479e7cf526
F src/vtab.c aedd76e8670d5a5379f93804398d3ba960125547 F src/vtab.c 10df5c77cea34a49f2ad4e5de763f820d6223eb4
F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04 F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
F src/where.c b9ad2d2db4a7d1cda7bed8a7299eb73fde63b5b1 F src/where.c b9ad2d2db4a7d1cda7bed8a7299eb73fde63b5b1
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -234,12 +231,12 @@ F test/async4.test aafa6328c559d3e4bb587de770cbdecfca06f0da
F test/async5.test f3592d79c84d6e83a5f50d3fd500445f7d97dfdf F test/async5.test f3592d79c84d6e83a5f50d3fd500445f7d97dfdf
F test/attach.test 1d1be27b9e4c654f9bb14d011a4a87753c0b197a F test/attach.test 1d1be27b9e4c654f9bb14d011a4a87753c0b197a
F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437 F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437
F test/attach3.test 7b92dc8e40c1ebca9732ca6f2d3fefbd46f196df F test/attach3.test 512521ba94372dfd0efb07d04b2cdefc8c929e66
F test/attachmalloc.test cf8cf17d183de357b1147a9baacbdfc85b940b61 F test/attachmalloc.test cf8cf17d183de357b1147a9baacbdfc85b940b61
F test/auth.test 8e9a21d7321c9ad20d26f630acc02e15f2f2a3b6 F test/auth.test 8e9a21d7321c9ad20d26f630acc02e15f2f2a3b6
F test/auth2.test ee3ba272e2b975e913afc9b041ee75706e190005 F test/auth2.test ee3ba272e2b975e913afc9b041ee75706e190005
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5 F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
F test/autoinc.test 71bc5183c93ed5e2b8b3a71c218d777b55e4fffc F test/autoinc.test f1959df99bca67066db8ff816ae89b49f1f42e00
F test/autovacuum.test 25f891bc343a8bf5d9229e2e9ddab9f31a9ab5ec F test/autovacuum.test 25f891bc343a8bf5d9229e2e9ddab9f31a9ab5ec
F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6 F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6
F test/avtrans.test 1e901d8102706b63534dbd2bdd4d8f16c4082650 F test/avtrans.test 1e901d8102706b63534dbd2bdd4d8f16c4082650
@@ -672,14 +669,14 @@ F test/trace.test 19ffbc09885c3321d56358a5738feae8587fb377
F test/trans.test d887cb07630dc39879a322d958ad8b006137485c F test/trans.test d887cb07630dc39879a322d958ad8b006137485c
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22 F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97 F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97
F test/trigger1.test 4ecf469e6f46610e0864f52b29d0ea162a2767c1 F test/trigger1.test 2e18561f85e448bb633c9c9de792e9bbf7b2dd3e
F test/trigger2.test 53cf2209f68eb5bbb0ea0a07d694007fa702f1ed F test/trigger2.test 53cf2209f68eb5bbb0ea0a07d694007fa702f1ed
F test/trigger3.test 501b8489eb6b9cb5b005f60b071583c01a3c3041 F test/trigger3.test 501b8489eb6b9cb5b005f60b071583c01a3c3041
F test/trigger4.test 8e90ee98cba940cd5f96493f82e55083806ab8a0 F test/trigger4.test 8e90ee98cba940cd5f96493f82e55083806ab8a0
F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83 F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9 F test/trigger6.test 0e411654f122552da6590f0b4e6f781048a4a9b9
F test/trigger7.test 72feaf8dbc52cea84de0c3e6ce7559ff19c479af F test/trigger7.test 72feaf8dbc52cea84de0c3e6ce7559ff19c479af
F test/trigger8.test 83d92c212f36442d26527d6f7701575905a52ae1 F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
F test/trigger9.test e6e8dbab673666b3c0a63f0fefcff2329fe6bba8 F test/trigger9.test e6e8dbab673666b3c0a63f0fefcff2329fe6bba8
F test/triggerA.test 0718ad2d9bfef27c7af00e636df79bee6b988da7 F test/triggerA.test 0718ad2d9bfef27c7af00e636df79bee6b988da7
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
@@ -750,14 +747,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746 F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
P 9b4d9ab62d687289837b13b07885e72cc3abe8a9 P dee1b8eb402f47c6d5ee60aac28f8e3dcf98167f
R 8c0a86d1dda3ad47818cafa2a01a76cc R ef22ff6ef84d7c63fbbf7ca259ee2aa2
U drh U dan
Z a8886aa9c2f39a29c2eeac74183b7a19 Z 871f833b67d466cf35f851400879eb8b
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFKke5hoxKgR168RlERAlsiAJ99uio4dN8iQWmrdPv34d66O3Nb/wCdEt6E
0TzPUVqtgGFOj3LSxkTm7fQ=
=7r/8
-----END PGP SIGNATURE-----

View File

@@ -1 +1 @@
dee1b8eb402f47c6d5ee60aac28f8e3dcf98167f 9b9c19211593d5ff7b39254a29c284560a8bcedb

View File

@@ -196,10 +196,10 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){
for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
if( pTrig->pSchema==pTempSchema ){ if( pTrig->pSchema==pTempSchema ){
if( !zWhere ){ if( !zWhere ){
zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name); zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->zName);
}else{ }else{
tmp = zWhere; 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); 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){ for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){
int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
assert( iTrigDb==iDb || iTrigDb==1 ); 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 #endif

View File

@@ -69,6 +69,10 @@ void sqlite3TableLock(
TableLock *p; TableLock *p;
assert( iDb>=0 ); assert( iDb>=0 );
if( pParse->pRoot ){
pParse = pParse->pRoot;
}
for(i=0; i<pParse->nTableLock; i++){ for(i=0; i<pParse->nTableLock; i++){
p = &pParse->aTableLock[i]; p = &pParse->aTableLock[i];
if( p->iDb==iDb && p->iTab==iTab ){ if( p->iDb==iDb && p->iTab==iTab ){
@@ -194,7 +198,7 @@ void sqlite3FinishCoding(Parse *pParse){
#endif #endif
assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem, sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem,
pParse->nTab, pParse->explain); pParse->nTab, pParse->nArg, pParse->explain);
pParse->rc = SQLITE_DONE; pParse->rc = SQLITE_DONE;
pParse->colNamesSet = 0; pParse->colNamesSet = 0;
}else if( pParse->rc==SQLITE_OK ){ }else if( pParse->rc==SQLITE_OK ){
@@ -3425,23 +3429,27 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
sqlite3 *db; sqlite3 *db;
Vdbe *v; Vdbe *v;
int mask; int mask;
Parse *pRoot = pParse->pRoot; /* Root parse structure */
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v==0 ) return; /* This only happens if there was a prior error */ if( v==0 ) return; /* This only happens if there was a prior error */
db = pParse->db; db = pParse->db;
if( pParse->cookieGoto==0 ){ if( pParse->cookieGoto==0 && pRoot==0 ){
pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1;
} }
if( iDb>=0 ){ if( iDb>=0 ){
if( pRoot==0 ){
pRoot = pParse;
}
assert( iDb<db->nDb ); assert( iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( db->aDb[iDb].pBt!=0 || iDb==1 );
assert( iDb<SQLITE_MAX_ATTACHED+2 ); assert( iDb<SQLITE_MAX_ATTACHED+2 );
mask = 1<<iDb; mask = 1<<iDb;
if( (pParse->cookieMask & mask)==0 ){ if( (pRoot->cookieMask & mask)==0 ){
pParse->cookieMask |= mask; pRoot->cookieMask |= mask;
pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; pRoot->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
if( !OMIT_TEMPDB && iDb==1 ){ 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. ** necessary to undo a write and the checkpoint should not be set.
*/ */
void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
Parse *pRoot = pParse->pRoot;
sqlite3CodeVerifySchema(pParse, iDb); sqlite3CodeVerifySchema(pParse, iDb);
pParse->writeMask |= 1<<iDb; if( pRoot==0 ){
if( setStatement && pParse->nested==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 /* Every place where this routine is called with setStatement!=0 has
** already successfully created a VDBE. */ ** already successfully created a VDBE. */
assert( pParse->pVdbe ); assert( pParse->pVdbe );

View File

@@ -228,7 +228,6 @@ void sqlite3DeleteFrom(
int iCur; /* VDBE Cursor number for pTab */ int iCur; /* VDBE Cursor number for pTab */
sqlite3 *db; /* Main database structure */ sqlite3 *db; /* Main database structure */
AuthContext sContext; /* Authorization context */ AuthContext sContext; /* Authorization context */
int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
NameContext sNC; /* Name context to resolve expressions in */ NameContext sNC; /* Name context to resolve expressions in */
int iDb; /* Database number */ int iDb; /* Database number */
int memCnt = -1; /* Memory cell used for change counting */ 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 */ int isView; /* True if attempting to delete from a view */
Trigger *pTrigger; /* List of table triggers, if required */ Trigger *pTrigger; /* List of table triggers, if required */
#endif #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)); memset(&sContext, 0, sizeof(sContext));
db = pParse->db; db = pParse->db;
@@ -293,12 +287,6 @@ void sqlite3DeleteFrom(
} }
assert(!isView || pTrigger); 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. /* Assign cursor number to the table and all its indices.
*/ */
assert( pTabList->nSrc==1 ); assert( pTabList->nSrc==1 );
@@ -322,24 +310,6 @@ void sqlite3DeleteFrom(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb); 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 /* If we are trying to delete from a view, realize that view into
** a ephemeral table. ** a ephemeral table.
*/ */
@@ -385,8 +355,9 @@ void sqlite3DeleteFrom(
** the table and pick which records to delete. ** 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 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 */ int regRowid; /* Actual register containing rowids */
/* Collect rowids of every row to be deleted. /* Collect rowids of every row to be deleted.
@@ -401,35 +372,31 @@ void sqlite3DeleteFrom(
} }
sqlite3WhereEnd(pWInfo); 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 /* Delete every item whose key was written to the list during the
** database scan. We have to delete items after the scan is complete ** 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); 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 ){ if( !isView ){
/* Open cursors for the table we are deleting from and
** all its indices.
*/
sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); 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); 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 ){ 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 /* If the record is no longer present in the table, jump to the
** next iteration of the loop through the contents of the fifo. ** next iteration of the loop through the contents of the fifo.
@@ -437,16 +404,16 @@ void sqlite3DeleteFrom(
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid);
/* Populate the OLD.* pseudo-table */ /* Populate the OLD.* pseudo-table */
if( old_col_mask ){ assert( regOld==iRowid+1 );
sqlite3VdbeAddOp2(v, OP_RowData, iCur, iData); for(i=0; i<pTab->nCol; i++){
}else{ if( mask==0xffffffff || mask&(1<<i) ){
sqlite3VdbeAddOp2(v, OP_Null, 0, iData); sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
}
} }
sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, iData, iRowid);
/* Jump back and run the BEFORE triggers */ sqlite3CodeRowTrigger(pParse, pTrigger,
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger); TK_DELETE, 0, TRIGGER_BEFORE, pTab, -1, regOld, OE_Default, addr
sqlite3VdbeJumpHere(v, iEndBeforeTrigger); );
} }
if( !isView ){ if( !isView ){
@@ -463,20 +430,16 @@ void sqlite3DeleteFrom(
} }
} }
/* If there are row triggers, close all cursors then invoke /* Code the AFTER triggers. This is a no-op if there are no triggers. */
** the AFTER triggers sqlite3CodeRowTrigger(pParse,
*/ pTrigger, TK_DELETE, 0, TRIGGER_AFTER, pTab, -1, regOld, OE_Default, addr
if( pTrigger ){ );
/* Jump back and run the AFTER triggers */
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
sqlite3VdbeJumpHere(v, iEndAfterTrigger);
}
/* End of the delete loop */ /* End of the delete loop */
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
sqlite3VdbeResolveLabel(v, end); sqlite3VdbeResolveLabel(v, end);
/* Close the cursors after the loop if there are no row triggers */ /* Close the cursors open on the table and its indexes. */
if( !isView && !IsVirtual(pTab) ){ if( !isView && !IsVirtual(pTab) ){
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum); sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
@@ -489,7 +452,7 @@ void sqlite3DeleteFrom(
** maximum rowid counter values recorded while inserting into ** maximum rowid counter values recorded while inserting into
** autoincrement tables. ** autoincrement tables.
*/ */
if( pParse->nested==0 && pParse->trigStack==0 ){ if( pParse->nested==0 && pParse->pTriggerTab==0 ){
sqlite3AutoincrementEnd(pParse); sqlite3AutoincrementEnd(pParse);
} }
@@ -498,7 +461,7 @@ void sqlite3DeleteFrom(
** generating code because of a call to sqlite3NestedParse(), do not ** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function. ** 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); sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);

View File

@@ -1521,7 +1521,7 @@ void sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once ** 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. ** 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; int mem = ++pParse->nMem;
sqlite3VdbeAddOp1(v, OP_If, mem); sqlite3VdbeAddOp1(v, OP_If, mem);
testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem);
@@ -2556,6 +2556,17 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
break; 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: ** Form A:
** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** 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 #ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: { case TK_RAISE: {
if( !pParse->trigStack ){ int vrc;
if( !pParse->pTriggerTab ){
sqlite3ErrorMsg(pParse, sqlite3ErrorMsg(pParse,
"RAISE() may only be used within a trigger-program"); "RAISE() may only be used within a trigger-program");
return 0; return 0;
} }
if( pExpr->affinity!=OE_Ignore ){ assert( pExpr->affinity==OE_Rollback
assert( pExpr->affinity==OE_Rollback || || pExpr->affinity==OE_Abort
pExpr->affinity == OE_Abort || || pExpr->affinity==OE_Fail
pExpr->affinity == OE_Fail ); || pExpr->affinity==OE_Ignore
);
assert( !ExprHasProperty(pExpr, EP_IntValue) ); assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0, vrc = (pExpr->affinity==OE_Ignore ? SQLITE_OK : SQLITE_CONSTRAINT);
pExpr->u.zToken, 0); sqlite3VdbeAddOp4(v, OP_Halt, vrc, 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)"));
}
break; break;
} }
#endif #endif

View File

@@ -536,11 +536,6 @@ void sqlite3Insert(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb); 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 #ifndef SQLITE_OMIT_XFER_OPT
/* If the statement is of the form /* If the statement is of the form
** **
@@ -745,12 +740,6 @@ void sqlite3Insert(
keyColumn = pTab->iPKey; 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 /* Initialize the count of rows to be inserted
*/ */
if( db->flags & SQLITE_CountRows ){ if( db->flags & SQLITE_CountRows ){
@@ -818,7 +807,6 @@ void sqlite3Insert(
if( tmask & TRIGGER_BEFORE ){ if( tmask & TRIGGER_BEFORE ){
int regTrigRowid; int regTrigRowid;
int regCols; int regCols;
int regRec;
/* build the NEW.* reference row. Note that if there is an INTEGER /* 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 ** 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, /* Cannot have triggers on a virtual table. If it were possible,
** this block would have to account for hidden column. ** this block would have to account for hidden column.
*/ */
assert(!IsVirtual(pTab)); assert( !IsVirtual(pTab) );
/* Create the new column data /* Create the new column data
*/ */
@@ -868,8 +856,6 @@ void sqlite3Insert(
sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i); 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, /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
** do not attempt any conversions before assembling the record. ** do not attempt any conversions before assembling the record.
@@ -877,18 +863,16 @@ void sqlite3Insert(
** table column affinities. ** table column affinities.
*/ */
if( !isView ){ if( !isView ){
sqlite3VdbeAddOp2(v, OP_Affinity, regCols, pTab->nCol);
sqlite3TableAffinityStr(v, pTab); 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 */ /* Fire BEFORE or INSTEAD OF triggers */
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE,
pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ pTab, regCols, -1, onError, endOfLoop);
goto insert_cleanup;
} sqlite3ReleaseTempReg(pParse, regTrigRowid);
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol);
} }
/* Push the record number for the new entry onto the stack. The /* Push the record number for the new entry onto the stack. The
@@ -1009,10 +993,8 @@ void sqlite3Insert(
if( pTrigger ){ if( pTrigger ){
/* Code AFTER triggers */ /* Code AFTER triggers */
if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER,
pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ pTab, regData, -1, onError, endOfLoop);
goto insert_cleanup;
}
} }
/* The bottom of the main insertion loop, if the data source /* 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 ** maximum rowid counter values recorded while inserting into
** autoincrement tables. ** autoincrement tables.
*/ */
if( pParse->nested==0 && pParse->trigStack==0 ){ if( pParse->nested==0 && pParse->pTriggerTab==0 ){
sqlite3AutoincrementEnd(pParse); sqlite3AutoincrementEnd(pParse);
} }
@@ -1050,7 +1032,7 @@ insert_end:
** generating code because of a call to sqlite3NestedParse(), do not ** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function. ** 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); sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
@@ -1423,11 +1405,6 @@ void sqlite3CompleteInsertion(
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
sqlite3TableAffinityStr(v, pTab); sqlite3TableAffinityStr(v, pTab);
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol); sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
#ifndef SQLITE_OMIT_TRIGGER
if( newIdx>=0 ){
sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid);
}
#endif
if( pParse->nested ){ if( pParse->nested ){
pik_flags = 0; pik_flags = 0;
}else{ }else{

View File

@@ -676,6 +676,13 @@ static int sqlite3Prepare(
sqlite3Error(db, rc, 0); 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: end_prepare:
sqlite3StackFree(db, pParse); sqlite3StackFree(db, pParse);

View File

@@ -137,6 +137,7 @@ static int lookupName(
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */ NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */ 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( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z 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 /* If we have not already resolved the name, then maybe
** it is a new.* or old.* trigger argument reference ** it is a new.* or old.* trigger argument reference
*/ */
if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){ if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
TriggerStack *pTriggerStack = pParse->trigStack;
Table *pTab = 0; Table *pTab = 0;
u32 *piColMask = 0; u32 *piColMask = 0;
if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){ if( pParse->triggerOp!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = pTriggerStack->newIdx; pExpr->iTable = 1;
assert( pTriggerStack->pTab ); pTab = pParse->pTriggerTab;
pTab = pTriggerStack->pTab; piColMask = &(pParse->newmask);
piColMask = &(pTriggerStack->newColMask); }else if( pParse->triggerOp!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
}else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){ pExpr->iTable = 0;
pExpr->iTable = pTriggerStack->oldIdx; pTab = pParse->pTriggerTab;
assert( pTriggerStack->pTab ); piColMask = &(pParse->oldmask);
pTab = pTriggerStack->pTab;
piColMask = &(pTriggerStack->oldColMask);
} }
if( pTab ){ if( pTab ){
int iCol; int iCol;
Column *pCol = pTab->aCol;
pSchema = pTab->pSchema; pSchema = pTab->pSchema;
cntTab++; 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 ){ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
cnt++; cnt++;
pExpr->iColumn = iCol==pTab->iPKey ? -1 : (i16)iCol; pExpr->iColumn = iCol==pTab->iPKey ? -1 : (i16)iCol;
pExpr->pTab = pTab;
testcase( iCol==31 ); testcase( iCol==31 );
testcase( iCol==32 ); testcase( iCol==32 );
if( iCol>=32 ){ if( iCol>=32 ){
@@ -382,6 +379,10 @@ lookupname_end:
if( pTopNC==pNC ) break; if( pTopNC==pNC ) break;
pTopNC = pTopNC->pNext; pTopNC = pTopNC->pNext;
} }
if( isTrigger ){
pExpr->pTab = pParse->pTriggerTab;
pExpr->op = TK_TRIGGER;
}
return WRC_Prune; return WRC_Prune;
} else { } else {
return WRC_Abort; return WRC_Abort;

View File

@@ -2733,9 +2733,10 @@ static int flattenSubquery(
*/ */
if( ALWAYS(pSubitem->pTab!=0) ){ if( ALWAYS(pSubitem->pTab!=0) ){
Table *pTabToDel = pSubitem->pTab; Table *pTabToDel = pSubitem->pTab;
Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse);
if( pTabToDel->nRef==1 ){ if( pTabToDel->nRef==1 ){
pTabToDel->pNextZombie = pParse->pZombieTab; pTabToDel->pNextZombie = pRoot->pZombieTab;
pParse->pZombieTab = pTabToDel; pRoot->pZombieTab = pTabToDel;
}else{ }else{
pTabToDel->nRef--; pTabToDel->nRef--;
} }

View File

@@ -2015,6 +2015,16 @@ struct AutoincInfo {
# define SQLITE_N_COLCACHE 10 # define SQLITE_N_COLCACHE 10
#endif #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 ** 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 ** 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 */ int regRoot; /* Register holding root page number for new objects */
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ 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 /* Above is constant between recursions. Below is reset before and after
** each recursion */ ** each recursion */
@@ -2092,7 +2111,9 @@ struct Parse {
const char *zTail; /* All SQL text past the last semicolon parsed */ const char *zTail; /* All SQL text past the last semicolon parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */ Table *pNewTable; /* A table being constructed by CREATE TABLE */
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
#if 0
TriggerStack *trigStack; /* Trigger actions being coded */ TriggerStack *trigStack; /* Trigger actions being coded */
#endif
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
Token sArg; /* Complete text of a module argument */ Token sArg; /* Complete text of a module argument */
@@ -2102,6 +2123,7 @@ struct Parse {
#endif #endif
int nHeight; /* Expression tree height of current sub-select */ int nHeight; /* Expression tree height of current sub-select */
Table *pZombieTab; /* List of Table objects to delete after code gen */ Table *pZombieTab; /* List of Table objects to delete after code gen */
CodedTrigger *pCodedTrigger; /* Linked list of coded triggers */
}; };
#ifdef SQLITE_OMIT_VIRTUALTABLE #ifdef SQLITE_OMIT_VIRTUALTABLE
@@ -2144,7 +2166,7 @@ struct AuthContext {
* containing the SQL statements specified as the trigger program. * containing the SQL statements specified as the trigger program.
*/ */
struct Trigger { 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 */ char *table; /* The table or view to which the trigger applies */
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
@@ -2687,8 +2709,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
void sqlite3DropTriggerPtr(Parse*, Trigger*); void sqlite3DropTriggerPtr(Parse*, Trigger*);
Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask); Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask);
Trigger *sqlite3TriggerList(Parse *, Table *); Trigger *sqlite3TriggerList(Parse *, Table *);
int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *, void sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *,
int, int, int, int, u32*, u32*); int, int, int, int);
void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
@@ -2698,12 +2720,13 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*); TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
void sqlite3DeleteTrigger(sqlite3*, Trigger*); void sqlite3DeleteTrigger(sqlite3*, Trigger*);
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
void sqlite3TriggerUses(Parse*,Trigger*,int,ExprList*,Table*,int,u32*,u32*);
#else #else
# define sqlite3TriggersExist(B,C,D,E,F) 0 # define sqlite3TriggersExist(B,C,D,E,F) 0
# define sqlite3DeleteTrigger(A,B) # define sqlite3DeleteTrigger(A,B)
# define sqlite3DropTriggerPtr(A,B) # define sqlite3DropTriggerPtr(A,B)
# define sqlite3UnlinkAndDeleteTrigger(A,B,C) # 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 # define sqlite3TriggerList(X, Y) 0
#endif #endif

View File

@@ -219,7 +219,7 @@ void sqlite3BeginTrigger(
/* Build the Trigger object */ /* Build the Trigger object */
pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger)); pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
if( pTrigger==0 ) goto trigger_cleanup; if( pTrigger==0 ) goto trigger_cleanup;
pTrigger->name = zName; pTrigger->zName = zName;
zName = 0; zName = 0;
pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName); pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
pTrigger->pSchema = db->aDb[iDb].pSchema; pTrigger->pSchema = db->aDb[iDb].pSchema;
@@ -262,14 +262,14 @@ void sqlite3FinishTrigger(
pTrig = pParse->pNewTrigger; pTrig = pParse->pNewTrigger;
pParse->pNewTrigger = 0; pParse->pNewTrigger = 0;
if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup;
zName = pTrig->name; zName = pTrig->zName;
iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema);
pTrig->step_list = pStepList; pTrig->step_list = pStepList;
while( pStepList ){ while( pStepList ){
pStepList->pTrig = pTrig; pStepList->pTrig = pTrig;
pStepList = pStepList->pNext; pStepList = pStepList->pNext;
} }
nameToken.z = pTrig->name; nameToken.z = pTrig->zName;
nameToken.n = sqlite3Strlen30(nameToken.z); nameToken.n = sqlite3Strlen30(nameToken.z);
if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken) if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken)
&& sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){ && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
@@ -451,7 +451,7 @@ TriggerStep *sqlite3TriggerDeleteStep(
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){ void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
if( pTrigger==0 ) return; if( pTrigger==0 ) return;
sqlite3DeleteTriggerStep(db, pTrigger->step_list); sqlite3DeleteTriggerStep(db, pTrigger->step_list);
sqlite3DbFree(db, pTrigger->name); sqlite3DbFree(db, pTrigger->zName);
sqlite3DbFree(db, pTrigger->table); sqlite3DbFree(db, pTrigger->table);
sqlite3ExprDelete(db, pTrigger->pWhen); sqlite3ExprDelete(db, pTrigger->pWhen);
sqlite3IdListDelete(db, pTrigger->pColumns); sqlite3IdListDelete(db, pTrigger->pColumns);
@@ -558,11 +558,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3OpenMasterTable(pParse, iDb); sqlite3OpenMasterTable(pParse, iDb);
base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); 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); sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
sqlite3ChangeCookie(pParse, iDb); sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_Close, 0, 0); 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 ){ if( pParse->nMem<3 ){
pParse->nMem = 3; pParse->nMem = 3;
} }
@@ -674,70 +674,226 @@ static int codeTriggerProgram(
TriggerStep *pStepList, /* List of statements inside the trigger body */ TriggerStep *pStepList, /* List of statements inside the trigger body */
int orconfin /* Conflict algorithm. (OE_Abort, etc) */ int orconfin /* Conflict algorithm. (OE_Abort, etc) */
){ ){
TriggerStep * pTriggerStep = pStepList; TriggerStep * pStep = pStepList;
int orconf;
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
assert( pTriggerStep!=0 ); assert( pParse->pRoot );
assert( pStep!=0 );
assert( v!=0 ); assert( v!=0 );
sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0); /* sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0); */
VdbeComment((v, "begin trigger %s", pStepList->pTrig->name)); while( pStep ){
while( pTriggerStep ){ /* Figure out the ON CONFLICT policy that will be used for this step
sqlite3ExprCacheClear(pParse); ** of the trigger program. If the statement that caused this trigger
orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use
pParse->trigStack->orconf = orconf; ** the ON CONFLICT policy that was specified as part of the trigger
switch( pTriggerStep->op ){ ** 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: { case TK_UPDATE: {
SrcList *pSrc; sqlite3Update(pParse,
pSrc = targetSrcList(pParse, pTriggerStep); targetSrcList(pParse, pStep),
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3Update(pParse, pSrc, sqlite3ExprDup(db, pStep->pWhere, 0),
sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), pParse->orconf
sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf); );
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break; break;
} }
case TK_INSERT: { case TK_INSERT: {
SrcList *pSrc; sqlite3Insert(pParse,
pSrc = targetSrcList(pParse, pTriggerStep); targetSrcList(pParse, pStep),
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3Insert(pParse, pSrc, sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), sqlite3IdListDup(db, pStep->pIdList),
sqlite3SelectDup(db, pTriggerStep->pSelect, 0), pParse->orconf
sqlite3IdListDup(db, pTriggerStep->pIdList), orconf); );
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break; break;
} }
case TK_DELETE: { case TK_DELETE: {
SrcList *pSrc; sqlite3DeleteFrom(pParse,
sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); targetSrcList(pParse, pStep),
pSrc = targetSrcList(pParse, pTriggerStep); sqlite3ExprDup(db, pStep->pWhere, 0)
sqlite3DeleteFrom(pParse, pSrc, );
sqlite3ExprDup(db, pTriggerStep->pWhere, 0));
sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0);
break; break;
} }
default: assert( pTriggerStep->op==TK_SELECT ); { default: assert( pStep->op==TK_SELECT ); {
Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0); SelectDest sDest;
if( ss ){ Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0);
SelectDest dest; sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
sqlite3Select(pParse, pSelect, &sDest);
sqlite3SelectDestInit(&dest, SRT_Discard, 0); sqlite3SelectDelete(db, pSelect);
sqlite3Select(pParse, ss, &dest);
sqlite3SelectDelete(db, ss);
}
break; break;
} }
} }
pTriggerStep = pTriggerStep->pNext; pStep = pStep->pNext;
} }
sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); /* sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); */
VdbeComment((v, "end trigger %s", pStepList->pTrig->name));
return 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. ** 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. ** output mask is set to the special value 0xffffffff.
** **
*/ */
int sqlite3CodeRowTrigger( void sqlite3CodeRowTrigger(
Parse *pParse, /* Parse context */ Parse *pParse, /* Parse context */
Trigger *pTrigger, /* List of triggers on table pTab */ Trigger *pTrigger, /* List of triggers on table pTab */
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ 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 newIdx, /* The indice of the "new" row to access */
int oldIdx, /* The indice of the "old" row to access */ int oldIdx, /* The indice of the "old" row to access */
int orconf, /* ON CONFLICT policy */ int orconf, /* ON CONFLICT policy */
int ignoreJump, /* Instruction to jump to for RAISE(IGNORE) */ 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 */
){ ){
Trigger *p; 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(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER );
assert(newIdx != -1 || oldIdx != -1);
for(p=pTrigger; p; p=p->pNext){ for(p=pTrigger; p; p=p->pNext){
int fire_this = 0;
/* Sanity checking: The schema for the trigger and for the table are /* 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 ** always defined. The trigger must be in the same schema as the table
** or else it must be a TEMP trigger. */ ** or else it must be a TEMP trigger. */
assert( p->pSchema!=0 ); assert( p->pSchema!=0 );
assert( p->pTabSchema!=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 */ /* Determine whether we should code this trigger */
if( if( p->op==op
p->op==op && && p->tr_tm==tr_tm
p->tr_tm==tr_tm && && checkColumnOverlap(p->pColumns,pChanges)
checkColumnOverlap(p->pColumns,pChanges)
){ ){
TriggerStack *pS; /* Pointer to trigger-stack entry */ Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */
for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){} CodedTrigger *pC;
if( !pS ){ pC = getRowTrigger(pParse, p, op, pTab, orconf);
fire_this = 1; assert( pC || pParse->nErr || pParse->db->mallocFailed );
}
#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 ){ /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program
int endTrigger; ** is a pointer to the sub-vdbe containing the trigger program. */
Expr * whenExpr; if( pC ){
AuthContext sContext; sqlite3VdbeAddOp3(v, OP_Program, oldIdx, newIdx, ++pParse->nMem);
NameContext sNC; pC->pProgram->nRef++;
sqlite3VdbeChangeP4(v, -1, (const char *)pC->pProgram, P4_SUBPROGRAM);
#ifndef SQLITE_OMIT_TRACE VdbeComment((v, "Call trigger: %s (%s)", p->zName,onErrorText(orconf)));
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);
sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger);
} }
} }
if( piOldColMask ) *piOldColMask |= trigStackEntry.oldColMask;
if( piNewColMask ) *piNewColMask |= trigStackEntry.newColMask;
return 0;
} }
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);
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;
}
}
}
}
#endif /* !defined(SQLITE_OMIT_TRIGGER) */ #endif /* !defined(SQLITE_OMIT_TRIGGER) */

View File

@@ -120,21 +120,15 @@ void sqlite3Update(
int isView; /* Trying to update a view */ int isView; /* Trying to update a view */
Trigger *pTrigger; /* List of triggers on pTab, if required */ Trigger *pTrigger; /* List of triggers on pTab, if required */
#endif #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 old_col_mask = 0; /* Mask of OLD.* columns in use */
u32 new_col_mask = 0; /* Mask of NEW.* 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 */ /* Register Allocations */
int regRowCount = 0; /* A count of rows changed */ int regRowCount = 0; /* A count of rows changed */
int regOldRowid; /* The old rowid */ int regOldRowid; /* The old rowid */
int regNewRowid; /* The new 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 */ int regRowSet = 0; /* Rowset of rows to be updated */
memset(&sContext, 0, sizeof(sContext)); memset(&sContext, 0, sizeof(sContext));
@@ -175,14 +169,6 @@ void sqlite3Update(
if( aXRef==0 ) goto update_cleanup; if( aXRef==0 ) goto update_cleanup;
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1; 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. /* 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 ** 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 ** need to occur right after the database cursor. So go ahead and
@@ -268,24 +254,7 @@ void sqlite3Update(
aRegIdx[j] = reg; aRegIdx[j] = reg;
} }
/* Allocate a block of register used to store the change record /* Begin generating code. */
** 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.
*/
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
if( v==0 ) goto update_cleanup; if( v==0 ) goto update_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
@@ -302,40 +271,27 @@ void sqlite3Update(
} }
#endif #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 ){ if( isView ){
sqlite3AuthContextPush(pParse, &sContext, pTab->zName); sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
} }
/* Generate the code for triggers. /* If there are any triggers, set old_col_mask and new_col_mask. */
*/ sqlite3TriggerUses(pParse,
if( pTrigger ){ pTrigger, TK_UPDATE, pChanges, pTab, onError, &old_col_mask, &new_col_mask
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 we are trying to update a view, realize that view into /* If we are trying to update a view, realize that view into
** a ephemeral table. ** a ephemeral table.
@@ -374,7 +330,7 @@ void sqlite3Update(
/* Initialize the count of updated rows /* Initialize the count of updated rows
*/ */
if( db->flags & SQLITE_CountRows && !pParse->trigStack ){ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
regRowCount = ++pParse->nMem; regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
} }
@@ -408,11 +364,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 */ /* Top of the update loop */
if( okOnePass ){ if( okOnePass ){
int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid); int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
@@ -422,139 +373,87 @@ void sqlite3Update(
addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, 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);
/* If there are triggers on this table, populate an array of registers
** with the required old.* column data. */
if( pTrigger ){ 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);
/* 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);
for(i=0; i<pTab->nCol; i++){ for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){ if( aXRef[i]<0 || old_col_mask==0xffffffff || (old_col_mask & (1<<i)) ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i); sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regOld+i);
continue; sqlite3ColumnDefault(v, pTab, i, regOld+i);
}
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{ }else{
sqlite3ExprCodeAndCache(pParse, pChanges->a[j].pExpr, regCols+i); sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
}
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+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 ){ /* If the record number will change, set register regNewRowid to
/* Loop over every record that needs updating. We have to load ** contain the new value. If the record number is not being modified,
** the old data for each record to be updated because some columns ** then regNewRowid is the same register as regOldRowid, which is
** might not change and we will need to copy the old value. ** already populated. */
** Also, the old data is needed to delete the old index entries. assert( chngRowid || pTrigger || regOldRowid==regNewRowid );
** 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 ){ if( chngRowid ){
sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
}else if( pTrigger ){
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
} }
/* Compute new data for this record. /* 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++){ for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){ if( i==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regData+i); sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
continue; }else{
}
j = aXRef[i]; j = aXRef[i];
if( j<0 ){ if( j<0 ){
sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regData+i); sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
sqlite3ColumnDefault(v, pTab, i, regData+i); sqlite3ColumnDefault(v, pTab, i, regNew+i);
}else{ }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, sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
aRegIdx, chngRowid, 1, aRegIdx, chngRowid, 1, onError, addr, 0);
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); j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); 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 ){ if( chngRowid ){
sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
} }
sqlite3VdbeJumpHere(v, j1); sqlite3VdbeJumpHere(v, j1);
/* Create the new index entries and the new record. /* Create the new index entries and the new record. */
*/ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx,1,-1,0,0);
sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid,
aRegIdx, 1, -1, 0, 0);
} }
/* Increment the row counter /* Increment the row counter
*/ */
if( db->flags & SQLITE_CountRows && !pParse->trigStack){ if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
} }
/* If there are triggers, close all the cursors after each iteration sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
** through the loop. The fire the after triggers. TRIGGER_AFTER, pTab, regNew, regOld, onError, addr);
*/
if( pTrigger ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger);
sqlite3VdbeJumpHere(v, iEndAfterTrigger);
}
/* Repeat the above with the next record to be updated, until /* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated. ** all record selected by the WHERE clause have been updated.
@@ -569,16 +468,12 @@ void sqlite3Update(
} }
} }
sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); 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 /* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into ** maximum rowid counter values recorded while inserting into
** autoincrement tables. ** autoincrement tables.
*/ */
if( pParse->nested==0 && pParse->trigStack==0 ){ if( pParse->nested==0 && pParse->pTriggerTab==0 ){
sqlite3AutoincrementEnd(pParse); sqlite3AutoincrementEnd(pParse);
} }
@@ -587,7 +482,7 @@ void sqlite3Update(
** generating code because of a call to sqlite3NestedParse(), do not ** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function. ** 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); sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);

View File

@@ -846,9 +846,18 @@ case OP_HaltIfNull: { /* in3 */
** is the same as executing Halt. ** is the same as executing Halt.
*/ */
case OP_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->rc = pOp->p1;
p->pc = pc;
p->errorAction = (u8)pOp->p2; p->errorAction = (u8)pOp->p2;
p->pc = pc;
if( pOp->p4.z ){ if( pOp->p4.z ){
sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
} }
@@ -4770,6 +4779,137 @@ case OP_ContextPop: {
p->nChange = pContext->nChange; p->nChange = pContext->nChange;
break; 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 */ #endif /* #ifndef SQLITE_OMIT_TRIGGER */
#ifndef SQLITE_OMIT_AUTOINCREMENT #ifndef SQLITE_OMIT_AUTOINCREMENT

View File

@@ -34,6 +34,7 @@ typedef struct Vdbe Vdbe;
*/ */
typedef struct VdbeFunc VdbeFunc; typedef struct VdbeFunc VdbeFunc;
typedef struct Mem Mem; typedef struct Mem Mem;
typedef struct SubProgram SubProgram;
/* /*
** A single instruction of the virtual machine has an opcode ** A single instruction of the virtual machine has an opcode
@@ -48,7 +49,7 @@ struct VdbeOp {
int p1; /* First operand */ int p1; /* First operand */
int p2; /* Second parameter (often the jump destination) */ int p2; /* Second parameter (often the jump destination) */
int p3; /* The third parameter */ int p3; /* The third parameter */
union { /* forth parameter */ union { /* fourth parameter */
int i; /* Integer value if p4type==P4_INT32 */ int i; /* Integer value if p4type==P4_INT32 */
void *p; /* Generic pointer */ void *p; /* Generic pointer */
char *z; /* Pointer to data for string (char array) types */ char *z; /* Pointer to data for string (char array) types */
@@ -61,6 +62,7 @@ struct VdbeOp {
VTable *pVtab; /* Used when p4type is P4_VTAB */ VTable *pVtab; /* Used when p4type is P4_VTAB */
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
int *ai; /* Used when p4type is P4_INTARRAY */ int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
} p4; } p4;
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
char *zComment; /* Comment to improve readability */ char *zComment; /* Comment to improve readability */
@@ -72,6 +74,19 @@ struct VdbeOp {
}; };
typedef struct VdbeOp 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 ** A smaller version of VdbeOp used for the VdbeAddOpList() function because
** it takes up less space. ** it takes up less space.
@@ -85,7 +100,7 @@ struct VdbeOpList {
typedef struct VdbeOpList 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_NOTUSED 0 /* The P4 parameter is not used */
#define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ #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_INT64 (-13) /* P4 is a 64-bit signed integer */
#define P4_INT32 (-14) /* P4 is a 32-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_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 /* 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 ** 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); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
int sqlite3VdbeMakeLabel(Vdbe*); int sqlite3VdbeMakeLabel(Vdbe*);
void sqlite3VdbeDelete(Vdbe*); void sqlite3VdbeDelete(Vdbe*);
void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int); void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int);
int sqlite3VdbeFinalize(Vdbe*); int sqlite3VdbeFinalize(Vdbe*);
void sqlite3VdbeResolveLabel(Vdbe*, int); void sqlite3VdbeResolveLabel(Vdbe*, int);
int sqlite3VdbeCurrentAddr(Vdbe*); int sqlite3VdbeCurrentAddr(Vdbe*);
@@ -183,6 +199,8 @@ void sqlite3VdbeCountChanges(Vdbe*);
sqlite3 *sqlite3VdbeDb(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*);
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
void sqlite3VdbeSwap(Vdbe*,Vdbe*); void sqlite3VdbeSwap(Vdbe*,Vdbe*);
VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseMemory(int); int sqlite3VdbeReleaseMemory(int);

View File

@@ -89,6 +89,24 @@ struct VdbeCursor {
}; };
typedef struct VdbeCursor 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. ** 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 */ int nZero; /* Used when bit MEM_Zero is set in flags */
FuncDef *pDef; /* Used only when flags==MEM_Agg */ FuncDef *pDef; /* Used only when flags==MEM_Agg */
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
} u; } u;
double r; /* Real value */ double r; /* Real value */
sqlite3 *db; /* The associated database connection */ sqlite3 *db; /* The associated database connection */
@@ -144,6 +163,7 @@ struct Mem {
#define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_Blob 0x0010 /* Value is a BLOB */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */ #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 */ #define MEM_TypeMask 0x00ff /* Mask of type bits */
/* Whenever Mem contains a valid string or blob representation, one of /* Whenever Mem contains a valid string or blob representation, one of
@@ -302,6 +322,9 @@ struct Vdbe {
#ifdef SQLITE_DEBUG #ifdef SQLITE_DEBUG
FILE *trace; /* Write an execution trace here, if not NULL */ FILE *trace; /* Write an execution trace here, if not NULL */
#endif #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 sqlite3VdbeOpcodeHasProperty(int, int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
int sqlite3VdbeCloseStatement(Vdbe *, int); int sqlite3VdbeCloseStatement(Vdbe *, int);
void sqlite3VdbeFrameDelete(VdbeFrame*);
int sqlite3VdbeFrameRestore(VdbeFrame *);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseBuffers(Vdbe *p); int sqlite3VdbeReleaseBuffers(Vdbe *p);
#endif #endif

View File

@@ -76,7 +76,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt; Vdbe *v = (Vdbe*)pStmt;
sqlite3_mutex_enter(v->db->mutex); sqlite3_mutex_enter(v->db->mutex);
rc = sqlite3VdbeReset(v); rc = sqlite3VdbeReset(v);
sqlite3VdbeMakeReady(v, -1, 0, 0, 0); sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0);
assert( (rc & (v->db->errMask))==rc ); assert( (rc & (v->db->errMask))==rc );
rc = sqlite3ApiExit(v->db, rc); rc = sqlite3ApiExit(v->db, rc);
sqlite3_mutex_leave(v->db->mutex); sqlite3_mutex_leave(v->db->mutex);

View File

@@ -266,7 +266,7 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){
*/ */
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int i; int i;
int nMaxArgs = 0; int nMaxArgs = *pMaxFuncArgs;
Op *pOp; Op *pOp;
int *aLabel = p->aLabel; int *aLabel = p->aLabel;
int doesStatementRollback = 0; int doesStatementRollback = 0;
@@ -339,6 +339,14 @@ int sqlite3VdbeCurrentAddr(Vdbe *p){
return p->nOp; 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 ** Add a whole list of operations to the operation stack. Return the
** address of the first operation added. ** address of the first operation added.
@@ -482,8 +490,39 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
sqlite3VtabUnlock((VTable *)p4); sqlite3VtabUnlock((VTable *)p4);
break; 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, ...){ void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
va_list ap; va_list ap;
if( !p ) return;
assert( p->nOp>0 || p->aOp==0 ); assert( p->nOp>0 || p->aOp==0 );
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
if( p->nOp ){ if( p->nOp ){
@@ -616,6 +656,7 @@ void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
} }
void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
va_list ap; va_list ap;
if( !p ) return;
sqlite3VdbeAddOp0(p, OP_Noop); sqlite3VdbeAddOp0(p, OP_Noop);
assert( p->nOp>0 || p->aOp==0 ); assert( p->nOp>0 || p->aOp==0 );
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); 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"); sqlite3_snprintf(nTemp, zTemp, "intarray");
break; break;
} }
case P4_SUBPROGRAM: {
sqlite3_snprintf(nTemp, zTemp, "program");
break;
}
default: { default: {
zP4 = pOp->p4.z; zP4 = pOp->p4.z;
if( zP4==0 ){ if( zP4==0 ){
@@ -823,7 +868,7 @@ static void releaseMemArray(Mem *p, int N){
** with no indexes using a single prepared INSERT statement, bind() ** with no indexes using a single prepared INSERT statement, bind()
** and reset(). Inserts are grouped into a transaction. ** 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); sqlite3VdbeMemRelease(p);
}else if( p->zMalloc ){ }else if( p->zMalloc ){
sqlite3DbFree(db, 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 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
int sqlite3VdbeReleaseBuffers(Vdbe *p){ int sqlite3VdbeReleaseBuffers(Vdbe *p){
int ii; int ii;
@@ -872,6 +929,10 @@ int sqlite3VdbeReleaseBuffers(Vdbe *p){
int sqlite3VdbeList( int sqlite3VdbeList(
Vdbe *p /* The VDBE */ 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; sqlite3 *db = p->db;
int i; int i;
int rc = SQLITE_OK; int rc = SQLITE_OK;
@@ -886,7 +947,7 @@ int sqlite3VdbeList(
** the result, result columns may become dynamic if the user calls ** the result, result columns may become dynamic if the user calls
** sqlite3_column_text16(), causing a translation to UTF-16 encoding. ** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/ */
releaseMemArray(pMem, p->nMem); releaseMemArray(pMem, 8);
if( p->rc==SQLITE_NOMEM ){ if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or /* This happens if a malloc() inside a call to sqlite3_column_text() or
@@ -895,10 +956,24 @@ int sqlite3VdbeList(
return SQLITE_ERROR; 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{ do{
i = p->pc++; i = p->pc++;
}while( i<p->nOp && p->explain==2 && p->aOp[i].opcode!=OP_Explain ); }while( i<nRow && p->explain==2 && p->aOp[i].opcode!=OP_Explain );
if( i>=p->nOp ){ if( i>=nRow ){
p->rc = SQLITE_OK; p->rc = SQLITE_OK;
rc = SQLITE_DONE; rc = SQLITE_DONE;
}else if( db->u1.isInterrupted ){ }else if( db->u1.isInterrupted ){
@@ -907,7 +982,17 @@ int sqlite3VdbeList(
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc)); sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
}else{ }else{
char *z; 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 ){ if( p->explain==1 ){
pMem->flags = MEM_Int; pMem->flags = MEM_Int;
pMem->type = SQLITE_INTEGER; pMem->type = SQLITE_INTEGER;
@@ -921,6 +1006,20 @@ int sqlite3VdbeList(
pMem->type = SQLITE_TEXT; pMem->type = SQLITE_TEXT;
pMem->enc = SQLITE_UTF8; pMem->enc = SQLITE_UTF8;
pMem++; 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; pMem->flags = MEM_Int;
@@ -1095,6 +1194,7 @@ void sqlite3VdbeMakeReady(
int nVar, /* Number of '?' see in the SQL statement */ int nVar, /* Number of '?' see in the SQL statement */
int nMem, /* Number of memory cells to allocate */ int nMem, /* Number of memory cells to allocate */
int nCursor, /* Number of cursors 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 isExplain /* True if the EXPLAIN keywords is present */
){ ){
int n; int n;
@@ -1130,7 +1230,6 @@ void sqlite3VdbeMakeReady(
u8 *zCsr = (u8 *)&p->aOp[p->nOp]; u8 *zCsr = (u8 *)&p->aOp[p->nOp];
u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc];
int nByte; int nByte;
int nArg; /* Maximum number of args passed to a user function. */
resolveP2Values(p, &nArg); resolveP2Values(p, &nArg);
if( isExplain && nMem<10 ){ if( isExplain && nMem<10 ){
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){ static void closeAllCursors(Vdbe *p){
int i; 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++){ for(i=0; i<p->nCursor; i++){
VdbeCursor *pC = p->apCsr[i]; VdbeCursor *pC = p->apCsr[i];
if( pC ){ if( pC ){
@@ -1242,6 +1366,7 @@ static void closeAllCursors(Vdbe *p){
p->apCsr[i] = 0; p->apCsr[i] = 0;
} }
} }
releaseMemArray(&p->aMem[1], p->nMem);
} }
/* /*
@@ -1252,17 +1377,16 @@ static void closeAllCursors(Vdbe *p){
** variables in the aVar[] array. ** variables in the aVar[] array.
*/ */
static void Cleanup(Vdbe *p){ static void Cleanup(Vdbe *p){
int i;
sqlite3 *db = p->db; sqlite3 *db = p->db;
Mem *pMem;
closeAllCursors(p); #ifdef SQLITE_DEBUG
for(pMem=&p->aMem[1], i=1; i<=p->nMem; i++, pMem++){ /* Execute assert() statements to ensure that the Vdbe.apCsr[] and
if( pMem->flags & MEM_RowSet ){ ** Vdbe.aMem[] arrays have already been cleaned up. */
sqlite3RowSetClear(pMem->u.pRowSet); int i;
} for(i=0; i<p->nCursor; i++){ assert( p->apCsr[i]==0 ); }
MemSetTypeFlag(pMem, MEM_Null); for(i=1; i<=p->nMem; i++){ assert( p->aMem[i].flags==MEM_Null ); }
} #endif
releaseMemArray(&p->aMem[1], p->nMem);
if( p->contextStack ){ if( p->contextStack ){
sqlite3DbFree(db, p->contextStack); sqlite3DbFree(db, p->contextStack);
} }
@@ -1993,7 +2117,6 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
** Delete an entire VDBE. ** Delete an entire VDBE.
*/ */
void sqlite3VdbeDelete(Vdbe *p){ void sqlite3VdbeDelete(Vdbe *p){
int i;
sqlite3 *db; sqlite3 *db;
if( NEVER(p==0) ) return; if( NEVER(p==0) ) return;
@@ -2007,22 +2130,13 @@ void sqlite3VdbeDelete(Vdbe *p){
if( p->pNext ){ if( p->pNext ){
p->pNext->pPrev = p->pPrev; 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); releaseMemArray(p->aVar, p->nVar);
sqlite3DbFree(db, p->aLabel);
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
vdbeFreeOpArray(db, p->aOp, p->nOp);
sqlite3DbFree(db, p->aLabel);
sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql); sqlite3DbFree(db, p->zSql);
p->magic = VDBE_MAGIC_DEAD; p->magic = VDBE_MAGIC_DEAD;
sqlite3DbFree(db, p->aOp);
sqlite3DbFree(db, p->pFree); sqlite3DbFree(db, p->pFree);
sqlite3DbFree(db, p); sqlite3DbFree(db, p);
} }

View File

@@ -204,7 +204,7 @@ int sqlite3_blob_open(
sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
sqlite3VdbeChangeP2(v, 7, pTab->nCol); sqlite3VdbeChangeP2(v, 7, pTab->nCol);
if( !db->mallocFailed ){ if( !db->mallocFailed ){
sqlite3VdbeMakeReady(v, 1, 1, 1, 0); sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0);
} }
} }

View File

@@ -270,7 +270,7 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
*/ */
void sqlite3VdbeMemReleaseExternal(Mem *p){ void sqlite3VdbeMemReleaseExternal(Mem *p){
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) ); 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 ){ if( p->flags&MEM_Agg ){
sqlite3VdbeMemFinalize(p, p->u.pDef); sqlite3VdbeMemFinalize(p, p->u.pDef);
assert( (p->flags & MEM_Agg)==0 ); assert( (p->flags & MEM_Agg)==0 );
@@ -281,6 +281,9 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
p->xDel = 0; p->xDel = 0;
}else if( p->flags&MEM_RowSet ){ }else if( p->flags&MEM_RowSet ){
sqlite3RowSetClear(p->u.pRowSet); 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. ** Delete any previous value and set the value stored in *pMem to NULL.
*/ */
void sqlite3VdbeMemSetNull(Mem *pMem){ void sqlite3VdbeMemSetNull(Mem *pMem){
if( pMem->flags & MEM_Frame ){
sqlite3VdbeFrameDelete(pMem->u.pFrame);
}
if( pMem->flags & MEM_RowSet ){ if( pMem->flags & MEM_RowSet ){
sqlite3RowSetClear(pMem->u.pRowSet); sqlite3RowSetClear(pMem->u.pRowSet);
} }

View File

@@ -943,18 +943,19 @@ FuncDef *sqlite3VtabOverloadFunction(
void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
int i, n; int i, n;
Table **apVtabLock; Table **apVtabLock;
Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse);
assert( IsVirtual(pTab) ); assert( IsVirtual(pTab) );
for(i=0; i<pParse->nVtabLock; i++){ for(i=0; i<pRoot->nVtabLock; i++){
if( pTab==pParse->apVtabLock[i] ) return; if( pTab==pRoot->apVtabLock[i] ) return;
} }
n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]); n = (pRoot->nVtabLock+1)*sizeof(pRoot->apVtabLock[0]);
apVtabLock = sqlite3_realloc(pParse->apVtabLock, n); apVtabLock = sqlite3_realloc(pRoot->apVtabLock, n);
if( apVtabLock ){ if( apVtabLock ){
pParse->apVtabLock = apVtabLock; pRoot->apVtabLock = apVtabLock;
pParse->apVtabLock[pParse->nVtabLock++] = pTab; pRoot->apVtabLock[pRoot->nVtabLock++] = pTab;
}else{ }else{
pParse->db->mallocFailed = 1; pRoot->db->mallocFailed = 1;
} }
} }

View File

@@ -317,6 +317,7 @@ do_test attach3-12.9 {
db_list db_list
} {main temp {}} } {main temp {}}
do_test attach3-12.10 { do_test attach3-12.10 {
breakpoint
execsql { execsql {
DETACH ? DETACH ?
} }

View File

@@ -25,6 +25,8 @@ ifcapable {!autoinc} {
return return
} }
sqlite3_db_config_lookaside db 0 0 0
# The database is initially empty. # The database is initially empty.
# #
do_test autoinc-1.1 { do_test autoinc-1.1 {

View File

@@ -403,14 +403,14 @@ do_test trigger1-6.1 {
do_test trigger1-6.2 { do_test trigger1-6.2 {
execsql { execsql {
CREATE TRIGGER t2 BEFORE DELETE ON t2 BEGIN CREATE TRIGGER t2 BEFORE DELETE ON t2 BEGIN
SELECT RAISE(ABORT,'deletes are not allows'); SELECT RAISE(ABORT,'deletes are not permitted');
END; END;
SELECT type, name FROM sqlite_master; SELECT type, name FROM sqlite_master;
} }
} [concat $view_v1 {table t2 trigger t2}] } [concat $view_v1 {table t2 trigger t2}]
do_test trigger1-6.3 { do_test trigger1-6.3 {
catchsql {DELETE FROM t2} catchsql {DELETE FROM t2}
} {1 {deletes are not allows}} } {1 {deletes are not permitted}}
do_test trigger1-6.4 { do_test trigger1-6.4 {
execsql {SELECT * FROM t2} execsql {SELECT * FROM t2}
} {3 4 7 8} } {3 4 7 8}

View File

@@ -32,6 +32,7 @@ if {$tcl_platform(platform) == "symbian"} {
set nStatement 1000 set nStatement 1000
} }
set nStatement 5
do_test trigger8-1.1 { do_test trigger8-1.1 {
execsql { execsql {
CREATE TABLE t1(x); CREATE TABLE t1(x);