From 49d2a6dbf0e7faee255a07b914290db11d1f616b Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 28 May 2019 17:49:50 +0200 Subject: [PATCH] Fix migrations for SqlServer --- .../CreateKeysAndIndexesBuilder.cs | 10 +++++++ .../DeleteDefaultConstraintBuilder.cs | 10 +++++-- .../Expressions/Delete/DeleteBuilder.cs | 13 +++++---- .../DeleteDefaultConstraintExpression.cs | 10 ++++--- .../Expressions/Delete/IDeleteBuilder.cs | 4 +-- .../DeleteKeysAndIndexesBuilder.cs | 27 +++++++++++-------- .../Upgrade/Common/DeleteKeysAndIndexes.cs | 4 +-- .../Migrations/Upgrade/V_8_0_0/SuperZero.cs | 7 ++++- .../Upgrade/V_8_0_0/VariantsMigration.cs | 2 ++ .../SqlSyntax/ISqlSyntaxProvider.cs | 11 +++++++- .../SqlSyntax/SqlCeSyntaxProvider.cs | 12 ++++++++- .../SqlSyntax/SqlServerSyntaxProvider.cs | 13 ++++++++- .../SqlSyntax/SqlSyntaxProviderBase.cs | 3 +++ .../Migrations/AdvancedMigrationTests.cs | 4 +-- 14 files changed, 97 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Expressions/Create/KeysAndIndexes/CreateKeysAndIndexesBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Create/KeysAndIndexes/CreateKeysAndIndexesBuilder.cs index 8b5e83cacd..6bf450a9b8 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Create/KeysAndIndexes/CreateKeysAndIndexesBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Create/KeysAndIndexes/CreateKeysAndIndexesBuilder.cs @@ -34,6 +34,16 @@ namespace Umbraco.Core.Migrations.Expressions.Create.KeysAndIndexes ExecuteSql(sql); foreach (var sql in syntax.Format(tableDefinition.ForeignKeys)) ExecuteSql(sql); + + // note: we do *not* create the DF_ default constraints + /* + foreach (var column in tableDefinition.Columns) + { + var sql = syntax.FormatDefaultConstraint(column); + if (!sql.IsNullOrWhiteSpace()) + ExecuteSql(sql); + } + */ } private void ExecuteSql(string sql) diff --git a/src/Umbraco.Core/Migrations/Expressions/Delete/DefaultConstraint/DeleteDefaultConstraintBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Delete/DefaultConstraint/DeleteDefaultConstraintBuilder.cs index 373b375fa8..86b90b9d6d 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Delete/DefaultConstraint/DeleteDefaultConstraintBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Delete/DefaultConstraint/DeleteDefaultConstraintBuilder.cs @@ -10,9 +10,13 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.DefaultConstraint IDeleteDefaultConstraintOnTableBuilder, IDeleteDefaultConstraintOnColumnBuilder { - public DeleteDefaultConstraintBuilder(DeleteDefaultConstraintExpression expression) + private readonly IMigrationContext _context; + + public DeleteDefaultConstraintBuilder(IMigrationContext context, DeleteDefaultConstraintExpression expression) : base(expression) - { } + { + _context = context; + } /// public IDeleteDefaultConstraintOnColumnBuilder OnTable(string tableName) @@ -24,6 +28,8 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.DefaultConstraint /// public IExecutableBuilder OnColumn(string columnName) { + var defaultConstraint = _context.SqlContext.SqlSyntax.GetDefaultConstraint(_context.Database, Expression.TableName, columnName); + Expression.ConstraintName = defaultConstraint ?? string.Empty; Expression.ColumnName = columnName; return new ExecutableBuilder(Expression); } diff --git a/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs index a06ca66f29..9a4f437f62 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Delete/DeleteBuilder.cs @@ -1,5 +1,4 @@ -using NPoco; -using Umbraco.Core.Exceptions; +using Umbraco.Core.Exceptions; using Umbraco.Core.Migrations.Expressions.Common; using Umbraco.Core.Migrations.Expressions.Delete.Column; using Umbraco.Core.Migrations.Expressions.Delete.Constraint; @@ -30,19 +29,19 @@ namespace Umbraco.Core.Migrations.Expressions.Delete } /// - public IExecutableBuilder KeysAndIndexes(bool pk = true, bool fk = true, bool ix = true) + public IExecutableBuilder KeysAndIndexes(bool local = true, bool foreign = true) { var syntax = _context.SqlContext.SqlSyntax; var tableDefinition = DefinitionFactory.GetTableDefinition(typeof(TDto), syntax); - return KeysAndIndexes(tableDefinition.Name, pk, fk, ix); + return KeysAndIndexes(tableDefinition.Name, local, foreign); } /// - public IExecutableBuilder KeysAndIndexes(string tableName, bool pk = true, bool fk = true, bool ix = true) + public IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true) { if (tableName.IsNullOrWhiteSpace()) throw new ArgumentNullOrEmptyException(nameof(tableName)); - return new DeleteKeysAndIndexesBuilder(_context) { TableName = tableName, DeletePrimaryKey = pk, DeleteForeignKeys = fk, DeleteIndexes = ix}; + return new DeleteKeysAndIndexesBuilder(_context) { TableName = tableName, DeleteLocal = local, DeleteForeign = foreign }; } /// @@ -111,7 +110,7 @@ namespace Umbraco.Core.Migrations.Expressions.Delete public IDeleteDefaultConstraintOnTableBuilder DefaultConstraint() { var expression = new DeleteDefaultConstraintExpression(_context); - return new DeleteDefaultConstraintBuilder(expression); + return new DeleteDefaultConstraintBuilder(_context, expression); } } } diff --git a/src/Umbraco.Core/Migrations/Expressions/Delete/Expressions/DeleteDefaultConstraintExpression.cs b/src/Umbraco.Core/Migrations/Expressions/Delete/Expressions/DeleteDefaultConstraintExpression.cs index 8b0b20c0e2..294190c766 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Delete/Expressions/DeleteDefaultConstraintExpression.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Delete/Expressions/DeleteDefaultConstraintExpression.cs @@ -10,12 +10,16 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.Expressions public virtual string TableName { get; set; } public virtual string ColumnName { get; set; } + public virtual string ConstraintName { get; set; } protected override string GetSql() { - return string.Format(SqlSyntax.DeleteDefaultConstraint, - SqlSyntax.GetQuotedTableName(TableName), - SqlSyntax.GetQuotedColumnName(ColumnName)); + return ConstraintName.IsNullOrWhiteSpace() + ? string.Empty + : string.Format(SqlSyntax.DeleteDefaultConstraint, + SqlSyntax.GetQuotedTableName(TableName), + SqlSyntax.GetQuotedColumnName(ColumnName), + SqlSyntax.GetQuotedName(ConstraintName)); } } } diff --git a/src/Umbraco.Core/Migrations/Expressions/Delete/IDeleteBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Delete/IDeleteBuilder.cs index 1c04fcc716..84e44d0d93 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Delete/IDeleteBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Delete/IDeleteBuilder.cs @@ -21,12 +21,12 @@ namespace Umbraco.Core.Migrations.Expressions.Delete /// /// Builds a Delete Keys and Indexes expression, and executes. /// - IExecutableBuilder KeysAndIndexes(bool pk = true, bool fk = true, bool ix = true); + IExecutableBuilder KeysAndIndexes(bool local = true, bool foreign = true); /// /// Builds a Delete Keys and Indexes expression, and executes. /// - IExecutableBuilder KeysAndIndexes(string tableName, bool pk = true, bool fk = true, bool ix = true); + IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true); /// /// Specifies the column to delete. diff --git a/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs index b16748c851..9b13457b76 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs @@ -18,11 +18,9 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.KeysAndIndexes public string TableName { get; set; } - public bool DeletePrimaryKey { get; set; } + public bool DeleteLocal { get; set; } - public bool DeleteForeignKeys { get; set; } - - public bool DeleteIndexes { get; set; } + public bool DeleteForeign { get; set; } /// public void Do() @@ -30,19 +28,26 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.KeysAndIndexes _context.BuildingExpression = false; // drop keys - if (DeleteForeignKeys || DeletePrimaryKey) + if (DeleteLocal || DeleteForeign) { - var keys = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToList(); - if (DeleteForeignKeys) - foreach (var key in keys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("FK_"))) + // table, constraint + var tableKeys = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToList(); + if (DeleteForeign) + { + foreach (var key in tableKeys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("FK_"))) Delete.ForeignKey(key.Item2).OnTable(key.Item1).Do(); - if (DeletePrimaryKey) - foreach (var key in keys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("PK_"))) + } + if (DeleteLocal) + { + foreach (var key in tableKeys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("PK_"))) Delete.PrimaryKey(key.Item2).FromTable(key.Item1).Do(); + + // note: we do *not* delete the DEFAULT constraints + } } // drop indexes - if (DeleteIndexes) + if (DeleteLocal) { var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToList(); foreach (var index in indexes.Where(x => x.TableName == TableName)) diff --git a/src/Umbraco.Core/Migrations/Upgrade/Common/DeleteKeysAndIndexes.cs b/src/Umbraco.Core/Migrations/Upgrade/Common/DeleteKeysAndIndexes.cs index b62d492348..9e4af17c09 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/Common/DeleteKeysAndIndexes.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/Common/DeleteKeysAndIndexes.cs @@ -67,9 +67,9 @@ // delete *all* keys and indexes - because of FKs // on known v7 tables only foreach (var table in tables) - Delete.KeysAndIndexes(table, false, true, false).Do(); + Delete.KeysAndIndexes(table, false, true).Do(); foreach (var table in tables) - Delete.KeysAndIndexes(table, true, false, true).Do(); + Delete.KeysAndIndexes(table, true, false).Do(); } } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs index 3f2ba3e31c..64ac20d175 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/SuperZero.cs @@ -15,7 +15,12 @@ Database.Execute("set identity_insert umbracoUser on;"); Database.Execute(@" - insert into umbracoUser select + insert into umbracoUser (id, + userDisabled, userNoConsole, userName, userLogin, userPassword, passwordConfig, + userEmail, userLanguage, securityStampToken, failedLoginAttempts, lastLockoutDate, + lastPasswordChangeDate, lastLoginDate, emailConfirmedDate, invitedDate, + createDate, updateDate, avatar, tourData) + select -1 id, userDisabled, userNoConsole, userName, substring(userLogin, 1, len(userLogin) - 2) userLogin, userPassword, passwordConfig, userEmail, userLanguage, securityStampToken, failedLoginAttempts, lastLockoutDate, diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs index 6b0151723c..8c60d30680 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/VariantsMigration.cs @@ -213,8 +213,10 @@ WHERE versionId NOT IN (SELECT (versionId) FROM {PreTables.ContentVersion} WHERE Delete.Column("text").FromTable(PreTables.Document).Do(); Delete.Column("templateId").FromTable(PreTables.Document).Do(); Delete.Column("documentUser").FromTable(PreTables.Document).Do(); + Delete.DefaultConstraint().OnTable(PreTables.Document).OnColumn("updateDate").Do(); Delete.Column("updateDate").FromTable(PreTables.Document).Do(); Delete.Column("versionId").FromTable(PreTables.Document).Do(); + Delete.DefaultConstraint().OnTable(PreTables.Document).OnColumn("newest").Do(); Delete.Column("newest").FromTable(PreTables.Document).Do(); // add and populate edited column diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index c352e312ac..39419021e0 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -44,7 +44,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax string TruncateTable { get; } string CreateConstraint { get; } string DeleteConstraint { get; } - + string DeleteDefaultConstraint { get; } string FormatDateTime(DateTime date, bool includeTime = true); string Format(TableDefinition table); @@ -106,5 +106,14 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// A Tuple containing: TableName, IndexName, ColumnName, IsUnique /// IEnumerable> GetDefinedIndexes(IDatabase db); + + /// + /// Gets the name of the default constraint on a column. + /// + /// The database. + /// The table name. + /// The column name. + /// The name of the default constraint, or the empty string if there is no default constraint. + string GetDefaultConstraint(IDatabase db, string tableName, string columnName); } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs index 8f39e36fb9..bb91a83da4 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlCeSyntaxProvider.cs @@ -132,6 +132,16 @@ ORDER BY TABLE_NAME, INDEX_NAME"); item => new Tuple(item.TABLE_NAME, item.INDEX_NAME, item.COLUMN_NAME, item.UNIQUE)); } + /// + public override string GetDefaultConstraint(IDatabase db, string tableName, string columnName) + { + // cannot return a true default constraint name (does not exist on SqlCe) + // but we won't really need it anyways - just check whether there is a constraint + var hasDefault = db.Fetch(@"select column_hasdefault from information_schema.columns +where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault(); + return hasDefault ? "XXXXX" : string.Empty; + } + public override bool DoesTableExist(IDatabase db, string tableName) { var result = @@ -175,7 +185,7 @@ ORDER BY TABLE_NAME, INDEX_NAME"); { get { - return "ALTER TABLE [{0}] ALTER COLUMN [{1}] DROP DEFAULT"; + return "ALTER TABLE {0} ALTER COLUMN {1} DROP DEFAULT"; } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index e51aa547b8..b0f95b0c56 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -225,6 +225,17 @@ order by T.name, I.name"); } + /// + public override string GetDefaultConstraint(IDatabase db, string tableName, string columnName) + { + return db.Fetch(@"select con.[name] as [constraintName] +from sys.default_constraints con +join sys.columns col on con.object_id=col.default_object_id +join sys.tables tbl on col.object_id=tbl.object_id +where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName) + .FirstOrDefault() ?? string.Empty; + } + public override bool DoesTableExist(IDatabase db, string tableName) { var result = @@ -276,7 +287,7 @@ order by T.name, I.name"); return null; } - public override string DeleteDefaultConstraint => "ALTER TABLE [{0}] DROP CONSTRAINT [DF_{0}_{1}]"; + public override string DeleteDefaultConstraint => "ALTER TABLE {0} DROP CONSTRAINT {2}"; public override string DropIndex => "DROP INDEX {0} ON {1}"; diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 5b6a9afb04..210d8e4cf1 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -223,6 +223,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax public abstract IEnumerable> GetDefinedIndexes(IDatabase db); + public abstract string GetDefaultConstraint(IDatabase db, string tableName, string columnName); + public virtual bool DoesTableExist(IDatabase db, string tableName) { return false; @@ -552,6 +554,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax public virtual string CreateConstraint => "ALTER TABLE {0} ADD CONSTRAINT {1} {2} ({3})"; public virtual string DeleteConstraint => "ALTER TABLE {0} DROP CONSTRAINT {1}"; public virtual string CreateForeignKeyConstraint => "ALTER TABLE {0} ADD CONSTRAINT {1} FOREIGN KEY ({2}) REFERENCES {3} ({4}){5}{6}"; + public virtual string CreateDefaultConstraint => "ALTER TABLE {0} ADD CONSTRAINT {1} DEFAULT ({2}) FOR {3}"; public virtual string ConvertIntegerToOrderableString => "REPLACE(STR({0}, 8), SPACE(1), '0')"; public virtual string ConvertDateToOrderableString => "CONVERT(nvarchar, {0}, 102)"; diff --git a/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs b/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs index ba5de9dbd3..c13d141fa5 100644 --- a/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs +++ b/src/Umbraco.Tests/Migrations/AdvancedMigrationTests.cs @@ -219,9 +219,9 @@ namespace Umbraco.Tests.Migrations // drops *all* tables keys and indexes var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToList(); foreach (var table in tables) - Delete.KeysAndIndexes(table, false).Do(); + Delete.KeysAndIndexes(table, false, true).Do(); foreach (var table in tables) - Delete.KeysAndIndexes(table, true, false, false).Do(); + Delete.KeysAndIndexes(table, true, false).Do(); } }