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

More fixes and comment updates.

FossilOrigin-Name: 38a9327bad1a01e3d7a47fad44ece2f6c7e88643
This commit is contained in:
dan
2009-09-01 12:16:01 +00:00
parent 2832ad4221
commit 65a7cd1631
16 changed files with 267 additions and 169 deletions

View File

@@ -1,5 +1,5 @@
C More\sfixes\sfor\stest\scases. C More\sfixes\sand\scomment\supdates.
D 2009-08-31T15:27:28 D 2009-09-01T12:16:01
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
@@ -109,24 +109,24 @@ F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7
F src/btree.c 6b60ece56141bbe23aa6efca10f1612d34271c2f F src/btree.c 6b60ece56141bbe23aa6efca10f1612d34271c2f
F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe
F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705 F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705
F src/build.c 212476dc971756e7f7429e677059fafc678afbd5 F src/build.c df8dfff696329c192240e3d532f9dad0ef5abace
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 6b95963dabd558d45385e9b5be1fb4aa7ba7fa62 F src/delete.c 6b95963dabd558d45385e9b5be1fb4aa7ba7fa62
F src/expr.c d95a040f0633d0f33b7c7123ad09463dcb828538 F src/expr.c cadf65591f130e052aaa9718e19ec019b7cb598e
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 845f0f5aee678e131e3e944bb2ceea25b915bb1b F src/insert.c c90b07a1c8c8c31f55ad906cc3fc2de10ba182dd
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
F src/loadext.c 0e88a335665db0b2fb4cece3e49dcb65d832635a F src/loadext.c 0e88a335665db0b2fb4cece3e49dcb65d832635a
F src/main.c 45b3779140336d21a54f289888ff40cb9d2316c7 F src/main.c 09506443147ada14d98973a917667f4b2275aac7
F src/malloc.c b1725183bcc4ce2e569f1b65da844dc3e4c7a643 F src/malloc.c b1725183bcc4ce2e569f1b65da844dc3e4c7a643
F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c
F src/mem1.c e6d5c23941288df8191b8a98c28e3f57771e2270 F src/mem1.c e6d5c23941288df8191b8a98c28e3f57771e2270
@@ -154,16 +154,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 6b1fa9f180d88b3f905cebd593ef6aef9334be43 F src/pragma.c 6b1fa9f180d88b3f905cebd593ef6aef9334be43
F src/prepare.c 1be1832c0dea54dda436f8269e51372b7d0660af F src/prepare.c 9803fc01f0db29ac4a17fa662902af285f37c06b
F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1 F src/printf.c 508a1c59433353552b6553cba175eaa7331f8fc1
F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628 F src/random.c 676b9d7ac820fe81e6fb2394ac8c10cff7f38628
F src/resolve.c 522d6804a058937db9a7c6fd50ec0845abfff84c F src/resolve.c e78029b04dd811e4e0b7be6c51e7c31accc024b6
F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f F src/rowset.c c64dafba1f9fd876836c8db8682966b9d197eb1f
F src/select.c 56ecb073e6f6696173ad80493aa14355225b6e53 F src/select.c 8bc7347fe88cf16ac2cb1a37fb918d1417ab4142
F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb F src/shell.c db2643650b9268df89a4bedca3f1c6d9e786f1bb
F src/sqlite.h.in 9d03ceaad971882482c158c0e3d39d361c2c18a1 F src/sqlite.h.in 9d03ceaad971882482c158c0e3d39d361c2c18a1
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
F src/sqliteInt.h 9036e5d0162426f94fa3989c537167586d731bef F src/sqliteInt.h f25f4fcefe6f0c3b5fcbddc0e4dee759adde0f37
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
@@ -200,19 +200,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 d652d3a073d17bfa9bd9af88350206a81ef08d71 F src/trigger.c 8158237fc5fa4b51a126a294791729c64d27a0a9
F src/update.c 12ffd68c2f241c730d5e051eb29b23e90682ff8a F src/update.c 12ffd68c2f241c730d5e051eb29b23e90682ff8a
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 69890b64d5f7a5ef0e5f0479baa79cb65da4e018 F src/vdbe.c fcc15fb2cfb84e261a423e6f71e0b963b8739280
F src/vdbe.h 080fe6bc1264438becb8bf9b9f3c84074c336b78 F src/vdbe.h 080fe6bc1264438becb8bf9b9f3c84074c336b78
F src/vdbeInt.h 1291908344bcbaa8cf47de86d7108cb92c3a71a3 F src/vdbeInt.h 6094e60f64d7a9d4352d4e6b90d1219dce947ad9
F src/vdbeapi.c 8d5013ab6104be757c208a70ffb191cc27d2b688 F src/vdbeapi.c 8d5013ab6104be757c208a70ffb191cc27d2b688
F src/vdbeaux.c 1cc9dd48848059d1c1cd05775659323e0a8654dc F src/vdbeaux.c a88ef864f13ed698a267bcec29af22fbcd727f82
F src/vdbeblob.c f93cb60ac388633ed3bde8a94ef161ad2dbfb689 F src/vdbeblob.c f93cb60ac388633ed3bde8a94ef161ad2dbfb689
F src/vdbemem.c dc551981833756ea34a3e0b238f759479e7cf526 F src/vdbemem.c dc551981833756ea34a3e0b238f759479e7cf526
F src/vtab.c 10df5c77cea34a49f2ad4e5de763f820d6223eb4 F src/vtab.c 3e54fe39374e5feb8b174de32a90e7a21966025d
F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04 F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
F src/where.c a3218dfcf32e3d933270b76a72b97065f24b3f2c F src/where.c a3218dfcf32e3d933270b76a72b97065f24b3f2c
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -747,8 +747,8 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff 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 d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 8a746fbfd51f70f56e25ade59df49d2dc03c131c P 85d9f23be1e8673dbda42e43b9b23332ada9225f
R c8861b3298b0eed46e58ea3f1386cd62 R 95c6e7fce5f16ea2a0796830099d3492
U dan U dan
Z 83f9b1037a455c2681b775be9c2fdcd7 Z 4646825fb4a0fb39bbc35c33fbd7d7cc

View File

@@ -1 +1 @@
85d9f23be1e8673dbda42e43b9b23332ada9225f 38a9327bad1a01e3d7a47fad44ece2f6c7e88643

View File

@@ -64,35 +64,32 @@ void sqlite3TableLock(
u8 isWriteLock, /* True for a write lock */ u8 isWriteLock, /* True for a write lock */
const char *zName /* Name of the table to be locked */ const char *zName /* Name of the table to be locked */
){ ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
int i; int i;
int nBytes; int nBytes;
TableLock *p; TableLock *p;
assert( iDb>=0 ); assert( iDb>=0 );
if( pParse->pRoot ){ for(i=0; i<pToplevel->nTableLock; i++){
pParse = pParse->pRoot; p = &pToplevel->aTableLock[i];
}
for(i=0; i<pParse->nTableLock; i++){
p = &pParse->aTableLock[i];
if( p->iDb==iDb && p->iTab==iTab ){ if( p->iDb==iDb && p->iTab==iTab ){
p->isWriteLock = (p->isWriteLock || isWriteLock); p->isWriteLock = (p->isWriteLock || isWriteLock);
return; return;
} }
} }
nBytes = sizeof(TableLock) * (pParse->nTableLock+1); nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1);
pParse->aTableLock = pToplevel->aTableLock =
sqlite3DbReallocOrFree(pParse->db, pParse->aTableLock, nBytes); sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes);
if( pParse->aTableLock ){ if( pToplevel->aTableLock ){
p = &pParse->aTableLock[pParse->nTableLock++]; p = &pToplevel->aTableLock[pToplevel->nTableLock++];
p->iDb = iDb; p->iDb = iDb;
p->iTab = iTab; p->iTab = iTab;
p->isWriteLock = isWriteLock; p->isWriteLock = isWriteLock;
p->zName = zName; p->zName = zName;
}else{ }else{
pParse->nTableLock = 0; pToplevel->nTableLock = 0;
pParse->db->mallocFailed = 1; pToplevel->db->mallocFailed = 1;
} }
} }
@@ -198,7 +195,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->nArg, pParse->explain); pParse->nTab, pParse->nMaxArg, 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 ){
@@ -3426,30 +3423,26 @@ int sqlite3OpenTempDatabase(Parse *pParse){
** early in the code, before we know if any database tables will be used. ** early in the code, before we know if any database tables will be used.
*/ */
void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
sqlite3 *db; Parse *pToplevel = sqlite3ParseToplevel(pParse);
Vdbe *v;
int mask;
Parse *pRoot = pParse->pRoot; /* Root parse structure */
v = sqlite3GetVdbe(pParse); if( pToplevel->cookieGoto==0 ){
if( v==0 ) return; /* This only happens if there was a prior error */ Vdbe *v = sqlite3GetVdbe(pToplevel);
db = pParse->db; if( v==0 ) return; /* This only happens if there was a prior error */
if( pParse->cookieGoto==0 && pRoot==0 ){ pToplevel->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 ){ sqlite3 *db = pToplevel->db;
pRoot = pParse; int mask;
}
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( (pRoot->cookieMask & mask)==0 ){ if( (pToplevel->cookieMask & mask)==0 ){
pRoot->cookieMask |= mask; pToplevel->cookieMask |= mask;
pRoot->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
if( !OMIT_TEMPDB && iDb==1 ){ if( !OMIT_TEMPDB && iDb==1 ){
sqlite3OpenTempDatabase(pRoot); sqlite3OpenTempDatabase(pToplevel);
} }
} }
} }
@@ -3469,13 +3462,10 @@ 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; Parse *pToplevel = sqlite3ParseToplevel(pParse);
sqlite3CodeVerifySchema(pParse, iDb); sqlite3CodeVerifySchema(pParse, iDb);
if( pRoot==0 ){ pToplevel->writeMask |= 1<<iDb;
pRoot = pParse; if( setStatement && pParse->nested==0 && pParse==pToplevel ){
}
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

@@ -1398,7 +1398,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
if( iCol<0 ){ if( iCol<0 ){
int iMem = ++pParse->nMem; int iMem = ++pParse->nMem;
int iAddr; int iAddr;
sqlite3VdbeUsesBtree(v, iDb);
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
@@ -1432,9 +1431,6 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
char *pKey; char *pKey;
pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
iDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
sqlite3VdbeUsesBtree(v, iDb);
iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem);
@@ -2559,14 +2555,48 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
} }
case TK_TRIGGER: { case TK_TRIGGER: {
/* If the opcode is TK_TRIGGER, then the expression is a reference
** to a column in the new.* or old.* pseudo-tables available to
** trigger programs. In this case Expr.iTable is set to 1 for the
** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
** is set to the column of the pseudo-table to read, or to -1 to
** read the rowid field.
**
** The expression is implemented using an OP_Param opcode. The p1
** parameter is set to 0 for an old.rowid reference, or to (i+1)
** to reference another column of the old.* pseudo-table, where
** i is the index of the column. For a new.rowid reference, p1 is
** set to (n+1), where n is the number of columns in each pseudo-table.
** For a reference to any other column in the new.* pseudo-table, p1
** is set to (n+2+i), where n and i are as defined previously. For
** example, if the table on which triggers are being fired is
** declared as:
**
** CREATE TABLE t1(a, b);
**
** Then p1 is interpreted as follows:
**
** p1==0 -> old.rowid p1==3 -> new.rowid
** p1==1 -> old.a p1==4 -> new.a
** p1==2 -> old.b p1==5 -> new.b
*/
Table *pTab = pExpr->pTab; Table *pTab = pExpr->pTab;
int iVal = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn;
sqlite3VdbeAddOp2(v, OP_Param, iVal, target);
assert( pExpr->iTable==0 || pExpr->iTable==1 );
assert( pExpr->iColumn>=-1 && pExpr->iColumn<pTab->nCol );
assert( pTab->iPKey<0 || pExpr->iColumn!=pTab->iPKey );
assert( p1>=0 && p1<(pTab->nCol*2+2) );
sqlite3VdbeAddOp2(v, OP_Param, p1, target);
VdbeComment((v, "%s.%s -> $%d", VdbeComment((v, "%s.%s -> $%d",
(pExpr->iTable ? "new" : "old"), (pExpr->iTable ? "new" : "old"),
(pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName), (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName),
target target
)); ));
/* If the column has REAL affinity, it may currently be stored as an
** integer. Use OP_RealAffinity to make sure it is really real. */
if( pExpr->iColumn>=0 if( pExpr->iColumn>=0
&& pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL && pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL
){ ){

View File

@@ -197,21 +197,21 @@ static int autoIncBegin(
){ ){
int memId = 0; /* Register holding maximum rowid */ int memId = 0; /* Register holding maximum rowid */
if( pTab->tabFlags & TF_Autoincrement ){ if( pTab->tabFlags & TF_Autoincrement ){
Parse *pRoot = (pParse->pRoot ? pParse->pRoot : pParse); Parse *pToplevel = sqlite3ParseToplevel(pParse);
AutoincInfo *pInfo; AutoincInfo *pInfo;
pInfo = pRoot->pAinc; pInfo = pToplevel->pAinc;
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){ if( pInfo==0 ){
pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo)); pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo));
if( pInfo==0 ) return 0; if( pInfo==0 ) return 0;
pInfo->pNext = pRoot->pAinc; pInfo->pNext = pToplevel->pAinc;
pRoot->pAinc = pInfo; pToplevel->pAinc = pInfo;
pInfo->pTab = pTab; pInfo->pTab = pTab;
pInfo->iDb = iDb; pInfo->iDb = iDb;
pRoot->nMem++; /* Register to hold name of table */ pToplevel->nMem++; /* Register to hold name of table */
pInfo->regCtr = ++pRoot->nMem; /* Max rowid register */ pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */
pRoot->nMem++; /* Rowid in sqlite_sequence */ pToplevel->nMem++; /* Rowid in sqlite_sequence */
} }
memId = pInfo->regCtr; memId = pInfo->regCtr;
} }
@@ -454,7 +454,6 @@ void sqlite3Insert(
int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */
int addrSelect = 0; /* Address of coroutine that implements the SELECT */ int addrSelect = 0; /* Address of coroutine that implements the SELECT */
SelectDest dest; /* Destination for SELECT on rhs of INSERT */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */
int newIdx = -1; /* Cursor for the NEW pseudo-table */
int iDb; /* Index of database holding TABLE */ int iDb; /* Index of database holding TABLE */
Db *pDb; /* The database containing table being inserted into */ Db *pDb; /* The database containing table being inserted into */
int appendFlag = 0; /* True if the insert is likely to be an append */ int appendFlag = 0; /* True if the insert is likely to be an append */
@@ -470,7 +469,6 @@ void sqlite3Insert(
int regEof = 0; /* Register recording end of SELECT data */ int regEof = 0; /* Register recording end of SELECT data */
int *aRegIdx = 0; /* One register allocated to each index */ int *aRegIdx = 0; /* One register allocated to each index */
#ifndef SQLITE_OMIT_TRIGGER #ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to insert into a view */ int isView; /* True if attempting to insert into a view */
Trigger *pTrigger; /* List of triggers on pTab, if required */ Trigger *pTrigger; /* List of triggers on pTab, if required */
@@ -1050,26 +1048,24 @@ insert_cleanup:
** **
** The input is a range of consecutive registers as follows: ** The input is a range of consecutive registers as follows:
** **
** 1. The rowid of the row to be updated before the update. This ** 1. The rowid of the row after the update.
** value is omitted unless we are doing an UPDATE that involves a
** change to the record number or writing to a virtual table.
** **
** 2. The rowid of the row after the update. ** 2. The data in the first column of the entry after the update.
**
** 3. The data in the first column of the entry after the update.
** **
** i. Data from middle columns... ** i. Data from middle columns...
** **
** N. The data in the last column of the entry after the update. ** N. The data in the last column of the entry after the update.
** **
** The regRowid parameter is the index of the register containing (2). ** The regRowid parameter is the index of the register containing (1).
** **
** The old rowid shown as entry (1) above is omitted unless both isUpdate ** If isUpdate is true and rowidChng is non-zero, then rowidChng contains
** and rowidChng are 1. isUpdate is true for UPDATEs and false for ** the address of a register containing the rowid before the update takes
** INSERTs. RowidChng means that the new rowid is explicitly specified by ** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate
** the update or insert statement. If rowidChng is false, it means that ** is false, indicating an INSERT statement, then a non-zero rowidChng
** the rowid is computed automatically in an insert or that the rowid value ** indicates that the rowid was explicitly specified as part of the
** is not modified by the update. ** INSERT statement. If rowidChng is false, it means that the rowid is
** computed automatically in an insert or that the rowid value is not
** modified by an update.
** **
** The code generated by this routine store new index entries into ** The code generated by this routine store new index entries into
** registers identified by aRegIdx[]. No index entry is created for ** registers identified by aRegIdx[]. No index entry is created for
@@ -1144,7 +1140,7 @@ void sqlite3GenerateConstraintChecks(
int iCur; /* Table cursor number */ int iCur; /* Table cursor number */
Index *pIdx; /* Pointer to one of the indices */ Index *pIdx; /* Pointer to one of the indices */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
int hasTwoRowids = (isUpdate && rowidChng); int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid;
v = sqlite3GetVdbe(pParse); v = sqlite3GetVdbe(pParse);
assert( v!=0 ); assert( v!=0 );
@@ -1304,7 +1300,7 @@ void sqlite3GenerateConstraintChecks(
/* Check to see if the new index entry will be unique */ /* Check to see if the new index entry will be unique */
regR = sqlite3GetTempReg(pParse); regR = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR); sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR);
j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0, j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0,
regR, SQLITE_INT_TO_PTR(regIdx), regR, SQLITE_INT_TO_PTR(regIdx),
P4_INT32); P4_INT32);

View File

@@ -1591,7 +1591,7 @@ static int openDatabase(
#ifdef SQLITE_ENABLE_LOAD_EXTENSION #ifdef SQLITE_ENABLE_LOAD_EXTENSION
| SQLITE_LoadExtension | SQLITE_LoadExtension
#endif #endif
#ifdef SQLITE_DISABLE_RECURSIVE_TRIGGERS #if 1 || defined(SQLITE_DISABLE_RECURSIVE_TRIGGERS)
| SQLITE_NoRecTriggers | SQLITE_NoRecTriggers
#endif #endif
; ;

View File

@@ -676,6 +676,7 @@ static int sqlite3Prepare(
sqlite3Error(db, rc, 0); sqlite3Error(db, rc, 0);
} }
/* Delete any TriggerPrg structures allocated while parsing this statement. */
while( pParse->pTriggerPrg ){ while( pParse->pTriggerPrg ){
TriggerPrg *pT = pParse->pTriggerPrg; TriggerPrg *pT = pParse->pTriggerPrg;
pParse->pTriggerPrg = pT->pNext; pParse->pTriggerPrg = pT->pNext;

View File

@@ -224,11 +224,13 @@ static int lookupName(
** 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->pTriggerTab!=0 ){ if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
int op = pParse->eTriggerOp;
Table *pTab = 0; Table *pTab = 0;
if( pParse->triggerOp!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = 1; pExpr->iTable = 1;
pTab = pParse->pTriggerTab; pTab = pParse->pTriggerTab;
}else if( pParse->triggerOp!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
pExpr->iTable = 0; pExpr->iTable = 0;
pTab = pParse->pTriggerTab; pTab = pParse->pTriggerTab;
} }

View File

@@ -2733,10 +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 = pRoot->pZombieTab; Parse *pToplevel = sqlite3ParseToplevel(pParse);
pRoot->pZombieTab = pTabToDel; pTabToDel->pNextZombie = pToplevel->pZombieTab;
pToplevel->pZombieTab = pTabToDel;
}else{ }else{
pTabToDel->nRef--; pTabToDel->nRef--;
} }

View File

@@ -2029,6 +2029,10 @@ struct AutoincInfo {
** TriggerPrg.orconf, is stored in the TriggerPrg.pProgram variable. ** TriggerPrg.orconf, is stored in the TriggerPrg.pProgram variable.
** The Parse.pTriggerPrg list never contains two entries with the same ** The Parse.pTriggerPrg list never contains two entries with the same
** values for both pTrigger and orconf. ** values for both pTrigger and orconf.
**
** The TriggerPrg.oldmask variable is set to a mask of old.* columns
** accessed (or set to 0 for triggers fired as a result of INSERT
** statements).
*/ */
struct TriggerPrg { struct TriggerPrg {
Trigger *pTrigger; /* Trigger this program was coded from */ Trigger *pTrigger; /* Trigger this program was coded from */
@@ -2098,15 +2102,14 @@ struct Parse {
int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRowid; /* Register holding rowid of CREATE TABLE entry */
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 */
int nMaxArg; /* Max args passed to user function by sub-program */
/* Information used while coding trigger programs. */ /* Information used while coding trigger programs. */
Parse *pRoot; /* Root Parse structure */ Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */ Table *pTriggerTab; /* Table triggers are being coded for */
u32 oldmask; u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
int triggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
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 */
@@ -2692,6 +2695,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
void sqlite3DeleteTrigger(sqlite3*, Trigger*); void sqlite3DeleteTrigger(sqlite3*, Trigger*);
void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
u32 sqlite3TriggerOldmask(Parse*,Trigger*,int,ExprList*,Table*,int); u32 sqlite3TriggerOldmask(Parse*,Trigger*,int,ExprList*,Table*,int);
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
#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)
@@ -2699,6 +2703,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
# define sqlite3UnlinkAndDeleteTrigger(A,B,C) # define sqlite3UnlinkAndDeleteTrigger(A,B,C)
# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J) # define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J)
# define sqlite3TriggerList(X, Y) 0 # define sqlite3TriggerList(X, Y) 0
# define sqlite3ParseToplevel(p) p
#endif #endif
int sqlite3JoinType(Parse*, Token*, Token*, Token*); int sqlite3JoinType(Parse*, Token*, Token*, Token*);

View File

@@ -666,22 +666,22 @@ static SrcList *targetSrcList(
} }
/* /*
** Generate VDBE code for zero or more statements inside the body of a ** Generate VDBE code for the statements inside the body of a single
** trigger. ** trigger.
*/ */
static int codeTriggerProgram( static int codeTriggerProgram(
Parse *pParse, /* The parser context */ Parse *pParse, /* The parser context */
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 orconf /* Conflict algorithm. (OE_Abort, etc) */
){ ){
TriggerStep * pStep = pStepList; TriggerStep *pStep;
Vdbe *v = pParse->pVdbe; Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db; sqlite3 *db = pParse->db;
assert( pParse->pRoot ); assert( pParse->pTriggerTab && pParse->pToplevel );
assert( pStep!=0 ); assert( pStepList );
assert( v!=0 ); assert( v!=0 );
while( pStep ){ for(pStep=pStepList; pStep; pStep=pStep->pNext){
/* Figure out the ON CONFLICT policy that will be used for this step /* Figure out the ON CONFLICT policy that will be used for this step
** of the trigger program. If the statement that caused this trigger ** of the trigger program. If the statement that caused this trigger
** to fire had an explicit ON CONFLICT, then use it. Otherwise, use ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use
@@ -695,7 +695,7 @@ static int codeTriggerProgram(
** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy ** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy
** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy ** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy
*/ */
pParse->orconf = (orconfin==OE_Default)?pStep->orconf:orconfin; pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:orconf;
switch( pStep->op ){ switch( pStep->op ){
case TK_UPDATE: { case TK_UPDATE: {
@@ -703,7 +703,7 @@ static int codeTriggerProgram(
targetSrcList(pParse, pStep), targetSrcList(pParse, pStep),
sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3ExprDup(db, pStep->pWhere, 0), sqlite3ExprDup(db, pStep->pWhere, 0),
pParse->orconf pParse->eOrconf
); );
break; break;
} }
@@ -713,7 +713,7 @@ static int codeTriggerProgram(
sqlite3ExprListDup(db, pStep->pExprList, 0), sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3SelectDup(db, pStep->pSelect, 0), sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList), sqlite3IdListDup(db, pStep->pIdList),
pParse->orconf pParse->eOrconf
); );
break; break;
} }
@@ -736,7 +736,6 @@ static int codeTriggerProgram(
if( pStep->op!=TK_SELECT ){ if( pStep->op!=TK_SELECT ){
sqlite3VdbeAddOp1(v, OP_ResetCount, 1); sqlite3VdbeAddOp1(v, OP_ResetCount, 1);
} }
pStep = pStep->pNext;
} }
return 0; return 0;
@@ -776,16 +775,19 @@ static void transferParseError(Parse *pTo, Parse *pFrom){
} }
} }
/*
** Create and populate a new TriggerPrg object with a sub-program
** implementing trigger pTrigger with ON CONFLICT policy orconf.
*/
static TriggerPrg *codeRowTrigger( static TriggerPrg *codeRowTrigger(
Parse *pRoot, /* Root parse context */
Parse *pParse, /* Current parse context */ Parse *pParse, /* Current parse context */
Trigger *pTrigger, /* Trigger to code */ Trigger *pTrigger, /* Trigger to code */
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ Table *pTab, /* The table pTrigger is attached to */
Table *pTab, /* The table to code triggers from */ int orconf /* ON CONFLICT policy to code trigger program with */
int orconf
){ ){
sqlite3 *db = pParse->db; Parse *pTop = sqlite3ParseToplevel(pParse);
TriggerPrg *pPrg; sqlite3 *db = pParse->db; /* Database handle */
TriggerPrg *pPrg; /* Value to return */
Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */ Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */
Vdbe *v; /* Temporary VM */ Vdbe *v; /* Temporary VM */
NameContext sNC; /* Name context for sub-vdbe */ NameContext sNC; /* Name context for sub-vdbe */
@@ -793,35 +795,41 @@ static TriggerPrg *codeRowTrigger(
Parse *pSubParse; /* Parse context for sub-vdbe */ Parse *pSubParse; /* Parse context for sub-vdbe */
int iEndTrigger = 0; /* Label to jump to if WHEN is false */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */
assert( pTab==tableOfTrigger(pTrigger) );
/* Allocate the TriggerPrg and SubProgram objects. To ensure that they
** are freed if an error occurs, link them into the Parse.pTriggerPrg
** list of the top-level Parse object sooner rather than later. */
pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg)); pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg));
if( !pPrg ) return 0; if( !pPrg ) return 0;
pPrg->pNext = pRoot->pTriggerPrg; pPrg->pNext = pTop->pTriggerPrg;
pRoot->pTriggerPrg = pPrg; pTop->pTriggerPrg = pPrg;
pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram)); pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram));
if( !pProgram ) return 0; if( !pProgram ) return 0;
pProgram->nRef = 1; pProgram->nRef = 1;
pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
if( !pSubParse ) return 0;
pPrg->pProgram = pProgram;
pPrg->pTrigger = pTrigger; pPrg->pTrigger = pTrigger;
pPrg->orconf = orconf; pPrg->orconf = orconf;
/* Allocate and populate a new Parse context to use for coding the
** trigger sub-program. */
pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
if( !pSubParse ) return 0;
memset(&sNC, 0, sizeof(sNC)); memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pSubParse; sNC.pParse = pSubParse;
pSubParse->db = db; pSubParse->db = db;
pSubParse->pTriggerTab = pTab; pSubParse->pTriggerTab = pTab;
pSubParse->pRoot = pRoot; pSubParse->pToplevel = pTop;
pSubParse->zAuthContext = pTrigger->zName; pSubParse->zAuthContext = pTrigger->zName;
pSubParse->eTriggerOp = pTrigger->op;
v = sqlite3GetVdbe(pSubParse); v = sqlite3GetVdbe(pSubParse);
if( v ){ if( v ){
VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)",
pTrigger->zName, onErrorText(orconf), pTrigger->zName, onErrorText(orconf),
(pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"),
(op==TK_UPDATE ? "UPDATE" : ""), (pTrigger->op==TK_UPDATE ? "UPDATE" : ""),
(op==TK_INSERT ? "INSERT" : ""), (pTrigger->op==TK_INSERT ? "INSERT" : ""),
(op==TK_DELETE ? "DELETE" : ""), (pTrigger->op==TK_DELETE ? "DELETE" : ""),
pTab->zName pTab->zName
)); ));
#ifndef SQLITE_OMIT_TRACE #ifndef SQLITE_OMIT_TRACE
@@ -830,9 +838,10 @@ static TriggerPrg *codeRowTrigger(
); );
#endif #endif
/* If one was specified, code the WHEN clause. If it evaluates to false
** (or NULL) the sub-vdbe is immediately halted by jumping to the
** OP_Halt inserted at the end of the program. */
if( pTrigger->pWhen ){ 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); pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0);
if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen)
&& db->mallocFailed==0 && db->mallocFailed==0
@@ -845,6 +854,8 @@ static TriggerPrg *codeRowTrigger(
/* Code the trigger program into the sub-vdbe. */ /* Code the trigger program into the sub-vdbe. */
codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); codeTriggerProgram(pSubParse, pTrigger->step_list, orconf);
/* Insert an OP_Halt at the end of the sub-program. */
if( iEndTrigger ){ if( iEndTrigger ){
sqlite3VdbeResolveLabel(v, iEndTrigger); sqlite3VdbeResolveLabel(v, iEndTrigger);
} }
@@ -853,49 +864,51 @@ static TriggerPrg *codeRowTrigger(
transferParseError(pParse, pSubParse); transferParseError(pParse, pSubParse);
if( db->mallocFailed==0 ){ if( db->mallocFailed==0 ){
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pParse->nArg); pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
} }
pProgram->nMem = pSubParse->nMem; pProgram->nMem = pSubParse->nMem;
pProgram->nCsr = pSubParse->nTab; pProgram->nCsr = pSubParse->nTab;
pProgram->token = (void *)pTrigger; pProgram->token = (void *)pTrigger;
pPrg->oldmask = pSubParse->oldmask; pPrg->oldmask = pSubParse->oldmask;
sqlite3VdbeDelete(v); sqlite3VdbeDelete(v);
while( pSubParse->pAinc ){
AutoincInfo *p = pSubParse->pAinc;
pSubParse->pAinc = p->pNext;
sqlite3DbFree(db, p);
}
} }
assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
sqlite3StackFree(db, pSubParse); sqlite3StackFree(db, pSubParse);
return pPrg; return pPrg;
} }
/*
** Return a pointer to a TriggerPrg object containing the sub-program for
** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such
** TriggerPrg object exists, a new object is allocated and populated before
** being returned.
*/
static TriggerPrg *getRowTrigger( static TriggerPrg *getRowTrigger(
Parse *pParse, Parse *pParse, /* Current parse context */
Trigger *pTrigger, /* Trigger to code */ Trigger *pTrigger, /* Trigger to code */
int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ Table *pTab, /* The table trigger pTrigger is attached to */
Table *pTab, /* The table to code triggers from */ int orconf /* ON CONFLICT algorithm. */
int orconf
){ ){
Parse *pRoot = sqlite3ParseToplevel(pParse);
TriggerPrg *pPrg; TriggerPrg *pPrg;
Parse *pRoot = pParse;
assert( pTab==tableOfTrigger(pTrigger) );
/* It may be that this trigger has already been coded (or is in the /* 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 ** process of being coded). If this is the case, then an entry with
** a matching TriggerPrg.pTrigger field will be present somewhere ** a matching TriggerPrg.pTrigger field will be present somewhere
** in the Parse.pTriggerPrg list. Search for such an entry. */ ** in the Parse.pTriggerPrg list. Search for such an entry. */
if( pParse->pRoot ){
pRoot = pParse->pRoot;
}
for(pPrg=pRoot->pTriggerPrg; for(pPrg=pRoot->pTriggerPrg;
pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf); pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf);
pPrg=pPrg->pNext pPrg=pPrg->pNext
); );
/* If an existing TriggerPrg could not be located, create a new one. */
if( !pPrg ){ if( !pPrg ){
pPrg = codeRowTrigger(pRoot, pParse, pTrigger, op, pTab, orconf); pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf);
} }
return pPrg; return pPrg;
@@ -962,7 +975,7 @@ void sqlite3CodeRowTrigger(
){ ){
Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */
TriggerPrg *pPrg; TriggerPrg *pPrg;
pPrg = getRowTrigger(pParse, p, op, pTab, orconf); pPrg = getRowTrigger(pParse, p, pTab, orconf);
assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); assert( pPrg || pParse->nErr || pParse->db->mallocFailed );
/* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program
@@ -1011,7 +1024,7 @@ u32 sqlite3TriggerOldmask(
for(p=pTrigger; p; p=p->pNext){ for(p=pTrigger; p; p=p->pNext){
if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){ if( p->op==op && checkColumnOverlap(p->pColumns,pChanges) ){
TriggerPrg *pPrg; TriggerPrg *pPrg;
pPrg = getRowTrigger(pParse, p, op, pTab, orconf); pPrg = getRowTrigger(pParse, p, pTab, orconf);
if( pPrg ){ if( pPrg ){
mask |= pPrg->oldmask; mask |= pPrg->oldmask;
} }

View File

@@ -4772,9 +4772,17 @@ case OP_RowSetTest: { /* jump, in1, in3 */
** P4 is a pointer to the VM containing the trigger program. ** P4 is a pointer to the VM containing the trigger program.
*/ */
case OP_Program: { /* jump */ case OP_Program: { /* jump */
VdbeFrame *pFrame; int nMem; /* Number of memory registers for sub-program */
SubProgram *pProgram = pOp->p4.pProgram; int nByte; /* Bytes of runtime space required for sub-program */
Mem *pRt = &p->aMem[pOp->p3]; /* Register to allocate runtime space */ Mem *pRt; /* Register to allocate runtime space */
Mem *pMem; /* Used to iterate through memory cells */
Mem *pEnd; /* Last memory cell in new array */
VdbeFrame *pFrame; /* New vdbe frame to execute in */
SubProgram *pProgram; /* Sub-program to execute */
void *t; /* Token identifying trigger */
pProgram = pOp->p4.pProgram;
pRt = &p->aMem[pOp->p3];
assert( pProgram->nOp>0 ); assert( pProgram->nOp>0 );
/* If the SQLITE_NoRecTriggers flag it set, then recursive invocation of /* If the SQLITE_NoRecTriggers flag it set, then recursive invocation of
@@ -4789,7 +4797,7 @@ case OP_Program: { /* jump */
** variable. ** variable.
*/ */
if( db->flags&SQLITE_NoRecTriggers ){ if( db->flags&SQLITE_NoRecTriggers ){
void *t = pProgram->token; t = pProgram->token;
for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent); for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent);
if( pFrame ) break; if( pFrame ) break;
} }
@@ -4806,16 +4814,13 @@ case OP_Program: { /* jump */
** the trigger program. If this trigger has been fired before, then pRt ** the trigger program. If this trigger has been fired before, then pRt
** is already allocated. Otherwise, it must be initialized. */ ** is already allocated. Otherwise, it must be initialized. */
if( (pRt->flags&MEM_Frame)==0 ){ if( (pRt->flags&MEM_Frame)==0 ){
Mem *pMem;
Mem *pEnd;
/* SubProgram.nMem is set to the number of memory cells used by the /* SubProgram.nMem is set to the number of memory cells used by the
** program stored in SubProgram.aOp. As well as these, one memory ** program stored in SubProgram.aOp. As well as these, one memory
** cell is required for each cursor used by the program. Set local ** cell is required for each cursor used by the program. Set local
** variable nMem (and later, VdbeFrame.nChildMem) to this value. ** variable nMem (and later, VdbeFrame.nChildMem) to this value.
*/ */
int nMem = pProgram->nMem + pProgram->nCsr; nMem = pProgram->nMem + pProgram->nCsr;
int nByte = ROUND8(sizeof(VdbeFrame)) nByte = ROUND8(sizeof(VdbeFrame))
+ nMem * sizeof(Mem) + nMem * sizeof(Mem)
+ pProgram->nCsr * sizeof(VdbeCursor *); + pProgram->nCsr * sizeof(VdbeCursor *);
pFrame = sqlite3DbMallocZero(db, nByte); pFrame = sqlite3DbMallocZero(db, nByte);
@@ -4880,8 +4885,10 @@ case OP_Program: { /* jump */
** calling OP_Program instruction. ** calling OP_Program instruction.
*/ */
case OP_Param: { /* out2-prerelease */ case OP_Param: { /* out2-prerelease */
VdbeFrame *pFrame = p->pFrame; VdbeFrame *pFrame;
Mem *pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1]; Mem *pIn;
pFrame = p->pFrame;
pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1];
sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem); sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem);
break; break;
} }

View File

@@ -89,6 +89,19 @@ struct VdbeCursor {
}; };
typedef struct VdbeCursor VdbeCursor; typedef struct VdbeCursor VdbeCursor;
/*
** When a sub-program is executed (OP_Program), a structure of this type
** is allocated to store the current value of the program counter, as
** well as the current memory cell array and various other frame specific
** values stored in the Vdbe struct. When the sub-program is finished,
** these values are copied back to the Vdbe from the VdbeFrame structure,
** restoring the state of the VM to as it was before the sub-program
** began executing.
**
** Frames are stored in a linked list headed at Vdbe.pParent. Vdbe.pParent
** is the parent of the current frame, or zero if the current frame
** is the main Vdbe program.
*/
typedef struct VdbeFrame VdbeFrame; typedef struct VdbeFrame VdbeFrame;
struct VdbeFrame { struct VdbeFrame {
Vdbe *v; /* VM this frame belongs to */ Vdbe *v; /* VM this frame belongs to */
@@ -99,12 +112,12 @@ struct VdbeFrame {
int nMem; /* Number of entries in aMem */ int nMem; /* Number of entries in aMem */
VdbeCursor **apCsr; /* Element of Vdbe cursors */ VdbeCursor **apCsr; /* Element of Vdbe cursors */
u16 nCursor; /* Number of entries in apCsr */ u16 nCursor; /* Number of entries in apCsr */
VdbeFrame *pParent; /* Parent of this frame */
void *token; /* Copy of SubProgram.token */ void *token; /* Copy of SubProgram.token */
int nChildMem; /* Number of memory cells for child frame */ int nChildMem; /* Number of memory cells for child frame */
int nChildCsr; /* Number of cursors for child frame */ int nChildCsr; /* Number of cursors for child frame */
i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
int nChange; /* Statement changes (Vdbe.nChanges) */ int nChange; /* Statement changes (Vdbe.nChanges) */
VdbeFrame *pParent; /* Parent of this frame */
}; };
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))]) #define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
@@ -308,7 +321,6 @@ struct Vdbe {
#endif #endif
VdbeFrame *pFrame; /* Parent frame */ VdbeFrame *pFrame; /* Parent frame */
int nFrame; /* Number of frames in pFrame list */ int nFrame; /* Number of frames in pFrame list */
u8 noRecTrigger; /* True to disable recursive triggers */
}; };
/* /*

View File

@@ -339,9 +339,24 @@ int sqlite3VdbeCurrentAddr(Vdbe *p){
return p->nOp; return p->nOp;
} }
/*
** This function returns a pointer to the array of opcodes associated with
** the Vdbe passed as the first argument. It is the callers responsibility
** to arrange for the returned array to be eventually freed using the
** vdbeFreeOpArray() function.
**
** Before returning, *pnOp is set to the number of entries in the returned
** array. Also, *pnMaxArg is set to the larger of its current value and
** the number of entries in the Vdbe.apArg[] array required to execute the
** returned program.
*/
VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
VdbeOp *aOp = p->aOp; VdbeOp *aOp = p->aOp;
assert( aOp && !p->db->mallocFailed ); assert( aOp && !p->db->mallocFailed );
/* Check that sqlite3VdbeUsesBtree() was not called on this VM */
assert( p->aMutex.nMutex==0 );
resolveP2Values(p, pnMaxArg); resolveP2Values(p, pnMaxArg);
*pnOp = p->nOp; *pnOp = p->nOp;
p->aOp = 0; p->aOp = 0;
@@ -499,6 +514,11 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
} }
} }
/*
** Free the space allocated for aOp and any p4 values allocated for the
** opcodes contained within. If aOp is not NULL it is assumed to contain
** nOp entries.
*/
static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
if( aOp ){ if( aOp ){
Op *pOp; Op *pOp;
@@ -512,6 +532,19 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
sqlite3DbFree(db, aOp); sqlite3DbFree(db, aOp);
} }
/*
** Decrement the ref-count on the SubProgram structure passed as the
** second argument. If the ref-count reaches zero, free the structure.
**
** The array of VDBE opcodes stored as SubProgram.aOp is freed if
** either the ref-count reaches zero or parameter freeop is non-zero.
**
** Since the array of opcodes pointed to by SubProgram.aOp may directly
** or indirectly contain a reference to the SubProgram structure itself.
** By passing a non-zero freeop parameter, the caller may ensure that all
** SubProgram structures and their aOp arrays are freed, even when there
** are such circular references.
*/
void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){ void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){
if( p ){ if( p ){
assert( p->nRef>0 ); assert( p->nRef>0 );
@@ -815,7 +848,6 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
/* /*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
**
*/ */
void sqlite3VdbeUsesBtree(Vdbe *p, int i){ void sqlite3VdbeUsesBtree(Vdbe *p, int i){
int mask; int mask;
@@ -887,6 +919,10 @@ static void releaseMemArray(Mem *p, int N){
} }
} }
/*
** Delete a VdbeFrame object and its contents. VdbeFrame objects are
** allocated by the OP_Program opcode in sqlite3VdbeExec().
*/
void sqlite3VdbeFrameDelete(VdbeFrame *p){ void sqlite3VdbeFrameDelete(VdbeFrame *p){
int i; int i;
Mem *aMem = VdbeFrameMem(p); Mem *aMem = VdbeFrameMem(p);
@@ -1334,6 +1370,11 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
} }
} }
/*
** Copy the values stored in the VdbeFrame structure to its Vdbe. This
** is used, for example, when a trigger sub-program is halted to restore
** control to the main program.
*/
int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v; Vdbe *v = pFrame->v;
v->aOp = pFrame->aOp; v->aOp = pFrame->aOp;

View File

@@ -941,21 +941,21 @@ FuncDef *sqlite3VtabOverloadFunction(
** is a no-op. ** is a no-op.
*/ */
void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
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<pRoot->nVtabLock; i++){ for(i=0; i<pToplevel->nVtabLock; i++){
if( pTab==pRoot->apVtabLock[i] ) return; if( pTab==pToplevel->apVtabLock[i] ) return;
} }
n = (pRoot->nVtabLock+1)*sizeof(pRoot->apVtabLock[0]); n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]);
apVtabLock = sqlite3_realloc(pRoot->apVtabLock, n); apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n);
if( apVtabLock ){ if( apVtabLock ){
pRoot->apVtabLock = apVtabLock; pToplevel->apVtabLock = apVtabLock;
pRoot->apVtabLock[pRoot->nVtabLock++] = pTab; pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab;
}else{ }else{
pRoot->db->mallocFailed = 1; pToplevel->db->mallocFailed = 1;
} }
} }

View File

@@ -96,6 +96,7 @@ while {![eof stdin]} {
append line " " append line " "
foreach v $vlist { foreach v $vlist {
regsub -all "(\[^a-zA-Z0-9>.\])${v}(\\W)" $line "\\1u.$sname.$v\\2" line regsub -all "(\[^a-zA-Z0-9>.\])${v}(\\W)" $line "\\1u.$sname.$v\\2" line
regsub -all "(\[^a-zA-Z0-9>.\])${v}(\\W)" $line "\\1u.$sname.$v\\2" line
} }
append afterUnion [string trimright $line]\n append afterUnion [string trimright $line]\n
} elseif {$line=="" && [eof stdin]} { } elseif {$line=="" && [eof stdin]} {