Structural coverage tests for vdbeblob.c. Including experimental new API sqlite3_blob_reopen().

FossilOrigin-Name: 97c6b2616ddcce2337778c6ee88a973cc4fe999d
diff --git a/src/test1.c b/src/test1.c
index 6ea6e82..a29c91f 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -1590,6 +1590,82 @@
 
 #ifndef SQLITE_OMIT_INCRBLOB
 
+static int blobHandleFromObj(
+  Tcl_Interp *interp, 
+  Tcl_Obj *pObj,
+  sqlite3_blob **ppBlob
+){
+  char *z;
+  int n;
+
+  z = Tcl_GetStringFromObj(pObj, &n);
+  if( n==0 ){
+    *ppBlob = 0;
+  }else{
+    int notUsed;
+    Tcl_Channel channel;
+    ClientData instanceData;
+    
+    channel = Tcl_GetChannel(interp, z, &notUsed);
+    if( !channel ) return TCL_ERROR;
+
+    Tcl_Flush(channel);
+    Tcl_Seek(channel, 0, SEEK_SET);
+
+    instanceData = Tcl_GetChannelInstanceData(channel);
+    *ppBlob = *((sqlite3_blob **)instanceData);
+  }
+
+  return TCL_OK;
+}
+
+/*
+** sqlite3_blob_bytes  CHANNEL
+*/
+static int test_blob_bytes(
+  ClientData clientData, /* Not used */
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int objc,              /* Number of arguments */
+  Tcl_Obj *CONST objv[]  /* Command arguments */
+){
+  sqlite3_blob *pBlob;
+  int nByte;
+  
+  if( objc!=2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
+    return TCL_ERROR;
+  }
+
+  if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+  nByte = sqlite3_blob_bytes(pBlob);
+  Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte));
+
+  return TCL_OK;
+}
+
+/*
+** sqlite3_blob_close  CHANNEL
+*/
+static int test_blob_close(
+  ClientData clientData, /* Not used */
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int objc,              /* Number of arguments */
+  Tcl_Obj *CONST objv[]  /* Command arguments */
+){
+  sqlite3_blob *pBlob;
+  int nByte;
+  
+  if( objc!=2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL");
+    return TCL_ERROR;
+  }
+
+  if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+  sqlite3_blob_close(pBlob);
+
+  return TCL_OK;
+}
+
 /*
 ** sqlite3_blob_read  CHANNEL OFFSET N
 **
@@ -1611,10 +1687,7 @@
   int objc,              /* Number of arguments */
   Tcl_Obj *CONST objv[]  /* Command arguments */
 ){
-  Tcl_Channel channel;
-  ClientData instanceData;
   sqlite3_blob *pBlob;
-  int notUsed;
   int nByte;
   int iOffset;
   unsigned char *zBuf;
@@ -1625,18 +1698,13 @@
     return TCL_ERROR;
   }
 
-  channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
-  if( !channel
-   || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
+  if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+  if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset)
    || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte)
-   || nByte<0 || iOffset<0
   ){ 
     return TCL_ERROR;
   }
 
-  instanceData = Tcl_GetChannelInstanceData(channel);
-  pBlob = *((sqlite3_blob **)instanceData);
-
   zBuf = (unsigned char *)Tcl_Alloc(nByte);
   rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset);
   if( rc==SQLITE_OK ){
@@ -1669,10 +1737,7 @@
   int objc,              /* Number of arguments */
   Tcl_Obj *CONST objv[]  /* Command arguments */
 ){
-  Tcl_Channel channel;
-  ClientData instanceData;
   sqlite3_blob *pBlob;
-  int notUsed;
   int iOffset;
   int rc;
 
@@ -1684,14 +1749,11 @@
     return TCL_ERROR;
   }
 
-  channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
-  if( !channel || TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){ 
+  if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+  if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){ 
     return TCL_ERROR;
   }
 
-  instanceData = Tcl_GetChannelInstanceData(channel);
-  pBlob = *((sqlite3_blob **)instanceData);
-
   zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf);
   if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){
     return TCL_ERROR;
@@ -1711,10 +1773,7 @@
   Tcl_Obj *CONST objv[]  /* Command arguments */
 ){
   Tcl_WideInt iRowid;
-  Tcl_Channel channel;
-  ClientData instanceData;
   sqlite3_blob *pBlob;
-  int notUsed;
   int rc;
 
   unsigned char *zBuf;
@@ -1725,20 +1784,8 @@
     return TCL_ERROR;
   }
 
-  channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), &notUsed);
-  if( !channel || TCL_OK!=Tcl_GetWideIntFromObj(interp, objv[2], &iRowid) ){ 
-    return TCL_ERROR;
-  }
-
-  if( TCL_OK!=(rc = Tcl_Flush(channel)) ){
-    return rc;
-  }
-  if( TCL_OK!=(rc = Tcl_Seek(channel, 0, SEEK_SET)) ){
-    return rc;
-  }
-
-  instanceData = Tcl_GetChannelInstanceData(channel);
-  pBlob = *((sqlite3_blob **)instanceData);
+  if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR;
+  if( Tcl_GetWideIntFromObj(interp, objv[2], &iRowid) ) return TCL_ERROR;
 
   rc = sqlite3_blob_reopen(pBlob, iRowid);
   if( rc!=SQLITE_OK ){
@@ -5371,9 +5418,11 @@
      { "sqlite3_table_column_metadata", test_table_column_metadata, 0  },
 #endif
 #ifndef SQLITE_OMIT_INCRBLOB
-     { "sqlite3_blob_read",  test_blob_read, 0  },
-     { "sqlite3_blob_write", test_blob_write, 0  },
+     { "sqlite3_blob_read",   test_blob_read, 0  },
+     { "sqlite3_blob_write",  test_blob_write, 0  },
      { "sqlite3_blob_reopen", test_blob_reopen, 0  },
+     { "sqlite3_blob_bytes",  test_blob_bytes, 0  },
+     { "sqlite3_blob_close",  test_blob_close, 0  },
 #endif
      { "pcache_stats",       test_pcache_stats, 0  },
 #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index 1bfce65..f43fc64 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -150,12 +150,11 @@
     {OP_Halt, 0, 0, 0},            /* 11 */
   };
 
-  Vdbe *v = 0;
   int rc = SQLITE_OK;
   char *zErr = 0;
   Table *pTab;
-  Parse *pParse;
-  Incrblob *pBlob;
+  Parse *pParse = 0;
+  Incrblob *pBlob = 0;
 
   flags = !!flags;                /* flags = (flags ? 1 : 0); */
   *ppBlob = 0;
@@ -163,15 +162,15 @@
   sqlite3_mutex_enter(db->mutex);
 
   pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob));
+  if( !pBlob ) goto blob_open_out;
   pParse = sqlite3StackAllocRaw(db, sizeof(*pParse));
-  if( pParse==0 || pBlob==0 ){
-    assert( db->mallocFailed );
-    goto blob_open_out;
-  }
+  if( !pParse ) goto blob_open_out;
 
   do {
     memset(pParse, 0, sizeof(Parse));
     pParse->db = db;
+    sqlite3DbFree(db, zErr);
+    zErr = 0;
 
     sqlite3BtreeEnterAll(db);
     pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
@@ -197,7 +196,7 @@
     }
 
     /* Now search pTab for the exact column. */
-    for(iCol=0; iCol < pTab->nCol; iCol++) {
+    for(iCol=0; iCol<pTab->nCol; iCol++) {
       if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){
         break;
       }
@@ -251,11 +250,15 @@
       }
     }
 
-    v = sqlite3VdbeCreate(db);
-    if( v ){
+    pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db);
+    assert( pBlob->pStmt || db->mallocFailed );
+    if( pBlob->pStmt ){
+      Vdbe *v = (Vdbe *)pBlob->pStmt;
       int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+
       sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
 
+
       /* Configure the OP_Transaction */
       sqlite3VdbeChangeP1(v, 0, iDb);
       sqlite3VdbeChangeP2(v, 0, flags);
@@ -298,11 +301,9 @@
     }
    
     pBlob->flags = flags;
-    pBlob->pStmt = (sqlite3_stmt *)v;
     pBlob->iCol = iCol;
     pBlob->db = db;
     sqlite3BtreeLeaveAll(db);
-    v = 0;
     if( db->mallocFailed ){
       goto blob_open_out;
     }
@@ -314,7 +315,6 @@
   if( rc==SQLITE_OK && db->mallocFailed==0 ){
     *ppBlob = (sqlite3_blob *)pBlob;
   }else{
-    if( v ) sqlite3VdbeFinalize(v);
     if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
     sqlite3DbFree(db, pBlob);
   }