]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Readded the sqlite3_open_v2() interface. No test cases yet.
authordrh <drh@noemail.net>
Tue, 21 Aug 2007 15:13:19 +0000 (15:13 +0000)
committerdrh <drh@noemail.net>
Tue, 21 Aug 2007 15:13:19 +0000 (15:13 +0000)
Additional progress toward adding mutexes to all interfaces. (CVS 4261)

FossilOrigin-Name: 3787563e90d7210d349ee36484c3f008c955552e

manifest
manifest.uuid
src/loadext.c
src/main.c
src/sqlite.h.in
src/vdbeapi.c
src/vdbeblob.c

index 7848ebdfb475da6c1cc1ce5e536531cc502c11c7..123eaf6b835c64c9be9b03195e937c68272ebe4f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Delay\sopening\stemporary\spager\sfiles\suntil\sthey\sare\sfirst\swritten.\s(CVS\s4260)
-D 2007-08-21T14:27:02
+C Readded\sthe\ssqlite3_open_v2()\sinterface.\s\sNo\stest\scases\syet.\nAdditional\sprogress\stoward\sadding\smutexes\sto\sall\sinterfaces.\s(CVS\s4261)
+D 2007-08-21T15:13:19
 F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe
 F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -96,8 +96,8 @@ F src/hash.h 3ad3da76bfb954978d227bf495568b0e6da2c19e
 F src/insert.c 633322aef1799f6604fa805e12488bc628570b0c
 F src/legacy.c a83519a8fbb488c3155fca577b010d590ec479e9
 F src/limits.h 71ab25f17e35e0a9f3f6f234b8ed49cc56731d35
-F src/loadext.c 8563d7ef0d7fe424378ef7b945d56c28ba78fe24
-F src/main.c 3f8917757c7f6a937e300b613b9cfd1bbe703c70
+F src/loadext.c 780748f3f55a3b5af6ed0adfd58035f728cde4ca
+F src/main.c 5f6ab3380477a7f134f7c664e3fdcdbf12a3d9dc
 F src/malloc.c c2f5da620d8e030c6974a0ddcaeb7b408c9bdb3d
 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217
 F src/mem1.c 30bf8be3846f92fdf88c490c5e5378512383bcbe
@@ -124,7 +124,7 @@ F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da
 F src/select.c 98c367bce3f38c5adfcc97de9ab5c79b0e5dc2b2
 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
 F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb
-F src/sqlite.h.in 09d8d2780c2c25dedbcfdfcafc39e8b213fc8432
+F src/sqlite.h.in 62a2cb5d0ce867e206a0b5465dedd5dfd941cc37
 F src/sqlite3ext.h 647a6b8a8f76ff6c9611e4a071531d8e63ff2d6b
 F src/sqliteInt.h c9ba3861d1e8835caeb0c89fca7cddd12779e1ac
 F src/sqliteLimit.h f14609c27636ebc217c9603ade26dbdd7d0f6afa
@@ -159,9 +159,9 @@ F src/vacuum.c 318ccae7c4e3ddf241aeaee4d2611bfe1949a373
 F src/vdbe.c f1a9a29da48ccfa49042df478abb478520589f37
 F src/vdbe.h 001c5b257567c1d3de7feb2203aac71d0d7b16a3
 F src/vdbeInt.h 8e360d326328e7a66100f468697edf9cfb4567dc
-F src/vdbeapi.c ffd28fa0f731467a1e63155abc74cd149e57d671
+F src/vdbeapi.c 16268e7a2614051c9c7f93679d9102b03206c129
 F src/vdbeaux.c 14b48bfc6334682e5e5858a0835f8b00d8751953
-F src/vdbeblob.c cf9ee3c7d9977cbd896f8b118da4fb4268637f4f
+F src/vdbeblob.c ac223e6d3acaa3321ce09c11c47bf0d05b37372f
 F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6
 F src/vdbemem.c 2843e6f91e8f19cfd72cfc7b24a03f1647db3c28
 F src/vtab.c ee29237ecc9b310dc43c0c2ac5caa6c6a20787be
@@ -558,7 +558,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 6225cd461cdd2132eeb480aa4deb8986b7f63c15
-R 75c714761e5d5dec4034adbc16c873e3
-U danielk1977
-Z ca42567df49c41054078dbc0243fa598
+P 3fb97a63ef70662abdba18ce8b480e6b0badcfb1
+R ebd0701e4b840e7f444fe74a84efea35
+U drh
+Z 4a9c7281bbaf4cab164c43383497e97b
index f405a8ffe5ea51918439549c67cb92b55f501fbc..082b6ce149c857d53f96cceb874221ba7ef22f6e 100644 (file)
@@ -1 +1 @@
-3fb97a63ef70662abdba18ce8b480e6b0badcfb1
\ No newline at end of file
+3787563e90d7210d349ee36484c3f008c955552e
\ No newline at end of file
index 5e854e933414adda953439dc9250fd616c89a7b5..b5dc4f5fd11ae200c3b0ba2549811410fa27d818 100644 (file)
@@ -356,13 +356,16 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
 }
 
 /*
-** A list of automatically loaded extensions.
+** The following object holds the list of automatically loaded
+** extensions.
 **
-** This list is shared across threads, so be sure to hold the
-** mutex while accessing or changing it.
+** This list is shared across threads.  The SQLITE_MUTEX_STATIC_MASTER
+** mutex must be held while accessing this list.
 */
-static int nAutoExtension = 0;
-static void **aAutoExtension = 0;
+static struct {
+  int nExt;        /* Number of entries in aExt[] */          
+  void **aExt;     /* Pointers to the extension init functions */
+} autoext = { 0, 0 };
 
 
 /*
@@ -374,17 +377,19 @@ int sqlite3_auto_extension(void *xInit){
   int rc = SQLITE_OK;
   sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
   sqlite3_mutex_enter(mutex);
-  for(i=0; i<nAutoExtension; i++){
-    if( aAutoExtension[i]==xInit ) break;
+  for(i=0; i<autoext.nExt; i++){
+    if( autoext.aExt[i]==xInit ) break;
   }
-  if( i==nAutoExtension ){
-    int nByte = (++nAutoExtension)*sizeof(aAutoExtension[0]);
-    aAutoExtension = sqlite3_realloc(aAutoExtension, nByte);
-    if( aAutoExtension==0 ){
-      nAutoExtension = 0;
+  if( i==autoext.nExt ){
+    int nByte = (autoext.nExt+1)*sizeof(autoext.aExt[0]);
+    void **aNew;
+    aNew = sqlite3_realloc(autoext.aExt, nByte);
+    if( aNew==0 ){
       rc = SQLITE_NOMEM;
     }else{
-      aAutoExtension[nAutoExtension-1] = xInit;
+      autoext.aExt = aNew;
+      autoext.aExt[autoext.nExt] = xInit;
+      autoext.nExt++;
     }
   }
   sqlite3_mutex_leave(mutex);
@@ -398,9 +403,9 @@ int sqlite3_auto_extension(void *xInit){
 void sqlite3_reset_auto_extension(void){
   sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
   sqlite3_mutex_enter(mutex);
-  sqlite3_free(aAutoExtension);
-  aAutoExtension = 0;
-  nAutoExtension = 0;
+  sqlite3_free(autoext.aExt);
+  autoext.aExt = 0;
+  autoext.nExt = 0;
   sqlite3_mutex_leave(mutex);
 }
 
@@ -413,7 +418,7 @@ int sqlite3AutoLoadExtensions(sqlite3 *db){
   int rc = SQLITE_OK;
   int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
 
-  if( nAutoExtension==0 ){
+  if( autoext.nExt==0 ){
     /* Common case: early out without every having to acquire a mutex */
     return SQLITE_OK;
   }
@@ -421,12 +426,12 @@ int sqlite3AutoLoadExtensions(sqlite3 *db){
     char *zErrmsg = 0;
     sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
     sqlite3_mutex_enter(mutex);
-    if( i>=nAutoExtension ){
+    if( i>=autoext.nExt ){
       xInit = 0;
       go = 0;
     }else{
       xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
-              aAutoExtension[i];
+              autoext.aExt[i];
     }
     sqlite3_mutex_leave(mutex);
     if( xInit && xInit(db, &zErrmsg, &sqlite3_apis) ){
index 719ecb634ba2558b88a1bb06898d776b0fdb9248..0cdda648e186de2f69da6b9d3c07c95153470aa2 100644 (file)
@@ -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.390 2007/08/21 10:44:16 drh Exp $
+** $Id: main.c,v 1.391 2007/08/21 15:13:19 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -118,6 +118,7 @@ int sqlite3_close(sqlite3 *db){
   if( sqlite3SafetyCheck(db) ){
     return SQLITE_MISUSE;
   }
+  sqlite3_mutex_enter(db->mutex);
 
 #ifdef SQLITE_SSE
   {
@@ -218,6 +219,8 @@ int sqlite3_close(sqlite3 *db){
   */
   sqlite3_free(db->aDb[1].pSchema);
   sqlite3_vfs_release(db->pVfs);
+  sqlite3_mutex_leave(db->mutex);
+  sqlite3_mutex_free(db->mutex);
   sqlite3_free(db);
   return SQLITE_OK;
 }
@@ -890,7 +893,9 @@ static int createCollation(
 */
 static int openDatabase(
   const char *zFilename, /* Database filename UTF-8 encoded */
-  sqlite3 **ppDb         /* OUT: Returned database handle */
+  sqlite3 **ppDb,        /* OUT: Returned database handle */
+  unsigned flags,        /* Operational flags */
+  const char *zVfs       /* Name of the VFS to use */
 ){
   sqlite3 *db;
   int rc;
@@ -899,7 +904,12 @@ static int openDatabase(
   /* Allocate the sqlite data structure */
   db = sqlite3MallocZero( sizeof(sqlite3) );
   if( db==0 ) goto opendb_out;
-  db->pVfs = sqlite3_vfs_find(0);
+  db->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE);
+  if( db->mutex==0 ){
+    db->mallocFailed = 1;
+    goto opendb_out;
+  }
+  db->pVfs = sqlite3_vfs_find(zVfs);
   db->errMask = 0xff;
   db->priorNewRowid = 0;
   db->magic = SQLITE_MAGIC_BUSY;
@@ -1041,7 +1051,16 @@ int sqlite3_open(
   const char *zFilename, 
   sqlite3 **ppDb 
 ){
-  return openDatabase(zFilename, ppDb);
+  return openDatabase(zFilename, ppDb,
+                      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
+}
+int sqlite3_open_v2(
+  const void *filename,   /* Database filename (UTF-8) */
+  sqlite3 **ppDb,         /* OUT: SQLite db handle */
+  int flags,              /* Flags */
+  const char *zVfs        /* Name of VFS module to use */
+){
+  return openDatabase(filename, ppDb, flags, zVfs);
 }
 
 #ifndef SQLITE_OMIT_UTF16
@@ -1063,7 +1082,8 @@ int sqlite3_open16(
   sqlite3ValueSetStr(0, pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC);
   zFilename8 = sqlite3ValueText(0, pVal, SQLITE_UTF8);
   if( zFilename8 ){
-    rc = openDatabase(zFilename8, ppDb);
+    rc = openDatabase(zFilename8, ppDb,
+                      SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
     if( rc==SQLITE_OK && *ppDb ){
       rc = sqlite3_exec(*ppDb, "PRAGMA encoding = 'UTF-16'", 0, 0, 0);
       if( rc!=SQLITE_OK ){
index d70e0444e68ba011dd0f108b4c327ae124b3d834..55f9204220c49a7276d29d2831c5054c67af3fdd 100644 (file)
@@ -30,7 +30,7 @@
 ** the version number) and changes its name to "sqlite3.h" as
 ** part of the build process.
 **
-** @(#) $Id: sqlite.h.in,v 1.231 2007/08/20 22:48:43 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.232 2007/08/21 15:13:19 drh Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -1619,6 +1619,10 @@ int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
 ** are used (where NNN is an integer) then there might be gaps in the
 ** numbering and the value returned by this interface is the index of the
 ** host parameter with the largest index value.
+**
+** The prepared statement must not not be [sqlite3_finalize | finalized]
+** prior to this routine returnning.  Otherwise the results are undefined
+** and probably undesirable.
 */
 int sqlite3_bind_parameter_count(sqlite3_stmt*);
 
@@ -2222,7 +2226,7 @@ int sqlite3_value_numeric_type(sqlite3_value*);
 ** function.
 **
 ** This routine must be called from the same thread in which
-** the aggregate SQL function was originally invoked.
+** the aggregate SQL function is running.
 */
 void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
 
@@ -2788,7 +2792,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
 **
 ** Register an extension entry point that is automatically invoked
 ** whenever a new database connection is opened using
-** [sqlite3_open()] or [sqlite3_open16()].
+** [sqlite3_open()], [sqlite3_open16()], or [sqlite3_open_v2()].
 **
 ** This API can be invoked at program startup in order to register
 ** one or more statically linked extensions that will be available
index 1226956080ba60d549552be92e65ea40255715de..ffe89b24c80235443ad308e148623447415e680e 100644 (file)
@@ -719,29 +719,34 @@ static int vdbeUnbind(Vdbe *p, int i){
 ** Bind a text or BLOB value.
 */
 static int bindText(
-  sqlite3_stmt *pStmt, 
-  int i, 
-  const void *zData, 
-  int nData, 
-  void (*xDel)(void*),
-  int encoding
+  sqlite3_stmt *pStmt,   /* The statement to bind against */
+  int i,                 /* Index of the parameter to bind */
+  const void *zData,     /* Pointer to the data to be bound */
+  int nData,             /* Number of bytes of data to be bound */
+  void (*xDel)(void*),   /* Destructor for the data */
+  int encoding           /* Encoding for the data */
 ){
   Vdbe *p = (Vdbe *)pStmt;
   Mem *pVar;
   int rc;
 
+  if( p==0 ){
+    return SQLITE_MISUSE;
+  }
+  sqlite3_mutex_enter(p->db->mutex);
   rc = vdbeUnbind(p, i);
   if( rc || zData==0 ){
     return rc;
   }
   pVar = &p->aVar[i-1];
-  rc = sqlite3VdbeMemSetStr(0, pVar, zData, nData, encoding, xDel);
+  rc = sqlite3VdbeMemSetStr(p->db, pVar, zData, nData, encoding, xDel);
   if( rc==SQLITE_OK && encoding!=0 ){
     rc = sqlite3VdbeChangeEncoding(p->db, pVar, ENC(p->db));
   }
-
   sqlite3Error(p->db, rc, 0);
-  return sqlite3ApiExit(p->db, rc);
+  rc = sqlite3ApiExit(p->db, rc);
+  sqlite3_mutex_leave(p->db->mutex);
+  return rc;
 }
 
 
@@ -760,10 +765,12 @@ int sqlite3_bind_blob(
 int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
   int rc;
   Vdbe *p = (Vdbe *)pStmt;
+  sqlite3_mutex_enter(p->db->mutex);
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
   }
+  sqlite3_mutex_leave(p->db->mutex);
   return rc;
 }
 int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
@@ -772,14 +779,21 @@ int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
 int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
   int rc;
   Vdbe *p = (Vdbe *)pStmt;
+  sqlite3_mutex_enter(p->db->mutex);
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
   }
+  sqlite3_mutex_leave(p->db->mutex);
   return rc;
 }
-int sqlite3_bind_null(sqlite3_stmt* p, int i){
-  return vdbeUnbind((Vdbe *)p, i);
+int sqlite3_bind_null(sqlite3_stmt *pStmt, int i){
+  int rc;
+  Vdbe *p = (Vdbe*)pStmt;
+  sqlite3_mutex_enter(p->db->mutex);
+  rc = vdbeUnbind(p, i);
+  sqlite3_mutex_leave(p->db->mutex);
+  return rc;
 }
 int sqlite3_bind_text( 
   sqlite3_stmt *pStmt, 
@@ -804,19 +818,23 @@ int sqlite3_bind_text16(
 int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
   int rc;
   Vdbe *p = (Vdbe *)pStmt;
+  sqlite3_mutex_enter(p->db->mutex);
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     rc = sqlite3VdbeMemCopy(0, &p->aVar[i-1], pValue);
   }
+  sqlite3_mutex_leave(p->db->mutex);
   return rc;
 }
 int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
   int rc;
   Vdbe *p = (Vdbe *)pStmt;
+  sqlite3_mutex_enter(p->db->mutex);
   rc = vdbeUnbind(p, i);
   if( rc==SQLITE_OK ){
     sqlite3VdbeMemSetZeroBlob(&p->aVar[i-1], n);
   }
+  sqlite3_mutex_leave(p->db->mutex);
   return rc;
 }
 
@@ -836,15 +854,19 @@ int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
 */
 static void createVarMap(Vdbe *p){
   if( !p->okVar ){
-    int j;
-    Op *pOp;
-    for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
-      if( pOp->opcode==OP_Variable ){
-        assert( pOp->p1>0 && pOp->p1<=p->nVar );
-        p->azVar[pOp->p1-1] = pOp->p3;
+    sqlite3_mutex_enter(p->db->mutex);
+    if( !p->okVar ){
+      int j;
+      Op *pOp;
+      for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
+        if( pOp->opcode==OP_Variable ){
+          assert( pOp->p1>0 && pOp->p1<=p->nVar );
+          p->azVar[pOp->p1-1] = pOp->p3;
+        }
       }
+      p->okVar = 1;
     }
-    p->okVar = 1;
+    sqlite3_mutex_leave(p->db->mutex);
   }
 }
 
index 8fdf063803e6999bce2c5297db82fe36ad857616..216803123a25c54829564062a1b4ec351dab0a11 100644 (file)
@@ -12,7 +12,7 @@
 **
 ** This file contains code used to implement incremental BLOB I/O.
 **
-** $Id: vdbeblob.c,v 1.12 2007/08/16 04:30:41 drh Exp $
+** $Id: vdbeblob.c,v 1.13 2007/08/21 15:13:19 drh Exp $
 */
 
 #include "sqliteInt.h"
@@ -30,6 +30,7 @@ struct Incrblob {
   int iOffset;            /* Byte offset of blob in cursor data */
   BtCursor *pCsr;         /* Cursor pointing at blob row */
   sqlite3_stmt *pStmt;    /* Statement holding cursor open */
+  sqlite3 *db;            /* The associated database */
 };
 
 /*
@@ -87,6 +88,7 @@ int sqlite3_blob_open(
   char zErr[128];
 
   zErr[0] = 0;
+  sqlite3_mutex_enter(db->mutex);
   do {
     Parse sParse;
     Table *pTab;
@@ -96,6 +98,7 @@ int sqlite3_blob_open(
 
     rc = sqlite3SafetyOn(db);
     if( rc!=SQLITE_OK ){
+      sqlite3_mutex_leave(db->mutex);
       return rc;
     }
 
@@ -219,6 +222,7 @@ int sqlite3_blob_open(
     pBlob->pStmt = (sqlite3_stmt *)v;
     pBlob->iOffset = v->apCsr[0]->aOffset[iCol];
     pBlob->nByte = sqlite3VdbeSerialTypeLen(type);
+    pBlob->db = db;
     *ppBlob = (sqlite3_blob *)pBlob;
     rc = SQLITE_OK;
   }else if( rc==SQLITE_OK ){
@@ -232,7 +236,9 @@ blob_open_out:
     sqlite3_finalize((sqlite3_stmt *)v);
   }
   sqlite3Error(db, rc, (rc==SQLITE_OK?0:zErr));
-  return sqlite3ApiExit(db, rc);
+  rc = sqlite3ApiExit(db, rc);
+  sqlite3_mutex_leave(db->mutex);
+  return rc;
 }
 
 /*
@@ -241,12 +247,20 @@ blob_open_out:
 */
 int sqlite3_blob_close(sqlite3_blob *pBlob){
   Incrblob *p = (Incrblob *)pBlob;
-  sqlite3_stmt *pStmt = p->pStmt;
+  sqlite3_stmt *pStmt;
+  sqlite3_mutex *mutex = p->db->mutex;
+  int rc;
+
+  sqlite3_mutex_enter(mutex);
+  rc = sqlite3_finalize(p->pStmt);
+  sqlite3_mutex_leave(mutex);
   sqlite3_free(p);
-  return sqlite3_finalize(pStmt);
+  return rc;
 }
 
-
+/*
+** Perform a read or write operation on a blob
+*/
 static int blobReadWrite(
   sqlite3_blob *pBlob, 
   void *z, 
@@ -256,33 +270,38 @@ static int blobReadWrite(
 ){
   int rc;
   Incrblob *p = (Incrblob *)pBlob;
-  Vdbe *v = (Vdbe *)(p->pStmt);
-  sqlite3 *db;  
-
-  /* If there is no statement handle, then the blob-handle has
-  ** already been invalidated. Return SQLITE_ABORT in this case.
-  */
-  if( !v ) return SQLITE_ABORT;
+  Vdbe *v;
+  sqlite3 *db = p->db;  
 
   /* Request is out of range. Return a transient error. */
   if( (iOffset+n)>p->nByte ){
     return SQLITE_ERROR;
   }
+  sqlite3_mutex_enter(db->mutex);
 
-  /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
-  ** returned, clean-up the statement handle.
+  /* If there is no statement handle, then the blob-handle has
+  ** already been invalidated. Return SQLITE_ABORT in this case.
   */
-  db = v->db;
-  rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
-  if( rc==SQLITE_ABORT ){
-    sqlite3VdbeFinalize(v);
-    p->pStmt = 0;
+  v = (Vdbe*)p->pStmt;
+  if( v==0 ){
+    rc = SQLITE_ABORT;
   }else{
-    db->errCode = rc;
-    v->rc = rc;
+    /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
+    ** returned, clean-up the statement handle.
+    */
+    assert( db == v->db );
+    rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
+    if( rc==SQLITE_ABORT ){
+      sqlite3VdbeFinalize(v);
+      p->pStmt = 0;
+    }else{
+      db->errCode = rc;
+      v->rc = rc;
+    }
   }
-
-  return sqlite3ApiExit(db, rc);
+  rc = sqlite3ApiExit(db, rc);
+  sqlite3_mutex_leave(db->mutex);
+  return rc;
 }
 
 /*
@@ -301,6 +320,9 @@ int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){
 
 /*
 ** Query a blob handle for the size of the data.
+**
+** The Incrblob.nByte field is fixed for the lifetime of the Incrblob
+** so no mutex is required for access.
 */
 int sqlite3_blob_bytes(sqlite3_blob *pBlob){
   Incrblob *p = (Incrblob *)pBlob;