Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch sqlite3_blob_reset Excluding Merge-Ins
This is equivalent to a diff from 97ccf3e4de to 61bd2a885d
2017-02-04
| ||
17:33 | Allow sqlite3session_apply() to apply changesets to tables that have been extended using ALTER TABLE ADD COLUMN. (check-in: b20ff81ff9 user: dan tags: trunk) | |
15:29 | Merge recent trunk enhancements. (check-in: 6c3f09028f user: drh tags: apple-osx) | |
14:30 | Merge the latest trunk changes, especially the RTREE enhancement to use sqlite3_blob objects. (Leaf check-in: 61bd2a885d user: drh tags: sqlite3_blob_reset) | |
14:24 | In RTREE, use an sqlite3_blob object rather than an sqlite3_stmt object for reading content out of the %_node shadow table. (check-in: 97ccf3e4de user: drh tags: trunk) | |
13:12 | Close sqlite3_blob objects on xSync rather than waiting until xCommit. (Closed-Leaf check-in: 95ee745fce user: drh tags: rtree-blob-agressive-release) | |
2017-02-03
| ||
20:54 | Improved performance and stack usage when processing VALUES clauses with a very large number of rows. (check-in: 5706d4708a user: drh tags: trunk) | |
2017-02-02
| ||
23:57 | Add the sqlite3_blob_reset() interface. Enhance the behavior of sqlite3_blob objects so that they can go active again after encountering an error by rerunning sqlite3_blob_reopen(). More work needed on the documentation. (check-in: 53b77838f0 user: drh tags: sqlite3_blob_reset) | |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
6134 6135 6136 6137 6138 6139 6140 | ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. */ /* ** CAPI3REF: A Handle To An Open BLOB | | | > | > > > > > > > > > > > | | | | | > | | 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 | ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. */ /* ** CAPI3REF: A Handle To An Open BLOB ** 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. ** ** ^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; /* ** CAPI3REF: Open A BLOB For Incremental I/O ** METHOD: sqlite3 ** CONSTRUCTOR: sqlite3_blob ** ** ^(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> ** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow; ** </pre>)^ ** ** ^(Parameter zDb is not the filename that contains the database, but ** rather the symbolic name of the database. For attached databases, this is ** the name that appears after the AS keyword in the [ATTACH] statement. ** For the main database file, the database name is "main". For TEMP ** tables, the database name is "temp".)^ ** ** ^If the flags parameter is non-zero, then the BLOB is opened for read ** 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 [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: ** <ul> ** <li> ^(Database zDb does not exist)^, ** <li> ^(Table zTable does not exist within database zDb)^, ** <li> ^(Table zTable is a WITHOUT ROWID table)^, |
︙ | ︙ | |||
6204 6205 6206 6207 6208 6209 6210 | ** [sqlite3_blob_write()]. The [BLOB handle] can be moved to a ** different row of the same table using the [sqlite3_blob_reopen()] ** interface. However, the column, table, or database of a [BLOB handle] ** cannot be changed after the [BLOB handle] is opened. ** ** ^(If the row that a BLOB handle points to is modified by an ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects | | > | > | > | | | | | < | | | > | | > | > | > | | > > > > > > > > > > | | | | | | | | | > > < < < < < | > | | | > > | < < < < < < < < < | 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 | ** [sqlite3_blob_write()]. The [BLOB handle] can be moved to a ** different row of the same table using the [sqlite3_blob_reopen()] ** interface. However, the column, table, or database of a [BLOB handle] ** cannot be changed after the [BLOB handle] is opened. ** ** ^(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" 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 ** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ ** ** ^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. 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 ** zero-filled blob to read or write using the incremental-blob interface. ** ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. ** ** See also: [sqlite3_blob_close()], ** [sqlite3_blob_reopen()], [sqlite3_blob_read()], ** [sqlite3_blob_bytes()], [sqlite3_blob_write()], ** [sqlite3_blob_reset()]. */ int sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Move an sqlite3_blob object to a New Row ** METHOD: sqlite3_blob ** ** ^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 [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 [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. */ int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); /* ** CAPI3REF: Close an sqlite3_blob object ** DESTRUCTOR: sqlite3_blob ** ** ^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 ** object is still closed.)^ ** ** ^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 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 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: Reset an sqlite3_blob object ** METHOD: sqlite3_blob ** ** ^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 ** content; they cannot change the size of a blob or string. ** ** 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. */ int sqlite3_blob_bytes(sqlite3_blob *); /* ** CAPI3REF: Read Data From A BLOB Incrementally ** METHOD: sqlite3_blob ** ** ^(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 ** less than zero, [SQLITE_ERROR] is returned and no data is read. ** ^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 [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.)^ ** ** See also: [sqlite3_blob_write()]. */ 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 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.)^ ** ^Unless SQLITE_MISUSE is returned, this function sets the ** [database connection] error code and message accessible via ** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions. ** ** ^If the [BLOB handle] passed as the first argument was not opened for ** writing (the flags parameter to [sqlite3_blob_open()] was zero), ** this function returns [SQLITE_READONLY]. ** ** This function may only modify the contents of the BLOB; it is ** not possible to increase the size of a BLOB using this API. ** ^If offset iOffset is less than N bytes from the end of the BLOB, ** [SQLITE_ERROR] is returned and no data is written. The size of the ** BLOB (and hence the maximum value of N+iOffset) can be determined ** 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 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()]. */ int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* ** CAPI3REF: Virtual File System Objects |
︙ | ︙ |
Changes to src/vdbeblob.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | #ifndef SQLITE_OMIT_INCRBLOB /* ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { | | < | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #ifndef SQLITE_OMIT_INCRBLOB /* ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { 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 */ sqlite3_stmt *pStmt; /* Statement holding cursor open */ sqlite3 *db; /* The associated database */ char *zDb; /* Database name */ 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. ** If successful, SQLITE_OK is returned and subsequent calls to ** sqlite3_blob_read() or sqlite3_blob_write() access the specified row. ** |
︙ | ︙ | |||
78 79 80 81 82 83 84 | testcase( pC->nHdrParsed==p->iCol ); testcase( pC->nHdrParsed==p->iCol+1 ); if( type<12 ){ zErr = sqlite3MPrintf(p->db, "cannot open value of type %s", type==0?"null": type==7?"real": "integer" ); rc = SQLITE_ERROR; | | | | | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | testcase( pC->nHdrParsed==p->iCol ); testcase( pC->nHdrParsed==p->iCol+1 ); if( type<12 ){ zErr = sqlite3MPrintf(p->db, "cannot open value of type %s", type==0?"null": type==7?"real": "integer" ); rc = SQLITE_ERROR; sqlite3_reset(p->pStmt); p->nByte = -1; }else{ p->iOffset = pC->aType[p->iCol + pC->nField]; p->nByte = sqlite3VdbeSerialTypeLen(type); p->pCsr = pC->uc.pCursor; sqlite3BtreeIncrblobCursor(p->pCsr); } } if( rc==SQLITE_ROW ){ rc = SQLITE_OK; }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; }else{ zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db)); } } |
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | 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); if( pTab && IsVirtual(pTab) ){ pTab = 0; sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable); } | > | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | if( !pParse ) goto blob_open_out; do { memset(pParse, 0, sizeof(Parse)); pParse->db = db; sqlite3DbFree(db, zErr); zErr = 0; sqlite3_finalize(pBlob->pStmt); sqlite3BtreeEnterAll(db); pTab = sqlite3LocateTable(pParse, 0, zTable, zDb); if( pTab && IsVirtual(pTab) ){ pTab = 0; sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable); } |
︙ | ︙ | |||
320 321 322 323 324 325 326 327 328 329 330 331 332 333 | pParse->nTab = 1; sqlite3VdbeMakeReady(v, pParse); } } pBlob->iCol = iCol; pBlob->db = db; sqlite3BtreeLeaveAll(db); if( db->mallocFailed ){ goto blob_open_out; } rc = blobSeekToRow(pBlob, iRow, &zErr); } while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA ); | > | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | pParse->nTab = 1; sqlite3VdbeMakeReady(v, pParse); } } pBlob->iCol = iCol; pBlob->db = db; pBlob->nByte = 0; sqlite3BtreeLeaveAll(db); if( db->mallocFailed ){ goto blob_open_out; } rc = blobSeekToRow(pBlob, iRow, &zErr); } while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA ); |
︙ | ︙ | |||
355 356 357 358 359 360 361 | Incrblob *p = (Incrblob *)pBlob; int rc; sqlite3 *db; if( p ){ db = p->db; sqlite3_mutex_enter(db->mutex); | | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | Incrblob *p = (Incrblob *)pBlob; int rc; sqlite3 *db; if( p ){ db = p->db; sqlite3_mutex_enter(db->mutex); rc = sqlite3VdbeFinalize((Vdbe*)p->pStmt); sqlite3DbFree(db, p); sqlite3_mutex_leave(db->mutex); }else{ rc = SQLITE_OK; } return rc; } |
︙ | ︙ | |||
380 381 382 383 384 385 386 387 388 389 390 391 392 393 | ){ int rc; Incrblob *p = (Incrblob *)pBlob; Vdbe *v; sqlite3 *db; if( p==0 ) return SQLITE_MISUSE_BKPT; db = p->db; sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; | > > > > < < < < < | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | ){ int rc; Incrblob *p = (Incrblob *)pBlob; Vdbe *v; 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; if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; }else{ /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is ** returned, clean-up the statement handle. */ assert( db == v->db ); sqlite3BtreeEnterCursor(p->pCsr); |
︙ | ︙ | |||
425 426 427 428 429 430 431 | ); } #endif rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); sqlite3BtreeLeaveCursor(p->pCsr); if( rc==SQLITE_ABORT ){ | | | | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | ); } #endif rc = xCall(p->pCsr, iOffset+p->iOffset, n, z); sqlite3BtreeLeaveCursor(p->pCsr); if( rc==SQLITE_ABORT ){ sqlite3_reset(p->pStmt); p->nByte = -1; }else{ v->rc = rc; } } sqlite3Error(db, rc); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); |
︙ | ︙ | |||
454 455 456 457 458 459 460 | int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); } /* ** Query a blob handle for the size of the data. ** | | | > > > > > > > > | > > > > > > > > > > > > > > > < < < < < < < | | | | < < | | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | int sqlite3_blob_write(sqlite3_blob *pBlob, const void *z, int n, int iOffset){ return blobReadWrite(pBlob, (void *)z, n, iOffset, sqlite3BtreePutData); } /* ** Query a blob handle for the size of the data. ** ** 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) ? 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. ** ** If an error occurs, or if the specified row does not exist or does not ** contain a blob or text value, then an error code is returned and the ** database handle error code and message set. If this happens, then all ** subsequent calls to sqlite3_blob_xxx() functions (except blob_close()) ** immediately return SQLITE_ABORT. */ 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); 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->nByte<0 ); sqlite3_mutex_leave(db->mutex); return rc; } #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ |
Changes to test/e_blobwrite.test.
︙ | ︙ | |||
137 138 139 140 141 142 143 | } {} blob_write_error_test 2.3.1 $B 5 $blob 5 \ SQLITE_ABORT {callback requested query abort} do_test 2.3.2 { execsql { SELECT 1, 2, 3 } sqlite3_errcode db } {SQLITE_OK} | | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | } {} blob_write_error_test 2.3.1 $B 5 $blob 5 \ SQLITE_ABORT {callback requested query abort} 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_OK {not an error} sqlite3_blob_close $B # EVIDENCE-OF: R-08382-59936 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. |
︙ | ︙ |
Changes to test/incrblob3.test.
︙ | ︙ | |||
58 59 60 61 62 63 64 | list [catch {sqlite3_blob_reopen $::blob 3} msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-2.1.2 { list [sqlite3_errcode db] [sqlite3_errmsg db] } {SQLITE_ERROR {no such rowid: 3}} do_test incrblob3-2.1.3 { list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | list [catch {sqlite3_blob_reopen $::blob 3} msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-2.1.2 { list [sqlite3_errcode db] [sqlite3_errmsg db] } {SQLITE_ERROR {no such rowid: 3}} do_test incrblob3-2.1.3 { list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg } {0 {}} do_test incrblob3-2.1.4 { close $::blob } {} do_execsql_test incrblob3-2.2.1 { INSERT INTO blobs VALUES(3, 42); INSERT INTO blobs VALUES(4, 54.4); INSERT INTO blobs VALUES(5, NULL); } |
︙ | ︙ | |||
81 82 83 84 85 86 87 | } {1 SQLITE_ERROR} do_test incrblob3-2.2.$tn.2 { list [sqlite3_errcode db] [sqlite3_errmsg db] } "SQLITE_ERROR {cannot open value of type $type}" do_test incrblob3-2.2.$tn.3 { list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg | | | | > > > | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | } {1 SQLITE_ERROR} do_test incrblob3-2.2.$tn.2 { list [sqlite3_errcode db] [sqlite3_errmsg db] } "SQLITE_ERROR {cannot open value of type $type}" do_test incrblob3-2.2.$tn.3 { list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg } {0 {}} do_test incrblob3-2.2.$tn.4 { list [catch {sqlite3_blob_read $::blob 0 10} msg] $msg } {0 {hello worl}} do_test incrblob3-2.2.$tn.5 { list [catch {sqlite3_blob_write $::blob 0 "abcd"} msg] $msg } {0 {}} do_test incrblob3-2.2.$tn.6 { sqlite3_blob_bytes $::blob } {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.8 { close $::blob } {} } # Test that passing NULL to sqlite3_blob_XXX() APIs returns SQLITE_MISUSE. # # incrblob3-3.1: sqlite3_blob_reopen() # incrblob3-3.2: sqlite3_blob_read() # incrblob3-3.3: sqlite3_blob_write() |
︙ | ︙ |