From 6c39e6a80008610147fda192405472c88578bf73 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 1 Oct 2015 16:35:57 +0000 Subject: [PATCH] Changes to the sesssions module ensure that tables appear within changesets and patchsets in the same order that they were attached to the sessions object. FossilOrigin-Name: 7695890230dc1e0c6db9b7aa509db2039c7f7239 --- ext/session/session1.test | 11 -- ext/session/session2.test | 4 +- ext/session/sessionB.test | 10 -- ext/session/sessionF.test | 295 +++++++++++++++++++++++++++++++++ ext/session/session_common.tcl | 22 +++ ext/session/sqlite3session.c | 24 ++- ext/session/sqlite3session.h | 25 +++ manifest | 25 +-- manifest.uuid | 2 +- 9 files changed, 376 insertions(+), 42 deletions(-) create mode 100644 ext/session/sessionF.test diff --git a/ext/session/session1.test b/ext/session/session1.test index fc74a1e84e..7aebe8c000 100644 --- a/ext/session/session1.test +++ b/ext/session/session1.test @@ -20,17 +20,6 @@ ifcapable !session {finish_test; return} set testprefix session1 -proc do_changeset_invert_test {tn session res} { - set r [list] - foreach x $res {lappend r $x} - uplevel do_test $tn [list [subst -nocommands { - set x [list] - set changeset [sqlite3changeset_invert [$session changeset]] - sqlite3session_foreach c [set changeset] { lappend x [set c] } - set x - }]] [list $r] -} - do_execsql_test 1.0 { CREATE TABLE t1(x PRIMARY KEY, y); INSERT INTO t1 VALUES('abc', 'def'); diff --git a/ext/session/session2.test b/ext/session/session2.test index 0035e39dbf..cd8c2869e7 100644 --- a/ext/session/session2.test +++ b/ext/session/session2.test @@ -298,8 +298,8 @@ foreach {tn sql changeset} { DELETE FROM t1 WHERE 1; UPDATE t2 SET x = 34; } { - {UPDATE t2 0 .X {i 36 i 37} {i 34 {} {}}} {DELETE t1 0 X {i 35} {}} + {UPDATE t2 0 .X {i 36 i 37} {i 34 {} {}}} } } { do_iterator_test 5.$tn * $sql $changeset @@ -413,8 +413,8 @@ do_iterator_test 6.1.8 * { SELECT indirect(0); UPDATE t4 SET a = 'two' WHERE b = 1; } { - {INSERT t4 0 .X {} {t two i 1}} {INSERT t3 1 .X {} {t two i 1}} + {INSERT t4 0 .X {} {t two i 1}} } sqlite3session S db main diff --git a/ext/session/sessionB.test b/ext/session/sessionB.test index f1af5d213c..eae41f76ad 100644 --- a/ext/session/sessionB.test +++ b/ext/session/sessionB.test @@ -31,16 +31,6 @@ set testprefix sessionB # 3.*: Test that sqlite3changeset_invert() works with patchset blobs. # Correct behaviour is to return SQLITE_CORRUPT. -proc do_patchset_test {tn session res} { - set r [list] - foreach x $res {lappend r $x} - uplevel do_test $tn [list [subst -nocommands { - set x [list] - sqlite3session_foreach c [$session patchset] { lappend x [set c] } - set x - }]] [list $r] -} - proc do_sql2patchset_test {tn sql res} { sqlite3session S db main S attach * diff --git a/ext/session/sessionF.test b/ext/session/sessionF.test new file mode 100644 index 0000000000..ba57bf85ec --- /dev/null +++ b/ext/session/sessionF.test @@ -0,0 +1,295 @@ +# 2015 June 02 +# +# 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 the sessions module. +# Specifically, it tests that tables appear in the correct order +# within changesets and patchsets. +# + + + +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 sessionF + +# +# Test plan: +# +# 1.*: Test that sqlite3session_changeset() and sqlite3session_patchset() +# output tables in the right order. +# +# 2.*: Test that sqlite3session_invert() does not modify the order of +# tables within a changeset. +# +# 3.*: Test that sqlite3session_concat outputs tables in the right order. +# + +# Create a db schema to use. +# +do_execsql_test 1.0 { + CREATE TABLE t3(e PRIMARY KEY, f); + CREATE TABLE t1(a PRIMARY KEY, b); + CREATE TABLE t2(c PRIMARY KEY, d); +} + +#----------------------------------------------------------------------- +# 1.* - changeset() and patchset(). +# + +foreach {tn setup result} { + 1 { + S attach * + } { + {INSERT t2 0 X. {} {i 2 t two}} + {INSERT t1 0 X. {} {i 1 t one}} + {INSERT t3 0 X. {} {i 3 t three}} + } + + 2 { + S attach t1 + S attach * + } { + {INSERT t1 0 X. {} {i 1 t one}} + {INSERT t2 0 X. {} {i 2 t two}} + {INSERT t3 0 X. {} {i 3 t three}} + } + + 3 { + S attach t3 + S attach t2 + S attach t1 + } { + {INSERT t3 0 X. {} {i 3 t three}} + {INSERT t2 0 X. {} {i 2 t two}} + {INSERT t1 0 X. {} {i 1 t one}} + } +} { + execsql { + DELETE FROM t1; + DELETE FROM t2; + DELETE FROM t3; + } + sqlite3session S db main + eval $setup + + do_execsql_test 1.$tn.1 { + INSERT INTO t2 VALUES(2, 'two'); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t3 VALUES(3, 'three'); + } + + do_changeset_test 1.1.$tn.2 S $result + do_patchset_test 1.1.$tn.3 S $result + + S delete +} + +foreach {tn setup result} { + 1 { + S attach * + } { + {INSERT t2 0 X. {} {i 4 t four}} + {INSERT t2 0 X. {} {i 5 t five}} + {INSERT t1 0 X. {} {i 1 t one}} + {INSERT t3 0 X. {} {i 6 t six}} + } + + 2 { + S attach t1 + S attach * + } { + {INSERT t1 0 X. {} {i 1 t one}} + {INSERT t2 0 X. {} {i 4 t four}} + {INSERT t2 0 X. {} {i 5 t five}} + {INSERT t3 0 X. {} {i 6 t six}} + } + + 3 { + S attach t3 + S attach t2 + S attach t1 + } { + {INSERT t3 0 X. {} {i 6 t six}} + {INSERT t2 0 X. {} {i 4 t four}} + {INSERT t2 0 X. {} {i 5 t five}} + {INSERT t1 0 X. {} {i 1 t one}} + } +} { + execsql { + DELETE FROM t1; + DELETE FROM t2; + DELETE FROM t3; + } + sqlite3session S db main + eval $setup + + do_execsql_test 1.$tn.1 { + INSERT INTO t2 VALUES(2, 'two'); + INSERT INTO t1 VALUES(1, 'one'); + DELETE FROM t2; + INSERT INTO t2 VALUES(4, 'four'); + INSERT INTO t2 VALUES(5, 'five'); + INSERT INTO t3 VALUES(6, 'six'); + } + + do_changeset_test 1.2.$tn.2 S $result + do_patchset_test 1.2.$tn.2 S $result + + S delete +} + +#------------------------------------------------------------------------- +# 2.* - invert() +# + +foreach {tn setup result} { + 1 { + S attach * + } { + {DELETE t2 0 X. {i 4 t four} {}} + {DELETE t2 0 X. {i 5 t five} {}} + {DELETE t1 0 X. {i 1 t one} {}} + {DELETE t3 0 X. {i 6 t six} {}} + } + + 2 { + S attach t1 + S attach * + } { + {DELETE t1 0 X. {i 1 t one} {}} + {DELETE t2 0 X. {i 4 t four} {}} + {DELETE t2 0 X. {i 5 t five} {}} + {DELETE t3 0 X. {i 6 t six} {}} + } + + 3 { + S attach t3 + S attach t2 + S attach t1 + } { + {DELETE t3 0 X. {i 6 t six} {}} + {DELETE t2 0 X. {i 4 t four} {}} + {DELETE t2 0 X. {i 5 t five} {}} + {DELETE t1 0 X. {i 1 t one} {}} + } +} { + execsql { + DELETE FROM t1; + DELETE FROM t2; + DELETE FROM t3; + } + sqlite3session S db main + eval $setup + + do_execsql_test 1.$tn.1 { + INSERT INTO t2 VALUES(2, 'two'); + INSERT INTO t1 VALUES(1, 'one'); + DELETE FROM t2; + INSERT INTO t2 VALUES(4, 'four'); + INSERT INTO t2 VALUES(5, 'five'); + INSERT INTO t3 VALUES(6, 'six'); + } + + do_changeset_invert_test 2.$tn.2 S $result + + S delete +} + +#------------------------------------------------------------------------- +# 3.* - concat() +# +foreach {tn setup1 sql1 setup2 sql2 result} { + 1 { + S attach * + } { + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t2 VALUES(2, 'two'); + } { + S attach t2 + S attach t1 + } { + INSERT INTO t1 VALUES(3, 'three'); + INSERT INTO t2 VALUES(4, 'four'); + } { + {INSERT t1 0 X. {} {i 1 t one}} + {INSERT t1 0 X. {} {i 3 t three}} + {INSERT t2 0 X. {} {i 2 t two}} + {INSERT t2 0 X. {} {i 4 t four}} + } + + 1 { + S attach t2 + S attach t1 + } { + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t2 VALUES(2, 'two'); + } { + S attach * + } { + INSERT INTO t1 VALUES(3, 'three'); + INSERT INTO t2 VALUES(4, 'four'); + } { + {INSERT t2 0 X. {} {i 2 t two}} + {INSERT t2 0 X. {} {i 4 t four}} + {INSERT t1 0 X. {} {i 1 t one}} + {INSERT t1 0 X. {} {i 3 t three}} + } + + 1 { + S attach * + } { + INSERT INTO t2 VALUES(2, 'two'); + } { + S attach * + } { + INSERT INTO t1 VALUES(3, 'three'); + INSERT INTO t2 VALUES(4, 'four'); + INSERT INTO t3 VALUES(5, 'five'); + } { + {INSERT t2 0 X. {} {i 2 t two}} + {INSERT t2 0 X. {} {i 4 t four}} + {INSERT t1 0 X. {} {i 3 t three}} + {INSERT t3 0 X. {} {i 5 t five}} + } + +} { + execsql { + DELETE FROM t1; + DELETE FROM t2; + DELETE FROM t3; + } + sqlite3session S db main + eval $setup1 + execsql $sql1 + set c1 [S changeset] + S delete + + sqlite3session S db main + eval $setup2 + execsql $sql2 + set c2 [S changeset] + S delete + + set res [list] + sqlite3session_foreach x [sqlite3changeset_concat $c1 $c2] { + lappend res $x + } + + do_test 3.$tn { set res } [list {*}$result] +} + + +finish_test + diff --git a/ext/session/session_common.tcl b/ext/session/session_common.tcl index f27ad11dc0..38eebec0a0 100644 --- a/ext/session/session_common.tcl +++ b/ext/session/session_common.tcl @@ -9,6 +9,28 @@ proc do_changeset_test {tn session res} { }]] [list $r] } +proc do_patchset_test {tn session res} { + set r [list] + foreach x $res {lappend r $x} + uplevel do_test $tn [list [subst -nocommands { + set x [list] + sqlite3session_foreach c [$session patchset] { lappend x [set c] } + set x + }]] [list $r] +} + + +proc do_changeset_invert_test {tn session res} { + set r [list] + foreach x $res {lappend r $x} + uplevel do_test $tn [list [subst -nocommands { + set x [list] + set changeset [sqlite3changeset_invert [$session changeset]] + sqlite3session_foreach c [set changeset] { lappend x [set c] } + set x + }]] [list $r] +} + proc do_conflict_test {tn args} { proc xConflict {args} { diff --git a/ext/session/sqlite3session.c b/ext/session/sqlite3session.c index 0937c9bb32..546a22a072 100644 --- a/ext/session/sqlite3session.c +++ b/ext/session/sqlite3session.c @@ -1188,7 +1188,7 @@ static int sessionFindTable( ){ rc = sqlite3session_attach(pSession, zName); if( rc==SQLITE_OK ){ - pRet = pSession->pTable; + for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext); assert( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) ); } } @@ -1675,12 +1675,17 @@ int sqlite3session_attach( if( !pTab ){ rc = SQLITE_NOMEM; }else{ - /* Populate the new SessionTable object and link it into the list. */ + /* Populate the new SessionTable object and link it into the list. + ** The new object must be linked onto the end of the list, not + ** simply added to the start of it in order to ensure that tables + ** appear in the correct order when a changeset or patchset is + ** eventually generated. */ + SessionTable **ppTab; memset(pTab, 0, sizeof(SessionTable)); pTab->zName = (char *)&pTab[1]; memcpy(pTab->zName, zName, nName+1); - pTab->pNext = pSession->pTable; - pSession->pTable = pTab; + for(ppTab=&pSession->pTable; *ppTab; ppTab=&(*ppTab)->pNext); + *ppTab = pTab; } } } @@ -4220,19 +4225,26 @@ static int sessionChangesetToHash( if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; } if( !pTab ){ + SessionTable **ppTab; + pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1); if( !pTab ){ rc = SQLITE_NOMEM; break; } memset(pTab, 0, sizeof(SessionTable)); - pTab->pNext = pGrp->pList; pTab->nCol = nCol; pTab->abPK = (u8*)&pTab[1]; memcpy(pTab->abPK, abPK, nCol); pTab->zName = (char*)&pTab->abPK[nCol]; memcpy(pTab->zName, zNew, nNew+1); - pGrp->pList = pTab; + + /* The new object must be linked on to the end of the list, not + ** simply added to the start of it. This is to ensure that the + ** tables within the output of sqlite3changegroup_output() are in + ** the right order. */ + for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext); + *ppTab = pTab; }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){ rc = SQLITE_SCHEMA; break; diff --git a/ext/session/sqlite3session.h b/ext/session/sqlite3session.h index f2df143e1a..71f02159cf 100644 --- a/ext/session/sqlite3session.h +++ b/ext/session/sqlite3session.h @@ -206,6 +206,14 @@ void sqlite3session_table_filter( ** a database with a compatible schema using the [sqlite3changeset_apply()] ** API. ** +** Within a changeset generated by this function, all changes related to a +** single table are grouped together. In other words, when iterating through +** a changeset or when applying a changeset to a database, all changes related +** to a single table are processed before moving on to the next table. Tables +** are sorted in the same order in which they were attached (or auto-attached) +** to the sqlite3_session object. The order in which the changes related to +** a single table are stored is undefined. +** ** Following a successful call to this function, it is the responsibility of ** the caller to eventually free the buffer that *ppChangeset points to using ** [sqlite3_free()]. @@ -358,6 +366,11 @@ int sqlite3session_diff( ** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset ** is passed to the sqlite3changeset_apply() API. Other conflict types work ** in the same way as for changesets. +** +** Changes within a patchset are ordered in the same way as for changesets +** generated by the sqlite3session_changeset() function (i.e. all changes for +** a single table are grouped together, tables appear in the order in which +** they were attached to the session object). */ int sqlite3session_patchset( sqlite3_session *pSession, /* Session object */ @@ -659,6 +672,9 @@ int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter); **
  • For each UPDATE change, the old.* and new.* values are exchanged. ** ** +** This function does not change the order in which changes appear within +** the changeset. It merely reverses the sense of each individual change. +** ** If successful, a pointer to a buffer containing the inverted changeset ** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and ** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are @@ -833,6 +849,15 @@ int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); ** were themselves changesets, the output is a changeset. Or, if the ** inputs were patchsets, the output is also a patchset. ** +** As with the output of the sqlite3session_changeset() and +** sqlite3session_patchset() functions, all changes related to a single +** table are grouped together in the output of this function. Tables appear +** in the same order as for the very first changeset added to the changegroup. +** If the second or subsequent changesets added to the changegroup contain +** changes for tables that do not appear in the first changeset, they are +** appended onto the end of the output changeset, again in the order in +** which they are first encountered. +** ** If an error occurs, an SQLite error code is returned and the output ** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK ** is returned and the output variables are set to the size of and a diff --git a/manifest b/manifest index bcf5b0db01..794f881303 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\srecent\senhancements\sfrom\strunk,\sand\sespecially\sthe\sfix\sfor\nticket\s[1b266395d6bc10]. -D 2015-09-30T14:50:39.955 +C Changes\sto\sthe\ssesssions\smodule\sensure\sthat\stables\sappear\swithin\schangesets\sand\spatchsets\sin\sthe\ssame\sorder\sthat\sthey\swere\sattached\sto\sthe\ssessions\sobject. +D 2015-10-01T16:35:57.175 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in fdcfdc361f0a3723da9b48b967f259f7aaff3ad5 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -256,8 +256,8 @@ F ext/rtree/sqlite3rtree.h 9c5777af3d2921c7b4ae4954e8e5697502289d28 F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024 F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a -F ext/session/session1.test d4f53b9b5add7eeb358e535d6e18552142092429 -F ext/session/session2.test a95a2d270b32638c1acba7cb9c81856712d469ac +F ext/session/session1.test 5dab50ce55c859e829bae24f0787013f51775fc5 +F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0 F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01 F ext/session/session4.test a6ed685da7a5293c5d6f99855bcf41dbc352ca84 F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169 @@ -265,14 +265,15 @@ F ext/session/session6.test 443789bc2fca12e4f7075cf692c60b8a2bea1a26 F ext/session/session8.test 8e194b3f655d861ca36de5d4de53f702751bab3b F ext/session/session9.test 5409d90d8141881d08285ed1c2c0d8d10fb92069 F ext/session/sessionA.test 1feeab0b8e03527f08f2f1defb442da25480138f -F ext/session/sessionB.test 06961b7c3641151f5d23088250ecad132501113c +F ext/session/sessionB.test 886252dcb7e692e62ef7e357456200912e367823 F ext/session/sessionC.test 3982f8577b0744c5ce3aaef7cfeb5bd903f17fe4 F ext/session/sessionD.test d4744c78334162851d2a2f285c7e603e31b49aa2 F ext/session/sessionE.test e60a238c47f0feb3bb707e7f35e22be09c7e8f26 -F ext/session/session_common.tcl f4b7b59c617edf0c9b00d94cd93498d225d43837 +F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce +F ext/session/session_common.tcl a1293167d14774b5e728836720497f40fe4ea596 F ext/session/sessionfault.test bef044d0952c0d62c31c8d2400be72c8684545cc -F ext/session/sqlite3session.c 78e5b310cd6e655e9166f40dc8a319c0c866cfdc -F ext/session/sqlite3session.h 772cffe38f11bd62f434eabf080157eabf3d553e +F ext/session/sqlite3session.c 1ace1d1e4cb32cf60ca6e8ed1ae8d4c9f99ca458 +F ext/session/sqlite3session.h 64e9e7f185725ef43b97f4a9a0c0df0669844f1d F ext/session/test_session.c 187bd344c5ae9d5be85e22ef7c3010f0c17307ce F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 @@ -1409,7 +1410,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P c91065f8edb1e54076791716fc20d3fcfe3070dc e796c0efb6cf17444b53af75046daf7d8fa82f78 -R 0237333bf6dc63bf0c4f4c66d7918366 -U drh -Z 5f6cedfd7517312f688a69652091b608 +P b2face9aa95ade96a5666c70b6b31064c1ad0977 +R 67f4bc03b9ceb2707f867778f6790d70 +U dan +Z c3622f418cca20fcda99ae888e36e276 diff --git a/manifest.uuid b/manifest.uuid index 75c74a20e2..5274d2fafa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2face9aa95ade96a5666c70b6b31064c1ad0977 \ No newline at end of file +7695890230dc1e0c6db9b7aa509db2039c7f7239 \ No newline at end of file