Fix some cases where executing SQL from within a user-function callback could cause problems related to statement-transactions. (CVS 6355)
FossilOrigin-Name: a60f4191791dd7bb49d5c95b350a9924845b59a8
This commit is contained in:
parent
7f75122149
commit
bd43455c38
33
manifest
33
manifest
@ -1,5 +1,5 @@
|
||||
C Move\sthe\srowid\scache\sout\sof\sVdbeCursor\sand\sinto\sBtCursor.\s\sWhen\smultiple\nBtCursors\sare\sopen\son\sthe\ssame\stable,\sset\stheir\srowid\scache\sall\sat\sthe\nsame\stime.\s\sTicket\s#3731.\s(CVS\s6354)
|
||||
D 2009-03-17T22:33:01
|
||||
C Fix\ssome\scases\swhere\sexecuting\sSQL\sfrom\swithin\sa\suser-function\scallback\scould\scause\sproblems\srelated\sto\sstatement-transactions.\s(CVS\s6355)
|
||||
D 2009-03-18T10:33:01
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in 583e87706abc3026960ed759aff6371faf84c211
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
@ -103,9 +103,9 @@ F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627
|
||||
F src/backup.c 0082d0e5a63f04e88faee0dff0a7d63d3e92a78d
|
||||
F src/bitvec.c 44f7059ac1f874d364b34af31b9617e52223ba75
|
||||
F src/btmutex.c 341502bc496dc0840dcb00cde65680fb0e85c3ab
|
||||
F src/btree.c 805ca9606405ae80fe1afa001e7054f13ecb6046
|
||||
F src/btree.h 8ed34391fe4288c48bc540d80103f3ffabf3d3a5
|
||||
F src/btreeInt.h 6d0c6fad247f27a09af1bb4f810bfad232aefaa7
|
||||
F src/btree.c 081e1aac3ce4c79c5ed628a1fec533857d175eea
|
||||
F src/btree.h e302c5747494067cd4f5763000fbe7bca767d816
|
||||
F src/btreeInt.h 17697718bb5281b12da5c65ecb0bffc45f2c0125
|
||||
F src/build.c 5f050f06ee4219689e211fa47fd3cc8a817ede57
|
||||
F src/callback.c 09c6fedc77a45db99ba25a75d61382830314b357
|
||||
F src/complete.c cb14e06dbe79dee031031f0d9e686ff306afe07c
|
||||
@ -122,7 +122,7 @@ F src/insert.c 71286d081a919a27ef22eaeccbe2718f93dc6aa9
|
||||
F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
|
||||
F src/legacy.c 8b3b95d48d202614946d7ce7256e7ba898905c3b
|
||||
F src/loadext.c 3f96631089fc4f3871a67f02f2e4fc7ea4d51edc
|
||||
F src/main.c 99955156dd3167e79a492187cf05e3a73196fbe2
|
||||
F src/main.c 76f953dabeed1096ae605a1a4c31aae0bbc426a1
|
||||
F src/malloc.c b9c59b33539af74641362a7496ecc3efd6477f6d
|
||||
F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c
|
||||
F src/mem1.c 3bfb39e4f60b0179713a7c087b2d4f0dc205735f
|
||||
@ -159,14 +159,14 @@ F src/select.c 4d0b77fd76ff80f09a798ee98953e344c9de8fbb
|
||||
F src/shell.c 0a11f831603f17fea20ca97133c0f64e716af4a7
|
||||
F src/sqlite.h.in 0f756e9e8db9d491d0f17ea9c07952974975e43d
|
||||
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
|
||||
F src/sqliteInt.h a48dc31146b89d5ed73fba50101030fd3c56ec4e
|
||||
F src/sqliteInt.h 250be86c98646e0d48436d6455b6fe916742bffa
|
||||
F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d
|
||||
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
|
||||
F src/table.c 332ab0ea691e63862e2a8bdfe2c0617ee61062a3
|
||||
F src/tclsqlite.c 8a472804b901d4559213eeda538c2eadb2ad7f2a
|
||||
F src/test1.c 17300af44640eea439778f5b5e03e0d68a6f00a2
|
||||
F src/test2.c 71c22e2974f8094fe0fd1eba8f27872dde9b2a39
|
||||
F src/test3.c 88a246b56b824275300e6c899634fbac1dc94b14
|
||||
F src/test3.c d3115b301c6ee761b102f315fe24125f3d6c3a4d
|
||||
F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c
|
||||
F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
|
||||
F src/test6.c 1a0a7a1f179469044b065b4a88aab9faee114101
|
||||
@ -200,11 +200,11 @@ F src/update.c 8ededddcde6f7b6da981dd0429a5d34518a475b7
|
||||
F src/utf.c 1da9c832dba0fa8f865b5b902d93f420a1ee4245
|
||||
F src/util.c 469d74f5bf09ed6398702c7da2ef8a34e979a1c1
|
||||
F src/vacuum.c 4929a585ef0fb1dfaf46302f8a9c4aa30c2d9cf5
|
||||
F src/vdbe.c 2ae831d43cb22b42dbbe90555bd57e053af53469
|
||||
F src/vdbe.c f8164c2830f82714a77b1f2a97c2e9c4efbcb3bb
|
||||
F src/vdbe.h d70a68bee196ab228914a3902c79dbd24342a0f2
|
||||
F src/vdbeInt.h 8732228974ca23aedfd74c37a444b7bede857927
|
||||
F src/vdbeInt.h 53a2f4696871712646c77351904576cca6ad9752
|
||||
F src/vdbeapi.c ffd5d8b493590da6e09fd54b1bea1a9d38247f11
|
||||
F src/vdbeaux.c feeafee5f9de51c0d30907e0600ce4db5d032df8
|
||||
F src/vdbeaux.c e9b76cf2ca8f78b692be984381cc4b27defc902a
|
||||
F src/vdbeblob.c 2852bae14c87129835938db58a77c3121e3ae962
|
||||
F src/vdbemem.c 543a79d722734d2f8b7ad70f08218c30bcc5bbf5
|
||||
F src/vtab.c bf409d2dc068e1bb82beeb9eef120ccfff541afb
|
||||
@ -460,7 +460,7 @@ F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
|
||||
F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
|
||||
F test/malloc_common.tcl 984baeb6c6b185e798827d1187d426acc2bc4962
|
||||
F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c
|
||||
F test/memdb.test 97d59567b282f0807a25eba8df34743476bf9ca8
|
||||
F test/memdb.test 09240c19e95b063e193b95d6e3eb99f528fbc2dd
|
||||
F test/memleak.test d2d2a1ff7105d32dc3fdf691458cf6cba58c7217
|
||||
F test/memsubsys1.test bdc24a38d198679d777ca4efcc089fd3948567a6
|
||||
F test/memsubsys2.test 72a731225997ad5e8df89fdbeae9224616b6aecc
|
||||
@ -628,6 +628,7 @@ F test/tkt3554.test 0463fea3650ac18d3694a8f39e6e25cddc1ac1fc
|
||||
F test/tkt3581.test 1966b7193f1e3f14951cce8c66907ae69454e9a3
|
||||
F test/tkt35xx.test 53bca895091e968126a858ee7da186f59f328994
|
||||
F test/tkt3630.test 929f64852103054125200bc825c316d5f75d42f7
|
||||
F test/tkt3718.test 3ee5e25702f3f5a31340b2766d7a7fac2b5ce99c
|
||||
F test/tkt3731.test 8a6e3732f5a8a24eb875a6faf287ef77bb8c0579
|
||||
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||
F test/trace.test 951cd0f5f571e7f36bf7bfe04be70f90fb16fb00
|
||||
@ -708,7 +709,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
P afadddc34eee3d6a39102b790ce1a869b33d4286
|
||||
R a87ad391e70921cc51f370f982e9d47a
|
||||
U drh
|
||||
Z 4d6edfbd554e5429583cecea6d7e5900
|
||||
P 189785832a7dc9f4a0a2113d850b92b987e0f9bf
|
||||
R d44f7a2fcf465cdcd3971d1da17f54cb
|
||||
U danielk1977
|
||||
Z 855899ed3ec24df7586ecfc973fa4034
|
||||
|
@ -1 +1 @@
|
||||
189785832a7dc9f4a0a2113d850b92b987e0f9bf
|
||||
a60f4191791dd7bb49d5c95b350a9924845b59a8
|
92
src/btree.c
92
src/btree.c
@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.574 2009/03/17 22:33:01 drh Exp $
|
||||
** $Id: btree.c,v 1.575 2009/03/18 10:33:01 danielk1977 Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** See the header comment on "btreeInt.h" for additional information.
|
||||
@ -1996,7 +1996,6 @@ static void unlockBtreeIfUnused(BtShared *pBt){
|
||||
releasePage(pBt->pPage1);
|
||||
}
|
||||
pBt->pPage1 = 0;
|
||||
pBt->inStmt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2141,9 +2140,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( wrflag ) pBt->inStmt = 0;
|
||||
}else{
|
||||
if( rc!=SQLITE_OK ){
|
||||
unlockBtreeIfUnused(pBt);
|
||||
}
|
||||
}while( rc==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
|
||||
@ -2643,7 +2640,6 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
|
||||
return rc;
|
||||
}
|
||||
pBt->inTransaction = TRANS_READ;
|
||||
pBt->inStmt = 0;
|
||||
}
|
||||
clearAllSharedCacheTableLocks(p);
|
||||
|
||||
@ -2800,7 +2796,6 @@ int sqlite3BtreeRollback(Btree *p){
|
||||
|
||||
btreeClearHasContent(pBt);
|
||||
p->inTrans = TRANS_NONE;
|
||||
pBt->inStmt = 0;
|
||||
unlockBtreeIfUnused(pBt);
|
||||
|
||||
btreeIntegrity(p);
|
||||
@ -2809,29 +2804,33 @@ int sqlite3BtreeRollback(Btree *p){
|
||||
}
|
||||
|
||||
/*
|
||||
** Start a statement subtransaction. The subtransaction can
|
||||
** can be rolled back independently of the main transaction.
|
||||
** You must start a transaction before starting a subtransaction.
|
||||
** The subtransaction is ended automatically if the main transaction
|
||||
** commits or rolls back.
|
||||
**
|
||||
** Only one subtransaction may be active at a time. It is an error to try
|
||||
** to start a new subtransaction if another subtransaction is already active.
|
||||
** Start a statement subtransaction. The subtransaction can can be rolled
|
||||
** back independently of the main transaction. You must start a transaction
|
||||
** before starting a subtransaction. The subtransaction is ended automatically
|
||||
** if the main transaction commits or rolls back.
|
||||
**
|
||||
** Statement subtransactions are used around individual SQL statements
|
||||
** that are contained within a BEGIN...COMMIT block. If a constraint
|
||||
** error occurs within the statement, the effect of that one statement
|
||||
** can be rolled back without having to rollback the entire transaction.
|
||||
**
|
||||
** A statement sub-transaction is implemented as an anonymous savepoint. The
|
||||
** value passed as the second parameter is the total number of savepoints,
|
||||
** including the new anonymous savepoint, open on the B-Tree. i.e. if there
|
||||
** are no active savepoints and no other statement-transactions open,
|
||||
** iStatement is 1. This anonymous savepoint can be released or rolled back
|
||||
** using the sqlite3BtreeSavepoint() function.
|
||||
*/
|
||||
int sqlite3BtreeBeginStmt(Btree *p){
|
||||
int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
|
||||
int rc;
|
||||
BtShared *pBt = p->pBt;
|
||||
sqlite3BtreeEnter(p);
|
||||
pBt->db = p->db;
|
||||
assert( p->inTrans==TRANS_WRITE );
|
||||
assert( !pBt->inStmt );
|
||||
assert( pBt->readOnly==0 );
|
||||
if( NEVER(p->inTrans!=TRANS_WRITE || pBt->inStmt || pBt->readOnly) ){
|
||||
assert( iStatement>0 );
|
||||
assert( iStatement>p->db->nSavepoint );
|
||||
if( NEVER(p->inTrans!=TRANS_WRITE || pBt->readOnly) ){
|
||||
rc = SQLITE_INTERNAL;
|
||||
}else{
|
||||
assert( pBt->inTransaction==TRANS_WRITE );
|
||||
@ -2840,53 +2839,7 @@ int sqlite3BtreeBeginStmt(Btree *p){
|
||||
** SQL statements. It is illegal to open, release or rollback any
|
||||
** such savepoints while the statement transaction savepoint is active.
|
||||
*/
|
||||
rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint+1);
|
||||
pBt->inStmt = 1;
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Commit the statment subtransaction currently in progress. If no
|
||||
** subtransaction is active, this is a no-op.
|
||||
*/
|
||||
int sqlite3BtreeCommitStmt(Btree *p){
|
||||
int rc = SQLITE_OK;
|
||||
BtShared *pBt = p->pBt;
|
||||
sqlite3BtreeEnter(p);
|
||||
pBt->db = p->db;
|
||||
if( p->inTrans==TRANS_WRITE && pBt->inStmt ){
|
||||
int iStmtpoint = p->db->nSavepoint;
|
||||
assert( pBt->readOnly==0 );
|
||||
rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
|
||||
pBt->inStmt = 0;
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Rollback the active statement subtransaction. If no subtransaction
|
||||
** is active this routine is a no-op.
|
||||
**
|
||||
** All cursors will be invalidated by this operation. Any attempt
|
||||
** to use a cursor that was open at the beginning of this operation
|
||||
** will result in an error.
|
||||
*/
|
||||
int sqlite3BtreeRollbackStmt(Btree *p){
|
||||
int rc = SQLITE_OK;
|
||||
BtShared *pBt = p->pBt;
|
||||
sqlite3BtreeEnter(p);
|
||||
pBt->db = p->db;
|
||||
if( p->inTrans==TRANS_WRITE && pBt->inStmt ){
|
||||
int iStmtpoint = p->db->nSavepoint;
|
||||
assert( pBt->readOnly==0 );
|
||||
rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_ROLLBACK, iStmtpoint);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSavepoint(pBt->pPager, SAVEPOINT_RELEASE, iStmtpoint);
|
||||
}
|
||||
pBt->inStmt = 0;
|
||||
rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement);
|
||||
}
|
||||
sqlite3BtreeLeave(p);
|
||||
return rc;
|
||||
@ -2908,7 +2861,6 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
|
||||
int rc = SQLITE_OK;
|
||||
if( p && p->inTrans==TRANS_WRITE ){
|
||||
BtShared *pBt = p->pBt;
|
||||
assert( pBt->inStmt==0 );
|
||||
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
|
||||
assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) );
|
||||
sqlite3BtreeEnter(p);
|
||||
@ -7401,14 +7353,6 @@ int sqlite3BtreeIsInTrans(Btree *p){
|
||||
return (p && (p->inTrans==TRANS_WRITE));
|
||||
}
|
||||
|
||||
/*
|
||||
** Return non-zero if a statement transaction is active.
|
||||
*/
|
||||
int sqlite3BtreeIsInStmt(Btree *p){
|
||||
assert( sqlite3BtreeHoldsMutex(p) );
|
||||
return ALWAYS(p->pBt) && p->pBt->inStmt;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return non-zero if a read (or write) transaction is active.
|
||||
*/
|
||||
|
@ -13,7 +13,7 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.110 2009/03/17 22:33:01 drh Exp $
|
||||
** @(#) $Id: btree.h,v 1.111 2009/03/18 10:33:01 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
@ -91,12 +91,9 @@ int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
|
||||
int sqlite3BtreeCommitPhaseTwo(Btree*);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*);
|
||||
int sqlite3BtreeCommitStmt(Btree*);
|
||||
int sqlite3BtreeRollbackStmt(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*,int);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInStmt(Btree*);
|
||||
int sqlite3BtreeIsInReadTrans(Btree*);
|
||||
int sqlite3BtreeIsInBackup(Btree*);
|
||||
void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
|
||||
|
@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btreeInt.h,v 1.44 2009/03/17 22:33:01 drh Exp $
|
||||
** $Id: btreeInt.h,v 1.45 2009/03/18 10:33:01 danielk1977 Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@ -380,7 +380,6 @@ struct BtShared {
|
||||
sqlite3 *db; /* Database connection currently using this Btree */
|
||||
BtCursor *pCursor; /* A list of all open cursors */
|
||||
MemPage *pPage1; /* First page of the database */
|
||||
u8 inStmt; /* True if we are in a statement subtransaction */
|
||||
u8 readOnly; /* True if the underlying file is readonly */
|
||||
u8 pageSizeFixed; /* True if the page size can no longer be changed */
|
||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||
|
@ -14,7 +14,7 @@
|
||||
** other files are for internal use by SQLite and should not be
|
||||
** accessed by users of the library.
|
||||
**
|
||||
** $Id: main.c,v 1.531 2009/03/16 13:19:36 danielk1977 Exp $
|
||||
** $Id: main.c,v 1.532 2009/03/18 10:33:01 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -560,6 +560,7 @@ void sqlite3CloseSavepoints(sqlite3 *db){
|
||||
sqlite3DbFree(db, pTmp);
|
||||
}
|
||||
db->nSavepoint = 0;
|
||||
db->nStatement = 0;
|
||||
db->isTransactionSavepoint = 0;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.842 2009/03/17 17:49:00 danielk1977 Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.843 2009/03/18 10:33:01 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
@ -807,6 +807,7 @@ struct sqlite3 {
|
||||
#endif
|
||||
Savepoint *pSavepoint; /* List of active savepoints */
|
||||
int nSavepoint; /* Number of non-transaction savepoints */
|
||||
int nStatement; /* Number of nested statement-transactions */
|
||||
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
|
11
src/test3.c
11
src/test3.c
@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test3.c,v 1.102 2008/10/27 13:59:34 danielk1977 Exp $
|
||||
** $Id: test3.c,v 1.103 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "btreeInt.h"
|
||||
@ -235,7 +235,7 @@ static int btree_begin_statement(
|
||||
}
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeBeginStmt(pBt);
|
||||
rc = sqlite3BtreeBeginStmt(pBt, 1);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
@ -264,7 +264,10 @@ static int btree_rollback_statement(
|
||||
}
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeRollbackStmt(pBt);
|
||||
rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0);
|
||||
}
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
@ -293,7 +296,7 @@ static int btree_commit_statement(
|
||||
}
|
||||
pBt = sqlite3TestTextToPtr(argv[1]);
|
||||
sqlite3BtreeEnter(pBt);
|
||||
rc = sqlite3BtreeCommitStmt(pBt);
|
||||
rc = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, 0);
|
||||
sqlite3BtreeLeave(pBt);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
|
28
src/vdbe.c
28
src/vdbe.c
@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.826 2009/03/17 22:33:01 drh Exp $
|
||||
** $Id: vdbe.c,v 1.827 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@ -1086,6 +1086,23 @@ case OP_ResultRow: {
|
||||
assert( pOp->p1>0 );
|
||||
assert( pOp->p1+pOp->p2<=p->nMem+1 );
|
||||
|
||||
/* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
|
||||
** DML statements invoke this opcode to return the number of rows
|
||||
** modified to the user. This is the only way that a VM that
|
||||
** opens a statement transaction may invoke this opcode.
|
||||
**
|
||||
** In case this is such a statement, close any statement transaction
|
||||
** opened by this VM before returning control to the user. This is to
|
||||
** ensure that statement-transactions are always nested, not overlapping.
|
||||
** If the open statement-transaction is not closed here, then the user
|
||||
** may step another VM that opens its own statement transaction. This
|
||||
** may lead to overlapping statement transactions.
|
||||
*/
|
||||
assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
|
||||
if( SQLITE_OK!=(rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE)) ){
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invalidate all ephemeral cursor row caches */
|
||||
p->cacheCtr = (p->cacheCtr + 2)|1;
|
||||
|
||||
@ -2386,10 +2403,12 @@ case OP_Statement: {
|
||||
pBt = db->aDb[i].pBt;
|
||||
assert( sqlite3BtreeIsInTrans(pBt) );
|
||||
assert( (p->btreeMask & (1<<i))!=0 );
|
||||
if( !sqlite3BtreeIsInStmt(pBt) ){
|
||||
rc = sqlite3BtreeBeginStmt(pBt);
|
||||
p->openedStatement = 1;
|
||||
if( p->iStatement==0 ){
|
||||
assert( db->nStatement>=0 && db->nSavepoint>=0 );
|
||||
db->nStatement++;
|
||||
p->iStatement = db->nSavepoint + db->nStatement;
|
||||
}
|
||||
rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2576,6 +2595,7 @@ case OP_AutoCommit: {
|
||||
goto vdbe_return;
|
||||
}
|
||||
}
|
||||
assert( db->nStatement==0 );
|
||||
sqlite3CloseSavepoints(db);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
rc = SQLITE_DONE;
|
||||
|
@ -15,7 +15,7 @@
|
||||
** 6000 lines long) it was split up into several smaller files and
|
||||
** this header information was factored out.
|
||||
**
|
||||
** $Id: vdbeInt.h,v 1.165 2009/03/17 22:33:01 drh Exp $
|
||||
** $Id: vdbeInt.h,v 1.166 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _VDBEINT_H_
|
||||
#define _VDBEINT_H_
|
||||
@ -306,7 +306,7 @@ struct Vdbe {
|
||||
#ifdef SQLITE_DEBUG
|
||||
FILE *trace; /* Write an execution trace here, if not NULL */
|
||||
#endif
|
||||
int openedStatement; /* True if this VM has opened a statement journal */
|
||||
int iStatement; /* Statement number (or 0 if has not opened stmt) */
|
||||
#ifdef SQLITE_SSE
|
||||
int fetchId; /* Statement number used by sqlite3_fetch_statement */
|
||||
int lru; /* Counter used for LRU cache replacement */
|
||||
@ -374,6 +374,7 @@ int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
|
||||
const char *sqlite3OpcodeName(int);
|
||||
int sqlite3VdbeOpcodeHasProperty(int, int);
|
||||
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
|
||||
int sqlite3VdbeCloseStatement(Vdbe *, int);
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
int sqlite3VdbeReleaseBuffers(Vdbe *p);
|
||||
#endif
|
||||
|
@ -14,7 +14,7 @@
|
||||
** to version 2.8.7, all this code was combined into the vdbe.c source file.
|
||||
** But that file was getting too big so this subroutines were split out.
|
||||
**
|
||||
** $Id: vdbeaux.c,v 1.442 2009/03/16 13:19:36 danielk1977 Exp $
|
||||
** $Id: vdbeaux.c,v 1.443 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "vdbeInt.h"
|
||||
@ -1145,7 +1145,7 @@ void sqlite3VdbeMakeReady(
|
||||
p->nChange = 0;
|
||||
p->cacheCtr = 1;
|
||||
p->minWriteFileFormat = 255;
|
||||
p->openedStatement = 0;
|
||||
p->iStatement = 0;
|
||||
#ifdef VDBE_PROFILE
|
||||
{
|
||||
int i;
|
||||
@ -1564,6 +1564,48 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If the Vdbe passed as the first argument opened a statement-transaction,
|
||||
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
|
||||
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
|
||||
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
|
||||
** statement transaction is commtted.
|
||||
**
|
||||
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
|
||||
** Otherwise SQLITE_OK.
|
||||
*/
|
||||
int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->iStatement ){
|
||||
int i;
|
||||
const int iSavepoint = p->iStatement-1;
|
||||
sqlite3 *const db = p->db;
|
||||
|
||||
assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE);
|
||||
assert( db->nStatement>0 );
|
||||
assert( p->iStatement==(db->nStatement+db->nSavepoint) );
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
int rc2 = SQLITE_OK;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
}
|
||||
if( rc2==SQLITE_OK ){
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
db->nStatement--;
|
||||
p->iStatement = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine is called the when a VDBE tries to halt. If the VDBE
|
||||
** has made changes and is in autocommit mode, then commit those
|
||||
@ -1578,10 +1620,8 @@ static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
|
||||
** means the close did not happen and needs to be repeated.
|
||||
*/
|
||||
int sqlite3VdbeHalt(Vdbe *p){
|
||||
int rc; /* Used to store transient return codes */
|
||||
sqlite3 *db = p->db;
|
||||
int i;
|
||||
int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */
|
||||
int isSpecialError; /* Set to true if SQLITE_NOMEM or IOERR */
|
||||
|
||||
/* This function contains the logic that determines if a statement or
|
||||
** transaction will be committed or rolled back as a result of the
|
||||
@ -1611,6 +1651,8 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
/* No commit or rollback needed if the program never started */
|
||||
if( p->pc>=0 ){
|
||||
int mrc; /* Primary error code from p->rc */
|
||||
int eStatementOp = 0;
|
||||
int isSpecialError; /* Set to true if a 'special' error */
|
||||
|
||||
/* Lock all btrees used by the statement */
|
||||
sqlite3BtreeMutexArrayEnter(&p->aMutex);
|
||||
@ -1625,11 +1667,11 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
*/
|
||||
if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
|
||||
if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
p->rc = SQLITE_BUSY;
|
||||
}else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL)
|
||||
&& p->usesStmtJournal ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
}else{
|
||||
/* We are forced to roll back the active transaction. Before doing
|
||||
** so, abort any other statements this handle currently has active.
|
||||
@ -1642,8 +1684,8 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}
|
||||
}
|
||||
|
||||
/* If the auto-commit flag is set and this is the only active vdbe, then
|
||||
** we do either a commit or rollback of the current transaction.
|
||||
/* If the auto-commit flag is set and this is the only active writer
|
||||
** VM, then we do either a commit or rollback of the current transaction.
|
||||
**
|
||||
** Note: This block also runs if one of the special errors handled
|
||||
** above has occurred.
|
||||
@ -1658,7 +1700,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
** successful or hit an 'OR FAIL' constraint. This means a commit
|
||||
** is required.
|
||||
*/
|
||||
int rc = vdbeCommit(db, p);
|
||||
rc = vdbeCommit(db, p);
|
||||
if( rc==SQLITE_BUSY ){
|
||||
sqlite3BtreeMutexArrayLeave(&p->aMutex);
|
||||
return SQLITE_BUSY;
|
||||
@ -1671,13 +1713,12 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}else{
|
||||
sqlite3RollbackAll(db);
|
||||
}
|
||||
}else if( !xFunc ){
|
||||
db->nStatement = 0;
|
||||
}else if( eStatementOp==0 ){
|
||||
if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
|
||||
if( p->openedStatement ){
|
||||
xFunc = sqlite3BtreeCommitStmt;
|
||||
}
|
||||
eStatementOp = SAVEPOINT_RELEASE;
|
||||
}else if( p->errorAction==OE_Abort ){
|
||||
xFunc = sqlite3BtreeRollbackStmt;
|
||||
eStatementOp = SAVEPOINT_ROLLBACK;
|
||||
}else{
|
||||
invalidateCursorsOnModifiedBtrees(db);
|
||||
sqlite3RollbackAll(db);
|
||||
@ -1686,33 +1727,26 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}
|
||||
}
|
||||
|
||||
/* If xFunc is not NULL, then it is one of sqlite3BtreeRollbackStmt or
|
||||
** sqlite3BtreeCommitStmt. Call it once on each backend. If an error occurs
|
||||
** and the return code is still SQLITE_OK, set the return code to the new
|
||||
** error value.
|
||||
/* If eStatementOp is non-zero, then a statement transaction needs to
|
||||
** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
|
||||
** do so. If this operation returns an error, and the current statement
|
||||
** error code is SQLITE_OK or SQLITE_CONSTRAINT, then set the error
|
||||
** code to the new value.
|
||||
*/
|
||||
assert(!xFunc ||
|
||||
xFunc==sqlite3BtreeCommitStmt ||
|
||||
xFunc==sqlite3BtreeRollbackStmt
|
||||
);
|
||||
for(i=0; xFunc && i<db->nDb; i++){
|
||||
int rc;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
rc = xFunc(pBt);
|
||||
if( eStatementOp ){
|
||||
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
|
||||
if( rc && (p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT) ){
|
||||
p->rc = rc;
|
||||
sqlite3DbFree(db, p->zErrMsg);
|
||||
p->zErrMsg = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If this was an INSERT, UPDATE or DELETE and the statement was committed,
|
||||
** set the change counter.
|
||||
/* If this was an INSERT, UPDATE or DELETE and no statement transaction
|
||||
** has been rolled back, update the database connection change-counter.
|
||||
*/
|
||||
if( p->changeCntOn && p->pc>=0 ){
|
||||
if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
|
||||
if( eStatementOp!=SAVEPOINT_ROLLBACK ){
|
||||
sqlite3VdbeSetChanges(db, p->nChange);
|
||||
}else{
|
||||
sqlite3VdbeSetChanges(db, 0);
|
||||
@ -1752,6 +1786,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
sqlite3ConnectionUnlocked(db);
|
||||
}
|
||||
|
||||
assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is in-memory database backend.
|
||||
#
|
||||
# $Id: memdb.test,v 1.17 2009/01/09 14:29:35 drh Exp $
|
||||
# $Id: memdb.test,v 1.18 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
@ -244,7 +244,7 @@ foreach {i conf1 conf2 cmd t0 t1 t2} {
|
||||
do_test memdb-5.$i {
|
||||
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
|
||||
if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
|
||||
set r0 [catch {execsql [subst {
|
||||
set r0 [catch {execsql "
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a,b,c, UNIQUE(a) $conf1);
|
||||
INSERT INTO t1 SELECT * FROM t2;
|
||||
@ -253,7 +253,7 @@ foreach {i conf1 conf2 cmd t0 t1 t2} {
|
||||
$cmd t3 SET x=1;
|
||||
$cmd t1 SET b=b*2;
|
||||
$cmd t1 SET a=c+5;
|
||||
}]} r1]
|
||||
"} r1]
|
||||
catch {execsql {COMMIT}}
|
||||
if {!$r0} {set r1 [execsql {SELECT a FROM t1 ORDER BY b}]}
|
||||
set r2 [execsql {SELECT x FROM t3}]
|
||||
|
231
test/tkt3718.test
Normal file
231
test/tkt3718.test
Normal file
@ -0,0 +1,231 @@
|
||||
# 2001 September 15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the execution of SQL statements from
|
||||
# within callbacks generated by VMs that themselves open statement
|
||||
# transactions.
|
||||
#
|
||||
# $Id: tkt3718.test,v 1.1 2009/03/18 10:33:02 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_test tkt3718-1.1 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
CREATE TABLE t2(a PRIMARY KEY, b);
|
||||
}
|
||||
} {}
|
||||
|
||||
# SQL scalar function:
|
||||
#
|
||||
# f1(<arg>)
|
||||
#
|
||||
# Uses database handle [db] to execute "SELECT f2(<arg>)". Returns either
|
||||
# the results or error message from the "SELECT f2(<arg>)" query to the
|
||||
# caller.
|
||||
#
|
||||
proc f1 {args} {
|
||||
set a [lindex $args 0]
|
||||
catch { db eval {SELECT f2($a)} } msg
|
||||
set msg
|
||||
}
|
||||
|
||||
# SQL scalar function:
|
||||
#
|
||||
# f2(<arg>)
|
||||
#
|
||||
# Return the value of <arg>. Unless <arg> is "three", in which case throw
|
||||
# an exception.
|
||||
#
|
||||
proc f2 {args} {
|
||||
set a [lindex $args 0]
|
||||
if {$a == "three"} { error "Three!!" }
|
||||
return $a
|
||||
}
|
||||
|
||||
db func f1 f1
|
||||
db func f2 f2
|
||||
|
||||
# The second INSERT statement below uses the f1 user function such that
|
||||
# half-way through the INSERT operation f1() will run an SQL statement
|
||||
# that throws an exception. At one point, before #3718 was fixed, this
|
||||
# caused the statement transaction belonging to the INSERT statement to
|
||||
# be rolled back. The result was that some (but not all) of the rows that
|
||||
# should have been inserted went missing.
|
||||
#
|
||||
do_test tkt3718-1.2 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
INSERT INTO t2 SELECT a, b FROM t1;
|
||||
INSERT INTO t2 SELECT a+5, f1(b) FROM t1;
|
||||
COMMIT;
|
||||
}
|
||||
execsql {
|
||||
SELECT a FROM t2;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
|
||||
# This test turns on the count_changes pragma (causing DML statements to
|
||||
# return SQLITE_ROW once, with a single integer result value reporting the
|
||||
# number of rows affected by the statement). It then executes an INSERT
|
||||
# statement that requires a statement journal. After stepping the statement
|
||||
# once, so that it returns SQLITE_ROW, a second SQL statement that throws an
|
||||
# exception is run. At one point, before #3718 was fixed, this caused the
|
||||
# statement transaction belonging to the INSERT statement to be rolled back.
|
||||
# The result was that none of the rows were actually inserted.
|
||||
#
|
||||
#
|
||||
do_test tkt3718-1.3 {
|
||||
execsql {
|
||||
DELETE FROM t2 WHERE a > 5;
|
||||
PRAGMA count_changes = 1;
|
||||
BEGIN;
|
||||
}
|
||||
db eval {INSERT INTO t2 SELECT a+5, b||'+5' FROM t1} {
|
||||
catch { db eval {SELECT f2('three')} } msg
|
||||
}
|
||||
execsql {
|
||||
COMMIT;
|
||||
SELECT a FROM t2;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
|
||||
do_test tkt3718-1.4 {
|
||||
execsql {pragma count_changes=0}
|
||||
} {}
|
||||
|
||||
# This SQL function executes the SQL specified as an argument against
|
||||
# database [db].
|
||||
#
|
||||
proc sql {doit zSql} {
|
||||
if {$doit} { catchsql $zSql }
|
||||
}
|
||||
db func sql [list sql]
|
||||
|
||||
# The following tests, tkt3718-2.*, test that a nested statement
|
||||
# transaction can be successfully committed or reverted without
|
||||
# affecting the parent statement transaction.
|
||||
#
|
||||
do_test tkt3718-2.1 {
|
||||
execsql { SELECT sql(1, 'DELETE FROM t2 WHERE a = '||a ) FROM t2 WHERE a>5 }
|
||||
execsql { SELECT a from t2 }
|
||||
} {1 2 3 4 5}
|
||||
do_test tkt3718-2.2 {
|
||||
execsql {
|
||||
DELETE FROM t2 WHERE a > 5;
|
||||
BEGIN;
|
||||
INSERT INTO t2 SELECT a+5, sql(a==3,
|
||||
'INSERT INTO t2 SELECT a+10, f2(b) FROM t1'
|
||||
) FROM t1;
|
||||
}
|
||||
execsql {
|
||||
COMMIT;
|
||||
SELECT a FROM t2;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
do_test tkt3718-2.3 {
|
||||
execsql {
|
||||
DELETE FROM t2 WHERE a > 5;
|
||||
BEGIN;
|
||||
INSERT INTO t2 SELECT a+5, sql(a==3,
|
||||
'INSERT INTO t2 SELECT a+10, b FROM t1'
|
||||
) FROM t1;
|
||||
COMMIT;
|
||||
}
|
||||
execsql { SELECT a FROM t2 ORDER BY a+0}
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
|
||||
integrity_check tkt3718.2-4
|
||||
|
||||
# The next set of tests, tkt3718-3.*, test that a statement transaction
|
||||
# that has a committed statement transaction nested inside of it can
|
||||
# be committed or reverted.
|
||||
#
|
||||
foreach {tn io ii results} {
|
||||
1 0 10 {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
|
||||
2 1 10 {6 7 8 9 10 16 17 18 19 20}
|
||||
3 0 11 {1 2 3 4 5 6 7 8 9 10 16 17 18 19 20}
|
||||
4 1 11 {6 7 8 9 10 16 17 18 19 20}
|
||||
} {
|
||||
do_test tkt3718-3.$tn {
|
||||
execsql {
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2 SELECT a+5, b FROM t1;
|
||||
INSERT INTO t2 SELECT a+15, b FROM t1;
|
||||
}
|
||||
|
||||
catchsql "
|
||||
BEGIN;
|
||||
INSERT INTO t2 SELECT a+$io, sql(a==3,
|
||||
'INSERT INTO t2 SELECT a+$ii, b FROM t1'
|
||||
) FROM t1;
|
||||
"
|
||||
|
||||
execsql { COMMIT }
|
||||
|
||||
execsql { SELECT a FROM t2 ORDER BY a+0}
|
||||
} $results
|
||||
|
||||
integrity_check tkt3718-3.$tn.integrity
|
||||
}
|
||||
|
||||
# This is the same test as tkt3718-3.*, but with 3 levels of nesting.
|
||||
#
|
||||
foreach {tn i1 i2 i3 results} {
|
||||
1 0 10 20 {5 10 15 20 25 30}
|
||||
2 0 10 21 {5 10 15 20 30}
|
||||
3 0 11 20 {5 10 20 30}
|
||||
4 0 11 21 {5 10 20 30}
|
||||
5 1 10 20 {10 20 30}
|
||||
6 1 10 21 {10 20 30}
|
||||
7 1 11 20 {10 20 30}
|
||||
8 1 11 21 {10 20 30}
|
||||
} {
|
||||
do_test tkt3718-4.$tn {
|
||||
execsql {
|
||||
DELETE FROM t2;
|
||||
INSERT INTO t2 SELECT a+5, b FROM t1;
|
||||
INSERT INTO t2 SELECT a+15, b FROM t1;
|
||||
INSERT INTO t2 SELECT a+25, b FROM t1;
|
||||
}
|
||||
|
||||
catchsql "
|
||||
BEGIN;
|
||||
INSERT INTO t2 SELECT a+$i1, sql(a==3,
|
||||
'INSERT INTO t2 SELECT a+$i2, sql(a==3,
|
||||
''INSERT INTO t2 SELECT a+$i3, b FROM t1''
|
||||
) FROM t1'
|
||||
) FROM t1;
|
||||
"
|
||||
|
||||
execsql { COMMIT }
|
||||
|
||||
execsql { SELECT a FROM t2 WHERE (a%5)==0 ORDER BY a+0}
|
||||
} $results
|
||||
|
||||
do_test tkt3718-4.$tn.extra {
|
||||
execsql {
|
||||
SELECT
|
||||
(SELECT sum(a) FROM t2)==(SELECT sum(a*5-10) FROM t2 WHERE (a%5)==0)
|
||||
}
|
||||
} {1}
|
||||
|
||||
integrity_check tkt3718-4.$tn.integrity
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user