1c2eef3b3Sdrh# 2001 September 15 2c2eef3b3Sdrh# 3c2eef3b3Sdrh# The author disclaims copyright to this source code. In place of 4c2eef3b3Sdrh# a legal notice, here is a blessing: 5c2eef3b3Sdrh# 6c2eef3b3Sdrh# May you do good and not evil. 7c2eef3b3Sdrh# May you find forgiveness for yourself and forgive others. 8c2eef3b3Sdrh# May you share freely, never taking more than you give. 9c2eef3b3Sdrh# 10c2eef3b3Sdrh#*********************************************************************** 11c2eef3b3Sdrh# This file implements regression tests for SQLite library. 12c2eef3b3Sdrh# 13c2eef3b3Sdrh# This file implements tests for foreign keys. 14c2eef3b3Sdrh# 15c2eef3b3Sdrh 16c2eef3b3Sdrhset testdir [file dirname $argv0] 17c2eef3b3Sdrhsource $testdir/tester.tcl 1868a494c0Sdanset testprefix fkey1 19c2eef3b3Sdrh 206bf89570Sdrhifcapable {!foreignkey} { 216bf89570Sdrh finish_test 226bf89570Sdrh return 236bf89570Sdrh} 246bf89570Sdrh 25c2eef3b3Sdrh# Create a table and some data to work with. 26c2eef3b3Sdrh# 27c2eef3b3Sdrhdo_test fkey1-1.0 { 28c2eef3b3Sdrh execsql { 29c2eef3b3Sdrh CREATE TABLE t1( 30c2eef3b3Sdrh a INTEGER PRIMARY KEY, 31c2eef3b3Sdrh b INTEGER 32c2eef3b3Sdrh REFERENCES t1 ON DELETE CASCADE 33c2eef3b3Sdrh REFERENCES t2, 34c2eef3b3Sdrh c TEXT, 35c2eef3b3Sdrh FOREIGN KEY (b,c) REFERENCES t2(x,y) ON UPDATE CASCADE 36c2eef3b3Sdrh ); 37c2eef3b3Sdrh } 38c2eef3b3Sdrh} {} 39c2eef3b3Sdrhdo_test fkey1-1.1 { 40c2eef3b3Sdrh execsql { 41c2eef3b3Sdrh CREATE TABLE t2( 42c2eef3b3Sdrh x INTEGER PRIMARY KEY, 43c2eef3b3Sdrh y TEXT 44c2eef3b3Sdrh ); 45c2eef3b3Sdrh } 46c2eef3b3Sdrh} {} 472dcc9aa2Sdrhdo_test fkey1-1.2 { 482dcc9aa2Sdrh execsql { 492dcc9aa2Sdrh CREATE TABLE t3( 501da40a38Sdan a INTEGER REFERENCES t2, 512dcc9aa2Sdrh b INTEGER REFERENCES t1, 522dcc9aa2Sdrh FOREIGN KEY (a,b) REFERENCES t2(x,y) 532dcc9aa2Sdrh ); 542dcc9aa2Sdrh } 552dcc9aa2Sdrh} {} 5685c23c61Sdrh 5785c23c61Sdrhdo_test fkey1-2.1 { 5885c23c61Sdrh execsql { 5985c23c61Sdrh CREATE TABLE t4(a integer primary key); 6085c23c61Sdrh CREATE TABLE t5(x references t4); 6185c23c61Sdrh CREATE TABLE t6(x references t4); 6285c23c61Sdrh CREATE TABLE t7(x references t4); 6385c23c61Sdrh CREATE TABLE t8(x references t4); 6485c23c61Sdrh CREATE TABLE t9(x references t4); 6585c23c61Sdrh CREATE TABLE t10(x references t4); 6685c23c61Sdrh DROP TABLE t7; 6785c23c61Sdrh DROP TABLE t9; 6885c23c61Sdrh DROP TABLE t5; 6985c23c61Sdrh DROP TABLE t8; 7085c23c61Sdrh DROP TABLE t6; 7185c23c61Sdrh DROP TABLE t10; 7285c23c61Sdrh } 7385c23c61Sdrh} {} 7485c23c61Sdrh 7550af3e1dSdanielk1977do_test fkey1-3.1 { 7650af3e1dSdanielk1977 execsql { 7750af3e1dSdanielk1977 CREATE TABLE t5(a PRIMARY KEY, b, c); 7850af3e1dSdanielk1977 CREATE TABLE t6( 7950af3e1dSdanielk1977 d REFERENCES t5, 8050af3e1dSdanielk1977 e REFERENCES t5(c) 8150af3e1dSdanielk1977 ); 8250af3e1dSdanielk1977 PRAGMA foreign_key_list(t6); 8350af3e1dSdanielk1977 } 8450af3e1dSdanielk1977} [concat \ 851da40a38Sdan {0 0 t5 e c {NO ACTION} {NO ACTION} NONE} \ 861da40a38Sdan {1 0 t5 d {} {NO ACTION} {NO ACTION} NONE} \ 8750af3e1dSdanielk1977] 8850af3e1dSdanielk1977do_test fkey1-3.2 { 8950af3e1dSdanielk1977 execsql { 9050af3e1dSdanielk1977 CREATE TABLE t7(d, e, f, 9150af3e1dSdanielk1977 FOREIGN KEY (d, e) REFERENCES t5(a, b) 9250af3e1dSdanielk1977 ); 9350af3e1dSdanielk1977 PRAGMA foreign_key_list(t7); 9450af3e1dSdanielk1977 } 9550af3e1dSdanielk1977} [concat \ 961da40a38Sdan {0 0 t5 d a {NO ACTION} {NO ACTION} NONE} \ 971da40a38Sdan {0 1 t5 e b {NO ACTION} {NO ACTION} NONE} \ 9850af3e1dSdanielk1977] 9950af3e1dSdanielk1977do_test fkey1-3.3 { 10050af3e1dSdanielk1977 execsql { 10150af3e1dSdanielk1977 CREATE TABLE t8(d, e, f, 10250af3e1dSdanielk1977 FOREIGN KEY (d, e) REFERENCES t5 ON DELETE CASCADE ON UPDATE SET NULL 10350af3e1dSdanielk1977 ); 10450af3e1dSdanielk1977 PRAGMA foreign_key_list(t8); 10550af3e1dSdanielk1977 } 10650af3e1dSdanielk1977} [concat \ 10750af3e1dSdanielk1977 {0 0 t5 d {} {SET NULL} CASCADE NONE} \ 10850af3e1dSdanielk1977 {0 1 t5 e {} {SET NULL} CASCADE NONE} \ 10950af3e1dSdanielk1977] 110d2cb50b7Sdrhdo_test fkey1-3.4 { 111d2cb50b7Sdrh execsql { 112d2cb50b7Sdrh CREATE TABLE t9(d, e, f, 113d2cb50b7Sdrh FOREIGN KEY (d, e) REFERENCES t5 ON DELETE CASCADE ON UPDATE SET DEFAULT 114d2cb50b7Sdrh ); 115d2cb50b7Sdrh PRAGMA foreign_key_list(t9); 116d2cb50b7Sdrh } 117d2cb50b7Sdrh} [concat \ 118d2cb50b7Sdrh {0 0 t5 d {} {SET DEFAULT} CASCADE NONE} \ 119d2cb50b7Sdrh {0 1 t5 e {} {SET DEFAULT} CASCADE NONE} \ 120d2cb50b7Sdrh] 121648e2643Sdrhdo_test fkey1-3.5 { 122648e2643Sdrh sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0 123648e2643Sdrh} {0 0 0} 124c2eef3b3Sdrh 125b6b676eaSdrh# Stress the dequoting logic. The first test is not so bad. 126b6b676eaSdrhdo_execsql_test fkey1-4.0 { 127b6b676eaSdrh PRAGMA foreign_keys=ON; 128b6b676eaSdrh CREATE TABLE "xx1"("xx2" TEXT PRIMARY KEY, "xx3" TEXT); 129b6b676eaSdrh INSERT INTO "xx1"("xx2","xx3") VALUES('abc','def'); 130b6b676eaSdrh CREATE TABLE "xx4"("xx5" TEXT REFERENCES "xx1" ON DELETE CASCADE); 131b6b676eaSdrh INSERT INTO "xx4"("xx5") VALUES('abc'); 132b6b676eaSdrh INSERT INTO "xx1"("xx2","xx3") VALUES('uvw','xyz'); 133b6b676eaSdrh SELECT 1, "xx5" FROM "xx4"; 134b6b676eaSdrh DELETE FROM "xx1"; 135b6b676eaSdrh SELECT 2, "xx5" FROM "xx4"; 136b6b676eaSdrh} {1 abc} 137b6b676eaSdrh 138b6b676eaSdrh# This case is identical to the previous except the "xx" in each name 139b6b676eaSdrh# is changed to a single escaped double-quote character. 140b6b676eaSdrhdo_execsql_test fkey1-4.1 { 141b6b676eaSdrh PRAGMA foreign_keys=ON; 142b6b676eaSdrh CREATE TABLE """1"("""2" TEXT PRIMARY KEY, """3" TEXT); 143b6b676eaSdrh INSERT INTO """1"("""2","""3") VALUES('abc','def'); 144b6b676eaSdrh CREATE TABLE """4"("""5" TEXT REFERENCES """1" ON DELETE CASCADE); 145b6b676eaSdrh INSERT INTO """4"("""5") VALUES('abc'); 146b6b676eaSdrh INSERT INTO """1"("""2","""3") VALUES('uvw','xyz'); 147b6b676eaSdrh SELECT 1, """5" FROM """4"; 148b6b676eaSdrh DELETE FROM """1"; 149b6b676eaSdrh SELECT 2, """5" FROM """4"; 150b6b676eaSdrh} {1 abc} 151b6b676eaSdrhdo_execsql_test fkey1-4.2 { 152b6b676eaSdrh PRAGMA table_info="""1"; 153b6b676eaSdrh} {0 {"2} TEXT 0 {} 1 1 {"3} TEXT 0 {} 0} 154b6b676eaSdrh 155e755e10aSdan#------------------------------------------------------------------------- 156e755e10aSdan# 157e755e10aSdando_execsql_test fkey1-5.1 { 158e755e10aSdan CREATE TABLE t11( 159e755e10aSdan x INTEGER PRIMARY KEY, 160e755e10aSdan parent REFERENCES t11 ON DELETE CASCADE 161e755e10aSdan ); 162e755e10aSdan INSERT INTO t11 VALUES (1, NULL), (2, 1), (3, 2); 163e755e10aSdan} {} 164e755e10aSdan 165e755e10aSdan# The REPLACE part of this statement deletes the row (2, 1). Then the 166e755e10aSdan# DELETE CASCADE caused by deleting that row removes the (3, 2) row. Which 167e755e10aSdan# would have been the parent of the new row being inserted. Causing an 168e755e10aSdan# FK violation. 169e755e10aSdan# 170e755e10aSdando_catchsql_test fkey1-5.2 { 171e755e10aSdan INSERT OR REPLACE INTO t11 VALUES (2, 3); 172e755e10aSdan} {1 {FOREIGN KEY constraint failed}} 173e755e10aSdan 174e307e11dSdrh# Make sure sqlite3_trace() output works with triggers used to implement 175e307e11dSdrh# FK constraints 176e307e11dSdrh# 1774064a4ccSdanifcapable trace { 178e307e11dSdrh proc sqltrace {txt} { 179e307e11dSdrh global traceoutput 180e307e11dSdrh lappend traceoutput $txt 181e307e11dSdrh } 182e307e11dSdrh do_test fkey1-5.2.1 { 183e307e11dSdrh unset -nocomplain traceoutput 184e307e11dSdrh db trace sqltrace 185e307e11dSdrh catch {db eval {INSERT OR REPLACE INTO t11 VALUES(2,3);}} 186e307e11dSdrh set traceoutput 187e307e11dSdrh } {{INSERT OR REPLACE INTO t11 VALUES(2,3);} {INSERT OR REPLACE INTO t11 VALUES(2,3);} {INSERT OR REPLACE INTO t11 VALUES(2,3);}} 1884064a4ccSdan} 189e307e11dSdrh 190e755e10aSdan# A similar test to the above. 191e755e10aSdando_execsql_test fkey1-5.3 { 192e755e10aSdan CREATE TABLE Foo ( 193e755e10aSdan Id INTEGER PRIMARY KEY, 194e755e10aSdan ParentId INTEGER REFERENCES Foo(Id) ON DELETE CASCADE, C1 195e755e10aSdan ); 196e755e10aSdan INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (1, null, 'A'); 197e755e10aSdan INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 1, 'A-2-1'); 198e755e10aSdan INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (3, 2, 'A-3-2'); 199e755e10aSdan INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (4, 3, 'A-4-3'); 200e755e10aSdan} 201e755e10aSdando_catchsql_test fkey1-5.4 { 202e755e10aSdan INSERT OR REPLACE INTO Foo(Id, ParentId, C1) VALUES (2, 3, 'A-2-3'); 203e755e10aSdan} {1 {FOREIGN KEY constraint failed}} 204e755e10aSdan 20568a494c0Sdan#------------------------------------------------------------------------- 20668a494c0Sdan# Check that foreign key processing is not fooled by partial indexes 20768a494c0Sdan# on the parent table. 20868a494c0Sdan# 20968a494c0Sdando_execsql_test 6.0 { 21068a494c0Sdan CREATE TABLE p1(x, y); 21168a494c0Sdan CREATE UNIQUE INDEX p1x ON p1(x) WHERE y<2; 21268a494c0Sdan INSERT INTO p1 VALUES(1, 1); 21368a494c0Sdan CREATE TABLE c1(a REFERENCES p1(x)); 21468a494c0Sdan} 21568a494c0Sdan 21668a494c0Sdando_catchsql_test 6.1 { 21768a494c0Sdan INSERT INTO c1 VALUES(1); 21868a494c0Sdan} {1 {foreign key mismatch - "c1" referencing "p1"}} 21968a494c0Sdan 22068a494c0Sdando_execsql_test 6.2 { 22168a494c0Sdan CREATE UNIQUE INDEX p1x2 ON p1(x); 22268a494c0Sdan INSERT INTO c1 VALUES(1); 22368a494c0Sdan} {} 22468a494c0Sdan 2255c46a7e5Sdrh# 2021-07-03 https://sqlite.org/forum/forumpost/a6b0c05277 2264f16ff9dSdrh# 2021-07-07 https://sqlite.org/forum/forumpost/79c9e4797d 2275c46a7e5Sdrh# Failure to allocate enough registers in the VDBE for a 2285c46a7e5Sdrh# PRAGMA foreign_key_check when the foreign key has more 2295c46a7e5Sdrh# columns than the table. 2305c46a7e5Sdrh# 2315c46a7e5Sdrhreset_db 2325c46a7e5Sdrhdo_execsql_test 7.1 { 2335c46a7e5Sdrh PRAGMA foreign_keys=OFF; 2345c46a7e5Sdrh CREATE TABLE t1(a,b,c,FOREIGN KEY(a,a,a,a,a,a,a,a,a,a,a,a,a,a) REFERENCES t0); 2355c46a7e5Sdrh INSERT INTO t1 VALUES(1,2,3); 2365c46a7e5Sdrh PRAGMA foreign_key_check; 2375c46a7e5Sdrh} {t1 1 t0 0} 2384f16ff9dSdrhdo_execsql_test 7.2 { 2394f16ff9dSdrh DROP TABLE t1; 2404f16ff9dSdrh CREATE TABLE t1(a,b,c AS(1),d, FOREIGN KEY(c,d,b,a,b,d,b,c) REFERENCES t0); 2414f16ff9dSdrh PRAGMA foreign_key_check; 2424f16ff9dSdrh} {} 2435c46a7e5Sdrh 244*39075608Sdrh# 2021-12-31 forum https://sqlite.org/forum/forumpost/24bd1fef7e9323ef 245*39075608Sdrh# Memory leak caused by sqlite3NestedParse() running on a corrupt system 246*39075608Sdrh# table. Discovered by Jingzhou Fu. 247*39075608Sdrh# 248*39075608Sdrhreset_db 249*39075608Sdrhdo_execsql_test 8.1 { 250*39075608Sdrh PRAGMA writable_schema=ON; 251*39075608Sdrh PRAGMA foreign_keys = ON; 252*39075608Sdrh CREATE TABLE sqlite_stat1 (tbl INTEGER PRIMARY KEY DESC, idx UNIQUE DEFAULT NULL) WITHOUT ROWID; 253*39075608Sdrh PRAGMA writable_schema=OFF; 254*39075608Sdrh CREATE TABLE sqlsim4(stat PRIMARY KEY);; 255*39075608Sdrh CREATE TABLE t1(sqlsim7 REFERENCES sqlite_stat1 ON DELETE CASCADE); 256*39075608Sdrh DROP table "sqlsim4"; 257*39075608Sdrh} {} 258*39075608Sdrh# 2022-01-01 dbsqlfuzz 1c57440219f6f0aedf5e8f72a8ddd75f15aea381 259*39075608Sdrh# Follow-up case to the above. Assertion is not true if the schema 260*39075608Sdrh# is corrupt. 261*39075608Sdrhreset_db 262*39075608Sdrhdatabase_may_be_corrupt 263*39075608Sdrhdo_execsql_test 8.2 { 264*39075608Sdrh CREATE TABLE t1(a REFERENCES sqlite_stat1 ON DELETE CASCADE); 265*39075608Sdrh CREATE TABLE t2(a TEXT PRIMARY KEY); 266*39075608Sdrh PRAGMA writable_schema=ON; 267*39075608Sdrh CREATE TABLE sqlite_stat1(tbl INTEGER PRIMARY KEY DESC, idx UNIQUE DEFAULT NULL) WITHOUT ROWID; 268*39075608Sdrh UPDATE sqlite_schema SET name='sqlite_autoindex_sqlite_stat1_1' WHERE name='sqlite_autoindex_sqlite_stat1_2'; 269*39075608Sdrh PRAGMA writable_schema=RESET; 270*39075608Sdrh} {} 271*39075608Sdrhdo_catchsql_test 8.3 { 272*39075608Sdrh REINDEX; 273*39075608Sdrh} {1 {database disk image is malformed}} 27468a494c0Sdan 275c2eef3b3Sdrhfinish_test 276