From 5876ac0e8a6c44b0136a92f3e6a36e24c9aa5652 Mon Sep 17 00:00:00 2001 From: Russ Kollmansberger Date: Fri, 9 Aug 2024 09:23:47 -0500 Subject: [PATCH] initial --- Class1.cs | 12 ----- DbMigrate.csproj | 32 ++++++------- Extensions.cs | 76 ++++++++++++++++++++++++++++++ SqlDatabase.cs | 120 +++++++++++++++++++++++++++++++++++++++++++++++ SqlTable.cs | 79 +++++++++++++++++++++++++++++++ 5 files changed, 289 insertions(+), 30 deletions(-) delete mode 100644 Class1.cs create mode 100644 Extensions.cs create mode 100644 SqlDatabase.cs create mode 100644 SqlTable.cs diff --git a/Class1.cs b/Class1.cs deleted file mode 100644 index 5edbcbd..0000000 --- a/Class1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DbMigrate -{ - public class Class1 - { - } -} diff --git a/DbMigrate.csproj b/DbMigrate.csproj index 03eeaa1..7c5b906 100644 --- a/DbMigrate.csproj +++ b/DbMigrate.csproj @@ -1,10 +1,10 @@ - + Debug AnyCPU - a4a58207-cf8c-46fd-9749-3d4d8816e11a + {A4A58207-CF8C-46FD-9749-3D4D8816E11A} Library Properties DbMigrate @@ -31,24 +31,20 @@ 4 - - - - - - - - - - - - - - + + + + + + + + - + + + - + \ No newline at end of file diff --git a/Extensions.cs b/Extensions.cs new file mode 100644 index 0000000..e97bcba --- /dev/null +++ b/Extensions.cs @@ -0,0 +1,76 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; + +namespace DbMigrate { + internal static class Extensions { + public static string[] GetCommonColumns(this SqlTable table, SqlTable table2) { + return table.GetColumnNames().Where(f => table2.HasColumn(f)).ToArray(); + } + + public static void ParseSql(this SqlTable table, string sql, string tableName = null) { + if (tableName != null) { + table.TableName = tableName; + } + + bool inTable = false; + bool inColumns = false; + + Match m = null; + foreach (string line in Regex.Split(sql, "\\r\\n")) { + if (string.IsNullOrEmpty(line) || line.StartsWith("--")) { + continue; + } + + m = Regex.Match(line, "^CREATE TABLE( IF NOT EXISTS)? (\\S+) "); + if (m.Success) { + if (string.IsNullOrEmpty(tableName) && string.IsNullOrEmpty(table.TableName)) { + // Set table name from the above regex match + table.TableName = m.Groups[2].Value.Trim(); + } + if (!inTable && (table.TableName == null || m.Groups[2].Value.Trim() == table.TableName)) { + table.CreateTableSql += line + Environment.NewLine; + inTable = true; + inColumns = true; + continue; + } else { + if (inTable) { + // We are done with this table + return; + } + continue; + } + } + + m = Regex.Match(line, "^CREATE INDEX( IF NOT EXISTS)? (\\S+) "); + if (m.Success && inTable) { + table.Indexes.Add(m.Groups[2].Value.Trim(), line.Trim()); + continue; + } + + m = Regex.Match(line, "^CREATE TRIGGER( IF NOT EXISTS)? (\\S+) "); + if (m.Success && inTable) { + table.Triggers.Add(m.Groups[2].Value.Trim(), line.Trim()); + continue; + } + + if (inColumns) { + if (line.Trim().StartsWith(")")) { + inColumns = false; + } else { + m = Regex.Match(line.Trim(), "^(\\S+).*"); + if (m.Success) { + string columnLine = line.Trim(); + if (columnLine.EndsWith(",")) { + columnLine = columnLine.Substring(0, columnLine.Length - 1); + } + table.Columns.Add(m.Groups[1].Value.Trim(), columnLine); + } + } + } + + table.CreateTableSql += line + Environment.NewLine; + } + } + } +} diff --git a/SqlDatabase.cs b/SqlDatabase.cs new file mode 100644 index 0000000..8b9f495 --- /dev/null +++ b/SqlDatabase.cs @@ -0,0 +1,120 @@ +using System.Collections.Generic; +using System.Data.OleDb; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace DbMigrate { + public class SqlDatabase { + public string ConnectionString { get; set; } + public string SqlScript { get; set; } + public List Tables { get; set; } = new List(); + + + public SqlDatabase() { + + } + + public void Connect(string connectionString) { + ConnectionString = connectionString; + + } + + public void LoadSql(string sql) { + SqlScript = sql; + Tables = ParseTablesFromSql(sql).ToList(); + } + + public void LoadSqlFromFile(string fileName) { + if (!File.Exists(fileName)) { + throw new FileNotFoundException("SQL file '" + fileName + "' was not found and could not be loaded."); + } + + string sql = File.ReadAllText(fileName); + LoadSql(sql); + } + + public IEnumerable ParseTablesFromSql(string sql) { + SqlTable table = null; + StringBuilder sb = new StringBuilder(); + + foreach (string line in Regex.Split(sql, "\\r\\n")) { + if (string.IsNullOrEmpty(line) || line.StartsWith("--")) { + continue; + } + + if (line.ToUpper().StartsWith("CREATE TABLE ")) { + if (table != null) { + table.ParseSql(sb.ToString()); + yield return table; + } + + // Start a new table + table = new SqlTable(); + sb = new StringBuilder(); + sb.AppendLine(line); + continue; + } + sb.AppendLine(line); + } + + table.ParseSql(sb.ToString()); + yield return table; + } + + public async Task BuildSql(string dbConnectionString, bool includeIfNotExist = false) { + using (OleDbConnection cn = new OleDbConnection(dbConnectionString)) { + string sql = ""; + + //List TableSql = new List(); + IEnumerable TableDefs = await cn.QueryAsync("select * from sqlite_master"); + + foreach (SqliteTableDefinition table in TableDefs.Where(f => f.type == "table").OrderBy(f => f.tbl_name)) { + if (table.tbl_name == "sqlite_sequence") { continue; } + Match m = Regex.Match(table.sql, "CREATE TABLE \\S+ \\((.*)\\)", RegexOptions.Singleline); + if (!m.Success) { + Trace.TraceWarning("Unable to match regex on table " + table.name); + continue; + } + + 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"); + + sql += "-- BEGIN TABLE " + table.tbl_name + " --\r\n"; + sql += table.sql.Substring(0, startIndex) + "\r\n\t" + + columns.Trim() + "\r\n" + + table.sql.Substring(startIndex + length) + ";\r\n"; + + + List indexes = TableDefs.Where(f => f.type == "index" && f.tbl_name == table.tbl_name && !string.IsNullOrEmpty(f.sql)).ToList(); + if (indexes.Count > 0) { + sql += "\r\n-- INDEXES --\r\n"; + foreach (var index in indexes) { + if (string.IsNullOrEmpty(index.sql)) { continue; } + sql += index.sql + ";\r\n"; + } + } + + List triggers = TableDefs.Where(f => f.type == "trigger" && f.tbl_name == table.tbl_name && !string.IsNullOrEmpty(f.sql)).ToList(); + if (triggers.Count > 0) { + sql += "\r\n-- TRIGGERS --\r\n"; + foreach (var trigger in triggers) { + if (string.IsNullOrEmpty(trigger.sql)) { continue; } + sql += trigger.sql + ";\r\n"; + } + } + + sql += "-- END TABLE " + table.tbl_name + " --\r\n\r\n"; + //TableSql.Add(sql); + } + + return sql; + } + } + } +} diff --git a/SqlTable.cs b/SqlTable.cs new file mode 100644 index 0000000..fdbc95a --- /dev/null +++ b/SqlTable.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DbMigrate { + public class SqlTable { + public string TableName { get; set; } + public string CreateTableSql { get; set; } + public string OriginalSql { get; set; } + public Dictionary Columns { get; set; } + public Dictionary Indexes { get; set; } + public Dictionary Triggers { get; set; } + + + + public SqlTable() { + Columns = new Dictionary(); + Indexes = new Dictionary(); + Triggers = new Dictionary(); + } + + public SqlTable(string sql) { + OriginalSql = sql; + this.ParseSql(sql); + } + + public string FullSql() { + StringBuilder sb = new StringBuilder(); + //sb.AppendLine("-- Create Table " + TableName); + sb.AppendLine(CreateTableSql); + + if (Indexes.Count > 0) { + sb.AppendLine("\r\n-- Create Indexes"); + foreach (string index in Indexes.Keys) { + sb.AppendLine(Indexes[index]); + } + } + + if (Triggers.Count > 0) { + sb.AppendLine("\r\n-- Create Triggers"); + foreach (string trigger in Triggers.Keys) { + sb.AppendLine(Triggers[trigger]); + } + } + + sb.AppendLine(); + return sb.ToString(); + } + + public string[] GetColumnNames() { + return Columns.Keys.ToArray(); + } + + public string[] GetColumns() { + return Columns.Values.ToArray(); + } + + public bool HasColumn(string columnName) { + return Columns.ContainsKey(columnName); + } + + public string[] GetTriggerNames() { + return Triggers.Keys.ToArray(); + } + + public string[] GetTriggers() { + return Triggers.Values.ToArray(); + } + + public string[] GetIndexNames() { + return Indexes.Keys.ToArray(); + } + + public string[] GetIndexes() { + return Indexes.Values.ToArray(); + } + + } +}