using DbTools.Model;
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace DbTools {
internal static class TableExtensions {
///
/// Retrieves the column names that are common between the current table and the specified table.
///
/// The source table from which to retrieve column names.
/// The table to compare against for common column names.
/// An array of strings containing the names of columns that exist in both tables. The array will be empty if no
/// common columns are found.
public static string[] GetCommonColumns(this Table table1, Table table2) {
return table1.GetColumnNames().Where(f => table2.HasColumn(f)).ToArray();
}
///
/// Parses the provided SQL script to extract and populate table metadata, including the table name, columns,
/// indexes, and triggers.
///
/// This method processes the SQL script line by line to identify and extract the table
/// definition, column definitions, indexes, and triggers. - If is specified, only
/// metadata for the specified table is extracted. - If is not specified, the table
/// name is inferred from the first `CREATE TABLE` statement in the script. Lines starting with `--` are
/// ignored as comments. The method stops processing once the metadata for the specified or inferred table is
/// fully extracted.
/// The instance to populate with metadata extracted from the SQL script.
/// The SQL script to parse. The script should define a table and optionally include indexes and triggers.
/// An optional name of the table to extract from the SQL script. If not provided, the table name will be
/// inferred from the SQL script.
public static void ParseSql(this Table 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;
}
}
public static string GenerateDropTable(this Table table) {
StringBuilder sb = new StringBuilder();
if (table.HasTriggers()) {
foreach (var trigger in table.Triggers.Keys) {
sb.AppendLine($"DROP TRIGGER IF EXISTS {trigger};");
}
}
if (table.HasIndexes()) {
foreach (var index in table.Indexes.Keys) {
sb.AppendLine($"DROP INDEX IF EXISTS {index};");
}
}
sb.AppendLine($"DROP TABLE IF EXISTS {table.TableName};");
return sb.ToString();
}
public static string GenerateTableMigration(this Table newTable, Table oldTable) {
StringBuilder sb = new StringBuilder();
string tableName = newTable.TableName;
sb.AppendLine("\r\n-- Table " + tableName + " requires alteration - Create temp table and move data");
string[] commonColumns = newTable.GetCommonColumns(oldTable);
string columnList = string.Join(",", commonColumns);
string sql = newTable.CreateTableSql;
string newTableName = tableName + "_" + DateTime.Now.ToString("yyyyMMddHHmmss");
sql = Regex.Replace(sql, "CREATE TABLE (\\w*)", "CREATE TABLE IF NOT EXISTS $1_" + newTableName.Substring(newTableName.IndexOf("_") + 1));
sb.AppendLine(sql);
sb.AppendLine("\r\n-- Copy data from existing table to new table");
sb.AppendLine("INSERT INTO " + newTableName + " (" + columnList + ")");
sb.AppendLine("\tSELECT " + columnList);
sb.AppendLine("\tFROM " + tableName + ";");
sb.AppendLine("\r\n-- Drop existing table");
sb.AppendLine("DROP TABLE " + tableName + ";");
sb.AppendLine("\r\n-- Rename the new table to replace the old table");
sb.AppendLine("ALTER TABLE " + newTableName + " RENAME TO " + tableName + ";");
return sb.ToString();
}
}
}