]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Cause incremental-blob read/write operations lock shared-cache tables in the same...
authordanielk1977 <danielk1977@noemail.net>
Mon, 29 Jun 2009 06:00:37 +0000 (06:00 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Mon, 29 Jun 2009 06:00:37 +0000 (06:00 +0000)
FossilOrigin-Name: f17ef37897da9bcaf20b5acdce6840522c0a0b16

manifest
manifest.uuid
src/btree.c
src/test3.c
src/vdbe.c
src/vdbeblob.c
test/incrblob2.test
test/types.test

index 9a91efed14e652d1ef22748b8fb35400f16b1218..0667b722b0e03be4b87d59db177f1aa32ef2acfb 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Version\s3.6.16\s(CVS\s6829)
-D 2009-06-27T14:10:30
+C Cause\sincremental-blob\sread/write\soperations\slock\sshared-cache\stables\sin\sthe\ssame\sway\sas\snormal\sSQL\sread/writes.\sAdd\scomplex\sassert\sstatements\sto\smake\ssure\stehe\scorrect\sshared-cache\slocks\sare\sheld\swhen\saccessing\sthe\sdatabase.\sEliminate\ssome\sredundant\schecks\sfrom\sbtree.c.\s(CVS\s6830)
+D 2009-06-29T06:00:37
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in 8b8fb7823264331210cddf103831816c286ba446
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -106,7 +106,7 @@ F src/auth.c 98db07c2088455797678eb1031f42d4d94d18a71
 F src/backup.c ff50af53184a5fd7bdee4d620b5dabef74717c79
 F src/bitvec.c 0ef0651714728055d43de7a4cdd95e703fac0119
 F src/btmutex.c 9b899c0d8df3bd68f527b0afe03088321b696d3c
-F src/btree.c 617d674eb77060f7fd6a05d27d72946c901191c8
+F src/btree.c 078eb41016d033707f8a94075cdde18249e48f75
 F src/btree.h f70b694e8c163227369a66863b01fbff9009f323
 F src/btreeInt.h 55346bc14b939ad41b297942e8b1b581e960fb99
 F src/build.c 813f6bdab5e4fb5ff94a5340c199a4930da9d66e
@@ -169,7 +169,7 @@ F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
 F src/tclsqlite.c d3195e0738c101a155404ecdb1cd9532a2fd34f2
 F src/test1.c c8f9358879876660b721369f576bf6e4ac5b9210
 F src/test2.c d73e4a490349245fb196b990b80684513e0ceaee
-F src/test3.c abd651f387a42696976dd5560c46b596e421cfb8
+F src/test3.c 1e1778ca7e0234c9eb329ac6a5729067b822f3d5
 F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c
 F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
 F src/test6.c 1a0a7a1f179469044b065b4a88aab9faee114101
@@ -203,12 +203,12 @@ F src/update.c a1bbe774bce495d62dce3df3f42a5f04c1de173a
 F src/utf.c 9541d28f40441812c0b40f00334372a0542c00ff
 F src/util.c 861d5b5c58be4921f0a254489ea94cb15f550ef8
 F src/vacuum.c 0e14f371ea3326c6b8cfba257286d798cd20db59
-F src/vdbe.c dfd508c9f6c183f0f39535dee51d9f0bc9420088
+F src/vdbe.c f2462fdb71b8dfd788e2be1228b1b6c1b702ea80
 F src/vdbe.h 35a648bc3279a120da24f34d9a25213ec15daf8a
 F src/vdbeInt.h 831c254a6eef237ef4664c8381a0137586567007
 F src/vdbeapi.c 0ab8ada7260b32031ca97f338caecf0812460624
 F src/vdbeaux.c 3773217a73f93fb292d264b3b1da98c179a0f2f0
-F src/vdbeblob.c c25d7e7bc6d5917feeb17270bd275fa771f26e5c
+F src/vdbeblob.c f3d3151d8ddfe9fed288f9e2c98c96e12e914fbb
 F src/vdbemem.c 1618f685d19b4bcc96e40b3c478487bafd2ae246
 F src/vtab.c 98fbffc5efe68d8107511dec0a650efc7daa9446
 F src/walker.c 1edca756275f158b80f20eb6f104c8d3fcc96a04
@@ -397,7 +397,7 @@ F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
 F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
 F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
 F test/incrblob.test c80087a8ec28b4a58b5299251074048e17754f8f
-F test/incrblob2.test 7ef4581745dd80155a451637aa779b49df90787d
+F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
 F test/incrblob_err.test c577c91d4ed9e8336cdb188b15d6ee2a6fe9604e
 F test/incrvacuum.test d0fb6ef6d747ef5c5ebe878aafa72dd3e178856b
 F test/incrvacuum2.test 46ef65f377e3937cfd1ba66e818309dab46f590d
@@ -672,7 +672,7 @@ F test/trigger8.test 83d92c212f36442d26527d6f7701575905a52ae1
 F test/trigger9.test e6e8dbab673666b3c0a63f0fefcff2329fe6bba8
 F test/triggerA.test 208dbda4d2f7c918b02f8a0dfa3acd2a0fe00691
 F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
-F test/types.test 98e7a631bddf0806204358b452b02d0e319318a6
+F test/types.test 9a825ec8eea4e965d7113b74c76a78bb5240f2ac
 F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
 F test/types3.test a0f66bf12f80fad89493535474f7a6d16fa58150
 F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
@@ -737,7 +737,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
-P 49f22e55d69d0b5a34400b36332a2eb861362eb2
-R 081c6cf0e8f2499b8b69ecc027b9626f
-U drh
-Z b0d3fc590df87bc7076fef537dbc78a6
+P ff691a6b2a302fe7978459cb8df9d56184892ee0
+R 78ed1dc8020260be15a9afc4441673c9
+U danielk1977
+Z d5676c92a64d0bdadc5400d03f5280e3
index 57a23540a66a4552800e312485531311ac7d6a38..5713b3af12ad98481d3595bbe927ebb1d5243ff4 100644 (file)
@@ -1 +1 @@
-ff691a6b2a302fe7978459cb8df9d56184892ee0
\ No newline at end of file
+f17ef37897da9bcaf20b5acdce6840522c0a0b16
\ No newline at end of file
index 6c770cd0f2e3031edb9c6c726fa854bd04af0fde..68f196206fb5f5c31566e45c2073e86a12000d63 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.645 2009/06/26 16:32:13 shane Exp $
+** $Id: btree.c,v 1.646 2009/06/29 06:00:37 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -67,11 +67,6 @@ int sqlite3_enable_shared_cache(int enable){
 #endif
 
 
-/*
-** Forward declaration
-*/
-static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64);
-
 
 #ifdef SQLITE_OMIT_SHARED_CACHE
   /*
@@ -86,9 +81,113 @@ static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64);
   #define querySharedCacheTableLock(a,b,c) SQLITE_OK
   #define setSharedCacheTableLock(a,b,c) SQLITE_OK
   #define clearAllSharedCacheTableLocks(a)
+  #define hasSharedCacheTableLock(a,b,c,d) 1
+  #define hasReadConflicts(a, b) 0
 #endif
 
 #ifndef SQLITE_OMIT_SHARED_CACHE
+
+#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 
+** Btree connection pBtree:
+**
+**    assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) );
+**
+** When writing to an index b-tree 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
+** function has to search through the database schema.
+**
+** Instead of a lock on the b-tree rooted at page iRoot, the caller may
+** hold a write-lock on the schema table (root page 1). This is also
+** acceptable.
+*/
+static int hasSharedCacheTableLock(
+  Btree *pBtree,         /* Handle that must hold lock */
+  Pgno iRoot,            /* Root page of b-tree */
+  int isIndex,           /* True if iRoot is the root of an index b-tree */
+  int eLockType          /* Required lock type (READ_LOCK or WRITE_LOCK) */
+){
+  Schema *pSchema = (Schema *)pBtree->pBt->pSchema;
+  Pgno iTab = 0;
+  BtLock *pLock;
+
+  /* If this b-tree 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.  */
+  if( (pBtree->sharable==0)
+   || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted))
+   || (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
+  ** table.  */
+  if( isIndex ){
+    HashElem *p;
+    for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){
+      Index *pIdx = (Index *)sqliteHashData(p);
+      if( pIdx->tnum==iRoot ){
+       iTab = pIdx->pTable->tnum;
+      }
+    }
+  }else{
+    iTab = iRoot;
+  }
+
+  /* Search for the required lock. Either a write-lock on root-page iTab, a 
+  ** write-lock on the schema table, or (if the client is reading) a
+  ** read-lock on iTab will suffice. Return 1 if any of these are found.  */
+  for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){
+    if( pLock->pBtree==pBtree 
+     && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1))
+     && pLock->eLock>=eLockType 
+    ){
+      return 1;
+    }
+  }
+
+  /* Failed to find the required lock. */
+  return 0;
+}
+
+/*
+** 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.
+**
+** For example, before writing to page iRoot:
+**
+**    assert( !hasReadConflicts(pBtree, iRoot) );
+*/
+static int hasReadConflicts(Btree *pBtree, Pgno iRoot){
+  BtCursor *p;
+  for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+    if( p->pgnoRoot==iRoot 
+     && p->pBtree!=pBtree
+     && 0==(p->pBtree->db->flags & SQLITE_ReadUncommitted)
+    ){
+      return 1;
+    }
+  }
+  return 0;
+}
+#endif    /* #ifdef SQLITE_DEBUG */
+
 /*
 ** 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
@@ -326,9 +425,41 @@ static void invalidateAllOverflowCache(BtShared *pBt){
     invalidateOverflowCache(p);
   }
 }
+
+/*
+** This function is called before modifying the contents of a table
+** b-tree to invalidate any incrblob cursors that are open on the
+** row or one of the rows being modified. Argument pgnoRoot is the 
+** root-page of the table b-tree. 
+**
+** If argument isClearTable is true, then the entire contents of the
+** table is about to be deleted. In this case invalidate all incrblob
+** cursors open on any row within the table with root-page pgnoRoot.
+**
+** 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.
+*/
+static void invalidateIncrblobCursors(
+  Btree *pBtree,          /* The database file to check */
+  Pgno pgnoRoot,          /* Look for read cursors on this btree */
+  i64 iRow,               /* The rowid that might be changing */
+  int isClearTable        /* True if all rows are being deleted */
+){
+  BtCursor *p;
+  BtShared *pBt = pBtree->pBt;
+  assert( sqlite3BtreeHoldsMutex(pBtree) );
+  for(p=pBt->pCursor; p; p=p->pNext){
+    if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){
+      p->eState = CURSOR_INVALID;
+    }
+  }
+}
+
 #else
   #define invalidateOverflowCache(x)
   #define invalidateAllOverflowCache(x)
+  #define invalidateIncrblobCursors(w,x,y,z)
 #endif
 
 /*
@@ -3055,16 +3186,22 @@ static int btreeCursor(
 
   assert( sqlite3BtreeHoldsMutex(p) );
   assert( wrFlag==0 || wrFlag==1 );
-  if( wrFlag ){
-    assert( !pBt->readOnly );
-    if( NEVER(pBt->readOnly) ){
-      return SQLITE_READONLY;
-    }
-    rc = checkForReadConflicts(p, iTable, 0, 0);
-    if( rc!=SQLITE_OK ){
-      assert( rc==SQLITE_LOCKED_SHAREDCACHE );
-      return rc;
-    }
+
+  /* The following assert statements verify that if this is a sharable b-tree
+  ** database, the connection is holding the required table locks, and that
+  ** no other connection has any open cursor that conflicts with this lock.
+  **
+  ** The exception to this is read-only cursors open on the schema table.
+  ** Such a cursor is opened without a lock while reading the database
+  ** schema. This is safe because BtShared.mutex is held for the entire
+  ** lifetime of this cursor.  */
+  assert( (iTable==1 && wrFlag==0) 
+       || hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) 
+  );
+  assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
+
+  if( NEVER(wrFlag && pBt->readOnly) ){
+    return SQLITE_READONLY;
   }
 
   if( pBt->pPage1==0 ){
@@ -6210,75 +6347,6 @@ static int balance(BtCursor *pCur){
   return rc;
 }
 
-/*
-** This routine checks all cursors that point to table pgnoRoot.
-** If any of those cursors were opened with wrFlag==0 in a different
-** database connection (a database connection that shares the pager
-** cache with the current connection) and that other connection 
-** is not in the ReadUncommmitted state, then this routine returns 
-** SQLITE_LOCKED.
-**
-** As well as cursors with wrFlag==0, cursors with 
-** isIncrblobHandle==1 are also considered 'read' cursors because
-** incremental blob cursors are used for both reading and writing.
-**
-** When pgnoRoot is the root page of an intkey table, this function is also
-** responsible for invalidating incremental blob cursors when the table row
-** on which they are opened is deleted or modified. Cursors are invalidated
-** according to the following rules:
-**
-**   1) When BtreeClearTable() is called to completely delete the contents
-**      of a B-Tree table, pExclude is set to zero and parameter iRow is 
-**      set to non-zero. In this case all incremental blob cursors open
-**      on the table rooted at pgnoRoot are invalidated.
-**
-**   2) When BtreeInsert(), BtreeDelete() or BtreePutData() is called to 
-**      modify a table row via an SQL statement, pExclude is set to the 
-**      write cursor used to do the modification and parameter iRow is set
-**      to the integer row id of the B-Tree entry being modified. Unless
-**      pExclude is itself an incremental blob cursor, then all incremental
-**      blob cursors open on row iRow of the B-Tree are invalidated.
-**
-**   3) If both pExclude and iRow are set to zero, no incremental blob 
-**      cursors are invalidated.
-*/
-static int checkForReadConflicts(
-  Btree *pBtree,          /* The database file to check */
-  Pgno pgnoRoot,          /* Look for read cursors on this btree */
-  BtCursor *pExclude,     /* Ignore this cursor */
-  i64 iRow                /* The rowid that might be changing */
-){
-  BtCursor *p;
-  BtShared *pBt = pBtree->pBt;
-  sqlite3 *db = pBtree->db;
-  assert( sqlite3BtreeHoldsMutex(pBtree) );
-  for(p=pBt->pCursor; p; p=p->pNext){
-    if( p==pExclude ) continue;
-    if( p->pgnoRoot!=pgnoRoot ) continue;
-#ifndef SQLITE_OMIT_INCRBLOB
-    if( p->isIncrblobHandle && ( 
-         (!pExclude && iRow)
-      || (pExclude && !pExclude->isIncrblobHandle && p->info.nKey==iRow)
-    )){
-      p->eState = CURSOR_INVALID;
-    }
-#endif
-    if( p->eState!=CURSOR_VALID ) continue;
-    if( p->wrFlag==0 
-#ifndef SQLITE_OMIT_INCRBLOB
-     || p->isIncrblobHandle
-#endif
-    ){
-      sqlite3 *dbOther = p->pBtree->db;
-      assert(dbOther);
-      if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){
-        sqlite3ConnectionBlocked(db, dbOther);
-        return SQLITE_LOCKED_SHAREDCACHE;
-      }
-    }
-  }
-  return SQLITE_OK;
-}
 
 /*
 ** Insert a new record into the BTree.  The key is given by (pKey,nKey)
@@ -6322,12 +6390,15 @@ int sqlite3BtreeInsert(
   assert( pBt->inTransaction==TRANS_WRITE );
   assert( !pBt->readOnly );
   assert( pCur->wrFlag );
-  rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey);
-  if( rc ){             
-    /* The table pCur points to has a read lock */
-    assert( rc==SQLITE_LOCKED_SHAREDCACHE );
-    return rc;
+  assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
+
+  /* If this is an insert into a table b-tree, invalidate any incrblob 
+  ** cursors open on the row being replaced (assuming this is a replace
+  ** operation - if it is not, the following is a no-op).  */
+  if( pCur->pKeyInfo==0 ){
+    invalidateIncrblobCursors(p, pCur->pgnoRoot, nKey, 0);
   }
+
   if( pCur->eState==CURSOR_FAULT ){
     return pCur->skip;
   }
@@ -6448,16 +6519,19 @@ int sqlite3BtreeDelete(BtCursor *pCur){
   assert( pBt->inTransaction==TRANS_WRITE );
   assert( !pBt->readOnly );
   assert( pCur->wrFlag );
+  assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
+  assert( !hasReadConflicts(p, pCur->pgnoRoot) );
+
   if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) 
    || NEVER(pCur->eState!=CURSOR_VALID)
   ){
     return SQLITE_ERROR;  /* Something has gone awry. */
   }
 
-  rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey);
-  if( rc!=SQLITE_OK ){
-    assert( rc==SQLITE_LOCKED_SHAREDCACHE );
-    return rc;            /* The table pCur points to has a read lock */
+  /* If this is a delete operation to remove a row from a table b-tree,
+  ** invalidate any incrblob cursors open on the row being deleted.  */
+  if( pCur->pKeyInfo==0 ){
+    invalidateIncrblobCursors(p, pCur->pgnoRoot, pCur->info.nKey, 0);
   }
 
   iCellDepth = pCur->iPage;
@@ -6757,11 +6831,13 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
   BtShared *pBt = p->pBt;
   sqlite3BtreeEnter(p);
   assert( p->inTrans==TRANS_WRITE );
-  if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){
-    /* nothing to do */
-  }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
-    /* nothing to do */
-  }else{
+
+  /* Invalidate all incrblob cursors open on table iTable (assuming iTable
+  ** is the root of a table b-tree - if it is not, the following call is
+  ** a no-op).  */
+  invalidateIncrblobCursors(p, iTable, 0, 1);
+
+  if( SQLITE_OK==(rc = saveAllCursors(pBt, (Pgno)iTable, 0)) ){
     rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
   }
   sqlite3BtreeLeave(p);
@@ -7687,11 +7763,9 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
 ** to change the length of the data stored.
 */
 int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
-  int rc;
-
   assert( cursorHoldsMutex(pCsr) );
   assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
-  assert(pCsr->isIncrblobHandle);
+  assert( pCsr->isIncrblobHandle );
 
   restoreCursorPosition(pCsr);
   assert( pCsr->eState!=CURSOR_REQUIRESEEK );
@@ -7707,14 +7781,10 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
   if( !pCsr->wrFlag ){
     return SQLITE_READONLY;
   }
-  assert( !pCsr->pBt->readOnly 
-          && pCsr->pBt->inTransaction==TRANS_WRITE );
-  rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0);
-  if( rc!=SQLITE_OK ){
-    /* The table pCur points to has a read lock */
-    assert( rc==SQLITE_LOCKED_SHAREDCACHE );
-    return rc;
-  }
+  assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE );
+  assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );
+  assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );
+
   if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){
     return SQLITE_ERROR;
   }
index 3a795b70476a6f6ee2e1d2b7ab8cd918bf74ca14..f6059627001519fbd9a2b1407dc28c996ee10cc5 100644 (file)
@@ -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.104 2009/05/04 11:42:30 danielk1977 Exp $
+** $Id: test3.c,v 1.105 2009/06/29 06:00:37 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "btreeInt.h"
@@ -626,7 +626,10 @@ static int btree_cursor(
   pCur = (BtCursor *)ckalloc(sqlite3BtreeCursorSize());
   memset(pCur, 0, sqlite3BtreeCursorSize());
   sqlite3BtreeEnter(pBt);
-  rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
+  rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, 0, pCur);
+  }
   sqlite3BtreeLeave(pBt);
   if( rc ){
     ckfree((char *)pCur);
index c865a7f57b72453c6e1916d719802aa988483f80..c5b87da4bce0ca3a2c303319a6ba1b476f6e9b51 100644 (file)
@@ -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.866 2009/06/26 16:32:13 shane Exp $
+** $Id: vdbe.c,v 1.867 2009/06/29 06:00:37 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "vdbeInt.h"
@@ -4994,7 +4994,7 @@ case OP_Expire: {
 ** Obtain a lock on a particular table. This instruction is only used when
 ** the shared-cache feature is enabled. 
 **
-** If P1 is  the index of the database in sqlite3.aDb[] of the database
+** P1 is the index of the database in sqlite3.aDb[] of the database
 ** on which the lock is acquired.  A readlock is obtained if P3==0 or
 ** a write lock if P3==1.
 **
index d60a0d98c32bcb579da54f411789dd849bec434e..b08cec3447a4b44d9601e031081bc319bad14cc3 100644 (file)
@@ -12,7 +12,7 @@
 **
 ** This file contains code used to implement incremental BLOB I/O.
 **
-** $Id: vdbeblob.c,v 1.33 2009/06/01 19:53:31 drh Exp $
+** $Id: vdbeblob.c,v 1.34 2009/06/29 06:00:37 danielk1977 Exp $
 */
 
 #include "sqliteInt.h"
@@ -66,19 +66,18 @@ int sqlite3_blob_open(
   static const VdbeOpList openBlob[] = {
     {OP_Transaction, 0, 0, 0},     /* 0: Start a transaction */
     {OP_VerifyCookie, 0, 0, 0},    /* 1: Check the schema cookie */
-
-    /* One of the following two instructions is replaced by an
-    ** OP_Noop before exection.
-    */
-    {OP_OpenRead, 0, 0, 0},        /* 2: Open cursor 0 for reading */
-    {OP_OpenWrite, 0, 0, 0},       /* 3: Open cursor 0 for read/write */
-
-    {OP_Variable, 1, 1, 1},        /* 4: Push the rowid to the stack */
-    {OP_NotExists, 0, 8, 1},       /* 5: Seek the cursor */
-    {OP_Column, 0, 0, 1},          /* 6  */
-    {OP_ResultRow, 1, 0, 0},       /* 7  */
-    {OP_Close, 0, 0, 0},           /* 8  */
-    {OP_Halt, 0, 0, 0},            /* 9 */
+    {OP_TableLock, 0, 0, 0},       /* 2: Acquire a read or write lock */
+
+    /* One of the following two instructions is replaced by an OP_Noop. */
+    {OP_OpenRead, 0, 0, 0},        /* 3: Open cursor 0 for reading */
+    {OP_OpenWrite, 0, 0, 0},       /* 4: Open cursor 0 for read/write */
+
+    {OP_Variable, 1, 1, 1},        /* 5: Push the rowid to the stack */
+    {OP_NotExists, 0, 9, 1},       /* 6: Seek the cursor */
+    {OP_Column, 0, 0, 1},          /* 7  */
+    {OP_ResultRow, 1, 0, 0},       /* 8  */
+    {OP_Close, 0, 0, 0},           /* 9  */
+    {OP_Halt, 0, 0, 0},            /* 10 */
   };
 
   Vdbe *v = 0;
@@ -170,10 +169,11 @@ int sqlite3_blob_open(
     if( v ){
       int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
       sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
+      flags = !!flags;                 /* flags = (flags ? 1 : 0); */
 
       /* Configure the OP_Transaction */
       sqlite3VdbeChangeP1(v, 0, iDb);
-      sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0));
+      sqlite3VdbeChangeP2(v, 0, flags);
 
       /* Configure the OP_VerifyCookie */
       sqlite3VdbeChangeP1(v, 1, iDb);
@@ -182,13 +182,17 @@ int sqlite3_blob_open(
       /* Make sure a mutex is held on the table to be accessed */
       sqlite3VdbeUsesBtree(v, iDb); 
 
+      /* Configure the OP_TableLock instruction */
+      sqlite3VdbeChangeP1(v, 2, iDb);
+      sqlite3VdbeChangeP2(v, 2, pTab->tnum);
+      sqlite3VdbeChangeP3(v, 2, flags);
+      sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
+
       /* Remove either the OP_OpenWrite or OpenRead. Set the P2 
-      ** parameter of the other to pTab->tnum. 
-      */
-      flags = !!flags;
-      sqlite3VdbeChangeToNoop(v, 3 - flags, 1);
-      sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum);
-      sqlite3VdbeChangeP3(v, 2 + flags, iDb);
+      ** parameter of the other to pTab->tnum.  */
+      sqlite3VdbeChangeToNoop(v, 4 - flags, 1);
+      sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
+      sqlite3VdbeChangeP3(v, 3 + flags, iDb);
 
       /* Configure the number of columns. Configure the cursor to
       ** think that the table has one more column than it really
@@ -197,8 +201,8 @@ int sqlite3_blob_open(
       ** we can invoke OP_Column to fill in the vdbe cursors type 
       ** and offset cache without causing any IO.
       */
-      sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
-      sqlite3VdbeChangeP2(v, 6, pTab->nCol);
+      sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
+      sqlite3VdbeChangeP2(v, 7, pTab->nCol);
       if( !db->mallocFailed ){
         sqlite3VdbeMakeReady(v, 1, 1, 1, 0);
       }
index 5f7c1d432ca08b44c4c3b067878cf522d48c1460..9046de278173234d6842c2e6af960da518a46663 100644 (file)
@@ -12,7 +12,7 @@
 # Test that it is possible to have two open blob handles on a single
 # blob object.
 #
-# $Id: incrblob2.test,v 1.10 2009/03/16 13:19:36 danielk1977 Exp $
+# $Id: incrblob2.test,v 1.11 2009/06/29 06:00:37 danielk1977 Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -274,15 +274,24 @@ ifcapable shared_cache {
   } {0 {}}
 
   do_test incrblob2-5.5 {
-    set blob [db incrblob -readonly t1 data 1]
-    catchsql { INSERT INTO t1 VALUES(5, 'uvwxy') } db2
-  } {1 {database table is locked}}
+    set rc [catch { db incrblob -readonly t1 data 1 } msg]
+    list $rc $msg
+  } {1 {database table is locked: t1}}
 
   do_test incrblob2-5.6 {
-    close $blob
+    execsql { PRAGMA read_uncommitted=1 }
+    set blob [db incrblob -readonly t1 data 4]
+    read $blob
+  } {pqrst}
+
+  do_test incrblob2-5.7 {
     catchsql { INSERT INTO t1 VALUES(3, 'klmno') } db2
   } {0 {}}
 
+  do_test incrblob2-5.8 {
+    close $blob
+  } {}
+
   db2 close
   db close
   sqlite3_enable_shared_cache $::enable_shared_cache
index 6ebaeb8f5ea502266e404d0ad4b4a7a8eafa1e63..a2dd632251fc51253cf7cb2564aa7016d77dbaa5 100644 (file)
@@ -12,7 +12,7 @@
 # it tests that the different storage classes (integer, real, text etc.)
 # all work correctly.
 #
-# $Id: types.test,v 1.19 2006/06/27 12:51:13 drh Exp $
+# $Id: types.test,v 1.20 2009/06/29 06:00:37 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -136,6 +136,7 @@ execsql {
 # in the table, in the tables default scanning order.
 proc record_sizes {rootpage} {
   set bt [btree_open test.db 10 0]
+  btree_begin_transaction $bt
   set c [btree_cursor $bt $rootpage 0]
   btree_first $c
   while 1 {