From 2ba643cd68a281a5c5faf51d4c19f21f104b8e09 Mon Sep 17 00:00:00 2001 From: "joao.faria" Date: Mon, 3 Nov 2025 23:41:52 -0300 Subject: [PATCH] fix: prevent DROP TABLE when table is referenced by foreign keys Add foreign key constraint check in translate_drop_table to reject dropping tables that are referenced by foreign keys when PRAGMA foreign_keys=ON --- core/translate/mod.rs | 2 +- core/translate/schema.rs | 12 +++++++++++- testing/drop_table.test | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/core/translate/mod.rs b/core/translate/mod.rs index 71fa52fb7..6a36baef6 100644 --- a/core/translate/mod.rs +++ b/core/translate/mod.rs @@ -231,7 +231,7 @@ pub fn translate_inner( ast::Stmt::DropTable { if_exists, tbl_name, - } => translate_drop_table(tbl_name, resolver, if_exists, program)?, + } => translate_drop_table(tbl_name, resolver, if_exists, program, connection)?, ast::Stmt::DropTrigger { .. } => bail_parse_error!("DROP TRIGGER not supported yet"), ast::Stmt::DropView { if_exists, diff --git a/core/translate/schema.rs b/core/translate/schema.rs index d44d8838c..6356f049a 100644 --- a/core/translate/schema.rs +++ b/core/translate/schema.rs @@ -13,7 +13,7 @@ use crate::util::PRIMARY_KEY_AUTOMATIC_INDEX_NAME_PREFIX; use crate::vdbe::builder::CursorType; use crate::vdbe::insn::{CmpInsFlags, Cookie, InsertFlags, Insn}; use crate::Connection; -use crate::{bail_parse_error, Result}; +use crate::{bail_constraint_error, bail_parse_error, Result}; use turso_ext::VTabKind; @@ -608,6 +608,7 @@ pub fn translate_drop_table( resolver: &Resolver, if_exists: bool, mut program: ProgramBuilder, + connection: &Connection, ) -> Result { if tbl_name .name @@ -657,6 +658,15 @@ pub fn translate_drop_table( tbl_name.name.as_str() ); } + + // Check if foreign keys are enabled and if this table is referenced by foreign keys + if connection.foreign_keys_enabled() + && resolver + .schema + .any_resolved_fks_referencing(table.get_name()) + { + bail_constraint_error!("FOREIGN KEY constraint failed"); + } let cdc_table = prepare_cdc_if_necessary(&mut program, resolver.schema, SQLITE_TABLEID)?; let null_reg = program.alloc_register(); // r1 diff --git a/testing/drop_table.test b/testing/drop_table.test index d7d02256c..7d3423a0c 100755 --- a/testing/drop_table.test +++ b/testing/drop_table.test @@ -62,3 +62,24 @@ do_execsql_test_on_specific_db {:memory:} drop-table-after-ops-1 { DROP TABLE t6; SELECT count(*) FROM sqlite_schema WHERE type='table' AND name='t6'; } {0} + +# Test that DROP TABLE fails when table is referenced by foreign keys +do_execsql_test_in_memory_any_error drop-table-fk-constraint-failed { + PRAGMA foreign_keys=ON; + CREATE TABLE parent(a INTEGER PRIMARY KEY); + CREATE TABLE child(a INTEGER PRIMARY KEY, b INTEGER, FOREIGN KEY(b) REFERENCES parent(a)); + INSERT INTO parent VALUES (1); + INSERT INTO child VALUES (123, 1); + DROP TABLE parent; +} + +# Test that DROP TABLE succeeds when foreign keys are disabled +do_execsql_test_on_specific_db {:memory:} drop-table-fk-disabled-ok { + PRAGMA foreign_keys=OFF; + CREATE TABLE parent(a INTEGER PRIMARY KEY); + CREATE TABLE child(a INTEGER PRIMARY KEY, b INTEGER, FOREIGN KEY(b) REFERENCES parent(a)); + INSERT INTO parent VALUES (1); + INSERT INTO child VALUES (123, 1); + DROP TABLE parent; + SELECT count(*) FROM sqlite_schema WHERE type='table' AND name='parent'; +} {0}