diff options
Diffstat (limited to '')
-rw-r--r-- | ext/rtree/rtree.c | 30 | ||||
-rw-r--r-- | ext/rtree/rtreeJ.test | 273 |
2 files changed, 292 insertions, 11 deletions
diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 013bb0b..02127fa 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -694,11 +694,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ ** 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); - } + sqlite3_blob *pBlob = pRtree->pNodeBlob; + pRtree->pNodeBlob = 0; + sqlite3_blob_close(pBlob); } /* @@ -742,7 +740,6 @@ static int nodeAcquire( &pRtree->pNodeBlob); } 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. */ @@ -802,6 +799,7 @@ static int nodeAcquire( } *ppNode = pNode; }else{ + nodeBlobReset(pRtree); if( pNode ){ pRtree->nNodeRef--; sqlite3_free(pNode); @@ -946,6 +944,7 @@ static void nodeGetCoord( int iCoord, /* Which coordinate to extract */ RtreeCoord *pCoord /* OUT: Space to write result to */ ){ + assert( iCell<NCELL(pNode) ); readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord); } @@ -1135,7 +1134,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){ sqlite3_finalize(pCsr->pReadAux); sqlite3_free(pCsr); pRtree->nCursor--; - nodeBlobReset(pRtree); + if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){ + nodeBlobReset(pRtree); + } return SQLITE_OK; } @@ -1720,7 +1721,11 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ int rc = SQLITE_OK; RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc); if( rc==SQLITE_OK && ALWAYS(p) ){ - *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); + if( p->iCell>=NCELL(pNode) ){ + rc = SQLITE_ABORT; + }else{ + *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell); + } } return rc; } @@ -1738,6 +1743,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ if( rc ) return rc; if( NEVER(p==0) ) return SQLITE_OK; + if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT; if( i==0 ){ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell)); }else if( i<=pRtree->nDim2 ){ @@ -3219,8 +3225,7 @@ constraint: */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; - assert( pRtree->inWrTrans==0 ); - pRtree->inWrTrans++; + pRtree->inWrTrans = 1; return SQLITE_OK; } @@ -3234,6 +3239,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){ nodeBlobReset(pRtree); return SQLITE_OK; } +static int rtreeRollback(sqlite3_vtab *pVtab){ + return rtreeEndTransaction(pVtab); +} /* ** The xRename method for rtree module virtual tables. @@ -3352,7 +3360,7 @@ static sqlite3_module rtreeModule = { rtreeBeginTransaction, /* xBegin - begin transaction */ rtreeEndTransaction, /* xSync - sync transaction */ rtreeEndTransaction, /* xCommit - commit transaction */ - rtreeEndTransaction, /* xRollback - rollback transaction */ + rtreeRollback, /* xRollback - rollback transaction */ 0, /* xFindFunction - function overloading */ rtreeRename, /* xRename - rename the table */ rtreeSavepoint, /* xSavepoint */ diff --git a/ext/rtree/rtreeJ.test b/ext/rtree/rtreeJ.test new file mode 100644 index 0000000..b091d2c --- /dev/null +++ b/ext/rtree/rtreeJ.test @@ -0,0 +1,273 @@ +# 2024-02-03 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# ROLLBACK in the middle of an RTREE query +# +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source $testdir/tester.tcl +set testprefix rtreeJ +ifcapable !rtree { finish_test ; return } + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2); + INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2); +} {} + +do_execsql_test 1.1 { + SELECT * FROM t1 +} {1 1.0 1.0 2 2.0 2.0} + +# If a ROLLBACK occurs that backs out changes to the RTREE, then +# all pending queries to the RTREE are aborted. +# +do_test 1.2 { + db eval { + BEGIN; + INSERT INTO t1 VALUES(3, 3, 3); + INSERT INTO t1 VALUES(4, 4, 4); + } + set rc [catch { + db eval { SELECT * FROM t1 } { + if {$id==1} { + db eval { ROLLBACK } + } + lappend res $id $x1 $x2 + } + } msg] + list $rc $msg +} {1 {query aborted}} + +do_execsql_test 1.3 { + SELECT * FROM t1; +} {1 1.0 1.0 2 2.0 2.0} + +# A COMMIT of changes to the RTREE does not affect pending queries +# +do_test 1.4 { + set res {} + db eval { + BEGIN; + INSERT INTO t1 VALUES(5, 5, 5); + INSERT INTO t1 VALUES(6, 6, 6); + } + db eval { SELECT * FROM t1 } { + if {$id==1} { + db eval { COMMIT } + } + lappend res $id $x1 $x2 + } + set res +} {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0} + +do_execsql_test 1.5 { + SELECT * FROM t1; +} {1 1.0 1.0 2 2.0 2.0 5 5.0 5.0 6 6.0 6.0} + +do_execsql_test 1.6 { + DELETE FROM t1; + INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3),(4,4,4); + CREATE TABLE t2(x); + SELECT * FROM t1; +} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0} + +# A rollback that does not affect the rtree table because +# the rtree table has not been written to does not cause +# a query abort. +# +do_test 1.7 { + set res {} + db eval { + BEGIN; + INSERT INTO t2(x) VALUES(12345); + } + db eval { SELECT * FROM t1 } { + if {$id==1} { + db eval { ROLLBACK } + } + lappend res $id $x1 $x2 + } + set res +} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0 4 4.0 4.0} + +# ROLLBACK TO that affects the RTREE does cause a query abort. +# +do_test 1.8 { + db eval { + DELETE FROM t1 WHERE rowid>1; + BEGIN; + DELETE FROM t2; + INSERT INTO t2(x) VALUES(23456); + SAVEPOINT 'one'; + INSERT INTO t1 VALUES(2,2,2),(3,3,3); + } + set rc [catch { + db eval { SELECT * FROM t1 } { + if {$id==1} { + db eval { ROLLBACK TO 'one'; } + } + lappend res $id $x1 $x2 + } + } msg] + list $rc $msg +} {1 {query aborted}} + +do_execsql_test 1.9 { + COMMIT; + SELECT * FROM t1; +} {1 1.0 1.0} + +# ROLLBACK TO that does not affect the RTREE does not cause a query abort. +# +do_execsql_test 1.10 { + DELETE FROM t1; + INSERT INTO t1 VALUES(1,1,1),(2,2,2),(3,3,3); + BEGIN; + DELETE FROM t2; + INSERT INTO t2(x) VALUES(34567); + SAVEPOINT 'one'; + INSERT INTO t2(x) VALUES('a string'); + SELECT * FROM t1; +} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0} +do_test 1.11 { + set rc [catch { + set res {} + db eval { SELECT * FROM t1 } { + if {$id==2} { + # db eval { ROLLBACK TO 'one'; } + } + lappend res $id $x1 $x2 + } + set res + } msg] + list $rc $msg +} {0 {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0}} + +do_execsql_test 1.12 { + COMMIT; + SELECT * FROM t1; +} {1 1.0 1.0 2 2.0 2.0 3 3.0 3.0} + +#---------------------------------------------------------------------- + +reset_db +do_execsql_test 2.0 { + CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2); + INSERT INTO t1 VALUES(1, 1, 1), (2, 2, 2); + CREATE TABLE t2(x); +} {} + +do_test 2.1 { + db eval { + BEGIN; + INSERT INTO t1 VALUES(3, 3, 3); + PRAGMA writable_schema = RESET; + } + + set rc [catch { + db eval { SELECT x1, x2 FROM t1 } { + if {$x1==1} { + db eval { ROLLBACK } + } + lappend res $x1 $x2 + } + } msg] + list $rc $msg +} {1 {query aborted}} + +do_execsql_test 2.1 { + CREATE TABLE bak_node(nodeno, data); + CREATE TABLE bak_parent(nodeno, parentnode); + CREATE TABLE bak_rowid(rowid, nodeno); +} +proc save_t1 {} { + db eval { + DELETE FROM bak_node; + DELETE FROM bak_parent; + DELETE FROM bak_rowid; + INSERT INTO bak_node SELECT * FROM t1_node; + INSERT INTO bak_parent SELECT * FROM t1_parent; + INSERT INTO bak_rowid SELECT * FROM t1_rowid; + } +} +proc restore_t1 {} { + db eval { + DELETE FROM t1_node; + DELETE FROM t1_parent; + DELETE FROM t1_rowid; + INSERT INTO t1_node SELECT * FROM bak_node; + INSERT INTO t1_parent SELECT * FROM bak_parent; + INSERT INTO t1_rowid SELECT * FROM bak_rowid; + } +} + +do_test 2.3 { + save_t1 + db eval { + INSERT INTO t1 VALUES(3, 3, 3); + } + set rc [catch { + db eval { SELECT rowid, x1, x2 FROM t1 } { + if {$x1==1} { + restore_t1 + } + lappend res $x1 $x2 + } + } msg] + list $rc $msg +} {1 {query aborted}} +do_execsql_test 2.4 { + SELECT * FROM t1 +} {1 1.0 1.0 2 2.0 2.0} + +do_test 2.5 { + save_t1 + db eval { + INSERT INTO t1 VALUES(3, 3, 3); + } + set rc [catch { + db eval { SELECT x1 FROM t1 } { + if {$x1==1} { + restore_t1 + } + lappend res $x1 $x2 + } + } msg] + list $rc $msg +} {1 {query aborted}} +do_execsql_test 2.6 { + SELECT * FROM t1 +} {1 1.0 1.0 2 2.0 2.0} + +do_test 2.7 { + save_t1 + db eval { + INSERT INTO t1 VALUES(3, 3, 3); + } + set ::res [list] + set rc [catch { + db eval { SELECT 'abc' FROM t1 } { + if {$::res==[list]} { + restore_t1 + set ::bDone 1 + } + lappend res abc + } + } msg] + set res +} {abc abc abc} +do_execsql_test 2.6 { + SELECT * FROM t1 +} {1 1.0 1.0 2 2.0 2.0} + + +finish_test |