using DbTools.Model; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DbTools { public class Delta { public string Db1Sql { get; set; } public string Db2Sql { get; set; } public string BuildDelta(IDbConnection currentDb, IDbConnection newDb, bool removeUnusedTables = false, bool removeUnusedColumns = false, bool removeUnusedTriggers = true, bool removeUnusedIndexes = true) { Database db1 = new Database(currentDb, true); Database db2 = new Database(newDb, true); Db1Sql = db1.ToSql(); Db2Sql = db2.ToSql(); return BuildDelta(db1, db2, removeUnusedTables, removeUnusedColumns, removeUnusedTriggers, removeUnusedIndexes); } public string BuildDelta(string currentDbSql, string newDbSql, bool removeUnusedTables = false, bool removeUnusedColumns = false, bool removeUnusedTriggers = true, bool removeUnusedIndexes = true) { Database db1 = new Database(currentDbSql); Database db2 = new Database(newDbSql); return BuildDelta(db1, db2, removeUnusedTables, removeUnusedColumns, removeUnusedTriggers, removeUnusedIndexes); } public string BuildDelta(IDbConnection currentDb, string newDbSql, bool removeUnusedTables = false, bool removeUnusedColumns = false, bool removeUnusedTriggers = true, bool removeUnusedIndexes = true) { Database db1 = new Database(currentDb); Database db2 = new Database(newDbSql); return BuildDelta(db1, db2, removeUnusedTables, removeUnusedColumns, removeUnusedTriggers, removeUnusedIndexes); } public string BuildDelta(string currentDbSql, IDbConnection newDb, bool removeUnusedTables = false, bool removeUnusedColumns = false, bool removeUnusedTriggers = true, bool removeUnusedIndexes = true) { Database db1 = new Database(currentDbSql); Database db2 = new Database(newDb); return BuildDelta(db1, db2, removeUnusedTables, removeUnusedColumns, removeUnusedTriggers, removeUnusedIndexes); } public async Task ApplyDeltaAsync(IDbConnection cn, string sql) { return await Task.Run(() => { return ApplyDelta(cn, sql); }); } public bool ApplyDelta(IDbConnection cn, string sql) { bool success = false; using (var cmd = cn.CreateCommand()) { cmd.CommandText = sql; success = cmd.ExecuteNonQuery() > 0; } return success; } private string BuildDelta(Database db1, Database db2, bool removeUnusedTables, bool removeUnusedColumns, bool removeUnusedTriggers, bool removeUnusedIndexes) { StringBuilder sb = new StringBuilder(); // Remove tables that are not in db2 if requested if (removeUnusedTables) { var unusedTables = db1.Tables.GetTables().Where(t1 => !db2.ContainsTable(t1.TableName)).ToArray(); if (unusedTables.Length > 0) { sb.AppendLine("-- DROP UNUSED TABLES --"); foreach (var table in unusedTables) { sb.AppendLine(table.GenerateDropTable()); } sb.AppendLine(); } } foreach (var table2 in db2.Tables.GetTables()) { var table1 = db1.Tables[table2.TableName]; if (table1 == null) { sb.AppendLine(table2.FullSql()); } else { // Remove unused triggers if requested if (removeUnusedTriggers) { var unusedTriggers = table1.Triggers.Keys.Where(t1 => !table2.Triggers.ContainsKey(t1)).ToArray(); if (unusedTriggers.Length > 0) { sb.AppendLine("-- DROP UNUSED TRIGGERS " + table2.TableName + " --"); foreach (var trigger in unusedTriggers) { sb.AppendLine($"DROP TRIGGER IF EXISTS {trigger};"); } sb.AppendLine(); } } // Remove unused indexes if requested if (removeUnusedIndexes) { var unusedIndexes = table1.Indexes.Keys.Where(i1 => !table2.Indexes.ContainsKey(i1)).ToArray(); if (unusedIndexes.Length > 0) { sb.AppendLine("-- DROP UNUSED INDEXES IN TABLE " + table2.TableName + " --"); foreach (var index in unusedIndexes) { sb.AppendLine($"DROP INDEX IF EXISTS {index};"); } sb.AppendLine(); } } // Add missing columns var commonColumns = table1.GetCommonColumns(table2); var onlyInTable2 = table2.Columns.GetColumnNames().Where(c => !commonColumns.Contains(c)).ToArray(); var onlyInTable1 = table1.Columns.GetColumnNames().Where(c => !commonColumns.Contains(c)).ToArray(); if (removeUnusedColumns && onlyInTable1.Length > 0) { // We have columns in table1 that are not in table2 and we want to remove them, // so we need to recreate the table sb.AppendLine("-- UNUSED COLUMNS EXIST IN TABLE " + table2.TableName + " --"); string sql = table2.GenerateTableMigration(table1); sb.AppendLine(sql); } if (onlyInTable2.Length > 0) { // We have columns in table2 that are not in table1, so we can just add them sb.AppendLine($"-- ALTER TABLE {table2.TableName} --"); foreach (var colName in onlyInTable2) { var col = table2.Columns[colName]; sb.AppendLine($"ALTER TABLE {table2.TableName} ADD COLUMN {col};"); } sb.AppendLine($"-- END ALTER TABLE {table2.TableName} --"); sb.AppendLine(); } } } return sb.ToString(); } } }