From 7b99efb061f0aad74cd8b74e77168dde6ee7827c Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 16 Oct 2014 11:24:29 +1000 Subject: [PATCH] Working on: U4-4133 Database key changes are not fixed in the Umbraco 7 installer! --- .../AlterCmsMacroPropertyTable.cs | 56 +++++++++++++++++-- .../AlterTagRelationsTable.cs | 26 +++++++-- .../UpdateRelatedLinksData.cs | 2 + .../TargetVersionSix/UpdateCmsContentTable.cs | 34 +++++++++++ .../UpdateCmsContentTypeTable.cs | 25 ++++++++- .../SqlSyntax/SqlServerSyntaxProvider.cs | 13 ++++- src/Umbraco.Core/Umbraco.Core.csproj | 1 + 7 files changed, 144 insertions(+), 13 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTable.cs diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs index 93afeee7bb..e4425673f2 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs @@ -1,5 +1,12 @@ using System; +using System.Linq; using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Migrations.Syntax.Delete.DefaultConstraint; +using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions; +using Umbraco.Core.Persistence.Migrations.Syntax.Execute; +using Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven @@ -16,13 +23,52 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { //now that the controlId column is renamed and now a string we need to convert if (Context == null || Context.Database == null) return; - - //"DF_cmsMacroProperty_macroPropertyHidden"" - Delete.DefaultConstraint().OnTable("cmsMacroProperty").OnColumn("macroPropertyHidden"); + //var cpt = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerTable(Context.Database); + //var di = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database); + + if (Context.CurrentDatabaseProvider != DatabaseProviders.SqlServer) + { + Delete.DefaultConstraint().OnTable("cmsMacroProperty").OnColumn("macroPropertyHidden"); + } + else + { + //If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these default constraints + // consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue: + // http://issues.umbraco.org/issue/U4-4133 + var sqlServerSyntaxProvider = new SqlServerSyntaxProvider(); + var defaultConstraints = sqlServerSyntaxProvider.GetDefaultConstraintsPerColumn(Context.Database).Distinct(); + + //lookup the constraint we want to delete, normally would be called "DF_cmsMacroProperty_macroPropertyHidden" but + // we cannot be sure with really old versions + var constraint = defaultConstraints + .SingleOrDefault(x => x.Item1 == "cmsMacroProperty" && x.Item2 == "macroPropertyHidden"); + if (constraint != null) + { + Execute.Sql(string.Format("ALTER TABLE [{0}] DROP CONSTRAINT [{1}]", "cmsMacroProperty", constraint.Item3)); + } + } + Delete.Column("macroPropertyHidden").FromTable("cmsMacroProperty"); - Delete.ForeignKey().FromTable("cmsMacroProperty").ForeignColumn("macroPropertyType").ToTable("cmsMacroPropertyType").PrimaryColumn("id"); + if (Context.CurrentDatabaseProvider != DatabaseProviders.SqlServer) + { + Delete.ForeignKey().FromTable("cmsMacroProperty").ForeignColumn("macroPropertyType").ToTable("cmsMacroPropertyType").PrimaryColumn("id"); + } + else + { + //If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these key constraints + // consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue: + // http://issues.umbraco.org/issue/U4-4133 + + var keyConstraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct(); + var constraint = keyConstraints + .SingleOrDefault(x => x.Item1 == "cmsMacroProperty" && x.Item2 == "macroPropertyType" && x.Item3.InvariantStartsWith("PK_") == false); + if (constraint != null) + { + Delete.ForeignKey(constraint.Item3).OnTable("cmsMacroProperty"); + } + } Alter.Table("cmsMacroProperty").AddColumn("editorAlias").AsString(255).NotNullable().WithDefaultValue(""); @@ -46,7 +92,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven //drop the column now Delete.Column("macroPropertyType").FromTable("cmsMacroProperty"); - //drop the default constraing + //drop the default constraint Delete.DefaultConstraint().OnTable("cmsMacroProperty").OnColumn("editorAlias"); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs index 07c2a02475..8ccb08d298 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs @@ -25,19 +25,35 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven private void Initial() { + var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); + //create a new col which we will make a foreign key, but first needs to be populated with data. Alter.Table("cmsTagRelationship").AddColumn("propertyTypeId").AsInt32().Nullable(); - - //drop the foreign key on umbracoNode. Must drop foreign key first before primary key can be removed in MySql. - Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id"); + //drop the foreign key on umbracoNode. Must drop foreign key first before primary key can be removed in MySql. + if (Context.CurrentDatabaseProvider != DatabaseProviders.SqlServer) + { + Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id"); + } + else + { + //If we are on SQLServer, we need to delete constraints by name, older versions of umbraco did not name these key constraints + // consistently so we need to look up the constraint name to delete, this only pertains to SQL Server and this issue: + // http://issues.umbraco.org/issue/U4-4133 + + var constraint = constraints + .SingleOrDefault(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "nodeId" && x.Item3.InvariantStartsWith("PK_") == false); + if (constraint != null) + { + Delete.ForeignKey(constraint.Item3).OnTable("cmsTagRelationship"); + } + } //we need to drop the primary key, this is sql specific since MySQL has never had primary keys on this table // at least since 6.0 and the new installation way but perhaps it had them way back in 4.x so we need to check // it exists before trying to drop it. if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql) - { - var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray(); + { //this will let us know if this pk exists on this table if (constraints.Count(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantEquals("PRIMARY")) > 0) { diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs index 776aa47c12..d1fb7018b7 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs @@ -30,6 +30,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven var dtSql = new Sql().Select("nodeId").From().Where(dto => dto.PropertyEditorAlias == Constants.PropertyEditors.RelatedLinksAlias); var dataTypeIds = database.Fetch(dtSql); + if (!dataTypeIds.Any()) return string.Empty; + var propertyData = database.Fetch( "WHERE propertyTypeId in (SELECT id from cmsPropertyType where dataTypeID IN (@dataTypeIds))", new { dataTypeIds = dataTypeIds }); diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTable.cs new file mode 100644 index 0000000000..24c3ae1086 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTable.cs @@ -0,0 +1,34 @@ +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix +{ + [Migration("6.0.0", 11, GlobalSettings.UmbracoMigrationName)] + public class UpdateCmsContentTable : MigrationBase + { + public override void Up() + { + //Some very old schemas don't have an index on the cmsContent.nodeId column, I'm not actually sure when it was added but + // it is absolutely required to exist in order to add other foreign keys and much better for perf, so we'll need to check it's existence + // this came to light from this issue: http://issues.umbraco.org/issue/U4-4133 + var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database) + .Select(x => new DbIndexDefinition() + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContent")) == false) + { + Create.Index("IX_cmsContent").OnTable("cmsContent").OnColumn("nodeId").Ascending().WithOptions().Unique(); + } + } + + public override void Down() + { + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs index 3e6b4cf05c..33d24098a1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs @@ -1,4 +1,7 @@ -using Umbraco.Core.Configuration; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { @@ -10,6 +13,26 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix Alter.Table("cmsContentType").AddColumn("isContainer").AsBoolean().NotNullable().WithDefaultValue(0); Alter.Table("cmsContentType").AddColumn("allowAtRoot").AsBoolean().NotNullable().WithDefaultValue(0); + + //Some very old schemas don't have an index on the cmsContentType.nodeId column, I'm not actually sure when it was added but + // it is absolutely required to exist in order to add other foreign keys and much better for perf, so we'll need to check it's existence + // this came to light from this issue: http://issues.umbraco.org/issue/U4-4133 + var dbIndexes = SqlSyntaxContext.SqlSyntaxProvider.GetDefinedIndexes(Context.Database) + .Select(x => new DbIndexDefinition() + { + TableName = x.Item1, + IndexName = x.Item2, + ColumnName = x.Item3, + IsUnique = x.Item4 + }).ToArray(); + if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsContentType")) == false) + { + Create.Index("IX_cmsContentType").OnTable("cmsContentType").OnColumn("nodeId").Ascending().WithOptions().Unique(); + } + if (dbIndexes.Any(x => x.TableName.InvariantEquals("cmsContentType") && x.ColumnName.InvariantEquals("icon")) == false) + { + Create.Index("IX_cmsContentType_icon").OnTable("cmsContentType").OnColumn("icon").Ascending().WithOptions().NonClustered(); + } } public override void Down() diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs index 3388b984ee..1228f699b9 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlServerSyntaxProvider.cs @@ -32,8 +32,17 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// Gets/sets the version of the current SQL server instance /// internal Lazy VersionName { get; set; } - - + + /// + /// SQL Server stores default values assigned to columns as constraints, it also stores them with named values, this is the only + /// server type that does this, therefore this method doesn't exist on any other syntax provider + /// + /// + public IEnumerable> GetDefaultConstraintsPerColumn(Database db) + { + var items = db.Fetch("SELECT TableName = t.Name,ColumnName = c.Name,dc.Name,dc.[Definition] FROM sys.tables t INNER JOIN sys.default_constraints dc ON t.object_id = dc.parent_object_id INNER JOIN sys.columns c ON dc.parent_object_id = c.object_id AND c.column_id = dc.parent_column_id"); + return items.Select(x => new Tuple(x.TableName, x.ColumnName, x.Name, x.Definition)); + } public override IEnumerable GetTablesInSchema(Database db) { diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index afaf5d4a0b..7e107be4b2 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -399,6 +399,7 @@ +