diff --git a/DbTools.nuspec b/DbTools.nuspec index 2f7c670..0e6492c 100644 --- a/DbTools.nuspec +++ b/DbTools.nuspec @@ -2,15 +2,14 @@ DbTools - 1.0.1.0 + 1.0.2.0 DbTools Russ Kollmansberger false MIT - http://project_url_here_or_delete_this_line/ A library to sync two database structures and apply migrations. - Added new functions to SqlBuilder to generate scripts on database differences. + Removed async methods, except when capturing data. $copyright$ Tag1 Tag2 diff --git a/DbToolsTester/Forms/MainForm.cs b/DbToolsTester/Forms/MainForm.cs index 45de827..2a90cd6 100644 --- a/DbToolsTester/Forms/MainForm.cs +++ b/DbToolsTester/Forms/MainForm.cs @@ -57,13 +57,22 @@ namespace DbToolsTester.Forms { private async void ToolbarItemClicked(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { if (e.Item.Equals(bbiDelta)) { + deltaSql.Clear(); + SQLiteConnection db1 = new SQLiteConnection("Data Source=" + Database1File + ";Version=3;"); SQLiteConnection db2 = new SQLiteConnection("Data Source=" + Database2File + ";Version=3;"); - Delta delta = new Delta(); - deltaSql.Text = delta.BuildDelta(db1, db2, btsRemoveUnusedTables.Checked, btsRemoveUnusedColumns.Checked); - db1Sql.Text = delta.Db1Sql; - db2Sql.Text = delta.Db2Sql; + SqlBuilder builder = new SqlBuilder(); + string[] missingTables = builder.GetMissingTables(db1, db2); + foreach (string table in missingTables) { + string sql = await builder.GetTableCreateSqlAsync(db2, table); + deltaSql.AppendText(sql + "\r\n"); + } + + //Delta delta = new Delta(); + //deltaSql.Text = delta.BuildDelta(db1, db2, btsRemoveUnusedTables.Checked, btsRemoveUnusedColumns.Checked); + //db1Sql.Text = delta.Db1Sql; + //db2Sql.Text = delta.Db2Sql; } else if (e.Item.Equals(bbiApplyBoth)) { if (string.IsNullOrEmpty(deltaSql.Text)) { MessageBox.Show("No delta SQL to apply.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); diff --git a/SqlBuilder.cs b/SqlBuilder.cs index c919054..37793dc 100644 --- a/SqlBuilder.cs +++ b/SqlBuilder.cs @@ -155,14 +155,14 @@ namespace DbTools { sb.AppendLine("\t" + columns.Trim()); sb.AppendLine(sql.Substring(startIndex + length) + ";"); - string indexes = await GetIndexCreateSqlAsync(cn, tableName); + string indexes = GetIndexCreateSql(cn, tableName); if (!string.IsNullOrEmpty(indexes)) { sb.AppendLine(); sb.AppendLine("-- INDEXES --"); sb.AppendLine(indexes); } - string triggers = await GetTriggerCreateSqlAsync(cn, tableName); + string triggers = GetTriggerCreateSql(cn, tableName); if (!string.IsNullOrEmpty(triggers)) { sb.AppendLine(); sb.AppendLine("-- TRIGGERS --"); @@ -183,6 +183,60 @@ namespace DbTools { } } + public string GetTableCreateSql(IDbConnection cn, string tableName) { + if (cn == null) { + throw new ArgumentNullException("cn"); + } + if (string.IsNullOrEmpty(tableName)) { + throw new ArgumentNullException("tableName"); + } + + if (cn.State != ConnectionState.Open) { + cn.Open(); + } + using (IDbCommand cmd = cn.CreateCommand()) { + cmd.CommandText = $"select sql from sqlite_master where tbl_name='{tableName}' and type='table'"; + using (IDataReader reader = cmd.ExecuteReader()) { + StringBuilder sb = new StringBuilder(); + reader.Read(); + string sql = reader.GetString(0); + + Match m = Regex.Match(sql, "CREATE TABLE \\S+ \\((.*)\\)", RegexOptions.Singleline); + if (!m.Success) { + Trace.TraceWarning("Unable to match regex on table " + tableName); + return string.Empty; + } + + sb.AppendLine("-- TABLE " + tableName + " --"); + int startIndex = m.Groups[1].Index; + int length = m.Groups[1].Length; + string columns = Regex.Replace(m.Groups[1].Value, "\\s{2,}", " "); + columns = Regex.Replace(columns.Replace(", ", ",").Replace(",\n", ","), ",(?!\\d+\\))", ",\r\n\t"); + + sb.AppendLine(sql.Substring(0, startIndex)); + sb.AppendLine("\t" + columns.Trim()); + sb.AppendLine(sql.Substring(startIndex + length) + ";"); + + string indexes = GetIndexCreateSql(cn, tableName); + if (!string.IsNullOrEmpty(indexes)) { + sb.AppendLine(); + sb.AppendLine("-- INDEXES --"); + sb.AppendLine(indexes); + } + + string triggers = GetTriggerCreateSql(cn, tableName); + if (!string.IsNullOrEmpty(triggers)) { + sb.AppendLine(); + sb.AppendLine("-- TRIGGERS --"); + sb.AppendLine(triggers); + } + + sb.AppendLine("-- END TABLE " + tableName + " --"); + return sb.ToString(); + } + } + } + public string GetTableDropSql(IDbConnection cn, string tableName) { if (cn == null) { @@ -193,7 +247,7 @@ namespace DbTools { } StringBuilder sb = new StringBuilder(); - string[] indexes = GetIndexCreateSqlAsync(cn, tableName).Result.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); + string[] indexes = GetIndexCreateSql(cn, tableName).Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (string index in indexes) { Match m = Regex.Match(index, "CREATE INDEX (\\S+) ON .*", RegexOptions.Singleline); if (m.Success) { @@ -201,7 +255,7 @@ namespace DbTools { } } - string[] triggers = GetTriggerCreateSqlAsync(cn, tableName).Result.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); + string[] triggers = GetTriggerCreateSql(cn, tableName).Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (string trigger in triggers) { Match m = Regex.Match(trigger, "CREATE TRIGGER (\\S+) .*", RegexOptions.Singleline); if (m.Success) { @@ -224,7 +278,7 @@ namespace DbTools { /// A string containing the SQL statements for creating all indexes on the specified table, separated by /// newlines. Returns an empty string if no indexes are found. /// Thrown if is null or if is null or empty. - public async Task GetIndexCreateSqlAsync(IDbConnection cn, string tableName) { + public string GetIndexCreateSql(IDbConnection cn, string tableName) { if (cn == null) { throw new ArgumentNullException("cn"); } @@ -236,16 +290,14 @@ namespace DbTools { } StringBuilder sb = new StringBuilder(); - await Task.Run(() => { - using (IDbCommand cmd = cn.CreateCommand()) { - cmd.CommandText = $"SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name='{tableName}' AND sql NOT NULL;"; - using (IDataReader reader = cmd.ExecuteReader()) { - while (reader.Read()) { - sb.AppendLine(reader.GetString(0) + ";"); - } + using (IDbCommand cmd = cn.CreateCommand()) { + cmd.CommandText = $"SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name='{tableName}' AND sql NOT NULL;"; + using (IDataReader reader = cmd.ExecuteReader()) { + while (reader.Read()) { + sb.AppendLine(reader.GetString(0) + ";"); } } - }); + } return sb.ToString(); } @@ -261,7 +313,7 @@ namespace DbTools { /// A string containing the SQL definitions of all triggers for the specified table, separated by semicolons. /// Returns an empty string if no triggers are found. /// Thrown if is null or if is null or empty. - public async Task GetTriggerCreateSqlAsync(IDbConnection cn, string tableName) { + public string GetTriggerCreateSql(IDbConnection cn, string tableName) { if (cn == null) { throw new ArgumentNullException("cn"); } @@ -272,16 +324,14 @@ namespace DbTools { cn.Open(); } StringBuilder sb = new StringBuilder(); - await Task.Run(() => { - using (IDbCommand cmd = cn.CreateCommand()) { - cmd.CommandText = $"SELECT sql FROM sqlite_master WHERE type='trigger' AND tbl_name='{tableName}' AND sql NOT NULL;"; - using (IDataReader reader = cmd.ExecuteReader()) { - while (reader.Read()) { - sb.AppendLine(reader.GetString(0) + ";"); - } + using (IDbCommand cmd = cn.CreateCommand()) { + cmd.CommandText = $"SELECT sql FROM sqlite_master WHERE type='trigger' AND tbl_name='{tableName}' AND sql NOT NULL;"; + using (IDataReader reader = cmd.ExecuteReader()) { + while (reader.Read()) { + sb.AppendLine(reader.GetString(0) + ";"); } } - }); + } return sb.ToString(); }