using System.Data; using System.Text; namespace Connected.Data.Schema.Sql { internal class DataCopy : TableTransaction { public DataCopy(ExistingSchema existing, string temporaryName) { Existing = existing; TemporaryName = temporaryName; } private ExistingSchema Existing { get; } public string TemporaryName { get; } protected override async Task OnExecute() { await Context.Execute(CommandText); } private string CommandText { get { var text = new StringBuilder(); var columnSet = new StringBuilder(); var sourceSet = new StringBuilder(); var comma = string.Empty; foreach (var column in Context.Schema.Columns) { if (column.IsVersion) continue; var existing = Existing.Columns.FirstOrDefault(f => string.Equals(column.Name, f.Name, StringComparison.OrdinalIgnoreCase)); if (existing is null) continue; columnSet.Append($"{comma}{Escape(column.Name)}"); if (NeedsConversion(column) && (existing.DataType != column.DataType || existing.Precision != column.Precision || existing.Scale != column.Scale)) sourceSet.Append($"{comma}CONVERT({ConversionString(column)},{Escape(column.Name)})"); else sourceSet.Append($"{comma}{Escape(column.Name)}"); comma = ","; } text.AppendLine($"IF EXISTS (SELECT * FROM {Escape(Existing.SchemaName(), Existing.Name)})"); text.AppendLine($"EXEC ('INSERT INTO {Escape(Context.Schema.SchemaName(), TemporaryName)} ({columnSet.ToString()})"); text.AppendLine($"SELECT {sourceSet.ToString()} FROM {Escape(Existing.SchemaName(), Existing.Name)}')"); return text.ToString(); } } private static string ConversionString(ISchemaColumn column) { return column.DataType switch { DbType.Byte => "tinyint", DbType.Currency => "money", DbType.Decimal => $"decimal({column.Precision}, {column.Scale})", DbType.Double => "real", DbType.Int16 => "smallint", DbType.Int32 => "int", DbType.Int64 => "bigint", DbType.SByte => "smallint", DbType.Single => "float", DbType.UInt16 => "int", DbType.UInt32 => "bigint", DbType.UInt64 => "float", DbType.VarNumeric => $"numeric({column.Precision}, {column.Scale})", _ => throw new NotSupportedException(), }; } private static bool NeedsConversion(ISchemaColumn column) { return column.DataType switch { DbType.Byte or DbType.Currency or DbType.Decimal or DbType.Double or DbType.Int16 or DbType.Int32 or DbType.Int64 or DbType.SByte or DbType.Single or DbType.UInt16 or DbType.UInt32 or DbType.UInt64 or DbType.VarNumeric => true, _ => false, }; } } }