This commit is contained in:
2024-08-09 09:23:47 -05:00
parent 9ece56f15c
commit 5876ac0e8a
5 changed files with 289 additions and 30 deletions

View File

@@ -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
{
}
}

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>a4a58207-cf8c-46fd-9749-3d4d8816e11a</ProjectGuid>
<ProjectGuid>{A4A58207-CF8C-46FD-9749-3D4D8816E11A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DbMigrate</RootNamespace>
@@ -31,24 +31,20 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System"/>
<Reference Include="System.Core"/>
<Reference Include="System.Xml.Linq"/>
<Reference Include="System.Data.DataSetExtensions"/>
<Reference Include="Microsoft.CSharp"/>
<Reference Include="System.Data"/>
<Reference Include="System.Net.Http"/>
<Reference Include="System.Xml"/>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Class1.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SqlDatabase.cs" />
<Compile Include="SqlTable.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

76
Extensions.cs Normal file
View File

@@ -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;
}
}
}
}

120
SqlDatabase.cs Normal file
View File

@@ -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<SqlTable> Tables { get; set; } = new List<SqlTable>();
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<SqlTable> 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<string> BuildSql(string dbConnectionString, bool includeIfNotExist = false) {
using (OleDbConnection cn = new OleDbConnection(dbConnectionString)) {
string sql = "";
//List<string> TableSql = new List<string>();
IEnumerable<SqliteTableDefinition> TableDefs = await cn.QueryAsync<SqliteTableDefinition>("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<SqliteTableDefinition> 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<SqliteTableDefinition> 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;
}
}
}
}

79
SqlTable.cs Normal file
View File

@@ -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<string, string> Columns { get; set; }
public Dictionary<string, string> Indexes { get; set; }
public Dictionary<string, string> Triggers { get; set; }
public SqlTable() {
Columns = new Dictionary<string, string>();
Indexes = new Dictionary<string, string>();
Triggers = new Dictionary<string, string>();
}
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();
}
}
}