]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix a problem causing sqlite3changeset_invert() to effectively drop UPDATE changes.
authordan <dan@noemail.net>
Tue, 26 Jul 2011 15:50:36 +0000 (15:50 +0000)
committerdan <dan@noemail.net>
Tue, 26 Jul 2011 15:50:36 +0000 (15:50 +0000)
FossilOrigin-Name: bb3e65d9724dcecdc54b4c9fb0448f95d14495ff

ext/session/session1.test
ext/session/session8.test [new file with mode: 0644]
ext/session/sqlite3session.c
ext/session/sqlite3session.h
manifest
manifest.uuid

index 91ccab21aa1e50c0fd1366a61ed1910efab545aa..7ef33e7fce7248b50b0fca8ed29fddbab42631e8 100644 (file)
@@ -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 (file)
index 0000000..93bd4e0
--- /dev/null
@@ -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
+
index 817954034f251c4e7fb9c7c2eac7bcb582983a95..b8bcc9b2869763b5bbbb064e7087ba8609b72f8d 100644 (file)
@@ -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;
index 3a1c470ca80f0a38296cee5f678bcddfc1480695..a1ab1e96451238c3cca4a068c8c52064e4264d4c 100644 (file)
@@ -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 */
 );
 
index 4e10bb59472bcafce14cb12a288649de46f8e8e2..996459f395d5d7b5e3fe13e8a045ddf795b805b6 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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
index cd26a2668cca4da3016b0184bc48030802d2a149..94d44ce8ae07f757b9b182f966c1d84d57e02151 100644 (file)
@@ -1 +1 @@
-110cfd6920cf3011aeaf7e586f8db867bfc69fbb
\ No newline at end of file
+bb3e65d9724dcecdc54b4c9fb0448f95d14495ff
\ No newline at end of file