Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a805a6e35 | |||
| c5fd65d3f7 | |||
| 3accc7e1af | |||
| ce655a2367 | |||
| 413e207e31 | |||
| 2033d1c8ca | |||
| 6d683c5c6e |
+105
-46
@@ -119,11 +119,13 @@ struct Rtree {
|
||||
u8 nDim2; /* Twice the number of dimensions */
|
||||
u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
|
||||
u8 nBytesPerCell; /* Bytes consumed per cell */
|
||||
u8 inWrTrans; /* True if inside write transaction */
|
||||
int iDepth; /* Current depth of the r-tree structure */
|
||||
char *zDb; /* Name of database containing r-tree table */
|
||||
char *zName; /* Name of r-tree table */
|
||||
int nBusy; /* Current number of users of this structure */
|
||||
u32 nBusy; /* Current number of users of this structure */
|
||||
i64 nRowEst; /* Estimated number of rows in this table */
|
||||
u32 nCursor; /* Number of open cursors */
|
||||
|
||||
/* List of nodes removed during a CondenseTree operation. List is
|
||||
** linked together via the pointer normally used for hash chains -
|
||||
@@ -133,8 +135,10 @@ struct Rtree {
|
||||
RtreeNode *pDeleted;
|
||||
int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */
|
||||
|
||||
/* Blob I/O on xxx_node */
|
||||
sqlite3_blob *pNodeBlob;
|
||||
|
||||
/* Statements to read/write/delete a record from xxx_node */
|
||||
sqlite3_stmt *pReadNode;
|
||||
sqlite3_stmt *pWriteNode;
|
||||
sqlite3_stmt *pDeleteNode;
|
||||
|
||||
@@ -615,6 +619,17 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
|
||||
return pNode;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the Rtree.pNodeBlob object
|
||||
*/
|
||||
static void nodeBlobReset(Rtree *pRtree){
|
||||
if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
|
||||
sqlite3_blob *pBlob = pRtree->pNodeBlob;
|
||||
pRtree->pNodeBlob = 0;
|
||||
sqlite3_blob_close(pBlob);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Obtain a reference to an r-tree node.
|
||||
*/
|
||||
@@ -624,9 +639,8 @@ static int nodeAcquire(
|
||||
RtreeNode *pParent, /* Either the parent node or NULL */
|
||||
RtreeNode **ppNode /* OUT: Acquired node */
|
||||
){
|
||||
int rc;
|
||||
int rc2 = SQLITE_OK;
|
||||
RtreeNode *pNode;
|
||||
int rc = SQLITE_OK;
|
||||
RtreeNode *pNode = 0;
|
||||
|
||||
/* Check if the requested node is already in the hash table. If so,
|
||||
** increase its reference count and return it.
|
||||
@@ -642,28 +656,45 @@ static int nodeAcquire(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
sqlite3_bind_int64(pRtree->pReadNode, 1, iNode);
|
||||
rc = sqlite3_step(pRtree->pReadNode);
|
||||
if( rc==SQLITE_ROW ){
|
||||
const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0);
|
||||
if( pRtree->iNodeSize==sqlite3_column_bytes(pRtree->pReadNode, 0) ){
|
||||
pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize);
|
||||
if( !pNode ){
|
||||
rc2 = SQLITE_NOMEM;
|
||||
}else{
|
||||
pNode->pParent = pParent;
|
||||
pNode->zData = (u8 *)&pNode[1];
|
||||
pNode->nRef = 1;
|
||||
pNode->iNode = iNode;
|
||||
pNode->isDirty = 0;
|
||||
pNode->pNext = 0;
|
||||
memcpy(pNode->zData, zBlob, pRtree->iNodeSize);
|
||||
nodeReference(pParent);
|
||||
}
|
||||
if( pRtree->pNodeBlob ){
|
||||
sqlite3_blob *pBlob = pRtree->pNodeBlob;
|
||||
pRtree->pNodeBlob = 0;
|
||||
rc = sqlite3_blob_reopen(pBlob, iNode);
|
||||
pRtree->pNodeBlob = pBlob;
|
||||
if( rc ){
|
||||
nodeBlobReset(pRtree);
|
||||
if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
if( pRtree->pNodeBlob==0 ){
|
||||
char *zTab = sqlite3_mprintf("%s_node", pRtree->zName);
|
||||
if( zTab==0 ) return SQLITE_NOMEM;
|
||||
rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0,
|
||||
&pRtree->pNodeBlob);
|
||||
sqlite3_free(zTab);
|
||||
}
|
||||
if( rc ){
|
||||
nodeBlobReset(pRtree);
|
||||
*ppNode = 0;
|
||||
/* If unable to open an sqlite3_blob on the desired row, that can only
|
||||
** be because the shadow tables hold erroneous data. */
|
||||
if( rc==SQLITE_ERROR ) rc = SQLITE_CORRUPT_VTAB;
|
||||
}else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){
|
||||
pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize);
|
||||
if( !pNode ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pNode->pParent = pParent;
|
||||
pNode->zData = (u8 *)&pNode[1];
|
||||
pNode->nRef = 1;
|
||||
pNode->iNode = iNode;
|
||||
pNode->isDirty = 0;
|
||||
pNode->pNext = 0;
|
||||
rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData,
|
||||
pRtree->iNodeSize, 0);
|
||||
nodeReference(pParent);
|
||||
}
|
||||
}
|
||||
rc = sqlite3_reset(pRtree->pReadNode);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
|
||||
/* If the root node was just loaded, set pRtree->iDepth to the height
|
||||
** of the r-tree structure. A height of zero means all data is stored on
|
||||
@@ -909,7 +940,9 @@ static void rtreeReference(Rtree *pRtree){
|
||||
static void rtreeRelease(Rtree *pRtree){
|
||||
pRtree->nBusy--;
|
||||
if( pRtree->nBusy==0 ){
|
||||
sqlite3_finalize(pRtree->pReadNode);
|
||||
pRtree->inWrTrans = 0;
|
||||
pRtree->nCursor = 0;
|
||||
nodeBlobReset(pRtree);
|
||||
sqlite3_finalize(pRtree->pWriteNode);
|
||||
sqlite3_finalize(pRtree->pDeleteNode);
|
||||
sqlite3_finalize(pRtree->pReadRowid);
|
||||
@@ -947,6 +980,7 @@ static int rtreeDestroy(sqlite3_vtab *pVtab){
|
||||
if( !zCreate ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
nodeBlobReset(pRtree);
|
||||
rc = sqlite3_exec(pRtree->db, zCreate, 0, 0, 0);
|
||||
sqlite3_free(zCreate);
|
||||
}
|
||||
@@ -962,6 +996,7 @@ static int rtreeDestroy(sqlite3_vtab *pVtab){
|
||||
*/
|
||||
static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
int rc = SQLITE_NOMEM;
|
||||
Rtree *pRtree = (Rtree *)pVTab;
|
||||
RtreeCursor *pCsr;
|
||||
|
||||
pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor));
|
||||
@@ -969,6 +1004,7 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
memset(pCsr, 0, sizeof(RtreeCursor));
|
||||
pCsr->base.pVtab = pVTab;
|
||||
rc = SQLITE_OK;
|
||||
pRtree->nCursor++;
|
||||
}
|
||||
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
|
||||
|
||||
@@ -1001,10 +1037,13 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
|
||||
Rtree *pRtree = (Rtree *)(cur->pVtab);
|
||||
int ii;
|
||||
RtreeCursor *pCsr = (RtreeCursor *)cur;
|
||||
assert( pRtree->nCursor>0 );
|
||||
freeCursorConstraints(pCsr);
|
||||
sqlite3_free(pCsr->aPoint);
|
||||
for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
|
||||
sqlite3_free(pCsr);
|
||||
pRtree->nCursor--;
|
||||
nodeBlobReset(pRtree);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -1077,7 +1116,6 @@ static int rtreeCallbackConstraint(
|
||||
sqlite3_rtree_dbl *prScore, /* OUT: score for the cell */
|
||||
int *peWithin /* OUT: visibility of the cell */
|
||||
){
|
||||
int i; /* Loop counter */
|
||||
sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; /* Callback info */
|
||||
int nCoord = pInfo->nCoord; /* No. of coordinates */
|
||||
int rc; /* Callback return code */
|
||||
@@ -1122,9 +1160,10 @@ static int rtreeCallbackConstraint(
|
||||
}
|
||||
}
|
||||
if( pConstraint->op==RTREE_MATCH ){
|
||||
int eWithin = 0;
|
||||
rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pInfo,
|
||||
nCoord, aCoord, &i);
|
||||
if( i==0 ) *peWithin = NOT_WITHIN;
|
||||
nCoord, aCoord, &eWithin);
|
||||
if( eWithin==0 ) *peWithin = NOT_WITHIN;
|
||||
*prScore = RTREE_ZERO;
|
||||
}else{
|
||||
pInfo->aCoord = aCoord;
|
||||
@@ -3152,6 +3191,27 @@ constraint:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called when a transaction starts.
|
||||
*/
|
||||
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
|
||||
Rtree *pRtree = (Rtree *)pVtab;
|
||||
assert( pRtree->inWrTrans==0 );
|
||||
pRtree->inWrTrans++;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Called when a transaction completes (either by COMMIT or ROLLBACK).
|
||||
** The sqlite3_blob object should be released at this point.
|
||||
*/
|
||||
static int rtreeEndTransaction(sqlite3_vtab *pVtab){
|
||||
Rtree *pRtree = (Rtree *)pVtab;
|
||||
pRtree->inWrTrans = 0;
|
||||
nodeBlobReset(pRtree);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** The xRename method for rtree module virtual tables.
|
||||
*/
|
||||
@@ -3173,6 +3233,7 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function populates the pRtree->nRowEst variable with an estimate
|
||||
** of the number of rows in the virtual table. If possible, this is based
|
||||
@@ -3232,15 +3293,15 @@ static sqlite3_module rtreeModule = {
|
||||
rtreeColumn, /* xColumn - read data */
|
||||
rtreeRowid, /* xRowid - read data */
|
||||
rtreeUpdate, /* xUpdate - write data */
|
||||
0, /* xBegin - begin transaction */
|
||||
rtreeBeginTransaction, /* xBegin - begin transaction */
|
||||
0, /* xSync - sync transaction */
|
||||
0, /* xCommit - commit transaction */
|
||||
0, /* xRollback - rollback transaction */
|
||||
rtreeEndTransaction, /* xCommit - commit transaction */
|
||||
rtreeEndTransaction, /* xRollback - rollback transaction */
|
||||
0, /* xFindFunction - function overloading */
|
||||
rtreeRename, /* xRename - rename the table */
|
||||
0, /* xSavepoint */
|
||||
0, /* xRelease */
|
||||
0 /* xRollbackTo */
|
||||
0, /* xRollbackTo */
|
||||
};
|
||||
|
||||
static int rtreeSqlInit(
|
||||
@@ -3252,10 +3313,9 @@ static int rtreeSqlInit(
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
#define N_STATEMENT 9
|
||||
#define N_STATEMENT 8
|
||||
static const char *azSql[N_STATEMENT] = {
|
||||
/* Read and write the xxx_node table */
|
||||
"SELECT data FROM '%q'.'%q_node' WHERE nodeno = :1",
|
||||
/* Write the xxx_node table */
|
||||
"INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)",
|
||||
"DELETE FROM '%q'.'%q_node' WHERE nodeno = :1",
|
||||
|
||||
@@ -3293,15 +3353,14 @@ static int rtreeSqlInit(
|
||||
}
|
||||
}
|
||||
|
||||
appStmt[0] = &pRtree->pReadNode;
|
||||
appStmt[1] = &pRtree->pWriteNode;
|
||||
appStmt[2] = &pRtree->pDeleteNode;
|
||||
appStmt[3] = &pRtree->pReadRowid;
|
||||
appStmt[4] = &pRtree->pWriteRowid;
|
||||
appStmt[5] = &pRtree->pDeleteRowid;
|
||||
appStmt[6] = &pRtree->pReadParent;
|
||||
appStmt[7] = &pRtree->pWriteParent;
|
||||
appStmt[8] = &pRtree->pDeleteParent;
|
||||
appStmt[0] = &pRtree->pWriteNode;
|
||||
appStmt[1] = &pRtree->pDeleteNode;
|
||||
appStmt[2] = &pRtree->pReadRowid;
|
||||
appStmt[3] = &pRtree->pWriteRowid;
|
||||
appStmt[4] = &pRtree->pDeleteRowid;
|
||||
appStmt[5] = &pRtree->pReadParent;
|
||||
appStmt[6] = &pRtree->pWriteParent;
|
||||
appStmt[7] = &pRtree->pDeleteParent;
|
||||
|
||||
rc = rtreeQueryStat1(db, pRtree);
|
||||
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
|
||||
@@ -3440,7 +3499,7 @@ static int rtreeInit(
|
||||
pRtree->zDb = (char *)&pRtree[1];
|
||||
pRtree->zName = &pRtree->zDb[nDb+1];
|
||||
pRtree->nDim = (u8)((argc-4)/2);
|
||||
pRtree->nDim2 = argc - 4;
|
||||
pRtree->nDim2 = pRtree->nDim*2;
|
||||
pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
||||
pRtree->eCoordType = (u8)eCoordType;
|
||||
memcpy(pRtree->zDb, argv[1], nDb);
|
||||
|
||||
@@ -109,7 +109,7 @@ do_corruption_tests rtreeA-1.1 {
|
||||
}
|
||||
|
||||
do_execsql_test rtreeA-1.2.0 { DROP TABLE t1_node } {}
|
||||
do_corruption_tests rtreeA-1.2 -error "SQL logic error or missing database" {
|
||||
do_corruption_tests rtreeA-1.2 -error "database disk image is malformed" {
|
||||
1 "SELECT * FROM t1"
|
||||
2 "SELECT * FROM t1 WHERE rowid=5"
|
||||
3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)"
|
||||
|
||||
@@ -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 Merge\sin\sthe\ssqlite3_blob_reset()\sinterface.
|
||||
D 2017-02-03T00:07:32.016
|
||||
F Makefile.in 5f415e7867296d678fed2e6779aea10c1318b4bc
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc ba953c8921fc7e18333f61898007206de7e23964
|
||||
@@ -264,7 +264,7 @@ F ext/rbu/sqlite3rbu.c bb0de6cdbdb14a7d55a097238a434b7e99caf318
|
||||
F ext/rbu/sqlite3rbu.h 6fb6294c34a9ca93b5894a33bca530c6f08decba
|
||||
F ext/rbu/test_rbu.c 5aa22616afac6f71ebd3d9bc9bf1006cfabcca88
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 70f2488eef5c04dccf15a52cbd4961492124f825
|
||||
F ext/rtree/rtree.c be9d44f5707c5a73887d3ac3ad14a355e9b92b58
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 42dadfc7b44a436cd74a1bebc0b9b689e4eaf7ec
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
@@ -275,7 +275,7 @@ F ext/rtree/rtree6.test 773a90db2dce6a8353dd0d5b64bca69b29761196
|
||||
F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971
|
||||
F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a
|
||||
F ext/rtree/rtree9.test b5eb13849545dfd271a54ff16784cb00d8792aea
|
||||
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
|
||||
F ext/rtree/rtreeA.test ac8b503931f2f397cc3c3303354e1e085dcadb86
|
||||
F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
|
||||
F ext/rtree/rtreeC.test c0a9c67f2efa98b6fae12acb8a28348d231a481d
|
||||
F ext/rtree/rtreeD.test bdfaaf26df8b4eea7364039aca9150bc1e1f8825
|
||||
@@ -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,7 @@ 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 e51dc0ec60d45cd57564735b6b2bb254a588533e 53b77838f05b4805b956b5763c75af434662fb20
|
||||
R 52ca33d63ed5b4d901324606572bed67
|
||||
U drh
|
||||
Z d16d6898d272ff11e9984476ae1d9ef3
|
||||
Z 25b1bddf1a1e881eb74da9827041b148
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
8e03a8e95fada5c24d369672a71f6e02288051da
|
||||
e9c94a1f402ce51a187ddd9605e1a48e4bf17367
|
||||
+76
-56
@@ -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
|
||||
** incremental blob I/O routines can only read or overwriting existing
|
||||
** blob content; they cannot change the size of a 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
|
||||
**
|
||||
** This routine only works on a [BLOB handle] which has been created
|
||||
** ^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.
|
||||
@@ -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()].
|
||||
*/
|
||||
|
||||
+46
-32
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+9
-6
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user