blob: 2d72f6fcf0c84b4225b4535eb114b7b3411004ae [file] [log] [blame]
# 2001 September 15
#
# 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.
#
# This file implements tests for foreign keys.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix fkey8
ifcapable {!foreignkey} {
finish_test
return
}
do_execsql_test 1.0 { PRAGMA foreign_keys = 1; }
foreach {tn use_stmt sql schema} {
1 1 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1);
}
2.1 0 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE);
}
2.2 0 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE SET NULL);
}
2.3 1 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE SET DEFAULT);
}
3 1 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE);
CREATE TRIGGER ct1 AFTER DELETE ON c1 BEGIN
INSERT INTO p1 VALUES('x');
END;
}
4 1 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
CREATE TABLE cc1(d REFERENCES c1);
}
5.1 0 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
CREATE TABLE cc1(d REFERENCES c1 ON DELETE CASCADE);
}
5.2 0 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
CREATE TABLE cc1(d REFERENCES c1 ON DELETE SET NULL);
}
5.3 1 "DELETE FROM p1" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON DELETE CASCADE, c PRIMARY KEY);
CREATE TABLE cc1(d REFERENCES c1 ON DELETE SET DEFAULT);
}
6.1 1 "UPDATE p1 SET a = ?" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON UPDATE SET NULL, c);
}
6.2 0 "UPDATE OR IGNORE p1 SET a = ?" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON UPDATE SET NULL, c);
}
6.3 1 "UPDATE OR IGNORE p1 SET a = ?" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b REFERENCES p1 ON UPDATE CASCADE, c);
}
6.4 1 "UPDATE OR IGNORE p1 SET a = ?" {
CREATE TABLE p1(a PRIMARY KEY);
CREATE TABLE c1(b NOT NULL REFERENCES p1 ON UPDATE SET NULL, c);
}
} {
drop_all_tables
do_test 1.$tn {
execsql $schema
set stmt [sqlite3_prepare_v2 db $sql -1 dummy]
set ret [uses_stmt_journal $stmt]
sqlite3_finalize $stmt
set ret
} $use_stmt
}
#-------------------------------------------------------------------------
# The following tests check that foreign key constaint counters are
# correctly updated for any implicit DELETE operations that occur
# when a REPLACE command is executed against a WITHOUT ROWID table
# that has no triggers or auxiliary indexes.
#
reset_db
do_execsql_test 2.1.0 {
PRAGMA foreign_keys = on;
CREATE TABLE p1(a PRIMARY KEY, b) WITHOUT ROWID;
CREATE TABLE c1(x REFERENCES p1 DEFERRABLE INITIALLY DEFERRED);
INSERT INTO p1 VALUES(1, 'one');
INSERT INTO p1 VALUES(2, 'two');
INSERT INTO c1 VALUES(1);
INSERT INTO c1 VALUES(2);
}
do_catchsql_test 2.1.2 {
BEGIN;
DELETE FROM p1 WHERE a=1;
INSERT OR REPLACE INTO p1 VALUES(2, 'two');
COMMIT;
} {1 {FOREIGN KEY constraint failed}}
reset_db
do_execsql_test 2.2.0 {
PRAGMA foreign_keys = on;
CREATE TABLE p2(a PRIMARY KEY, b);
CREATE TABLE c2(
x PRIMARY KEY,
y REFERENCES p2 DEFERRABLE INITIALLY DEFERRED
) WITHOUT ROWID;
}
do_catchsql_test 2.2.1 {
BEGIN;
INSERT INTO c2 VALUES(13, 13);
INSERT OR REPLACE INTO c2 VALUES(13, 13);
DELETE FROM c2;
COMMIT;
} {0 {}}
reset_db
do_execsql_test 2.3.0 {
PRAGMA foreign_keys = on;
CREATE TABLE p3(a PRIMARY KEY, b) WITHOUT ROWID;
CREATE TABLE c3(x REFERENCES p3);
INSERT INTO p3 VALUES(1, 'one');
INSERT INTO p3 VALUES(2, 'two');
INSERT INTO c3 VALUES(1);
INSERT INTO c3 VALUES(2);
CREATE TRIGGER p3d AFTER DELETE ON p3 WHEN old.a=1 BEGIN
INSERT OR REPLACE INTO p3 VALUES(2, 'three');
END;
}
do_catchsql_test 2.3.1 {
DELETE FROM p3 WHERE a=1
} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 3.0 {
PRAGMA foreign_keys=ON;
CREATE TABLE t2(
a PRIMARY KEY, b, c, d, e,
FOREIGN KEY(b, c) REFERENCES t2(d, e)
) WITHOUT ROWID;
CREATE UNIQUE INDEX idx ON t2(d, e);
INSERT INTO t2 VALUES(1, 'one', 'one', 'one', 'one'); -- row is parent of self
INSERT INTO t2 VALUES(2, 'one', 'one', 'one', NULL); -- parent is row 1
}
do_catchsql_test 3.1 {
DELETE FROM t2 WHERE a=1;
} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 4.0 {
CREATE TABLE t1 (
c1 PRIMARY KEY,
c2 NUMERIC,
FOREIGN KEY(c1) REFERENCES t1(c2)
) WITHOUT ROWID ;
CREATE INDEX t1c1 ON t1(c1);
CREATE UNIQUE INDEX t1c1unique ON t1(c2);
}
do_catchsql_test 4.1 {
INSERT OR REPLACE INTO t1 VALUES(10000, 20000);
} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 4.2 {
INSERT OR REPLACE INTO t1 VALUES(20000, 20000);
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 5.0 {
PRAGMA foreign_keys = true;
CREATE TABLE parent(
p TEXT PRIMARY KEY
);
CREATE TABLE child(
c INTEGER UNIQUE,
FOREIGN KEY(c) REFERENCES parent(p) DEFERRABLE INITIALLY DEFERRED
);
BEGIN;
INSERT INTO child VALUES(123);
INSERT INTO parent VALUES('123');
COMMIT;
}
do_execsql_test 5.1 {
PRAGMA integrity_check;
} {ok}
do_execsql_test 5.2 {
INSERT INTO parent VALUES(1200);
BEGIN;
INSERT INTO child VALUES(456);
UPDATE parent SET p = '456' WHERE p=1200;
COMMIT;
}
do_execsql_test 5.3 {
PRAGMA integrity_check;
} {ok}
#-------------------------------------------------------------------------
reset_db
forcedelete test.db2
do_execsql_test 6.1 {
PRAGMA foreign_keys = on;
CREATE TABLE c1(b);
INSERT INTO c1 VALUES(123);
}
do_execsql_test 6.2 {
ATTACH 'test.db2' AS aux;
CREATE TABLE aux.p1(a INTEGER PRIMARY KEY);
CREATE TABLE aux.c1(b REFERENCES p1(a) ON DELETE RESTRICT);
INSERT INTO aux.p1 VALUES(123);
}
do_execsql_test 6.3 {
DELETE FROM aux.p1 WHERE a=123;
}
finish_test