You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-20 23:30:05 +02:00
CLI v1.0.168
This commit is contained in:
@@ -0,0 +1,287 @@
|
||||
# 2013 July 04
|
||||
#
|
||||
# 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 tests that the sessions module handles foreign key constraint
|
||||
# violations when applying changesets as required.
|
||||
#
|
||||
|
||||
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 session9
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Basic tests.
|
||||
#
|
||||
proc populate_db {} {
|
||||
drop_all_tables
|
||||
execsql {
|
||||
PRAGMA foreign_keys = 1;
|
||||
CREATE TABLE p1(a PRIMARY KEY, b);
|
||||
CREATE TABLE c1(a PRIMARY KEY, b REFERENCES p1);
|
||||
CREATE TABLE c2(a PRIMARY KEY,
|
||||
b REFERENCES p1 DEFERRABLE INITIALLY DEFERRED
|
||||
);
|
||||
|
||||
INSERT INTO p1 VALUES(1, 'one');
|
||||
INSERT INTO p1 VALUES(2, 'two');
|
||||
INSERT INTO p1 VALUES(3, 'three');
|
||||
INSERT INTO p1 VALUES(4, 'four');
|
||||
}
|
||||
}
|
||||
|
||||
proc capture_changeset {sql} {
|
||||
sqlite3session S db main
|
||||
|
||||
foreach t [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
|
||||
S attach $t
|
||||
}
|
||||
execsql $sql
|
||||
set ret [S changeset]
|
||||
S delete
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
populate_db
|
||||
set cc [capture_changeset {
|
||||
INSERT INTO c1 VALUES('ii', 2);
|
||||
INSERT INTO c2 VALUES('iii', 3);
|
||||
}]
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
proc xConflict {args} {
|
||||
lappend ::xConflict {*}$args
|
||||
return $::conflictret
|
||||
}
|
||||
|
||||
foreach {tn delrow trans conflictargs conflictret} {
|
||||
1 2 0 {FOREIGN_KEY 1} OMIT
|
||||
2 3 0 {FOREIGN_KEY 1} OMIT
|
||||
3 2 1 {FOREIGN_KEY 1} OMIT
|
||||
4 3 1 {FOREIGN_KEY 1} OMIT
|
||||
5 2 0 {FOREIGN_KEY 1} ABORT
|
||||
6 3 0 {FOREIGN_KEY 1} ABORT
|
||||
7 2 1 {FOREIGN_KEY 1} ABORT
|
||||
8 3 1 {FOREIGN_KEY 1} ABORT
|
||||
} {
|
||||
|
||||
set A(OMIT) {0 {}}
|
||||
set A(ABORT) {1 SQLITE_CONSTRAINT}
|
||||
do_test 1.2.$tn.1 {
|
||||
populate_db
|
||||
execsql { DELETE FROM p1 WHERE a=($delrow+0) }
|
||||
if {$trans} { execsql BEGIN }
|
||||
|
||||
set ::xConflict [list]
|
||||
list [catch {sqlite3changeset_apply db $::cc xConflict} msg] $msg
|
||||
} $A($conflictret)
|
||||
|
||||
do_test 1.2.$tn.2 { set ::xConflict } $conflictargs
|
||||
|
||||
set A(OMIT) {1 1}
|
||||
set A(ABORT) {0 0}
|
||||
do_test 1.2.$tn.3 {
|
||||
execsql { SELECT count(*) FROM c1 UNION ALL SELECT count(*) FROM c2 }
|
||||
} $A($conflictret)
|
||||
|
||||
do_test 1.2.$tn.4 { expr ![sqlite3_get_autocommit db] } $trans
|
||||
do_test 1.2.$tn.5 {
|
||||
if { $trans } { execsql COMMIT }
|
||||
} {}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that closing a transaction clears the defer_foreign_keys flag.
|
||||
#
|
||||
foreach {tn open noclose close} {
|
||||
1 BEGIN {} COMMIT
|
||||
2 BEGIN {} ROLLBACK
|
||||
|
||||
3 {SAVEPOINT one} {} {RELEASE one}
|
||||
4 {SAVEPOINT one} {ROLLBACK TO one} {RELEASE one}
|
||||
} {
|
||||
execsql $open
|
||||
do_execsql_test 2.$tn.1 { PRAGMA defer_foreign_keys } {0}
|
||||
|
||||
do_execsql_test 2.$tn.2 {
|
||||
PRAGMA defer_foreign_keys = 1;
|
||||
PRAGMA defer_foreign_keys;
|
||||
} {1}
|
||||
|
||||
execsql $noclose
|
||||
do_execsql_test 2.$tn.3 { PRAGMA defer_foreign_keys } {1}
|
||||
|
||||
execsql $close
|
||||
do_execsql_test 2.$tn.4 { PRAGMA defer_foreign_keys } {0}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that a cyclic relationship can be inserted and deleted.
|
||||
#
|
||||
# This situation does not come up in practice, but testing it serves to
|
||||
# show that it does not matter which order parent and child keys
|
||||
# are processed in internally when applying a changeset.
|
||||
#
|
||||
drop_all_tables
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
CREATE TABLE t2(x PRIMARY KEY, y);
|
||||
}
|
||||
|
||||
# Create changesets as follows:
|
||||
#
|
||||
# $cc1 - Insert a row into t1.
|
||||
# $cc2 - Insert a row into t2.
|
||||
# $cc - Combination of $cc1 and $cc2.
|
||||
#
|
||||
# $ccdel1 - Delete the row from t1.
|
||||
# $ccdel2 - Delete the row from t2.
|
||||
# $ccdel - Combination of $cc1 and $cc2.
|
||||
#
|
||||
do_test 3.2 {
|
||||
set cc1 [capture_changeset {
|
||||
INSERT INTO t1 VALUES('one', 'value one');
|
||||
}]
|
||||
set ccdel1 [capture_changeset { DELETE FROM t1; }]
|
||||
set cc2 [capture_changeset {
|
||||
INSERT INTO t2 VALUES('value one', 'one');
|
||||
}]
|
||||
set ccdel2 [capture_changeset { DELETE FROM t2; }]
|
||||
set cc [capture_changeset {
|
||||
INSERT INTO t1 VALUES('one', 'value one');
|
||||
INSERT INTO t2 VALUES('value one', 'one');
|
||||
}]
|
||||
set ccdel [capture_changeset {
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
}]
|
||||
set {} {}
|
||||
} {}
|
||||
|
||||
# Now modify the database schema to create a cyclic foreign key dependency
|
||||
# between tables t1 and t2. This means that although changesets $cc and
|
||||
# $ccdel can be applied, none of the others may without violating the
|
||||
# foreign key constraints.
|
||||
#
|
||||
do_test 3.3 {
|
||||
|
||||
drop_all_tables
|
||||
execsql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t2);
|
||||
CREATE TABLE t2(x PRIMARY KEY, y REFERENCES t1);
|
||||
}
|
||||
|
||||
|
||||
proc conflict_handler {args} { return "ABORT" }
|
||||
sqlite3changeset_apply db $cc conflict_handler
|
||||
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {one {value one} {value one} one}
|
||||
|
||||
do_test 3.3.1 {
|
||||
list [catch {sqlite3changeset_apply db $::ccdel1 conflict_handler} msg] $msg
|
||||
} {1 SQLITE_CONSTRAINT}
|
||||
|
||||
do_test 3.3.2 {
|
||||
list [catch {sqlite3changeset_apply db $::ccdel2 conflict_handler} msg] $msg
|
||||
} {1 SQLITE_CONSTRAINT}
|
||||
|
||||
do_test 3.3.4.1 {
|
||||
list [catch {sqlite3changeset_apply db $::ccdel conflict_handler} msg] $msg
|
||||
} {0 {}}
|
||||
do_execsql_test 3.3.4.2 {
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t2;
|
||||
} {}
|
||||
|
||||
do_test 3.5.1 {
|
||||
list [catch {sqlite3changeset_apply db $::cc1 conflict_handler} msg] $msg
|
||||
} {1 SQLITE_CONSTRAINT}
|
||||
do_test 3.5.2 {
|
||||
list [catch {sqlite3changeset_apply db $::cc2 conflict_handler} msg] $msg
|
||||
} {1 SQLITE_CONSTRAINT}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that if a change that affects FK processing is not applied
|
||||
# due to a separate constraint, SQLite does not get confused and
|
||||
# increment FK counters anyway.
|
||||
#
|
||||
drop_all_tables
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE p1(x PRIMARY KEY, y);
|
||||
CREATE TABLE c1(a PRIMARY KEY, b REFERENCES p1);
|
||||
INSERT INTO p1 VALUES(1,1);
|
||||
}
|
||||
|
||||
do_execsql_test 4.2.1 {
|
||||
BEGIN;
|
||||
PRAGMA defer_foreign_keys = 1;
|
||||
INSERT INTO c1 VALUES('x', 'x');
|
||||
}
|
||||
do_catchsql_test 4.2.2 { COMMIT } {1 {FOREIGN KEY constraint failed}}
|
||||
do_catchsql_test 4.2.3 { ROLLBACK } {0 {}}
|
||||
|
||||
do_execsql_test 4.3.1 {
|
||||
BEGIN;
|
||||
PRAGMA defer_foreign_keys = 1;
|
||||
INSERT INTO c1 VALUES(1, 1);
|
||||
}
|
||||
do_catchsql_test 4.3.2 {
|
||||
INSERT INTO c1 VALUES(1, 'x')
|
||||
} {1 {UNIQUE constraint failed: c1.a}}
|
||||
|
||||
do_catchsql_test 4.3.3 { COMMIT } {0 {}}
|
||||
do_catchsql_test 4.3.4 { BEGIN ; COMMIT } {0 {}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that if a DELETE change cannot be applied due to an
|
||||
# SQLITE_CONSTRAINT error thrown by a trigger program, things do not
|
||||
# go awry.
|
||||
|
||||
drop_all_tables
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE x1(x PRIMARY KEY, y);
|
||||
CREATE TABLE x2(x PRIMARY KEY, y);
|
||||
INSERT INTO x2 VALUES(1, 1);
|
||||
INSERT INTO x1 VALUES(1, 1);
|
||||
}
|
||||
|
||||
set ::cc [changeset_from_sql { DELETE FROM x1; }]
|
||||
|
||||
do_execsql_test 5.2 {
|
||||
INSERT INTO x1 VALUES(1, 1);
|
||||
CREATE TRIGGER tr1 AFTER DELETE ON x1 BEGIN
|
||||
INSERT INTO x2 VALUES(old.x, old.y);
|
||||
END;
|
||||
} {}
|
||||
|
||||
proc conflict_handler {args} { return "ABORT" }
|
||||
do_test 5.3 {
|
||||
list [catch {sqlite3changeset_apply db $::cc conflict_handler} msg] $msg
|
||||
} {1 SQLITE_ABORT}
|
||||
|
||||
do_execsql_test 5.4 {
|
||||
SELECT * FROM X1;
|
||||
} {1 1}
|
||||
|
||||
finish_test
|
||||
Reference in New Issue
Block a user