mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Fix some bugs in sqlite3changeset_apply().
FossilOrigin-Name: 7250318dda542c5bbf28852c1f1d0f3c52ae8f96
This commit is contained in:
138
ext/session/session2.test
Normal file
138
ext/session/session2.test
Normal file
@ -0,0 +1,138 @@
|
||||
# 2011 Mar 16
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The focus of this file is testing the session module.
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
set testprefix session2
|
||||
|
||||
proc test_reset {} {
|
||||
catch { db close }
|
||||
catch { db2 close }
|
||||
forcedelete test.db test.db2
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db2
|
||||
}
|
||||
|
||||
proc do_common_sql {sql} {
|
||||
execsql $sql db
|
||||
execsql $sql db2
|
||||
}
|
||||
proc xConflict args { return "OMIT" }
|
||||
|
||||
proc do_then_apply_sql {sql} {
|
||||
sqlite3session S db main
|
||||
db eval {SELECT name FROM sqlite_master WHERE type = 'table'} {
|
||||
S attach $name
|
||||
}
|
||||
|
||||
db eval $sql
|
||||
sqlite3changeset_apply db2 [S changeset] xConflict
|
||||
S delete
|
||||
}
|
||||
|
||||
proc do_iterator_test {tn tbl_list sql res} {
|
||||
sqlite3session S db main
|
||||
foreach t $tbl_list {S attach $t}
|
||||
execsql $sql
|
||||
|
||||
set r [list]
|
||||
foreach v $res { lappend r $v }
|
||||
|
||||
set x [list]
|
||||
sqlite3session_foreach c [S changeset] { lappend x $c }
|
||||
uplevel do_test $tn [list [list set {} $x]] [list $r]
|
||||
|
||||
S delete
|
||||
}
|
||||
|
||||
# Compare the contents of all tables in [db1] and [db2]. Throw an error if
|
||||
# they are not identical, or return an empty string if they are.
|
||||
#
|
||||
proc compare_db {db1 db2} {
|
||||
|
||||
set sql {SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name}
|
||||
set lot1 [$db1 eval $sql]
|
||||
set lot2 [$db2 eval $sql]
|
||||
|
||||
if {$lot1 != $lot2} { error "databases contain different tables" }
|
||||
|
||||
foreach tbl $lot1 {
|
||||
set col1 [list]
|
||||
set col2 [list]
|
||||
|
||||
$db1 eval "PRAGMA table_info = $tbl" { lappend col1 $name }
|
||||
$db2 eval "PRAGMA table_info = $tbl" { lappend col2 $name }
|
||||
if {$col1 != $col2} { error "table $tbl schema mismatch" }
|
||||
|
||||
set sql "SELECT * FROM $tbl ORDER BY [join $col1 ,]"
|
||||
set data1 [$db1 eval $sql]
|
||||
set data2 [$db2 eval $sql]
|
||||
if {$data1 != $data2} { error "table $tbl data mismatch" }
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
##########################################################################
|
||||
# End of proc definitions. Start of tests.
|
||||
##########################################################################
|
||||
|
||||
test_reset
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES('i', 'one');
|
||||
}
|
||||
do_iterator_test 1.1 t1 {
|
||||
DELETE FROM t1 WHERE a = 'i';
|
||||
INSERT INTO t1 VALUES('ii', 'two');
|
||||
} {
|
||||
{DELETE t1 {t i t one} {}}
|
||||
{INSERT t1 {} {t ii t two}}
|
||||
}
|
||||
|
||||
test_reset
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
|
||||
CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
|
||||
}
|
||||
|
||||
foreach {tn sql} {
|
||||
1 { INSERT INTO t1 VALUES(1, 2) }
|
||||
2 {
|
||||
INSERT INTO t2 VALUES(1, NULL);
|
||||
INSERT INTO t2 VALUES(2, NULL);
|
||||
INSERT INTO t2 VALUES(3, NULL);
|
||||
DELETE FROM t2 WHERE a = 2;
|
||||
INSERT INTO t2 VALUES(4, NULL);
|
||||
UPDATE t2 SET b=0 WHERE b=1;
|
||||
}
|
||||
3 { INSERT INTO t3 SELECT *, NULL FROM t2 }
|
||||
4 {
|
||||
INSERT INTO t3 SELECT a||a, b||b, NULL FROM t3;
|
||||
DELETE FROM t3 WHERE rowid%2;
|
||||
}
|
||||
5 { UPDATE t3 SET c = a||b }
|
||||
6 { UPDATE t1 SET a = 32 }
|
||||
} {
|
||||
do_then_apply_sql $sql
|
||||
do_test $tn { compare_db db db2 } {}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
@ -1777,6 +1777,7 @@ static int sessionDeleteRow(
|
||||
const char *zSep = "";
|
||||
int rc = SQLITE_OK;
|
||||
SessionBuffer buf = {0, 0, 0};
|
||||
int nPk = 0;
|
||||
|
||||
sessionAppendStr(&buf, "DELETE FROM ", &rc);
|
||||
sessionAppendIdent(&buf, zTab, &rc);
|
||||
@ -1784,29 +1785,32 @@ static int sessionDeleteRow(
|
||||
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( p->abPK[i] ){
|
||||
nPk++;
|
||||
sessionAppendStr(&buf, zSep, &rc);
|
||||
sessionAppendIdent(&buf, p->azCol[i], &rc);
|
||||
sessionAppendStr(&buf, " = ?", &rc);
|
||||
sessionAppendInteger(&buf, i+1, &rc);
|
||||
zSep = "AND ";
|
||||
zSep = " AND ";
|
||||
}
|
||||
}
|
||||
|
||||
sessionAppendStr(&buf, " AND (?", &rc);
|
||||
sessionAppendInteger(&buf, p->nCol+1, &rc);
|
||||
sessionAppendStr(&buf, " OR ", &rc);
|
||||
if( nPk<p->nCol ){
|
||||
sessionAppendStr(&buf, " AND (?", &rc);
|
||||
sessionAppendInteger(&buf, p->nCol+1, &rc);
|
||||
sessionAppendStr(&buf, " OR ", &rc);
|
||||
|
||||
zSep = "";
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( !p->abPK[i] ){
|
||||
sessionAppendStr(&buf, zSep, &rc);
|
||||
sessionAppendIdent(&buf, p->azCol[i], &rc);
|
||||
sessionAppendStr(&buf, " IS ?", &rc);
|
||||
sessionAppendInteger(&buf, i+1, &rc);
|
||||
zSep = "AND ";
|
||||
zSep = "";
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( !p->abPK[i] ){
|
||||
sessionAppendStr(&buf, zSep, &rc);
|
||||
sessionAppendIdent(&buf, p->azCol[i], &rc);
|
||||
sessionAppendStr(&buf, " IS ?", &rc);
|
||||
sessionAppendInteger(&buf, i+1, &rc);
|
||||
zSep = "AND ";
|
||||
}
|
||||
}
|
||||
sessionAppendStr(&buf, ")", &rc);
|
||||
}
|
||||
sessionAppendStr(&buf, ")", &rc);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
|
||||
@ -2171,7 +2175,9 @@ static int sessionApplyOneOp(
|
||||
rc = sqlite3_bind_value(p->pDelete, i+1, pVal);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ) rc = sqlite3_bind_int(p->pDelete, nCol+1, pbRetry==0);
|
||||
if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
|
||||
rc = sqlite3_bind_int(p->pDelete, nCol+1, pbRetry==0);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
sqlite3_step(p->pDelete);
|
||||
|
Reference in New Issue
Block a user