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
This commit is contained in:
dan 2015-10-01 16:35:57 +00:00
parent 32af5eda6a
commit 6c39e6a800
9 changed files with 376 additions and 42 deletions

View File

@ -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');

View File

@ -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

View File

@ -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 *

295
ext/session/sessionF.test Normal file
View File

@ -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

View File

@ -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} {

View File

@ -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;

View File

@ -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);
** <li> For each UPDATE change, the old.* and new.* values are exchanged.
** </ul>
**
** 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

View File

@ -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

View File

@ -1 +1 @@
b2face9aa95ade96a5666c70b6b31064c1ad0977
7695890230dc1e0c6db9b7aa509db2039c7f7239