From 5782bc27fa701e827f26cd1140531a1fa6652be5 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 23 May 2016 16:16:13 +0000 Subject: [PATCH] Improve the error messages generated by the rtree module when a constraint fails. FossilOrigin-Name: 3ad2531efb64b7c53b777ddf3681203217052b32 --- ext/rtree/rtree.c | 53 ++++++++++++++++++++++++++++++++++++++++--- ext/rtree/rtree1.test | 20 ++++++++-------- ext/rtree/rtree3.test | 31 ++++++++++++++++++++++++- manifest | 18 +++++++-------- manifest.uuid | 2 +- 5 files changed, 101 insertions(+), 23 deletions(-) diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 012c48d6d1..311824aa85 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -2800,6 +2800,53 @@ static RtreeValue rtreeValueUp(sqlite3_value *v){ } #endif /* !defined(SQLITE_RTREE_INT_ONLY) */ +/* +** A constraint has failed while inserting a row into an rtree table. +** Assuming no OOM error occurs, this function sets the error message +** (at pRtree->base.zErrMsg) to an appropriate value and returns +** SQLITE_CONSTRAINT. +** +** Parameter iCol is the index of the leftmost column involved in the +** constraint failure. If it is 0, then the constraint that failed is +** the unique constraint on the id column. Otherwise, it is the rtree +** (c1<=c2) constraint on columns iCol and iCol+1 that has failed. +** +** If an OOM occurs, SQLITE_NOMEM is returned instead of SQLITE_CONSTRAINT. +*/ +static int rtreeConstraintError(Rtree *pRtree, int iCol){ + sqlite3_stmt *pStmt = 0; + char *zSql; + int rc; + + assert( iCol==0 || iCol%2 ); + zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", pRtree->zDb, pRtree->zName); + if( zSql ){ + rc = sqlite3_prepare_v2(pRtree->db, zSql, -1, &pStmt, 0); + }else{ + rc = SQLITE_NOMEM; + } + sqlite3_free(zSql); + + if( rc==SQLITE_OK ){ + if( iCol==0 ){ + const char *zCol = sqlite3_column_name(pStmt, 0); + pRtree->base.zErrMsg = sqlite3_mprintf( + "UNIQUE constraint failed: %s.%s", pRtree->zName, zCol + ); + }else{ + const char *zCol1 = sqlite3_column_name(pStmt, iCol); + const char *zCol2 = sqlite3_column_name(pStmt, iCol+1); + pRtree->base.zErrMsg = sqlite3_mprintf( + "rtree constraint failed: %s.(%s<=%s)", pRtree->zName, zCol1, zCol2 + ); + } + } + + sqlite3_finalize(pStmt); + return (rc==SQLITE_OK ? SQLITE_CONSTRAINT : rc); +} + + /* ** The xUpdate method for rtree module virtual tables. @@ -2850,7 +2897,7 @@ static int rtreeUpdate( cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]); cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]); if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){ - rc = SQLITE_CONSTRAINT; + rc = rtreeConstraintError(pRtree, ii+1); goto constraint; } } @@ -2861,7 +2908,7 @@ static int rtreeUpdate( cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]); cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]); if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){ - rc = SQLITE_CONSTRAINT; + rc = rtreeConstraintError(pRtree, ii+1); goto constraint; } } @@ -2882,7 +2929,7 @@ static int rtreeUpdate( if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){ rc = rtreeDeleteRowid(pRtree, cell.iRowid); }else{ - rc = SQLITE_CONSTRAINT; + rc = rtreeConstraintError(pRtree, 0); goto constraint; } } diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test index c9192de192..9dc101a7b2 100644 --- a/ext/rtree/rtree1.test +++ b/ext/rtree/rtree1.test @@ -194,13 +194,13 @@ do_test rtree-2.1.3 { do_test rtree-2.2.1 { catchsql { INSERT INTO t1 VALUES(2, 1, 3, 2, 4) } -} {1 {constraint failed}} +} {1 {UNIQUE constraint failed: t1.ii}} do_test rtree-2.2.2 { catchsql { INSERT INTO t1 VALUES(4, 1, 3, 4, 2) } -} {1 {constraint failed}} +} {1 {rtree constraint failed: t1.(y1<=y2)}} do_test rtree-2.2.3 { catchsql { INSERT INTO t1 VALUES(4, 3, 1, 2, 4) } -} {1 {constraint failed}} +} {1 {rtree constraint failed: t1.(x1<=x2)}} do_test rtree-2.2.4 { execsql { SELECT ii FROM t1 ORDER BY ii } } {1 2 3} @@ -236,7 +236,7 @@ do_test rtree-3.1.3 { # Test the constraint on the coordinates (c[i]<=c[i+1] where (i%2==0)): do_test rtree-3.2.1 { catchsql { INSERT INTO t1 VALUES(7, 2, 6, 4, 3) } -} {1 {constraint failed}} +} {1 {rtree constraint failed: t1.(y1<=y2)}} do_test rtree-3.2.2 { catchsql { INSERT INTO t1 VALUES(8, 2, 6, 3, 3) } } {0 {}} @@ -490,11 +490,11 @@ foreach {tn sql_template testdata} { } 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} + ROLLBACK 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6} + ABORT 0 2 {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} + FAIL 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} + REPLACE 0 2 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7} } } { @@ -510,7 +510,9 @@ foreach {tn sql_template testdata} { } set res(0) {0 {}} - set res(1) {1 {constraint failed}} + set res(1) {1 {UNIQUE constraint failed: t1.idx}} + set res(2) {1 {rtree constraint failed: t1.(x1<=x2)}} + 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 diff --git a/ext/rtree/rtree3.test b/ext/rtree/rtree3.test index fea5513069..1d863c6cc2 100644 --- a/ext/rtree/rtree3.test +++ b/ext/rtree/rtree3.test @@ -47,7 +47,8 @@ ifcapable !rtree { # # rtree3-8: Test OOM while registering the r-tree module with sqlite. # - +# rtree3-11: OOM following a constraint failure +# do_faultsim_test rtree3-1 -faults oom* -prep { faultsim_delete_and_reopen } -body { @@ -234,4 +235,32 @@ do_faultsim_test rtree3-10 -faults oom-* -prep { faultsim_test_result {0 2} } + +do_test rtree3-11.prep { + faultsim_delete_and_reopen + execsql { + CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); + INSERT INTO rt VALUES(1, 2, 3, 4, 5); + } + faultsim_save_and_close +} {} +do_faultsim_test rtree3-10.1 -faults oom-* -prep { + faultsim_restore_and_reopen + execsql { SELECT * FROM rt } +} -body { + execsql { INSERT INTO rt VALUES(1, 2, 3, 4, 5) } +} -test { + faultsim_test_result {1 {UNIQUE constraint failed: rt.ii}} \ + {1 {constraint failed}} +} +do_faultsim_test rtree3-10.2 -faults oom-* -prep { + faultsim_restore_and_reopen + execsql { SELECT * FROM rt } +} -body { + execsql { INSERT INTO rt VALUES(2, 2, 3, 5, 4) } +} -test { + faultsim_test_result {1 {rtree constraint failed: rt.(y1<=y2)}} \ + {1 {constraint failed}} +} + finish_test diff --git a/manifest b/manifest index 7893a5758b..1f97ff1987 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Lemon\senhancement:\savoid\sunnecessary\sreduce\sactions\sthat\sconvert\sone\nnon-terminal\sinto\sanother\sbut\shave\sno\sside\seffects. -D 2016-05-23T16:15:02.530 +C Improve\sthe\serror\smessages\sgenerated\sby\sthe\srtree\smodule\swhen\sa\sconstraint\sfails. +D 2016-05-23T16:16:13.470 F Makefile.in f59e0763ff448719fc1bd25513882b0567286317 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 306d73e854b1a92ea06e5d1e637faa5c44de53c7 @@ -253,11 +253,11 @@ F ext/rbu/sqlite3rbu.c bf36625990c6865ecf08bd844d8097ed2d0a6958 F ext/rbu/sqlite3rbu.h 2acd0a6344a6079de15c8dc9d84d3df83a665930 F ext/rbu/test_rbu.c 9bbdf6bd8efd58fbc4f192698df50569598fbb9e F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 -F ext/rtree/rtree.c 0b870ccb7b58b734a2a8e1e2755a7c0ded070920 +F ext/rtree/rtree.c 7ebca7d18261f402d67de9e151be7fb2ed62b8f5 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e -F ext/rtree/rtree1.test 96a80c08440c932cd72aac50660e7af2612d2cda +F ext/rtree/rtree1.test 42dadfc7b44a436cd74a1bebc0b9b689e4eaf7ec F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba -F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc +F ext/rtree/rtree3.test 2cafe8265d1ff28f206fce88d114f208349df482 F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0 F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e F ext/rtree/rtree6.test 773a90db2dce6a8353dd0d5b64bca69b29761196 @@ -1492,7 +1492,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 b91a5b8297756289d45c8fce0d3399c253517eb0 -R e62078de0c6378c8386e96fef92b2fe6 -U drh -Z f3f44e1ca319aa39a93bd7b358420b1f +P a86e782ad1aa6f5a8b2c54f9dcf0fa61960843f3 +R 656cb7a661842081cb14458e0c90ff63 +U dan +Z 1b4990ccdee5b0865b4104871945d857 diff --git a/manifest.uuid b/manifest.uuid index 4af496871d..194d4c4259 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a86e782ad1aa6f5a8b2c54f9dcf0fa61960843f3 \ No newline at end of file +3ad2531efb64b7c53b777ddf3681203217052b32 \ No newline at end of file