From 4dcabf37f16adb958a41dbfce845bbb25eda5b75 Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Sat, 18 Oct 2025 13:20:18 -0400 Subject: [PATCH] Fix to_sql method on BTreeTable to include foreign keys --- core/schema.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/core/schema.rs b/core/schema.rs index ce30d3950..a367e47a8 100644 --- a/core/schema.rs +++ b/core/schema.rs @@ -1358,12 +1358,13 @@ impl BTreeTable { /// `CREATE TABLE t (x)`, whereas sqlite stores it with the original extra whitespace. pub fn to_sql(&self) -> String { let mut sql = format!("CREATE TABLE {} (", self.name); + + // Add columns for (i, column) in self.columns.iter().enumerate() { if i > 0 { sql.push_str(", "); } - - // we need to wrap the column name in square brackets if it contains special characters + // Wrap column name in square brackets if it contains special characters let column_name = column.name.as_ref().expect("column name is None"); if identifier_contains_special_chars(column_name) { sql.push('['); @@ -1372,7 +1373,6 @@ impl BTreeTable { } else { sql.push_str(column_name); } - if !column.ty_str.is_empty() { sql.push(' '); sql.push_str(&column.ty_str); @@ -1380,20 +1380,75 @@ impl BTreeTable { if column.notnull { sql.push_str(" NOT NULL"); } - if column.unique { sql.push_str(" UNIQUE"); } - if column.primary_key { sql.push_str(" PRIMARY KEY"); } - if let Some(default) = &column.default { sql.push_str(" DEFAULT "); sql.push_str(&default.to_string()); } } + + let has_table_pk = !self.primary_key_columns.is_empty(); + // Add table-level PRIMARY KEY constraint if exists + if has_table_pk { + sql.push_str(", PRIMARY KEY ("); + for (i, col) in self.primary_key_columns.iter().enumerate() { + if i > 0 { + sql.push_str(", "); + } + sql.push_str(&col.0); + } + sql.push(')'); + } + + for fk in &self.foreign_keys { + sql.push_str(", FOREIGN KEY ("); + for (i, col) in fk.child_columns.iter().enumerate() { + if i > 0 { + sql.push_str(", "); + } + sql.push_str(col); + } + sql.push_str(") REFERENCES "); + sql.push_str(&fk.parent_table); + sql.push('('); + for (i, col) in fk.parent_columns.iter().enumerate() { + if i > 0 { + sql.push_str(", "); + } + sql.push_str(col); + } + sql.push(')'); + + // Add ON DELETE/UPDATE actions, NoAction is default so just make empty in that case + if fk.on_delete != RefAct::NoAction { + sql.push_str(" ON DELETE "); + sql.push_str(match fk.on_delete { + RefAct::SetNull => "SET NULL", + RefAct::SetDefault => "SET DEFAULT", + RefAct::Cascade => "CASCADE", + RefAct::Restrict => "RESTRICT", + _ => "", + }); + } + if fk.on_update != RefAct::NoAction { + sql.push_str(" ON UPDATE "); + sql.push_str(match fk.on_update { + RefAct::SetNull => "SET NULL", + RefAct::SetDefault => "SET DEFAULT", + RefAct::Cascade => "CASCADE", + RefAct::Restrict => "RESTRICT", + _ => "", + }); + } + if fk.deferred { + sql.push_str(" DEFERRABLE INITIALLY DEFERRED"); + } + } sql.push(')'); sql }