@@ -554,7 +554,8 @@ final class MainContentCoordinator: ObservableObject {
554554
555555 func saveChanges(
556556 pendingTruncates: inout Set < String > ,
557- pendingDeletes: inout Set < String >
557+ pendingDeletes: inout Set < String > ,
558+ tableOperationOptions: inout [ String : TableOperationOptions ]
558559 ) {
559560 let hasEditedCells = changeManager. hasChanges
560561 let hasPendingTableOps = !pendingTruncates. isEmpty || !pendingDeletes. isEmpty
@@ -568,14 +569,13 @@ final class MainContentCoordinator: ObservableObject {
568569 }
569570
570571 if hasPendingTableOps {
571- for tableName in pendingTruncates {
572- let quotedName = connection. type. quoteIdentifier ( tableName)
573- allStatements. append ( " TRUNCATE TABLE \( quotedName) " )
574- }
575- for tableName in pendingDeletes {
576- let quotedName = connection. type. quoteIdentifier ( tableName)
577- allStatements. append ( " DROP TABLE \( quotedName) " )
578- }
572+ // Generate table operation SQL with FK/cascade options
573+ let tableOpStatements = generateTableOperationSQL (
574+ truncates: pendingTruncates,
575+ deletes: pendingDeletes,
576+ options: tableOperationOptions
577+ )
578+ allStatements. append ( contentsOf: tableOpStatements)
579579 }
580580
581581 guard !allStatements. isEmpty else {
@@ -586,20 +586,107 @@ final class MainContentCoordinator: ObservableObject {
586586 }
587587
588588 let sql = allStatements. joined ( separator: " ; \n " )
589- executeCommitSQL ( sql, clearTableOps: hasPendingTableOps, pendingTruncates: & pendingTruncates, pendingDeletes: & pendingDeletes)
589+ executeCommitSQL (
590+ sql,
591+ clearTableOps: hasPendingTableOps,
592+ pendingTruncates: & pendingTruncates,
593+ pendingDeletes: & pendingDeletes,
594+ tableOperationOptions: & tableOperationOptions
595+ )
596+ }
597+
598+ /// Generate SQL for table truncate/delete operations with FK/cascade options
599+ private func generateTableOperationSQL(
600+ truncates: Set < String > ,
601+ deletes: Set < String > ,
602+ options: [ String : TableOperationOptions ]
603+ ) -> [ String ] {
604+ var statements : [ String ] = [ ]
605+ let dbType = connection. type
606+
607+ // Check if any operation needs FK disabled
608+ let needsDisableFK = truncates. union ( deletes) . contains { tableName in
609+ options [ tableName] ? . ignoreForeignKeys == true
610+ }
611+
612+ if needsDisableFK {
613+ statements. append ( fkDisableStatement ( for: dbType) )
614+ }
615+
616+ for tableName in truncates {
617+ let quotedName = dbType. quoteIdentifier ( tableName)
618+ let opts = options [ tableName] ?? TableOperationOptions ( )
619+ statements. append ( truncateStatement ( tableName: quotedName, options: opts, dbType: dbType) )
620+ }
621+
622+ for tableName in deletes {
623+ let quotedName = dbType. quoteIdentifier ( tableName)
624+ let opts = options [ tableName] ?? TableOperationOptions ( )
625+ statements. append ( dropTableStatement ( tableName: quotedName, options: opts, dbType: dbType) )
626+ }
627+
628+ if needsDisableFK {
629+ statements. append ( fkEnableStatement ( for: dbType) )
630+ }
631+
632+ return statements
633+ }
634+
635+ private func fkDisableStatement( for dbType: DatabaseType ) -> String {
636+ return switch dbType {
637+ case . mysql, . mariadb: " SET FOREIGN_KEY_CHECKS=0 "
638+ case . postgresql: " SET session_replication_role = 'replica' "
639+ case . sqlite: " PRAGMA foreign_keys = OFF "
640+ }
641+ }
642+
643+ private func fkEnableStatement( for dbType: DatabaseType ) -> String {
644+ return switch dbType {
645+ case . mysql, . mariadb: " SET FOREIGN_KEY_CHECKS=1 "
646+ case . postgresql: " SET session_replication_role = 'origin' "
647+ case . sqlite: " PRAGMA foreign_keys = ON "
648+ }
649+ }
650+
651+ private func truncateStatement( tableName: String , options: TableOperationOptions , dbType: DatabaseType ) -> String {
652+ return switch dbType {
653+ case . mysql, . mariadb: " TRUNCATE TABLE \( tableName) "
654+ case . postgresql: options. cascade ? " TRUNCATE TABLE \( tableName) CASCADE " : " TRUNCATE TABLE \( tableName) "
655+ case . sqlite: " DELETE FROM \( tableName) "
656+ }
657+ }
658+
659+ private func dropTableStatement( tableName: String , options: TableOperationOptions , dbType: DatabaseType ) -> String {
660+ let cascade = options. cascade ? " CASCADE " : " "
661+ return switch dbType {
662+ case . mysql, . mariadb, . postgresql: " DROP TABLE \( tableName) \( cascade) "
663+ case . sqlite: " DROP TABLE \( tableName) "
664+ }
590665 }
591666
592667 private func executeCommitSQL(
593668 _ sql: String ,
594669 clearTableOps: Bool ,
595670 pendingTruncates: inout Set < String > ,
596- pendingDeletes: inout Set < String >
671+ pendingDeletes: inout Set < String > ,
672+ tableOperationOptions: inout [ String : TableOperationOptions ]
597673 ) {
598674 guard !sql. isEmpty else { return }
599675
600676 let deletedTables = Set ( pendingDeletes)
677+ let truncatedTables = Set ( pendingTruncates)
601678 let conn = connection
602679
680+ // Clear operations immediately (before async execution)
681+ if clearTableOps {
682+ pendingTruncates. removeAll ( )
683+ pendingDeletes. removeAll ( )
684+ // Clear options for processed tables
685+ for table in deletedTables. union ( truncatedTables) {
686+ tableOperationOptions. removeValue ( forKey: table)
687+ }
688+ }
689+
603690 Task {
604691 let overallStartTime = Date ( )
605692
0 commit comments