Merge 'Prevent DROP TABLE when table is referenced by foreign keys' from Joao Faria

## Related issue
- closes #3885
## Description
Add a check to reject dropping a table when PRAGMA foreign_keys=ON and
the table is referenced by foreign keys

Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #3913
This commit is contained in:
Preston Thorpe
2025-11-06 15:10:05 -05:00
committed by GitHub
3 changed files with 33 additions and 2 deletions

View File

@@ -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,

View File

@@ -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<ProgramBuilder> {
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

View File

@@ -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}