]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the sqlite3_blob_reset() interface. Enhance the behavior of sqlite3_blob
authordrh <drh@noemail.net>
Thu, 2 Feb 2017 23:57:40 +0000 (23:57 +0000)
committerdrh <drh@noemail.net>
Thu, 2 Feb 2017 23:57:40 +0000 (23:57 +0000)
objects so that they can go active again after encountering an error by
rerunning sqlite3_blob_reopen().  More work needed on the documentation.

FossilOrigin-Name: 53b77838f05b4805b956b5763c75af434662fb20

manifest
manifest.uuid
src/sqlite.h.in
src/vdbeblob.c
test/e_blobwrite.test
test/incrblob3.test

index a07bc6f33275b5c66e8dae4fae9046e82bf4bc3c..0587ec07881caef6b790ab3348a086fe2830d50f 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Ensure\sthat\sall\scursors\shave\stheir\spositions\ssaved\sprior\sto\srolling\sback\na\ssavepoint.
-D 2017-02-02T20:32:28.731
+C Add\sthe\ssqlite3_blob_reset()\sinterface.\s\sEnhance\sthe\sbehavior\sof\ssqlite3_blob\nobjects\sso\sthat\sthey\scan\sgo\sactive\sagain\safter\sencountering\san\serror\sby\nrerunning\ssqlite3_blob_reopen().\s\sMore\swork\sneeded\son\sthe\sdocumentation.
+D 2017-02-02T23:57:40.053
 F Makefile.in 5f415e7867296d678fed2e6779aea10c1318b4bc
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc ba953c8921fc7e18333f61898007206de7e23964
@@ -395,7 +395,7 @@ F src/resolve.c f9bc0de45a30a450da47b3766de00be89bf9be79
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
 F src/select.c 3856db523b942062bca8722ba03b61c324ff94d6
 F src/shell.c a84e453c213f3e0d6935a582024da4e242f85a19
-F src/sqlite.h.in 751ff125eb159c8f92c182b8df980a5e4f50e966
+F src/sqlite.h.in 878b0f81f3f59d1f24c002127dd18740a81ba32a
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
 F src/sqliteInt.h 3724c48e82605b471bbf7e41109b5850c6e89f31
@@ -465,7 +465,7 @@ F src/vdbe.h 59998ffd71d7caa8886bc78dafaf8caeccd4c13c
 F src/vdbeInt.h 4e4b15b2e1330e1636e4e01974eab2b0b985092f
 F src/vdbeapi.c 3e4a8893feeb78620f4aac4ac5b85d92255b97e1
 F src/vdbeaux.c b9a36e530e6525ca9d9a685bc7b1d01fa77b5cf8
-F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
+F src/vdbeblob.c a0e2edbfff53935c5434e2bce031801decd0e6dd
 F src/vdbemem.c 3b5a9a5b375458d3e12a50ae1aaa41eeec2175fd
 F src/vdbesort.c eda25cb2d1727efca6f7862fea32b8aa33c0face
 F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
@@ -652,7 +652,7 @@ F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
 F test/e_blobbytes.test 439a945953b35cb6948a552edaec4dc31fd70a05
 F test/e_blobclose.test 4b3c8c60c2171164d472059c73e9f3c1844bb66d
 F test/e_blobopen.test e95e1d40f995056f6f322cd5e1a1b83a27e1a145
-F test/e_blobwrite.test 650ded42ee5e0c2dbf263583ce01adf280129599
+F test/e_blobwrite.test 98c2f532be5b4e5f7b4d653f716bec06835b7957
 F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
 F test/e_createtable.test d4c6059d44dcd4b636de9aae322766062b471844
 F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
@@ -847,7 +847,7 @@ F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
 F test/in5.test 6c006e0bcd7351b69350ef566e65f244023489e9
 F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
 F test/incrblob2.test a5ce5ed1d0b01e2ed347245a21170372528af0a5
-F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
+F test/incrblob3.test f1b473c65d8bb43b5f4e2bdcc3f7fc3ac83fd19c
 F test/incrblob4.test 21a52a6843a56cdcce968c6a86b72a7066d0e6ba
 F test/incrblob_err.test 69f9247fed50278d48ea710d1a8f9cdb09e4c0b8
 F test/incrblobfault.test 280474078f6da9e732cd2a215d3d854969014b6e
@@ -1552,8 +1552,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 5a0da77c22ebc7db5e63b1520d30f3ad97b9bb3b 01d97e5b6502b1811b52a681f445e1aaae6c0ee6
-R f67fbd165c607c8ab8b9fb8cff6e4d0d
-T +closed 01d97e5b6502b1811b52a681f445e1aaae6c0ee6
+P 8e03a8e95fada5c24d369672a71f6e02288051da
+R f941c08bfee140c0cc6872a3dcf84c2c
+T *branch * sqlite3_blob_reset
+T *sym-sqlite3_blob_reset *
+T -sym-trunk *
 U drh
-Z d16d6898d272ff11e9984476ae1d9ef3
+Z e33493eda3d7be90d201c2b56a3ef767
index 792cc0b3a3fb8c88de30026c2ea6c9b7b0a7212b..2222f832412fb79aa888ee45a69dc075f29f0bbf 100644 (file)
@@ -1 +1 @@
-8e03a8e95fada5c24d369672a71f6e02288051da
\ No newline at end of file
+53b77838f05b4805b956b5763c75af434662fb20
\ No newline at end of file
index eaa75fc249e1839305f00b36def6af0b94245b0a..d52e0ffa81d044767b31504730a982f01d1cd9dd 100644 (file)
@@ -6138,15 +6138,27 @@ int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg);
 
 /*
 ** CAPI3REF: A Handle To An Open BLOB
-** KEYWORDS: {BLOB handle} {BLOB handles}
+** KEYWORDS: {BLOB handle} {BLOB handles} {sqlite3_blob object}
+**
+** An instance of this object represents a connection to a single
+** column in a single row of a table that holds either a BLOB or string
+** and on which [sqlite3_blob_open | incremental BLOB I/O] can be performed.
 **
-** An instance of this object represents an open BLOB on which
-** [sqlite3_blob_open | incremental BLOB I/O] can be performed.
 ** ^Objects of this type are created by [sqlite3_blob_open()]
 ** and destroyed by [sqlite3_blob_close()].
 ** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces
 ** can be used to read or write small subsections of the BLOB.
 ** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes.
+**
+** An sqlite3_blob object can be in two states: ACTIVE and RESET.  When in
+** the ACTIVE state, the object is pointing to a specific entry and is
+** ready to do I/O.  When RESET, the sqlite3_blob object is in standby mode,
+** is not associated with any particular row of its table, 
+** and is not available for I/O.
+**
+** The sqlite3_blob object does not contain a mutex and so a single
+** sqlite3_blob object may not be safely used by multiple threads
+** concurrently.
 */
 typedef struct sqlite3_blob sqlite3_blob;
 
@@ -6155,8 +6167,8 @@ typedef struct sqlite3_blob sqlite3_blob;
 ** METHOD: sqlite3
 ** CONSTRUCTOR: sqlite3_blob
 **
-** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located
-** in row iRow, column zColumn, table zTable in database zDb;
+** ^(This interfaces creates an sqlite3_blob object pointing to the string
+** or BLOB located in row iRow, column zColumn, table zTable in database zDb;
 ** in other words, the same BLOB that would be selected by:
 **
 ** <pre>
@@ -6173,10 +6185,11 @@ typedef struct sqlite3_blob sqlite3_blob;
 ** and write access. ^If the flags parameter is zero, the BLOB is opened for
 ** read-only access.
 **
-** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored
-** in *ppBlob. Otherwise an [error code] is returned and, unless the error
-** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
-** the API is not misused, it is always safe to call [sqlite3_blob_close()] 
+** ^(On success, [SQLITE_OK] is returned and the new [sqlite3_blob object]
+** is stored in *ppBlob. Otherwise an [error code] is returned and
+** *ppBlob is set to NULL.)^ ^Because *ppBlob is always set to either a
+** valid sqlite3_blob object or NULL
+** it is always safe to call [sqlite3_blob_close()] 
 ** on *ppBlob after this function it returns.
 **
 ** This function fails with SQLITE_ERROR if any of the following are true:
@@ -6208,7 +6221,8 @@ typedef struct sqlite3_blob sqlite3_blob;
 **
 ** ^(If the row that a BLOB handle points to is modified by an
 ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
-** then the BLOB handle is marked as "expired".
+** then the BLOB handle is marked as "expired" and cannot be used
+** successfully with first being [sqlite3_blob_reopen|reopened].
 ** This is true if any column of the row is changed, even a column
 ** other than the one the BLOB handle is open on.)^
 ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for
@@ -6220,7 +6234,8 @@ typedef struct sqlite3_blob sqlite3_blob;
 ** ^Use the [sqlite3_blob_bytes()] interface to determine the size of
 ** the opened blob.  ^The size of a blob may not be changed by this
 ** interface.  Use the [UPDATE] SQL command to change the size of a
-** blob.
+** blob.  The [sqlite3_blob_bytes()] interface returns an arbitrary
+** number when used on an expired sqlite3_blob object.
 **
 ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
 ** and the built-in [zeroblob] SQL function may be used to create a 
@@ -6231,7 +6246,8 @@ typedef struct sqlite3_blob sqlite3_blob;
 **
 ** See also: [sqlite3_blob_close()],
 ** [sqlite3_blob_reopen()], [sqlite3_blob_read()],
-** [sqlite3_blob_bytes()], [sqlite3_blob_write()].
+** [sqlite3_blob_bytes()], [sqlite3_blob_write()],
+** [sqlite3_blob_reset()].
 */
 int sqlite3_blob_open(
   sqlite3*,
@@ -6244,24 +6260,23 @@ int sqlite3_blob_open(
 );
 
 /*
-** CAPI3REF: Move a BLOB Handle to a New Row
+** CAPI3REF: Move an sqlite3_blob object to a New Row
 ** METHOD: sqlite3_blob
 **
-** ^This function is used to move an existing [BLOB handle] so that it points
+** ^This function updates an existing [sqlite3_blob object] so that it points
 ** to a different row of the same database table. ^The new row is identified
 ** by the rowid value passed as the second argument. Only the row can be
 ** changed. ^The database, table and column on which the blob handle is open
-** remain the same. Moving an existing [BLOB handle] to a new row is
+** remain the same. Moving an existing [sqlite3_blob object] to a new row is
 ** faster than closing the existing handle and opening a new one.
 **
 ** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] -
 ** it must exist and there must be either a blob or text value stored in
 ** the nominated column.)^ ^If the new row is not present in the table, or if
 ** it does not contain a blob or text value, or if another error occurs, an
-** SQLite error code is returned and the blob handle is considered aborted.
-** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or
-** [sqlite3_blob_reopen()] on an aborted blob handle immediately return
-** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle
+** SQLite error code is returned and the [sqlite3_blob object] is changed
+** to the RESET state.
+** ^Calling [sqlite3_blob_bytes()] on an aborted blob handle
 ** always returns zero.
 **
 ** ^This function sets the database handle error code and message.
@@ -6269,38 +6284,52 @@ int sqlite3_blob_open(
 int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
 
 /*
-** CAPI3REF: Close A BLOB Handle
+** CAPI3REF: Close an sqlite3_blob object
 ** DESTRUCTOR: sqlite3_blob
 **
-** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed
+** ^This function is the destructor for an [sqlite3_blob object]. 
+** ^(The [sqlite3_blob object] is closed
 ** unconditionally.  Even if this routine returns an error code, the 
-** handle is still closed.)^
+** object is still closed.)^
 **
-** ^If the blob handle being closed was opened for read-write access, and if
-** the database is in auto-commit mode and there are no other open read-write
+** ^If the sqlite3_blob object being closed was opened for read-write access,
+** and is in the ACTIVE state,
+** and if the database is in auto-commit mode and there are no other open 
+** read-write
 ** blob handles or active write statements, the current transaction is
 ** committed. ^If an error occurs while committing the transaction, an error
 ** code is returned and the transaction rolled back.
 **
-** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine 
+** Calling this function with an argument that is not a NULL pointer or a
+** valid sqlite3_blob object pointer results in undefined behaviour. 
+** ^Calling this routine 
 ** with a null pointer (such as would be returned by a failed call to 
 ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
-** is passed a valid open blob handle, the values returned by the 
+** is passed a valid sqlite3_blob objecct pointer, the values returned by the 
 ** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning.
 */
 int sqlite3_blob_close(sqlite3_blob *);
 
 /*
-** CAPI3REF: Return The Size Of An Open BLOB
+** CAPI3REF: Reset an sqlite3_blob object
 ** METHOD: sqlite3_blob
 **
-** ^Returns the size in bytes of the BLOB accessible via the 
-** successfully opened [BLOB handle] in its only argument.  ^The
+** ^This function changes an [sqlite3_blob object] to the RESET state.
+** ^If the [sqlite3_blob object] was already in the RESET state, then this
+** routine is a harmless no-op.
+*/
+int sqlite3_blob_reset(sqlite3_blob *);
+
+/*
+** CAPI3REF: Return The Size Of A BLOB
+** METHOD: sqlite3_blob
+**
+** ^Returns the size in bytes of the BLOB or string accessible via the 
+** ACTIVE [sqlite3_blob object] in its only argument.  ^The
 ** incremental blob I/O routines can only read or overwriting existing
-** blob content; they cannot change the size of a blob.
+** content; they cannot change the size of a blob or string.
 **
-** This routine only works on a [BLOB handle] which has been created
+** This routine only works on an [sqlite3_blob object] that has been created
 ** by a prior successful call to [sqlite3_blob_open()] and which has not
 ** been closed by [sqlite3_blob_close()].  Passing any other pointer in
 ** to this routine results in undefined and probably undesirable behavior.
@@ -6311,9 +6340,9 @@ int sqlite3_blob_bytes(sqlite3_blob *);
 ** CAPI3REF: Read Data From A BLOB Incrementally
 ** METHOD: sqlite3_blob
 **
-** ^(This function is used to read data from an open [BLOB handle] into a
-** caller-supplied buffer. N bytes of data are copied into buffer Z
-** from the open BLOB, starting at offset iOffset.)^
+** ^(This function is used to read data from string or BLOB that an
+** [sqlite3_blob object] is pointing to into a caller-supplied buffer. 
+** N bytes of data are copied into buffer Z starting at offset iOffset.)^
 **
 ** ^If offset iOffset is less than N bytes from the end of the BLOB,
 ** [SQLITE_ERROR] is returned and no data is read.  ^If N or iOffset is
@@ -6321,17 +6350,14 @@ int sqlite3_blob_bytes(sqlite3_blob *);
 ** ^The size of the blob (and hence the maximum value of N+iOffset)
 ** can be determined using the [sqlite3_blob_bytes()] interface.
 **
-** ^An attempt to read from an expired [BLOB handle] fails with an
+** ^An attempt to read from an [sqlite3_blob object] that is in the RESET
+** state, or from a database row that has changed since the most recent
+** call to [sqlite3_blob_open()] or [sqlite3_blob_reopen()] fails with an
 ** error code of [SQLITE_ABORT].
 **
 ** ^(On success, sqlite3_blob_read() returns SQLITE_OK.
 ** Otherwise, an [error code] or an [extended error code] is returned.)^
 **
-** This routine only works on a [BLOB handle] which has been created
-** by a prior successful call to [sqlite3_blob_open()] and which has not
-** been closed by [sqlite3_blob_close()].  Passing any other pointer in
-** to this routine results in undefined and probably undesirable behavior.
-**
 ** See also: [sqlite3_blob_write()].
 */
 int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
@@ -6340,9 +6366,10 @@ int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
 ** CAPI3REF: Write Data Into A BLOB Incrementally
 ** METHOD: sqlite3_blob
 **
-** ^(This function is used to write data into an open [BLOB handle] from a
-** caller-supplied buffer. N bytes of data are copied from the buffer Z
-** into the open BLOB, starting at offset iOffset.)^
+** ^(This function is used to write data from a caller-supplied buffer
+** into a BLOB or string pointed to by an [sqlite3_blob object].
+** N bytes of data are copied from the buffer Z
+** into the BLOB or string, starting at offset iOffset.)^
 **
 ** ^(On success, sqlite3_blob_write() returns SQLITE_OK.
 ** Otherwise, an  [error code] or an [extended error code] is returned.)^
@@ -6362,17 +6389,10 @@ int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
 ** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less 
 ** than zero [SQLITE_ERROR] is returned and no data is written.
 **
-** ^An attempt to write to an expired [BLOB handle] fails with an
-** error code of [SQLITE_ABORT].  ^Writes to the BLOB that occurred
-** before the [BLOB handle] expired are not rolled back by the
-** expiration of the handle, though of course those changes might
-** have been overwritten by the statement that expired the BLOB handle
-** or by other independent statements.
-**
-** This routine only works on a [BLOB handle] which has been created
-** by a prior successful call to [sqlite3_blob_open()] and which has not
-** been closed by [sqlite3_blob_close()].  Passing any other pointer in
-** to this routine results in undefined and probably undesirable behavior.
+** ^An attempt to write into an [sqlite3_blob object] that is in the RESET
+** state, or into a database row that has changed since the most recent
+** call to [sqlite3_blob_open()] or [sqlite3_blob_reopen()] fails with an
+** error code of [SQLITE_ABORT].
 **
 ** See also: [sqlite3_blob_read()].
 */
index 5692c1c606d77001ac7f62be5788ef23352b9671..e75130aea293f28fee4b7812d4e5908f06efa227 100644 (file)
@@ -23,7 +23,7 @@
 */
 typedef struct Incrblob Incrblob;
 struct Incrblob {
-  int nByte;              /* Size of open blob, in bytes */
+  int nByte;              /* Size of open blob if ACTIVE. -1 if RESET */
   int iOffset;            /* Byte offset of blob in cursor data */
   u16 iCol;               /* Table column this handle is open on */
   BtCursor *pCsr;         /* Cursor pointing at blob row */
@@ -33,7 +33,6 @@ struct Incrblob {
   Table *pTab;            /* Table object */
 };
 
-
 /*
 ** This function is used by both blob_open() and blob_reopen(). It seeks
 ** the b-tree cursor associated with blob handle p to point to row iRow.
@@ -82,8 +81,8 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
           type==0?"null": type==7?"real": "integer"
       );
       rc = SQLITE_ERROR;
-      sqlite3_finalize(p->pStmt);
-      p->pStmt = 0;
+      sqlite3_reset(p->pStmt);
+      p->nByte = -1;
     }else{
       p->iOffset = pC->aType[p->iCol + pC->nField];
       p->nByte = sqlite3VdbeSerialTypeLen(type);
@@ -94,9 +93,9 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
 
   if( rc==SQLITE_ROW ){
     rc = SQLITE_OK;
-  }else if( p->pStmt ){
-    rc = sqlite3_finalize(p->pStmt);
-    p->pStmt = 0;
+  }else if( p->nByte>=0 ){
+    rc = sqlite3_reset(p->pStmt);
+    p->nByte = -1;
     if( rc==SQLITE_OK ){
       zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow);
       rc = SQLITE_ERROR;
@@ -157,6 +156,7 @@ int sqlite3_blob_open(
     pParse->db = db;
     sqlite3DbFree(db, zErr);
     zErr = 0;
+    sqlite3_finalize(pBlob->pStmt);
 
     sqlite3BtreeEnterAll(db);
     pTab = sqlite3LocateTable(pParse, 0, zTable, zDb);
@@ -324,6 +324,7 @@ int sqlite3_blob_open(
    
     pBlob->iCol = iCol;
     pBlob->db = db;
+    pBlob->nByte = 0;
     sqlite3BtreeLeaveAll(db);
     if( db->mallocFailed ){
       goto blob_open_out;
@@ -359,7 +360,7 @@ int sqlite3_blob_close(sqlite3_blob *pBlob){
   if( p ){
     db = p->db;
     sqlite3_mutex_enter(db->mutex);
-    rc = sqlite3_finalize(p->pStmt);
+    rc = sqlite3VdbeFinalize((Vdbe*)p->pStmt);
     sqlite3DbFree(db, p);
     sqlite3_mutex_leave(db->mutex);
   }else{
@@ -384,6 +385,10 @@ static int blobReadWrite(
   sqlite3 *db;
 
   if( p==0 ) return SQLITE_MISUSE_BKPT;
+  if( p->nByte<0 ){
+    /* The blob handle is in the RESET state.  Always return SQLITE_ABORT. */
+    return SQLITE_ABORT;
+  }
   db = p->db;
   sqlite3_mutex_enter(db->mutex);
   v = (Vdbe*)p->pStmt;
@@ -391,11 +396,6 @@ static int blobReadWrite(
   if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){
     /* Request is out of range. Return a transient error. */
     rc = SQLITE_ERROR;
-  }else if( v==0 ){
-    /* If there is no statement handle, then the blob-handle has
-    ** already been invalidated. Return SQLITE_ABORT in this case.
-    */
-    rc = SQLITE_ABORT;
   }else{
     /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is
     ** returned, clean-up the statement handle.
@@ -429,8 +429,8 @@ static int blobReadWrite(
     rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
     sqlite3BtreeLeaveCursor(p->pCsr);
     if( rc==SQLITE_ABORT ){
-      sqlite3VdbeFinalize(v);
-      p->pStmt = 0;
+      sqlite3_reset(p->pStmt);
+      p->nByte = -1;
     }else{
       v->rc = rc;
     }
@@ -458,14 +458,36 @@ 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.
+** The Incrblob.nByte field is fixed for as long as the sqlite3_blob
+** object is pointing to the same row, so no mutex is required for access.
+**
+** When the sqlite3_blob interface was first designed in 2007, we specified
+** that sqlite3_blob_bytes() returned 0 when in the RESET state.  It would
+** have been better to return -1 in order to distinguish a RESET blob handle
+** from an ACTIVE blob handle pointing to a zero-length string or blob.
+** But, sadly, we cannot change that now without breaking compatibility.
+** So 0 is returned for RESET blob handles and for ACTIVE blob handles
+** pointing to zero-length blobs.
 */
 int sqlite3_blob_bytes(sqlite3_blob *pBlob){
   Incrblob *p = (Incrblob *)pBlob;
-  return (p && p->pStmt) ? p->nByte : 0;
+  return (p && p->pStmt && p->nByte>0) ? p->nByte : 0;
 }
 
+/*
+** Move the sqlite3_blob object to the RESET state.  This releases
+** any locks and gets the object out of the way of commits and
+** rollbacks.
+*/
+int sqlite3_blob_reset(sqlite3_blob *pBlob){
+  Incrblob *p = (Incrblob *)pBlob;
+  if( p->nByte>=0 ){
+    sqlite3_reset(p->pStmt);
+    p->nByte = -1;
+  }
+  return SQLITE_OK;
+}  
+
 /*
 ** Move an existing blob handle to point to a different row of the same
 ** database table.
@@ -480,28 +502,20 @@ int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
   int rc;
   Incrblob *p = (Incrblob *)pBlob;
   sqlite3 *db;
+  char *zErr;
 
   if( p==0 ) return SQLITE_MISUSE_BKPT;
   db = p->db;
   sqlite3_mutex_enter(db->mutex);
 
-  if( p->pStmt==0 ){
-    /* If there is no statement handle, then the blob-handle has
-    ** already been invalidated. Return SQLITE_ABORT in this case.
-    */
-    rc = SQLITE_ABORT;
-  }else{
-    char *zErr;
-    rc = blobSeekToRow(p, iRow, &zErr);
-    if( rc!=SQLITE_OK ){
-      sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
-      sqlite3DbFree(db, zErr);
-    }
-    assert( rc!=SQLITE_SCHEMA );
+  rc = blobSeekToRow(p, iRow, &zErr);
+  if( rc!=SQLITE_OK ){
+    sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
+    sqlite3DbFree(db, zErr);
   }
 
   rc = sqlite3ApiExit(db, rc);
-  assert( rc==SQLITE_OK || p->pStmt==0 );
+  assert( rc==SQLITE_OK || p->nByte<0 );
   sqlite3_mutex_leave(db->mutex);
   return rc;
 }
index 7b2249c243f6ecb1d41545f18805fb78e0e135e7..178828b7b2d7ff85698b9892a92f292b7fa01b50 100644 (file)
@@ -141,8 +141,8 @@ do_test 2.3.2 {
   execsql { SELECT 1, 2, 3 }
   sqlite3_errcode db
 } {SQLITE_OK}
-blob_write_error_test 2.3.3 $B 5 $blob 5 \
-    SQLITE_ABORT {callback requested query abort}
+#blob_write_error_test 2.3.3 $B 5 $blob 5 \
+#    SQLITE_OK {not an error}
 sqlite3_blob_close $B
 
 # EVIDENCE-OF: R-08382-59936 Writes to the BLOB that occurred before the
index 5f2e860d08e62fa16d5e8e7c49371a92bda8a94a..daed8ea6729a0a01ac6f0a5c9b1ceee57ca96ab7 100644 (file)
@@ -62,7 +62,7 @@ do_test incrblob3-2.1.2 {
 } {SQLITE_ERROR {no such rowid: 3}}
 do_test incrblob3-2.1.3 {
   list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg
-} {1 SQLITE_ABORT}
+} {0 {}}
 do_test incrblob3-2.1.4 { close $::blob } {}
 
 do_execsql_test incrblob3-2.2.1 {
@@ -85,18 +85,21 @@ foreach {tn rowid type} {
 
   do_test incrblob3-2.2.$tn.3 {
     list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg
-  } {1 SQLITE_ABORT}
+  } {0 {}}
   do_test incrblob3-2.2.$tn.4 {
     list [catch {sqlite3_blob_read $::blob 0 10} msg] $msg
-  } {1 SQLITE_ABORT}
+  } {0 {hello worl}}
   do_test incrblob3-2.2.$tn.5 {
     list [catch {sqlite3_blob_write $::blob 0 "abcd"} msg] $msg
-  } {1 SQLITE_ABORT}
+  } {0 {}}
   do_test incrblob3-2.2.$tn.6 {
     sqlite3_blob_bytes $::blob
-  } {0}
+  } {100}
+  do_test incrblob3-2.2.$tn.7 {
+    list [catch {sqlite3_blob_write $::blob 0 "hello"} msg] $msg
+  } {0 {}}
 
-  do_test incrblob3-2.2.$tn.7 { close $::blob } {}
+  do_test incrblob3-2.2.$tn.8 { close $::blob } {}
 }
 
 # Test that passing NULL to sqlite3_blob_XXX() APIs returns SQLITE_MISUSE.