Skip to content

Commit c57b3d6

Browse files
committed
implement Initial Migration
1 parent 8085193 commit c57b3d6

File tree

6 files changed

+154
-38
lines changed

6 files changed

+154
-38
lines changed

Signum.Engine.Extensions/Migrations/MigrationLogic.cs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,35 @@ public static void Start(SchemaBuilder sb)
6464

6565
return false;
6666
};
67+
68+
Administrator.AvoidSimpleGenerate = () =>
69+
{
70+
if(SqlMigrationRunner.MigrationDirectoryIsEmpty())
71+
{
72+
Console.WriteLine("Your SQL Migrations Directory is empty.");
73+
74+
if (SafeConsole.Ask("Do you want to create the INITIAL SQL Migration instead?"))
75+
{
76+
SqlMigrationRunner.CreateInitialMigration();
77+
SqlMigrationRunner.SqlMigrations();
78+
return true;
79+
}
80+
}
81+
else
82+
{
83+
var hasInitial = SqlMigrationRunner.ReadMigrationsDirectory(silent: true).MinBy(a => a.Version)?.Comment.Contains("Initial Migration");
84+
85+
Console.WriteLine("You have an Initial SQL Migration.");
86+
87+
if (SafeConsole.Ask("Do you want to run the SQL Migrations instead?"))
88+
{
89+
SqlMigrationRunner.SqlMigrations();
90+
return true;
91+
}
92+
}
93+
94+
return false;
95+
};
6796
}
6897
}
6998

@@ -90,24 +119,24 @@ public static void EnsureMigrationTable<T>() where T : Entity
90119
{
91120
using (var tr = new Transaction())
92121
{
93-
if (Administrator.ExistsTable<T>())
94-
return;
122+
if (!Administrator.ExistsTable<T>())
123+
{
124+
var table = Schema.Current.Table<T>();
125+
var sqlBuilder = Connector.Current.SqlBuilder;
95126

96-
var table = Schema.Current.Table<T>();
97-
var sqlBuilder = Connector.Current.SqlBuilder;
127+
if (!table.Name.Schema.IsDefault() && !Administrator.ExistSchema(table.Name.Schema))
128+
sqlBuilder.CreateSchema(table.Name.Schema).ExecuteLeaves();
98129

99-
if (!table.Name.Schema.IsDefault() && !Database.View<SysSchemas>().Any(s => s.name == table.Name.Schema.Name))
100-
sqlBuilder.CreateSchema(table.Name.Schema).ExecuteLeaves();
130+
sqlBuilder.CreateTableSql(table).ExecuteLeaves();
101131

102-
sqlBuilder.CreateTableSql(table).ExecuteLeaves();
132+
foreach (var i in table.GeneratAllIndexes().Where(i => !(i is PrimaryKeyIndex)))
133+
{
134+
sqlBuilder.CreateIndex(i, checkUnique: null).ExecuteLeaves();
135+
}
103136

104-
foreach (var i in table.GeneratAllIndexes().Where(i => !(i is PrimaryKeyIndex)))
105-
{
106-
sqlBuilder.CreateIndex(i, checkUnique: null).ExecuteLeaves();
137+
SafeConsole.WriteLineColor(ConsoleColor.White, "Table " + table.Name + " auto-generated...");
107138
}
108139

109-
SafeConsole.WriteLineColor(ConsoleColor.White, "Table " + table.Name + " auto-generated...");
110-
111140
tr.Commit();
112141
}
113142
}

Signum.Engine.Extensions/Migrations/SqlMigrationRunner.cs

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,41 @@ public static void SqlMigrations(bool autoRun)
2020
{
2121
List<MigrationInfo> list = ReadMigrationsDirectory();
2222

23-
SetExecuted(list);
23+
if (!autoRun && !Connector.Current.HasTables() && list.Count == 0)
24+
{
25+
if (!SafeConsole.Ask("Create initial migration?"))
26+
return;
2427

25-
if (!Prompt(list, autoRun) || autoRun)
26-
return;
28+
CreateInitialMigration();
29+
}
30+
else
31+
{
32+
SetExecuted(list);
33+
34+
if (!Prompt(list, autoRun) || autoRun)
35+
return;
36+
}
2737
}
2838
}
2939

40+
public static void CreateInitialMigration()
41+
{
42+
var script = Schema.Current.GenerationScipt(databaseNameReplacement: DatabaseNameReplacement)!;
43+
44+
string version = DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss");
45+
46+
string comment = "Initial Migration";
47+
48+
string fileName = version + "_" + FileNameValidatorAttribute.RemoveInvalidCharts(comment) + ".sql";
49+
50+
File.WriteAllText(Path.Combine(MigrationsDirectory, fileName), script.ToString(), Encoding.UTF8);
51+
}
52+
3053
private static void SetExecuted(List<MigrationInfo> migrations)
3154
{
55+
if (!Connector.Current.HasTables())
56+
return;
57+
3258
MigrationLogic.EnsureMigrationTable<SqlMigrationEntity>();
3359

3460
var first = migrations.FirstOrDefault();
@@ -60,15 +86,30 @@ private static void SetExecuted(List<MigrationInfo> migrations)
6086
migrations.Sort(a => a.Version);
6187
}
6288

63-
public static List<MigrationInfo> ReadMigrationsDirectory()
89+
public static bool MigrationDirectoryIsEmpty()
6490
{
65-
Console.WriteLine();
66-
SafeConsole.WriteLineColor(ConsoleColor.DarkGray, "Reading migrations from: " + MigrationsDirectory);
91+
return !Directory.Exists(MigrationsDirectory) || Directory.EnumerateFiles(MigrationsDirectory).IsEmpty();
92+
}
6793

68-
if (!Directory.Exists(MigrationsDirectory))
94+
public static List<MigrationInfo> ReadMigrationsDirectory(bool silent = false)
95+
{
96+
if (silent)
6997
{
70-
Directory.CreateDirectory(MigrationsDirectory);
71-
SafeConsole.WriteLineColor(ConsoleColor.White, "Directory " + MigrationsDirectory + " auto-generated...");
98+
if (!Directory.Exists(MigrationsDirectory))
99+
return new List<MigrationInfo>();
100+
}
101+
else
102+
{
103+
if (!Directory.Exists(MigrationsDirectory))
104+
{
105+
Directory.CreateDirectory(MigrationsDirectory);
106+
SafeConsole.WriteLineColor(ConsoleColor.White, "Directory " + MigrationsDirectory + " auto-generated...");
107+
}
108+
else
109+
{
110+
Console.WriteLine();
111+
SafeConsole.WriteLineColor(ConsoleColor.DarkGray, "Reading migrations from: " + MigrationsDirectory);
112+
}
72113
}
73114

74115
Regex regex = new Regex(@"(?<version>\d{4}\.\d{2}\.\d{2}\-\d{2}\.\d{2}\.\d{2})(_(?<comment>.+))?\.sql");
@@ -211,6 +252,8 @@ private static void Execute(MigrationInfo mi)
211252

212253
SqlPreCommandExtensions.ExecuteScript(title, text);
213254

255+
MigrationLogic.EnsureMigrationTable<SqlMigrationEntity>();
256+
214257
new SqlMigrationEntity
215258
{
216259
VersionNumber = mi.Version,

Signum.Engine/Administrator.cs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@ namespace Signum.Engine;
1111

1212
public static class Administrator
1313
{
14+
public static Func<bool>? OnTotalGeneration;
15+
1416
public static void TotalGeneration()
1517
{
16-
foreach (var db in Schema.Current.DatabaseNames())
17-
{
18-
Connector.Current.CleanDatabase(db);
19-
SafeConsole.WriteColor(ConsoleColor.DarkGray, '.');
20-
}
18+
CleanAllDatabases();
2119

20+
ExecuteGenerationScript();
21+
}
22+
23+
public static void ExecuteGenerationScript()
24+
{
2225
SqlPreCommandConcat totalScript = (SqlPreCommandConcat)Schema.Current.GenerationScipt()!;
2326
foreach (SqlPreCommand command in totalScript.Commands)
2427
{
@@ -27,6 +30,15 @@ public static void TotalGeneration()
2730
}
2831
}
2932

33+
private static void CleanAllDatabases()
34+
{
35+
foreach (var db in Schema.Current.DatabaseNames())
36+
{
37+
Connector.Current.CleanDatabase(db);
38+
SafeConsole.WriteColor(ConsoleColor.DarkGray, '.');
39+
}
40+
}
41+
3042
public static string GenerateViewCodes(params string[] tableNames) => tableNames.ToString(tn => GenerateViewCode(tn), "\r\n\r\n");
3143

3244
public static string GenerateViewCode(string tableName) => GenerateViewCode(ObjectName.Parse(tableName, Schema.Current.Settings.IsPostgres));
@@ -77,12 +89,13 @@ private static string GenerateColumnCode(DiffColumn c)
7789
return Schema.Current.GenerationScipt();
7890
}
7991

80-
92+
93+
public static Func<bool>? AvoidSimpleGenerate;
8194

8295
public static void NewDatabase()
8396
{
8497
var databaseName = Connector.Current.DatabaseName();
85-
if (Database.View<SysTables>().Any())
98+
if (Connector.Current.HasTables())
8699
{
87100
SafeConsole.WriteLineColor(ConsoleColor.Red, $"Are you sure you want to delete all the data in the database '{databaseName}'?");
88101
Console.Write($"Confirm by writing the name of the database:");
@@ -95,8 +108,16 @@ public static void NewDatabase()
95108
}
96109
}
97110

98-
Console.Write("Creating new database...");
99-
Administrator.TotalGeneration();
111+
Console.Write("Cleaning database...");
112+
using(Connector.CommandTimeoutScope(5 * 60))
113+
CleanAllDatabases();
114+
Console.WriteLine("Done.");
115+
116+
if (AvoidSimpleGenerate?.Invoke() == true)
117+
return;
118+
119+
Console.Write("Generating new database database...");
120+
ExecuteGenerationScript();
100121
Console.WriteLine("Done.");
101122
}
102123

@@ -262,7 +283,13 @@ join s in Database.View<SysSchemas>() on t.schema_id equals s.schema_id
262283
}
263284
}
264285

286+
public static bool ExistSchema(SchemaName name)
287+
{
288+
if (Schema.Current.Settings.IsPostgres)
289+
return Database.View<PgNamespace>().Any(ns => ns.nspname == name.Name);
265290

291+
return Database.View<SysSchemas>().Any(s => s.name == name.Name);
292+
}
266293

267294
public static List<T> TryRetrieveAll<T>(Replacements replacements)
268295
where T : Entity
@@ -582,7 +609,7 @@ from ifk in targetTable.IncommingForeignKeys()
582609
ParentColumn = parentTable.Columns().SingleEx(c => c.column_id == ifk.ForeignKeyColumns().SingleEx().parent_column_id).name,
583610
}).ToList());
584611

585-
foreignKeys.ForEach(fk => sqlBuilder.AlterTableDropConstraint(fk.ParentTable!, fk.Name! /*CSBUG*/).ExecuteLeaves());
612+
foreignKeys.ForEach(fk => sqlBuilder.AlterTableDropConstraint(fk.ParentTable!, fk.Name).ExecuteLeaves());
586613

587614
return new Disposable(() =>
588615
{

Signum.Engine/Engine/SchemaGenerator.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,19 @@ public static class SchemaGenerator
2626
{
2727
var sqlBuilder = Connector.Current.SqlBuilder;
2828
Schema s = Schema.Current;
29+
2930
List<ITable> tables = s.GetDatabaseTables().Where(t => !s.IsExternalDatabase(t.Name.Schema.Database)).ToList();
3031

3132
SqlPreCommand? createTables = tables.Select(t => sqlBuilder.CreateTableSql(t)).Combine(Spacing.Double)?.PlainSqlCommand();
3233

34+
if (createTables != null)
35+
createTables.GoAfter = true;
36+
3337
SqlPreCommand? foreignKeys = tables.Select(sqlBuilder.AlterTableForeignKeys).Combine(Spacing.Double)?.PlainSqlCommand();
3438

39+
if (foreignKeys != null)
40+
foreignKeys.GoAfter = true;
41+
3542
SqlPreCommand? indices = tables.Select(t =>
3643
{
3744
var allIndexes = t.GeneratAllIndexes().Where(a => !(a is PrimaryKeyIndex)); ;
@@ -45,17 +52,24 @@ public static class SchemaGenerator
4552

4653
}).NotNull().Combine(Spacing.Double)?.PlainSqlCommand();
4754

55+
if (indices != null)
56+
indices.GoAfter = true;
4857

4958
return SqlPreCommand.Combine(Spacing.Triple, createTables, foreignKeys, indices);
5059
}
5160

5261
public static SqlPreCommand? InsertEnumValuesScript()
5362
{
54-
return (from t in Schema.Current.Tables.Values
55-
let enumType = EnumEntity.Extract(t.Type)
56-
where enumType != null
57-
select EnumEntity.GetEntities(enumType).Select((e, i) => t.InsertSqlSync(e, suffix: t.Name.Name + i)).Combine(Spacing.Simple)
63+
var result = (from t in Schema.Current.Tables.Values
64+
let enumType = EnumEntity.Extract(t.Type)
65+
where enumType != null
66+
select EnumEntity.GetEntities(enumType).Select((e, i) => t.InsertSqlSync(e, suffix: t.Name.Name + i)).Combine(Spacing.Simple)
5867
).Combine(Spacing.Double)?.PlainSqlCommand();
68+
69+
if (result != null)
70+
result.GoAfter = true;
71+
72+
return result;
5973
}
6074

6175
public static SqlPreCommand? PostgresExtensions()
@@ -90,7 +104,6 @@ select EnumEntity.GetEntities(enumType).Select((e, i) => t.InsertSqlSync(e, suff
90104
if (!connector.AllowsSetSnapshotIsolation)
91105
return null;
92106

93-
94107
var list = connector.Schema.DatabaseNames().Select(a => a?.Name).ToList();
95108

96109
if (list.Contains(null))
@@ -112,6 +125,7 @@ select EnumEntity.GetEntities(enumType).Select((e, i) => t.InsertSqlSync(e, suff
112125
).Combine(Spacing.Double);
113126

114127
return cmd;
128+
115129
}
116130

117131
private static bool SnapshotIsolationEnabled(DatabaseName dbName)

Signum.Engine/Engine/SqlPreCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public static void ExecuteScript(string title, string script)
130130
{
131131
using (Connector.CommandTimeoutScope(Timeout))
132132
{
133-
var regex = new Regex(@" *(GO|USE \w+|USE \[[^\]]+\]) *(\r?\n|$)", RegexOptions.IgnoreCase);
133+
var regex = new Regex(@"^ *(GO|USE \w+|USE \[[^\]]+\]) *(\r?\n|$)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
134134

135135
var parts = regex.Split(script);
136136

Signum.Engine/Schema/Schema.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,14 +459,17 @@ public Table View(Type viewType)
459459
return ViewBuilder.NewView(viewType);
460460
}
461461

462+
463+
462464
public event Func<SqlPreCommand?> Generating;
463-
internal SqlPreCommand? GenerationScipt()
465+
public SqlPreCommand? GenerationScipt(string? databaseNameReplacement = null)
464466
{
465467
OnBeforeDatabaseAccess();
466468

467469
if (Generating == null)
468470
return null;
469471

472+
using (databaseNameReplacement == null ? null : ObjectName.OverrideOptions(new ObjectNameOptions { DatabaseNameReplacement = databaseNameReplacement }))
470473
using (CultureInfoUtils.ChangeBothCultures(ForceCultureInfo))
471474
using (ExecutionMode.Global())
472475
{

0 commit comments

Comments
 (0)