diff options
author | dan <dan@noemail.net> | 2011-07-26 15:50:36 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2011-07-26 15:50:36 +0000 |
commit | cfec7eee203411e30cf3c55d975b562dc8f2c16e (patch) | |
tree | 9197e0693f2559a1f195662c0695081d67e9bd62 | |
parent | 8a3bb3fddd85f113cdc6e0cea297fcbc31be0876 (diff) | |
download | sqlite-cfec7eee203411e30cf3c55d975b562dc8f2c16e.tar.gz sqlite-cfec7eee203411e30cf3c55d975b562dc8f2c16e.zip |
Fix a problem causing sqlite3changeset_invert() to effectively drop UPDATE changes.
FossilOrigin-Name: bb3e65d9724dcecdc54b4c9fb0448f95d14495ff
-rw-r--r-- | ext/session/session1.test | 15 | ||||
-rw-r--r-- | ext/session/session8.test | 92 | ||||
-rw-r--r-- | ext/session/sqlite3session.c | 82 | ||||
-rw-r--r-- | ext/session/sqlite3session.h | 2 | ||||
-rw-r--r-- | manifest | 19 | ||||
-rw-r--r-- | manifest.uuid | 2 |
6 files changed, 183 insertions, 29 deletions
diff --git a/ext/session/session1.test b/ext/session/session1.test index 91ccab21a..7ef33e7fc 100644 --- a/ext/session/session1.test +++ b/ext/session/session1.test @@ -142,7 +142,7 @@ do_changeset_test 2.3.2 S { do_changeset_invert_test 2.3.3 S { {DELETE t1 0 X. {i 10 t Sukhothai} {}} {INSERT t1 0 X. {} {i 1 t Sukhothai}} - {UPDATE t1 0 X. {{} {} t Surin} {i 2 t Ayutthaya}} + {UPDATE t1 0 X. {i 2 t Surin} {{} {} t Ayutthaya}} {INSERT t1 0 X. {} {i 3 t Thonburi}} {DELETE t1 0 X. {i 20 t Thapae} {}} } @@ -492,5 +492,18 @@ do_test 8.3 { } {1} do_test 8.4 { S delete } {} +#------------------------------------------------------------------------- +# +do_execsql_test 9.1 { + CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g); + INSERT INTO t7 VALUES(1, 1, 1, 1, 1, 1, 1); +} +do_test 9.2 { + sqlite3session S db main + S attach * + execsql { UPDATE t7 SET b=2, d=2 } +} {} +do_changeset_test 9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}} +S delete catch { db2 close } finish_test diff --git a/ext/session/session8.test b/ext/session/session8.test new file mode 100644 index 000000000..93bd4e06c --- /dev/null +++ b/ext/session/session8.test @@ -0,0 +1,92 @@ +# 2011 July 13 +# +# 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. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source [file join [file dirname [info script]] session_common.tcl] +source $testdir/tester.tcl +ifcapable !session {finish_test; return} + +set testprefix session8 + +proc noop {args} {} + +# Like [dbcksum] in tester.tcl. Except this version is not sensitive +# to changes in the value of implicit IPK columns. +# +proc udbcksum {db dbname} { + if {$dbname=="temp"} { + set master sqlite_temp_master + } else { + set master $dbname.sqlite_master + } + set alltab [$db eval "SELECT name FROM $master WHERE type='table'"] + set txt [$db eval "SELECT * FROM $master"]\n + foreach tab $alltab { + append txt [lsort [$db eval "SELECT * FROM $dbname.$tab"]]\n + } + return [md5 $txt] +} + +proc do_then_undo {tn sql} { + set ck1 [udbcksum db main] + + sqlite3session S db main + S attach * + db eval $sql + + set ck2 [udbcksum db main] + + set invert [sqlite3changeset_invert [S changeset]] + S delete + sqlite3changeset_apply db $invert noop + + set ck3 [udbcksum db main] + + set a [expr {$ck1==$ck2}] + set b [expr {$ck1==$ck3}] + uplevel [list do_test $tn.1 "set {} $a" 0] + uplevel [list do_test $tn.2 "set {} $b" 1] +} + +do_execsql_test 1.1 { + CREATE TABLE t1(a PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES("abc", "xyz"); +} +do_then_undo 1.2 { INSERT INTO t1 VALUES(3, 4); } +do_then_undo 1.3 { DELETE FROM t1 WHERE b=2; } +do_then_undo 1.4 { UPDATE t1 SET b = 3 WHERE a = 1; } + +do_execsql_test 2.1 { + CREATE TABLE t2(a, b PRIMARY KEY); + INSERT INTO t2 VALUES(1, 2); + INSERT INTO t2 VALUES('abc', 'xyz'); +} +do_then_undo 1.2 { INSERT INTO t2 VALUES(3, 4); } +do_then_undo 1.3 { DELETE FROM t2 WHERE b=2; } +do_then_undo 1.4 { UPDATE t1 SET a = '123' WHERE b = 'xyz'; } + +do_execsql_test 3.1 { + CREATE TABLE t3(a, b, c, d, e, PRIMARY KEY(c, e)); + INSERT INTO t3 VALUES('x', 45, 0.0, 'abcdef', 12); + INSERT INTO t3 VALUES(45, 0.0, 'abcdef', 12, 'x'); + INSERT INTO t3 VALUES(0.0, 'abcdef', 12, 'x', 45); +} + +do_then_undo 3.2 { UPDATE t3 SET b=b||b WHERE e!='x' } +do_then_undo 3.3 { UPDATE t3 SET a = 46 } + +finish_test + diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 817954034..b8bcc9b28 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -2132,14 +2132,17 @@ int sqlite3changeset_finalize(sqlite3_changeset_iter *p){ */ int sqlite3changeset_invert( int nChangeset, /* Number of bytes in input */ - void *pChangeset, /* Input changeset */ + const void *pChangeset, /* Input changeset */ int *pnInverted, /* OUT: Number of bytes in output changeset */ void **ppInverted /* OUT: Inverse of pChangeset */ ){ + int rc = SQLITE_OK; /* Return value */ u8 *aOut; u8 *aIn; int i; - int nCol = 0; + int nCol = 0; /* Number of cols in current table */ + u8 *abPK = 0; /* PK array for current table */ + sqlite3_value **apVal = 0; /* Space for values for UPDATE inversion */ /* Zero the output variables in case an error occurs. */ *ppInverted = 0; @@ -2163,10 +2166,13 @@ int sqlite3changeset_invert( ** * A nul-terminated table name. */ int nByte = 1 + sessionVarintGet(&aIn[i+1], &nCol); + abPK = &aIn[i+nByte]; nByte += nCol; nByte += 1 + sqlite3Strlen30((char *)&aIn[i+nByte]); memcpy(&aOut[i], &aIn[i], nByte); i += nByte; + sqlite3_free(apVal); + apVal = 0; break; } @@ -2185,40 +2191,82 @@ int sqlite3changeset_invert( } case SQLITE_UPDATE: { - int nByte1; /* Size of old.* record in bytes */ - int nByte2; /* Size of new.* record in bytes */ - u8 *aEnd = &aIn[i+2]; + int iCol; + int nWrite = 0; + u8 *aEnd = &aIn[i+2]; - sessionReadRecord(&aEnd, nCol, 0); - nByte1 = (int)(aEnd - &aIn[i+2]); - sessionReadRecord(&aEnd, nCol, 0); - nByte2 = (int)(aEnd - &aIn[i+2]) - nByte1; + if( 0==apVal ){ + apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2); + if( 0==apVal ){ + rc = SQLITE_NOMEM; + goto finished_invert; + } + memset(apVal, 0, sizeof(apVal[0])*nCol*2); + } + /* Read the old.* and new.* records for the update change. */ + rc = sessionReadRecord(&aEnd, nCol, &apVal[0]); + if( rc==SQLITE_OK ){ + rc = sessionReadRecord(&aEnd, nCol, &apVal[nCol]); + } + + /* Write the header for the new UPDATE change. Same as the original. */ aOut[i] = SQLITE_UPDATE; aOut[i+1] = aIn[i+1]; - memcpy(&aOut[i+2], &aIn[i+2+nByte1], nByte2); - memcpy(&aOut[i+2+nByte2], &aIn[i+2], nByte1); + nWrite = 2; + + /* Write the new old.* record. Consists of the PK columns from the + ** original old.* record, and the other values from the original + ** new.* record. */ + for(iCol=0; rc==SQLITE_OK && iCol<nCol; iCol++){ + sqlite3_value *pVal = apVal[iCol + (abPK[iCol] ? 0 : nCol)]; + rc = sessionSerializeValue(&aOut[i+nWrite], pVal, &nWrite); + } + + /* Write the new new.* record. Consists of a copy of all values + ** from the original old.* record, except for the PK columns, which + ** are set to "undefined". */ + for(iCol=0; rc==SQLITE_OK && iCol<nCol; iCol++){ + sqlite3_value *pVal = (abPK[iCol] ? 0 : apVal[iCol]); + rc = sessionSerializeValue(&aOut[i+nWrite], pVal, &nWrite); + } - i += 2 + nByte1 + nByte2; + for(iCol=0; iCol<nCol*2; iCol++){ + sqlite3ValueFree(apVal[iCol]); + } + memset(apVal, 0, sizeof(apVal[0])*nCol*2); + if( rc!=SQLITE_OK ){ + goto finished_invert; + } + + i += nWrite; + assert( &aIn[i]==aEnd ); break; } default: - sqlite3_free(aOut); - return SQLITE_CORRUPT; + rc = SQLITE_CORRUPT; + goto finished_invert; } } + assert( rc==SQLITE_OK ); *pnInverted = nChangeset; *ppInverted = (void *)aOut; - return SQLITE_OK; + + finished_invert: + if( rc!=SQLITE_OK ){ + sqlite3_free(aOut); + } + sqlite3_free(apVal); + return rc; } typedef struct SessionApplyCtx SessionApplyCtx; struct SessionApplyCtx { sqlite3 *db; sqlite3_stmt *pDelete; /* DELETE statement */ - sqlite3_stmt *pUpdate; /* DELETE statement */ + sqlite3_stmt *pUpdate; /* UPDATE statement */ sqlite3_stmt *pInsert; /* INSERT statement */ sqlite3_stmt *pSelect; /* SELECT statement */ int nCol; /* Size of azCol[] and abPK[] arrays */ @@ -2995,9 +3043,9 @@ static int sessionChangeMerge( pNew = 0; } }else if( op2==SQLITE_UPDATE ){ /* UPDATE + UPDATE */ - assert( op1==SQLITE_UPDATE ); u8 *a1 = pExist->aRecord; u8 *a2 = aRec; + assert( op1==SQLITE_UPDATE ); sessionReadRecord(&a1, pTab->nCol, 0); sessionReadRecord(&a2, pTab->nCol, 0); pNew->op = SQLITE_UPDATE; diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index 3a1c470ca..a1ab1e964 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -545,7 +545,7 @@ int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter); ** changeset. If it is not, the results are undefined. */ int sqlite3changeset_invert( - int nIn, void *pIn, /* Input changeset */ + int nIn, const void *pIn, /* Input changeset */ int *pnOut, void **ppOut /* OUT: Inverse of input */ ); @@ -1,5 +1,5 @@ -C Merge\sthe\slatest\strunk\schanges\sinto\sthe\ssessions\sbranch. -D 2011-07-22T12:49:27.667 +C Fix\sa\sproblem\scausing\ssqlite3changeset_invert()\sto\seffectively\sdrop\sUPDATE\schanges. +D 2011-07-26T15:50:36.271 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 1e6988b3c11dee9bd5edc0c804bd4468d74a9cdc F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -102,16 +102,17 @@ F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea F ext/rtree/sqlite3rtree.h 1af0899c63a688e272d69d8e746f24e76f10a3f0 F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 -F ext/session/session1.test 98691eec553390d61114b6214d2397f399dc1198 +F ext/session/session1.test 502086908e4144dfaccb1baa77bc29d75a9daace F ext/session/session2.test 99ca0da7ddb617d42bafd83adccf99f18ae0384b F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01 F ext/session/session4.test a6ed685da7a5293c5d6f99855bcf41dbc352ca84 F ext/session/session5.test 8fdfaf9dba28a2f1c6b89b06168bdab1fef2d478 F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26 +F ext/session/session8.test 7d35947ad329b8966f095d34f9617a9eff52dc65 F ext/session/session_common.tcl 1539d8973b2aea0025c133eb0cc4c89fcef541a5 F ext/session/sessionfault.test 401045278298a242cbc2e4bc986c102f01ff2180 -F ext/session/sqlite3session.c 97295e187eade25398f52ed8b13fea68e0ee1e02 -F ext/session/sqlite3session.h 8dec372049532017d71c992609ca5450de7c5520 +F ext/session/sqlite3session.c 5a50e28759187440805fca653029946311593da6 +F ext/session/sqlite3session.h f374c9c4c96e08f67ac418871c29d423245c7673 F ext/session/test_session.c ea4dc9b4a1895c8e6bddcbfe3838d7eb57df2d99 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 @@ -964,7 +965,7 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5 F tool/tostr.awk 11760e1b94a5d3dcd42378f3cc18544c06cfa576 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings.sh 2ebae31e1eb352696f3c2f7706a34c084b28c262 -P d04e0fd82a15aee963e35830caf8159b4b6ccd87 8ce2b74a82264550b0e19da3e0e1a145db940a1c -R a5737ded89a8c66dbf565b51f6981ff8 -U drh -Z 81f45685ad26c120405ad7cb74da4306 +P 110cfd6920cf3011aeaf7e586f8db867bfc69fbb +R 95b1f91b4b1f21686c1d16002cdc06fd +U dan +Z 326ddfa62484f9d6c6538d0373ca9236 diff --git a/manifest.uuid b/manifest.uuid index cd26a2668..94d44ce8a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -110cfd6920cf3011aeaf7e586f8db867bfc69fbb
\ No newline at end of file +bb3e65d9724dcecdc54b4c9fb0448f95d14495ff
\ No newline at end of file |