diff options
-rw-r--r-- | manifest | 31 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/btree.c | 114 | ||||
-rw-r--r-- | src/btreeInt.h | 6 | ||||
-rw-r--r-- | src/vacuum.c | 6 | ||||
-rw-r--r-- | src/vdbe.c | 6 | ||||
-rw-r--r-- | test/tkt-f777251dc7a.test | 99 |
7 files changed, 202 insertions, 62 deletions
@@ -1,5 +1,8 @@ -C Fix\sa\sproblem\sin\sthe\sanalyze3.test\sscript. -D 2009-10-16T15:59:35 +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +C Merge\sthe\ssqlite3_reoptimize()\schanges\sinto\sthe\strunk. +D 2009-10-16T16:21:52 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 4ca3f1dd6efa2075bcb27f4dc43eef749877740d F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -106,9 +109,9 @@ F src/auth.c a5471a6951a18f79d783da34be22cd94dfbe603a F src/backup.c 6f1c2d9862c8a3feb7739dfcca02c1f5352e37f3 F src/bitvec.c ed215b95734045e58358c3b3e16448f8fe6a235a F src/btmutex.c 0f43a75bb5b8147b386e8e1c3e71ba734e3863b7 -F src/btree.c 9c425425784c5d569bc0309c22251698ba906451 +F src/btree.c b2d060e41b4318fb5e52d80b75e19bcef1b1f2db F src/btree.h 577448a890c2ab9b21e6ab74f073526184bceebe -F src/btreeInt.h 1c86297e69380f6577e7ae67452597dd8d5c2705 +F src/btreeInt.h cce1c3360cd5549ffa79f981951dfae93118ad79 F src/build.c 3c5762687d0554ebe8844dfaddb828fcc15fe16d F src/callback.c 10d237171472865f58fb07d515737238c9e06688 F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0 @@ -205,8 +208,8 @@ F src/trigger.c 2053afa9952f69cf451bc0e6ea88072701f2925e F src/update.c 2c8a64237e4fae604468d14380b877d169545b63 F src/utf.c 99cf927eabb104621ba889ac0dd075fc1657ad30 F src/util.c 59d4e9456bf1fe581f415a783fa0cee6115c8f35 -F src/vacuum.c 869d08eaab64e2a4eaf4ef9ea34b851892b65a75 -F src/vdbe.c a03993ed188c9b2c575766dfe2b8cdc9f0bd2262 +F src/vacuum.c f2347520907ee4ec867c9b804d24456b0fd912a7 +F src/vdbe.c f0d6e7dbd4515758c188c9dd7025eb9dfcf021e0 F src/vdbe.h 1fb725c38df7f79dc60e9a61cb368152d9457e3c F src/vdbeInt.h aafda2e9761298e12ef0a3e8b5caed9aaf9c7592 F src/vdbeapi.c a7669f434f1fb53457343e7e85d06d695f7bb4e8 @@ -590,6 +593,7 @@ F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9 F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28 F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e F test/tkt-5ee23731f.test 3581260f2a71e51db94e1506ba6b0f7311d002a9 +F test/tkt-f777251dc7a.test 6f24c053bc5cdb7e1e19be9a72c8887cf41d5e87 F test/tkt1435.test f8c52c41de6e5ca02f1845f3a46e18e25cadac00 F test/tkt1443.test bacc311da5c96a227bf8c167e77a30c99f8e8368 F test/tkt1444.test a9d72f9e942708bd82dde6c707da61c489e213e9 @@ -758,7 +762,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 9f0937066184421f23453ceb451fd726c75cb593 -R 86e25f1b6a52bb9c63882796d734f555 -U dan -Z fec49abff05b5e5ceea062664695ce9e +P 32966ba4796e70d0afcff6abdda9bdcba08b098a 9f0937066184421f23453ceb451fd726c75cb593 61174aea74db59f6792e275aa366b7f0e1f2270b +R 61972980031ca23020de60e9d0fbe40d +U drh +Z 1bd496ba22406bf1b4185ebae8272187 +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.6 (GNU/Linux) + +iD8DBQFK2J2moxKgR168RlERAnBlAJ0TOp4dztyQ8x3H8zZLjMwtg9ofCQCeM/sa +FdOVdtjuYXVkiT+lGN/mPWo= +=+vEN +-----END PGP SIGNATURE----- diff --git a/manifest.uuid b/manifest.uuid index 2cb492295..96ecc2564 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -61174aea74db59f6792e275aa366b7f0e1f2270b
\ No newline at end of file +50136840d54674c239613265ebbacaabf215f4e2
\ No newline at end of file diff --git a/src/btree.c b/src/btree.c index a9ab44504..7c95d68dc 100644 --- a/src/btree.c +++ b/src/btree.c @@ -90,22 +90,24 @@ int sqlite3_enable_shared_cache(int enable){ #ifdef SQLITE_DEBUG /* -** This function is only used as part of an assert() statement. It checks -** that connection p holds the required locks to read or write to the -** b-tree with root page iRoot. If so, true is returned. Otherwise, false. -** For example, when writing to a table b-tree with root-page iRoot via +**** This function is only used as part of an assert() statement. *** +** +** Check to see if pBtree holds the required locks to read or write to the +** table with root page iRoot. Return 1 if it does and 0 if not. +** +** For example, when writing to a table with root-page iRoot via ** Btree connection pBtree: ** ** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); ** -** When writing to an index b-tree that resides in a sharable database, the +** When writing to an index that resides in a sharable database, the ** caller should have first obtained a lock specifying the root page of -** the corresponding table b-tree. This makes things a bit more complicated, -** as this module treats each b-tree as a separate structure. To determine -** the table b-tree corresponding to the index b-tree being written, this +** the corresponding table. This makes things a bit more complicated, +** as this module treats each table as a separate structure. To determine +** the table corresponding to the index being written, this ** function has to search through the database schema. ** -** Instead of a lock on the b-tree rooted at page iRoot, the caller may +** Instead of a lock on the table/index rooted at page iRoot, the caller may ** hold a write-lock on the schema table (root page 1). This is also ** acceptable. */ @@ -119,20 +121,25 @@ static int hasSharedCacheTableLock( Pgno iTab = 0; BtLock *pLock; - /* If this b-tree database is not shareable, or if the client is reading + /* If this database is not shareable, or if the client is reading ** and has the read-uncommitted flag set, then no lock is required. - ** In these cases return true immediately. If the client is reading - ** or writing an index b-tree, but the schema is not loaded, then return - ** true also. In this case the lock is required, but it is too difficult - ** to check if the client actually holds it. This doesn't happen very - ** often. */ + ** Return true immediately. + */ if( (pBtree->sharable==0) || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted)) - || (isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0 )) ){ return 1; } + /* If the client is reading or writing an index and the schema is + ** not loaded, then it is too difficult to actually check to see if + ** the correct locks are held. So do not bother - just return true. + ** This case does not come up very often anyhow. + */ + if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){ + return 1; + } + /* Figure out the root-page that the lock should be held on. For table ** b-trees, this is just the root page of the b-tree being read or ** written. For index b-trees, it is the root page of the associated @@ -164,14 +171,24 @@ static int hasSharedCacheTableLock( /* Failed to find the required lock. */ return 0; } +#endif /* SQLITE_DEBUG */ +#ifdef SQLITE_DEBUG /* -** This function is also used as part of assert() statements only. It -** returns true if there exist one or more cursors open on the table -** with root page iRoot that do not belong to either connection pBtree -** or some other connection that has the read-uncommitted flag set. +**** This function may be used as part of assert() statements only. **** +** +** Return true if it would be illegal for pBtree to write into the +** table or index rooted at iRoot because other shared connections are +** simultaneously reading that same table or index. ** -** For example, before writing to page iRoot: +** It is illegal for pBtree to write if some other Btree object that +** shares the same BtShared object is currently reading or writing +** the iRoot table. Except, if the other Btree object has the +** read-uncommitted flag set, then it is OK for the other object to +** have a read cursor. +** +** For example, before writing to any part of the table or index +** rooted at page iRoot, one should call: ** ** assert( !hasReadConflicts(pBtree, iRoot) ); */ @@ -190,7 +207,7 @@ static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ #endif /* #ifdef SQLITE_DEBUG */ /* -** Query to see if btree handle p may obtain a lock of type eLock +** Query to see if Btree handle p may obtain a lock of type eLock ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return ** SQLITE_OK if the lock may be obtained (by calling ** setSharedCacheTableLock()), or SQLITE_LOCKED if not. @@ -211,7 +228,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); - /* This is a no-op if the shared-cache is not enabled */ + /* This routine is a no-op if the shared-cache is not enabled */ if( !p->sharable ){ return SQLITE_OK; } @@ -257,10 +274,10 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ ** ** This function assumes the following: ** -** (a) The specified b-tree connection handle is connected to a sharable -** b-tree database (one with the BtShared.sharable) flag set, and +** (a) The specified Btree object p is connected to a sharable +** database (one with the BtShared.sharable flag set), and ** -** (b) No other b-tree connection handle holds a lock that conflicts +** (b) No other Btree objects hold a lock that conflicts ** with the requested lock (i.e. querySharedCacheTableLock() has ** already been called and returned SQLITE_OK). ** @@ -325,9 +342,9 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ #ifndef SQLITE_OMIT_SHARED_CACHE /* ** Release all the table locks (locks obtained via calls to -** the setSharedCacheTableLock() procedure) held by Btree handle p. +** the setSharedCacheTableLock() procedure) held by Btree object p. ** -** This function assumes that handle p has an open read or write +** This function assumes that Btree p has an open read or write ** transaction. If it does not, then the BtShared.isPending variable ** may be incorrectly cleared. */ @@ -360,7 +377,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){ pBt->isExclusive = 0; pBt->isPending = 0; }else if( pBt->nTransaction==2 ){ - /* This function is called when connection p is concluding its + /* This function is called when Btree p is concluding its ** transaction. If there currently exists a writer, and p is not ** that writer, then the number of locks held by connections other ** than the writer must be about to drop to zero. In this case @@ -374,7 +391,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){ } /* -** This function changes all write-locks held by connection p to read-locks. +** This function changes all write-locks held by Btree p into read-locks. */ static void downgradeAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; @@ -395,9 +412,11 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){ static void releasePage(MemPage *pPage); /* Forward reference */ /* -** Verify that the cursor holds a mutex on the BtShared +***** This routine is used inside of assert() only **** +** +** Verify that the cursor holds the mutex on its BtShared */ -#ifndef NDEBUG +#ifdef SQLITE_DEBUG static int cursorHoldsMutex(BtCursor *p){ return sqlite3_mutex_held(p->pBt->mutex); } @@ -428,7 +447,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){ /* ** This function is called before modifying the contents of a table -** b-tree to invalidate any incrblob cursors that are open on the +** to invalidate any incrblob cursors that are open on the ** row or one of the rows being modified. ** ** If argument isClearTable is true, then the entire contents of the @@ -437,7 +456,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){ ** ** Otherwise, if argument isClearTable is false, then the row with ** rowid iRow is being replaced or deleted. In this case invalidate -** only those incrblob cursors open on this specific row. +** only those incrblob cursors open on that specific row. */ static void invalidateIncrblobCursors( Btree *pBtree, /* The database file to check */ @@ -455,10 +474,11 @@ static void invalidateIncrblobCursors( } #else + /* Stub functions when INCRBLOB is omitted */ #define invalidateOverflowCache(x) #define invalidateAllOverflowCache(x) #define invalidateIncrblobCursors(x,y,z) -#endif +#endif /* SQLITE_OMIT_INCRBLOB */ /* ** Set bit pgno of the BtShared.pHasContent bitvec. This is called @@ -491,7 +511,7 @@ static void invalidateIncrblobCursors( ** The solution is the BtShared.pHasContent bitvec. Whenever a page is ** moved to become a free-list leaf page, the corresponding bit is ** set in the bitvec. Whenever a leaf page is extracted from the free-list, -** optimization 2 above is ommitted if the corresponding bit is already +** optimization 2 above is omitted if the corresponding bit is already ** set in BtShared.pHasContent. The contents of the bitvec are cleared ** at the end of every transaction. */ @@ -587,8 +607,8 @@ static int saveCursorPosition(BtCursor *pCur){ } /* -** Save the positions of all cursors except pExcept open on the table -** with root-page iRoot. Usually, this is called just before cursor +** Save the positions of all cursors (except pExcept) that are open on +** the table with root-page iRoot. Usually, this is called just before cursor ** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). */ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ @@ -993,7 +1013,10 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){ assert( nSize==debuginfo.nSize ); return (u16)nSize; } -#ifndef NDEBUG + +#ifdef SQLITE_DEBUG +/* This variation on cellSizePtr() is used inside of assert() statements +** only. */ static u16 cellSize(MemPage *pPage, int iCell){ return cellSizePtr(pPage, findCell(pPage, iCell)); } @@ -2965,18 +2988,13 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ */ static void btreeEndTransaction(Btree *p){ BtShared *pBt = p->pBt; - BtCursor *pCsr; assert( sqlite3BtreeHoldsMutex(p) ); - /* Search for a cursor held open by this b-tree connection. If one exists, - ** then the transaction will be downgraded to a read-only transaction - ** instead of actually concluded. A subsequent call to CommitPhaseTwo() - ** or Rollback() will finish the transaction and unlock the database. */ - for(pCsr=pBt->pCursor; pCsr && pCsr->pBtree!=p; pCsr=pCsr->pNext); - assert( pCsr==0 || p->inTrans>TRANS_NONE ); - btreeClearHasContent(pBt); - if( pCsr ){ + if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){ + /* If there are other active statements that belong to this database + ** handle, downgrade to a read-only transaction. The other statements + ** may still be reading from the database. */ downgradeAllSharedCacheTableLocks(p); p->inTrans = TRANS_READ; }else{ diff --git a/src/btreeInt.h b/src/btreeInt.h index 239439b4a..a2559eeb5 100644 --- a/src/btreeInt.h +++ b/src/btreeInt.h @@ -329,8 +329,8 @@ struct BtLock { ** this structure. ** ** For some database files, the same underlying database cache might be -** shared between multiple connections. In that case, each contection -** has it own pointer to this object. But each instance of this object +** shared between multiple connections. In that case, each connection +** has it own instance of this object. But each instance of this object ** points to the same BtShared object. The database cache and the ** schema associated with the database file are all contained within ** the BtShared object. @@ -471,7 +471,7 @@ struct CellInfo { ** The entry is identified by its MemPage and the index in ** MemPage.aCell[] of the entry. ** -** When a single database file can shared by two more database connections, +** A single database file can shared by two more database connections, ** but cursors cannot be shared. Each cursor is associated with a ** particular database connection identified BtCursor.pBtree.db. ** diff --git a/src/vacuum.c b/src/vacuum.c index 00f3511d8..8f9221e5c 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -130,6 +130,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); pTemp = db->aDb[db->nDb-1].pBt; + /* The call to execSql() to attach the temp database has left the file + ** locked (as there was more than one active statement when the transaction + ** to read the schema was concluded. Unlock it here so that this doesn't + ** cause problems for the call to BtreeSetPageSize() below. */ + sqlite3BtreeCommit(pTemp); + nRes = sqlite3BtreeGetReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ diff --git a/src/vdbe.c b/src/vdbe.c index bf39585c9..6847b9027 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -2853,6 +2853,7 @@ case OP_SetCookie: { /* in3 */ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ sqlite3ExpirePreparedStatements(db); + p->expired = 0; } break; } @@ -2970,6 +2971,11 @@ case OP_OpenWrite: { VdbeCursor *pCur; Db *pDb; + if( p->expired ){ + rc = SQLITE_ABORT; + break; + } + nField = 0; pKeyInfo = 0; p2 = pOp->p2; diff --git a/test/tkt-f777251dc7a.test b/test/tkt-f777251dc7a.test new file mode 100644 index 000000000..6f0b43fa8 --- /dev/null +++ b/test/tkt-f777251dc7a.test @@ -0,0 +1,99 @@ +# 2009 October 16 +# +# 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. +# +# This file implements tests to verify that ticket [f777251dc7a] has been +# fixed. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_test tkt-f7772-1.1 { + execsql { + CREATE TEMP TABLE t1(x UNIQUE); + INSERT INTO t1 VALUES(1); + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES(1, 2); + CREATE TEMP TABLE t3(w, z); + } +} {} + +proc force_rollback {} { + catch {db eval {INSERT OR ROLLBACK INTO t1 VALUES(1)}} +} +db function force_rollback force_rollback + +do_test tkt-f7772-1.2 { + catchsql { + BEGIN IMMEDIATE; + SELECT x, force_rollback(), EXISTS(SELECT 1 FROM t3 WHERE w=x) FROM t2; + } +} {1 {callback requested query abort}} +do_test tkt-f7772-1.3 { + sqlite3_get_autocommit db +} {1} + +do_test tkt-f7772-2.1 { + execsql { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + DROP TABLE IF EXISTS t3; + + CREATE TEMP TABLE t1(x UNIQUE); + INSERT INTO t1 VALUES(1); + CREATE TABLE t2(x, y); + INSERT INTO t2 VALUES(1, 2); + } +} {} +do_test tkt-f7772-2.2 { + execsql { + BEGIN IMMEDIATE; + CREATE TEMP TABLE t3(w, z); + } + catchsql { + SELECT x, force_rollback(), EXISTS(SELECT 1 FROM t3 WHERE w=x) FROM t2 + } +} {1 {callback requested query abort}} +do_test tkt-f7772-2.3 { + sqlite3_get_autocommit db +} {1} + +do_test tkt-f7772-3.1 { + execsql { + DROP TABLE IF EXISTS t1; + DROP TABLE IF EXISTS t2; + DROP TABLE IF EXISTS t3; + + CREATE TEMP TABLE t1(x); + CREATE TABLE t2(x); + CREATE TABLE t3(x); + + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); + INSERT INTO t2 VALUES(1); + INSERT INTO t2 VALUES(2); + } +} {} + +proc ins {} { db eval {INSERT INTO t3 VALUES('hello')} } +db function ins ins + +do_test tkt-f7772-3.2 { + execsql { + SELECT ins() AS x FROM t2 UNION ALL SELECT ins() AS x FROM t1 + } +} {{} {} {} {}} +do_test tkt-f7772-3.3 { + execsql { SELECT * FROM t3 } +} {hello hello hello hello} + +finish_test |