Do not allow RTree writes when a read cursor is active on the same virtual

table, as the writes might rebalance and disrupt the read cursors.  Return
the new SQLITE_LOCKED_VTAB error code if this happens.

FossilOrigin-Name: d4ce66610851c825cb712f985216b63e015c753fdd5521f929c67ad18bfd7664
This commit is contained in:
drh 2018-05-24 22:31:01 +00:00
parent b9cd2c4536
commit c8c9cdd9dd
5 changed files with 45 additions and 18 deletions

View File

@ -133,6 +133,7 @@ struct Rtree {
u32 nBusy; /* Current number of users of this structure */
i64 nRowEst; /* Estimated number of rows in this table */
u32 nCursor; /* Number of open cursors */
u32 nNodeRef; /* Number RtreeNodes with positive nRef */
char *zReadAuxSql; /* SQL for statement to read aux data */
/* List of nodes removed during a CondenseTree operation. List is
@ -534,6 +535,7 @@ static int writeInt64(u8 *p, i64 i){
*/
static void nodeReference(RtreeNode *p){
if( p ){
assert( p->nRef>0 );
p->nRef++;
}
}
@ -601,6 +603,7 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize);
pNode->zData = (u8 *)&pNode[1];
pNode->nRef = 1;
pRtree->nNodeRef++;
pNode->pParent = pParent;
pNode->isDirty = 1;
nodeReference(pParent);
@ -634,10 +637,10 @@ static int nodeAcquire(
/* Check if the requested node is already in the hash table. If so,
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode)) ){
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
if( pParent && !pNode->pParent ){
nodeReference(pParent);
pParent->nRef++;
pNode->pParent = pParent;
}
pNode->nRef++;
@ -676,6 +679,7 @@ static int nodeAcquire(
pNode->pParent = pParent;
pNode->zData = (u8 *)&pNode[1];
pNode->nRef = 1;
pRtree->nNodeRef++;
pNode->iNode = iNode;
pNode->isDirty = 0;
pNode->pNext = 0;
@ -716,7 +720,10 @@ static int nodeAcquire(
}
*ppNode = pNode;
}else{
sqlite3_free(pNode);
if( pNode ){
pRtree->nNodeRef--;
sqlite3_free(pNode);
}
*ppNode = 0;
}
@ -813,8 +820,10 @@ static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){
int rc = SQLITE_OK;
if( pNode ){
assert( pNode->nRef>0 );
assert( pRtree->nNodeRef>0 );
pNode->nRef--;
if( pNode->nRef==0 ){
pRtree->nNodeRef--;
if( pNode->iNode==1 ){
pRtree->iDepth = -1;
}
@ -931,8 +940,9 @@ static void rtreeRelease(Rtree *pRtree){
pRtree->nBusy--;
if( pRtree->nBusy==0 ){
pRtree->inWrTrans = 0;
pRtree->nCursor = 0;
assert( pRtree->nCursor==0 );
nodeBlobReset(pRtree);
assert( pRtree->nNodeRef==0 );
sqlite3_finalize(pRtree->pWriteNode);
sqlite3_finalize(pRtree->pDeleteNode);
sqlite3_finalize(pRtree->pReadRowid);
@ -1403,7 +1413,7 @@ static RtreeSearchPoint *rtreeSearchPointNew(
if( ii<RTREE_CACHE_SZ ){
assert( pCur->aNode[ii]==0 );
pCur->aNode[ii] = pCur->aNode[0];
}else{
}else{
nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]);
}
pCur->aNode[0] = 0;
@ -2473,7 +2483,7 @@ static int SplitNode(
}else{
pLeft = pNode;
pRight = nodeNew(pRtree, pLeft->pParent);
nodeReference(pLeft);
pLeft->nRef++;
}
if( !pLeft || !pRight ){
@ -2963,6 +2973,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
rc = reinsertNodeContent(pRtree, pLeaf);
}
pRtree->pDeleted = pLeaf->pNext;
pRtree->nNodeRef--;
sqlite3_free(pLeaf);
}
@ -3067,6 +3078,12 @@ static int rtreeUpdate(
RtreeCell cell; /* New cell to insert if nData>1 */
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
if( pRtree->nNodeRef ){
/* Unable to write to the btree while another cursor is reading from it,
** since the write might do a rebalance which would disrupt the read
** cursor. */
return SQLITE_LOCKED_VTAB;
}
rtreeReference(pRtree);
assert(nData>=1);

View File

@ -38,11 +38,20 @@ do_test rtree8-1.1.1 {
} {}
do_test rtree8-1.1.2 {
set res [list]
db eval { SELECT * FROM t1 } {
lappend res $x1 $x2
set rc [catch {
db eval { SELECT * FROM t1 } {
lappend res $x1 $x2
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
}
} msg];
lappend rc $msg
set rc
} {1 {database table is locked}}
do_test rtree8-1.1.2b {
db eval { SELECT * FROM t1 ORDER BY +id } {
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
}
set res
db eval {SELECT x1, x2 FROM t1}
} {1 3 2 4 3 5}
do_test rtree8-1.1.3 {
execsql { SELECT * FROM t1 }

View File

@ -1,5 +1,5 @@
C In\sthe\sOOM\stesting\slogic,\sadd\sthe\ssqlite3FirstFault()\sroutine\sas\sa\splace\sto\nset\sa\sbreakpoint\sthe\sfirst\stime\sany\ssimulated\sOOM\sfault\soccurs\sfor\sa\ssingle\ntest\scase.
D 2018-05-24T17:38:00.681
C Do\snot\sallow\sRTree\swrites\swhen\sa\sread\scursor\sis\sactive\son\sthe\ssame\svirtual\ntable,\sas\sthe\swrites\smight\srebalance\sand\sdisrupt\sthe\sread\scursors.\s\sReturn\nthe\snew\sSQLITE_LOCKED_VTAB\serror\scode\sif\sthis\shappens.
D 2018-05-24T22:31:01.304
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
@ -355,7 +355,7 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782
F ext/repair/test/checkindex01.test 6945d0ffc0c1dc993b2ce88036b26e0f5d6fcc65da70fc9df27c2647bb358b0f
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 148ae743198ca9fe1dfdf2fed74e5c3b0e69330e5b71ddc0d9df9993324dcedc
F ext/rtree/rtree.c 204ddbb7cfe0aab0136aea8c8b68c24f102d54aa38ccb88f6b4782ad5a5c61c9
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
F ext/rtree/rtree1.test 587889855b26fb8637417921b0709ca843cb6d36db94a882371fd8b7dcc3aa0b
F ext/rtree/rtree2.test 5f25b01acd03470067a2d52783b2eb0a50bf836803d4342d20ca39e541220fe2
@ -364,7 +364,7 @@ F ext/rtree/rtree4.test 304de65d484540111b896827e4261815e5dca4ce28eeecd58be648cd
F ext/rtree/rtree5.test 49c9041d713d54560b315c2c7ef7207ee287eba1b20f8266968a06f2e55d3142
F ext/rtree/rtree6.test 593e0d36510d5ac1d1fb39b018274ff17604fe8fdca8cf1f8e16559cea1477f4
F ext/rtree/rtree7.test c8fb2e555b128dd0f0bdb520c61380014f497f8a23c40f2e820acc9f9e4fdce5
F ext/rtree/rtree8.test 649f5a37ec656028a4a32674b9b1183104285a7625a09d2a8f52a1cef72c93f2
F ext/rtree/rtree8.test f8aefd8b92f92333ae00b93eca7c197ab24522b219390a337f2a0c62a5d5f96a
F ext/rtree/rtree9.test c646f12c8c1c68ef015c6c043d86a0c42488e2e68ed1bb1b0771a7ca246cbabf
F ext/rtree/rtreeA.test 20623ca337ca3bd7e008cc9fb49e44dbe97f1a80b238e10a12bb4afcd0da3776
F ext/rtree/rtreeB.test 4cec297f8e5c588654bbf3c6ed0903f10612be8a2878055dd25faf8c71758bc9
@ -496,7 +496,7 @@ F src/resolve.c 6415381a0e9d22c0e7cba33ca4a53f81474190862f5d4838190f5eb5b0b47bc9
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c a35d462ee7a3c0856ad7a9d9c8921fbf3d91d911a8f39ad9d61302eb43b24a71
F src/shell.c.in 51c100206f4b7f86cd9affd80b764825e0edc36ca0190c442e4ca7994611bfe2
F src/sqlite.h.in ac6aec2abe10585733c1fea06622df5d73f7725c36c6cf054e89affeaccfa42b
F src/sqlite.h.in e379906b85cc3539c6a37ab972f2c28711b28854a457f54e137d20f75db5db7c
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
F src/sqliteInt.h 5abdade4744cf3bd567afb65bb144bb3c61f6132f86813b995a5ca79c7b584e8
@ -1729,7 +1729,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 36cdfbf2ce1811691c790fde3eaeed0238c03b4dc97dc3a20d1e0397520145d1
R 085dc0bd34d45e9f6435a293683fda7b
P b4d80bd287ca7f3a6d182ba2435273266035b94fdf6a44047a64a4eff931c571
R 2c56c09e62f3443ca0fffe92fcd36092
U drh
Z 2f994e2ed2922ec0ed95eb384d135eee
Z 5e9f190f156904a95f9a8ccad0e9448c

View File

@ -1 +1 @@
b4d80bd287ca7f3a6d182ba2435273266035b94fdf6a44047a64a4eff931c571
d4ce66610851c825cb712f985216b63e015c753fdd5521f929c67ad18bfd7664

View File

@ -504,6 +504,7 @@ int sqlite3_exec(
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))