1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Have r-tree virtual tables support on-conflict clauses.

FossilOrigin-Name: 822ab52f1023b1c4973c806cc75454acd4e95fd0
This commit is contained in:
dan
2011-04-28 18:46:46 +00:00
parent 3480a01dad
commit c6055c7374
6 changed files with 242 additions and 121 deletions

View File

@@ -2626,30 +2626,14 @@ static int newRowid(Rtree *pRtree, i64 *piRowid){
} }
/* /*
** The xUpdate method for rtree module virtual tables. ** Remove the entry with rowid=iDelete from the r-tree structure.
*/ */
static int rtreeUpdate( static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
sqlite3_vtab *pVtab, int rc; /* Return code */
int nData,
sqlite3_value **azData,
sqlite_int64 *pRowid
){
Rtree *pRtree = (Rtree *)pVtab;
int rc = SQLITE_OK;
rtreeReference(pRtree);
assert(nData>=1);
/* If azData[0] is not an SQL NULL value, it is the rowid of a
** record to delete from the r-tree table. The following block does
** just that.
*/
if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
i64 iDelete; /* The rowid to delete */
RtreeNode *pLeaf; /* Leaf node containing record iDelete */ RtreeNode *pLeaf; /* Leaf node containing record iDelete */
int iCell; /* Index of iDelete cell in pLeaf */ int iCell; /* Index of iDelete cell in pLeaf */
RtreeNode *pRoot; RtreeNode *pRoot; /* Root node of rtree structure */
/* Obtain a reference to the root node to initialise Rtree.iDepth */ /* Obtain a reference to the root node to initialise Rtree.iDepth */
rc = nodeAcquire(pRtree, 1, 0, &pRoot); rc = nodeAcquire(pRtree, 1, 0, &pRoot);
@@ -2658,7 +2642,6 @@ static int rtreeUpdate(
** about to be deleted. ** about to be deleted.
*/ */
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
iDelete = sqlite3_value_int64(azData[0]);
rc = findLeafNode(pRtree, iDelete, &pLeaf); rc = findLeafNode(pRtree, iDelete, &pLeaf);
} }
@@ -2722,17 +2705,40 @@ static int rtreeUpdate(
}else{ }else{
nodeRelease(pRtree, pRoot); nodeRelease(pRtree, pRoot);
} }
return rc;
} }
/* If the azData[] array contains more than one element, elements /*
** (azData[2]..azData[argc-1]) contain a new record to insert into ** The xUpdate method for rtree module virtual tables.
** the r-tree structure.
*/ */
if( rc==SQLITE_OK && nData>1 ){ static int rtreeUpdate(
/* Insert a new record into the r-tree */ sqlite3_vtab *pVtab,
RtreeCell cell; int nData,
sqlite3_value **azData,
sqlite_int64 *pRowid
){
Rtree *pRtree = (Rtree *)pVtab;
int rc = SQLITE_OK;
RtreeCell cell; /* New cell to insert if nData>1 */
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
rtreeReference(pRtree);
assert(nData>=1);
/* Constraint handling. A write operation on an r-tree table may return
** SQLITE_CONSTRAINT for two reasons:
**
** 1. A duplicate rowid value, or
** 2. The supplied data violates the "x2>=x1" constraint.
**
** In the first case, if the conflict-handling mode is REPLACE, then
** the conflicting row can be removed before proceeding. In the second
** case, SQLITE_CONSTRAINT must be returned regardless of the
** conflict-handling mode specified by the user.
*/
if( nData>1 ){
int ii; int ii;
RtreeNode *pLeaf;
/* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */ /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
assert( nData==(pRtree->nDim*2 + 3) ); assert( nData==(pRtree->nDim*2 + 3) );
@@ -2756,18 +2762,49 @@ static int rtreeUpdate(
} }
} }
/* Figure out the rowid of the new row. */ /* If a rowid value was supplied, check if it is already present in
if( sqlite3_value_type(azData[2])==SQLITE_NULL ){ ** the table. If so, the constraint has failed. */
rc = newRowid(pRtree, &cell.iRowid); if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){
}else{
cell.iRowid = sqlite3_value_int64(azData[2]); cell.iRowid = sqlite3_value_int64(azData[2]);
if( sqlite3_value_type(azData[0])==SQLITE_NULL
|| sqlite3_value_int64(azData[0])!=cell.iRowid
){
int steprc;
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid); sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){ steprc = sqlite3_step(pRtree->pReadRowid);
sqlite3_reset(pRtree->pReadRowid); rc = sqlite3_reset(pRtree->pReadRowid);
if( SQLITE_ROW==steprc ){
if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
rc = rtreeDeleteRowid(pRtree, cell.iRowid);
}else{
rc = SQLITE_CONSTRAINT; rc = SQLITE_CONSTRAINT;
goto constraint; goto constraint;
} }
rc = sqlite3_reset(pRtree->pReadRowid); }
}
bHaveRowid = 1;
}
}
/* If azData[0] is not an SQL NULL value, it is the rowid of a
** record to delete from the r-tree table. The following block does
** just that.
*/
if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0]));
}
/* If the azData[] array contains more than one element, elements
** (azData[2]..azData[argc-1]) contain a new record to insert into
** the r-tree structure.
*/
if( rc==SQLITE_OK && nData>1 ){
/* Insert the new record into the r-tree */
RtreeNode *pLeaf;
/* Figure out the rowid of the new row. */
if( bHaveRowid==0 ){
rc = newRowid(pRtree, &cell.iRowid);
} }
*pRowid = cell.iRowid; *pRowid = cell.iRowid;
@@ -3008,6 +3045,8 @@ static int rtreeInit(
return SQLITE_ERROR; return SQLITE_ERROR;
} }
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Allocate the sqlite3_vtab structure */ /* Allocate the sqlite3_vtab structure */
nDb = strlen(argv[1]); nDb = strlen(argv[1]);
nName = strlen(argv[2]); nName = strlen(argv[2]);

View File

@@ -31,6 +31,8 @@ source $testdir/tester.tcl
# rtree-7.*: Test renaming an r-tree table. # rtree-7.*: Test renaming an r-tree table.
# rtree-8.*: Test constrained scans of r-tree data. # rtree-8.*: Test constrained scans of r-tree data.
# #
# rtree-12.*: Test that on-conflict clauses are supported.
#
ifcapable !rtree { ifcapable !rtree {
finish_test finish_test
@@ -416,4 +418,83 @@ do_test rtree-11.2 {
} }
} {2} } {2}
#-------------------------------------------------------------------------
# Test on-conflict clause handling.
#
db_delete_and_reopen
do_execsql_test 12.0 {
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
CREATE TABLE source(idx, x1, x2, y1, y2);
INSERT INTO source VALUES(5, 8, 8, 8, 8);
INSERT INTO source VALUES(2, 7, 7, 7, 7);
}
db_save_and_close
foreach {tn sql_template testdata} {
1 "INSERT %CONF% INTO t1 VALUES(2, 7, 7, 7, 7)" {
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
REPLACE 0 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7}
}
2 "INSERT %CONF% INTO t1 SELECT * FROM source" {
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
REPLACE 1 0 {1 1 2 3 4 2 7 7 7 7 3 3 4 5 6 4 4 5 6 7 5 8 8 8 8}
}
3 "UPDATE %CONF% t1 SET idx = 2 WHERE idx = 4" {
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
REPLACE 1 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
}
3 "UPDATE %CONF% t1 SET idx = ((idx+1)%5)+1 WHERE idx > 2" {
ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 4 4 5 6 7 5 3 4 5 6}
REPLACE 1 0 {1 4 5 6 7 2 2 3 4 5 5 3 4 5 6}
}
4 "INSERT %CONF% INTO t1 VALUES(2, 7, 6, 7, 7)" {
ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
REPLACE 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
}
} {
foreach {mode uses error data} $testdata {
db_restore_and_reopen
set sql [string map [list %CONF% "OR $mode"] $sql_template]
set testname "12.$tn.[string tolower $mode]"
execsql {
BEGIN;
INSERT INTO t1 VALUES(4, 4, 5, 6, 7);
}
set res(0) {0 {}}
set res(1) {1 {constraint failed}}
do_catchsql_test $testname.1 $sql $res($error)
do_test $testname.2 [list sql_uses_stmt db $sql] $uses
do_execsql_test $testname.3 { SELECT * FROM t1 ORDER BY idx } $data
do_test $testname.4 { rtree_check db t1 } 0
db close
}
}
finish_test finish_test

View File

@@ -1,5 +1,5 @@
C Add\sdocumentation\sfor\sthe\snewly\sintroduced\ssqlite3_vtab_config()\sand\son_conflict()\sAPI\sfunctions.\sTest\sthat\sencountering\san\sSQLITE_MISMATCH\sin\sfts3\sdoes\snot\scorrupt\sthe\sfull\stext\sindex. C Have\sr-tree\svirtual\stables\ssupport\son-conflict\sclauses.
D 2011-04-27T16:02:46.459 D 2011-04-28T18:46:46.861
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2 F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -82,9 +82,9 @@ F ext/icu/README.txt bf8461d8cdc6b8f514c080e4e10dc3b2bbdfefa9
F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c f5fa951eba03c41d292958064604a033021acdee F ext/rtree/rtree.c 829c6901a2b065ff93a68d431f9eaba8de7128e0
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test dbd4250ac0ad367a262eb9676f7e3080b0368206 F ext/rtree/rtree1.test 28e1b8da4da98093ce3210187434dd760a8d89d8
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af F ext/rtree/rtree4.test 0061e6f464fd3dc6a79f82454c5a1c3dadbe42af
@@ -456,7 +456,7 @@ F test/fts3aux1.test 0b02743955d56fc0d4d66236a26177bd1b726de0
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984 F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958 F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
F test/fts3conf.test 600b366bb658842d78ed72e05476d14c2409d539 F test/fts3conf.test 8e65ea56f88ced6cdd2252bdddb1a8327ae5af7e
F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf F test/fts3corrupt.test 7890cc202406858386ddf390a879dcf80bc10abf
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7 F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
@@ -688,7 +688,7 @@ F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05 F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
F test/tester.tcl 6fa3d2f581b479a3a088b1b5b0d145e548ebe662 F test/tester.tcl d5139260aadd64f318ecbcf982316d5bbc254b1b
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@@ -932,7 +932,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P ff69f823f23e6fb6e8b2857c4576d9c0732d9572 P abdd70ae0424ccadb7edaf16e970c78b5257d23c
R da0922898dc0b07ed5b043ad92b8294b R c8f4a330c2633adfb2862fa8f9efbab2
U dan U dan
Z fc25e1a6e640452f4b097e45b1091129 Z e17a424fc4dea753d2cbffe0969a3e63

View File

@@ -1 +1 @@
abdd70ae0424ccadb7edaf16e970c78b5257d23c 822ab52f1023b1c4973c806cc75454acd4e95fd0

View File

@@ -56,16 +56,6 @@ proc fts3_integrity {tn db tbl} {
uplevel [list do_test $tn [list set {} $m1] $m2] uplevel [list do_test $tn [list set {} $m1] $m2]
} }
# Return true if the SQL statement passed as the second argument uses a
# statement transaction.
#
proc sql_uses_stmt {db sql} {
set stmt [sqlite3_prepare db $sql -1 dummy]
set uses [uses_stmt_journal $stmt]
sqlite3_finalize $stmt
return $uses
}
do_execsql_test 1.0.1 { do_execsql_test 1.0.1 {
CREATE VIRTUAL TABLE t1 USING fts3(x); CREATE VIRTUAL TABLE t1 USING fts3(x);
INSERT INTO t1(rowid, x) VALUES(1, 'a b c d'); INSERT INTO t1(rowid, x) VALUES(1, 'a b c d');

View File

@@ -747,6 +747,17 @@ proc integrity_check {name {db db}} {
} }
} }
# Return true if the SQL statement passed as the second argument uses a
# statement transaction.
#
proc sql_uses_stmt {db sql} {
set stmt [sqlite3_prepare $db $sql -1 dummy]
set uses [uses_stmt_journal $stmt]
sqlite3_finalize $stmt
return $uses
}
proc fix_ifcapable_expr {expr} { proc fix_ifcapable_expr {expr} {
set ret "" set ret ""
set state 0 set state 0