Fixes: U4-4430 MySql primary key issues

This commit is contained in:
Shannon
2014-03-14 17:11:21 +11:00
parent bbe6194b06
commit e1a134f587
9 changed files with 307 additions and 148 deletions

View File

@@ -10,7 +10,7 @@ namespace Umbraco.Core.Models.Rdbms
internal class PreviewXmlDto
{
[Column("nodeId")]
[PrimaryKeyColumn(AutoIncrement = false, Name = "", OnColumns = "nodeId, versionId")]
[PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsContentPreviewXml", OnColumns = "nodeId, versionId")]
[ForeignKey(typeof(ContentDto), Column = "nodeId")]
public int NodeId { get; set; }

View File

@@ -74,7 +74,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
return new Version(6, 0, 0);
}
//if the error is for an index
//if the error is for this IX_umbracoNodeTrashed which is added in 6.2, then it must mean we are on 6.1
if (Errors.Any(x => x.Item1.Equals("Index") && (x.Item2.InvariantEquals("IX_umbracoNodeTrashed"))))
{
return new Version(6, 1, 0);

View File

@@ -69,7 +69,7 @@ namespace Umbraco.Core.Persistence.Migrations
{
ExecuteMigrations(schemaMigrationContext, database);
}
catch (Exception)
catch (Exception ex)
{
//if this fails then the transaction will be rolled back, BUT if we are using MySql this is not the case,
//since it does not support schema changes in a transaction, see: http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
@@ -77,11 +77,21 @@ namespace Umbraco.Core.Persistence.Migrations
if (databaseProvider == DatabaseProviders.MySql)
{
var downgrades = OrderedDowngradeMigrations(foundMigrations.Where(x => (x is SchemaMigration))).ToList();
var downgradeMigrationContext = InitializeMigrations(downgrades, database, databaseProvider, false);
//lets hope that works! - if something cannot be rolled back then a CatastrophicDataLossException should
// be thrown.
ExecuteMigrations(downgradeMigrationContext, database);
try
{
var downgrades = OrderedDowngradeMigrations(foundMigrations.Where(x => (x is SchemaMigration))).ToList();
var downgradeMigrationContext = InitializeMigrations(downgrades, database, databaseProvider, false);
//lets hope that works! - if something cannot be rolled back then a CatastrophicDataLossException should
// be thrown.
ExecuteMigrations(downgradeMigrationContext, database);
}
catch (Exception e)
{
throw new CatastrophicDataLossException(
"An error occurred running a schema migration but the changes could not be rolled back. Error: " + ex.Message + ". In some cases, it may be required that the database be restored to it's original state before running this upgrade process again.",
ex);
}
}
//continue throwing the exception

View File

@@ -0,0 +1,97 @@
using System.Linq;
using System.Web.UI;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
{
//see: http://issues.umbraco.org/issue/U4-4430
[Migration("6.2.0", 3, GlobalSettings.UmbracoMigrationName)]
public class AssignMissingPrimaryForMySqlKeys : SchemaMigration
{
public override void Up()
{
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentType2ContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsContentType2ContentType")
.OnTable("cmsContentType2ContentType")
.Columns(new[] {"parentContentTypeId", "childContentTypeId"});
}
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsContentTypeAllowedContentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsContentTypeAllowedContentType")
.OnTable("cmsContentTypeAllowedContentType")
.Columns(new[] { "Id", "AllowedId" });
}
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsDocumentType") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsDocumentType")
.OnTable("cmsDocumentType")
.Columns(new[] { "contentTypeNodeId", "templateNodeId" });
}
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsMember2MemberGroup") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsMember2MemberGroup")
.OnTable("cmsMember2MemberGroup")
.Columns(new[] { "Member", "MemberGroup" });
}
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsPreviewXml") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsContentPreviewXml")
.OnTable("cmsPreviewXml")
.Columns(new[] { "nodeId", "versionId" });
}
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("cmsTagRelationship") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_cmsTagRelationship")
.OnTable("cmsTagRelationship")
.Columns(new[] { "nodeId", "tagId" });
}
//This should be 2 because this table has 2 keys
if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2app") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_user2app")
.OnTable("umbracoUser2app")
.Columns(new[] { "user", "app" });
}
//This should be 2 because this table has 3 keys
if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2NodeNotify") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_umbracoUser2NodeNotify")
.OnTable("umbracoUser2NodeNotify")
.Columns(new[] { "userId", "nodeId", "action" });
}
//This should be 2 because this table has 3 keys
if (constraints.Count(x => x.Item1.InvariantEquals("umbracoUser2NodePermission") && x.Item3.InvariantEquals("PRIMARY")) == 0)
{
Create.PrimaryKey("PK_umbracoUser2NodePermission")
.OnTable("umbracoUser2NodePermission")
.Columns(new[] { "userId", "nodeId", "permission" });
}
}
}
public override void Down()
{
//don't do anything, these keys should have always existed!
}
}
}

View File

@@ -218,7 +218,7 @@ ORDER BY TABLE_NAME, INDEX_NAME",
{
string primaryKey = string.Empty;
var columnDefinition = table.Columns.FirstOrDefault(x => x.IsPrimaryKey);
if (columnDefinition != null && columnDefinition.PrimaryKeyColumns.Contains(",") == false)
if (columnDefinition != null)
{
string columns = string.IsNullOrEmpty(columnDefinition.PrimaryKeyColumns)
? GetQuotedColumnName(columnDefinition.Name)

View File

@@ -216,6 +216,7 @@
<Compile Include="Persistence\Mappers\MemberGroupMapper.cs" />
<Compile Include="Persistence\Migrations\CatastrophicDataLossException.cs" />
<Compile Include="Persistence\Migrations\SchemaMigration.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\AssignMissingPrimaryForMySqlKeys.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\ChangePasswordColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSixTwoZero\UpdateToNewMemberPropertyAliases.cs" />
<Compile Include="Persistence\Querying\SqlStringExtensions.cs" />

View File

@@ -0,0 +1,49 @@
using System;
using NUnit.Framework;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Tests.Persistence.SyntaxProvider
{
[NUnit.Framework.Ignore("This doesn't actually test anything")]
[TestFixture]
public class MySqlSyntaxProviderTests
{
[SetUp]
public void SetUp()
{
SqlSyntaxContext.SqlSyntaxProvider = MySqlSyntax.Provider;
}
[Test]
public void Can_Generate_Create_Table_Statement()
{
var type = typeof(TagRelationshipDto);
var definition = DefinitionFactory.GetTableDefinition(type);
string create = SqlSyntaxContext.SqlSyntaxProvider.Format(definition);
string primaryKey = SqlSyntaxContext.SqlSyntaxProvider.FormatPrimaryKey(definition);
var indexes = SqlSyntaxContext.SqlSyntaxProvider.Format(definition.Indexes);
var keys = SqlSyntaxContext.SqlSyntaxProvider.Format(definition.ForeignKeys);
Console.WriteLine(create);
Console.WriteLine(primaryKey);
foreach (var sql in keys)
{
Console.WriteLine(sql);
}
foreach (var sql in indexes)
{
Console.WriteLine(sql);
}
}
[TearDown]
public void TearDown()
{
SqlSyntaxContext.SqlSyntaxProvider = null;
}
}
}

View File

@@ -1,139 +1,140 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Create.Index;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Tests.Persistence.SyntaxProvider
{
[TestFixture]
public class SqlSyntaxProviderTests
{
[SetUp]
public void SetUp()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlCeSyntax.Provider;
}
[Test]
public void Can_Generate_Delete_SubQuery_Statement()
{
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var subQuery = new Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType);
var sql = SqlSyntaxContext.SqlSyntaxProvider.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Assert.AreEqual(@"DELETE FROM [cmsContentXml] WHERE [nodeId] IN (SELECT [nodeId] FROM (SELECT DISTINCT cmsContentXml.nodeId
FROM [cmsContentXml]
INNER JOIN [umbracoNode]
ON [cmsContentXml].[nodeId] = [umbracoNode].[id]
WHERE ([umbracoNode].[nodeObjectType] = 'b796f64c-1f99-4ffb-b886-4bf4bc011a9c')) x)".Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "), sql.Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "));
}
[Test]
public void Can_Generate_Create_Table_Statement()
{
var type = typeof (NodeDto);
var definition = DefinitionFactory.GetTableDefinition(type);
string create = SqlSyntaxContext.SqlSyntaxProvider.Format(definition);
string primaryKey = SqlSyntaxContext.SqlSyntaxProvider.FormatPrimaryKey(definition);
var indexes = SqlSyntaxContext.SqlSyntaxProvider.Format(definition.Indexes);
var keys = SqlSyntaxContext.SqlSyntaxProvider.Format(definition.ForeignKeys);
Console.WriteLine(create);
Console.WriteLine(primaryKey);
foreach (var sql in keys)
{
Console.WriteLine(sql);
}
foreach (var sql in indexes)
{
Console.WriteLine(sql);
}
}
[Test]
public void Format_SqlServer_NonClusteredIndexDefinition_AddsNonClusteredDirective()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var indexDefinition = CreateIndexDefinition();
indexDefinition.IndexType = IndexTypes.NonClustered;
var actual = SqlSyntaxContext.SqlSyntaxProvider.Format(indexDefinition);
Assert.AreEqual("CREATE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", actual);
}
[Test]
public void Format_SqlServer_NonClusteredIndexDefinition_UsingIsClusteredFalse_AddsClusteredDirective()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var indexDefinition = CreateIndexDefinition();
indexDefinition.IsClustered = false;
var actual = SqlSyntaxContext.SqlSyntaxProvider.Format(indexDefinition);
Assert.AreEqual("CREATE CLUSTERED INDEX [IX_A] ON [TheTable] ([A])", actual);
}
[Test]
public void CreateIndexBuilder_SqlServer_NonClustered_CreatesNonClusteredIndex()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var createExpression = new CreateIndexExpression { Index = { Name = "IX_A" } };
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().WithOptions().NonClustered();
Assert.AreEqual("CREATE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Unique_CreatesUniqueNonClusteredIndex()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var createExpression = new CreateIndexExpression { Index = { Name = "IX_A" } };
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().WithOptions().Unique();
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Clustered_CreatesClusteredIndex()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var createExpression = new CreateIndexExpression { Index = { Name = "IX_A" } };
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().WithOptions().Clustered();
Assert.AreEqual("CREATE CLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
private static IndexDefinition CreateIndexDefinition()
{
return new IndexDefinition
{
ColumnName = "A",
Name = "IX_A",
TableName = "TheTable",
SchemaName = "dbo"
};
}
[TearDown]
public void TearDown()
{
SqlSyntaxContext.SqlSyntaxProvider = null;
}
}
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Create.Index;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Tests.Persistence.SyntaxProvider
{
[TestFixture]
public class SqlCeSyntaxProviderTests
{
[SetUp]
public void SetUp()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlCeSyntax.Provider;
}
[Test]
public void Can_Generate_Delete_SubQuery_Statement()
{
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var subQuery = new Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType);
var sql = SqlSyntaxContext.SqlSyntaxProvider.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Assert.AreEqual(@"DELETE FROM [cmsContentXml] WHERE [nodeId] IN (SELECT [nodeId] FROM (SELECT DISTINCT cmsContentXml.nodeId
FROM [cmsContentXml]
INNER JOIN [umbracoNode]
ON [cmsContentXml].[nodeId] = [umbracoNode].[id]
WHERE ([umbracoNode].[nodeObjectType] = 'b796f64c-1f99-4ffb-b886-4bf4bc011a9c')) x)".Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "), sql.Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "));
}
[NUnit.Framework.Ignore("This doesn't actually test anything")]
[Test]
public void Can_Generate_Create_Table_Statement()
{
var type = typeof (NodeDto);
var definition = DefinitionFactory.GetTableDefinition(type);
string create = SqlSyntaxContext.SqlSyntaxProvider.Format(definition);
string primaryKey = SqlSyntaxContext.SqlSyntaxProvider.FormatPrimaryKey(definition);
var indexes = SqlSyntaxContext.SqlSyntaxProvider.Format(definition.Indexes);
var keys = SqlSyntaxContext.SqlSyntaxProvider.Format(definition.ForeignKeys);
Console.WriteLine(create);
Console.WriteLine(primaryKey);
foreach (var sql in keys)
{
Console.WriteLine(sql);
}
foreach (var sql in indexes)
{
Console.WriteLine(sql);
}
}
[Test]
public void Format_SqlServer_NonClusteredIndexDefinition_AddsNonClusteredDirective()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var indexDefinition = CreateIndexDefinition();
indexDefinition.IndexType = IndexTypes.NonClustered;
var actual = SqlSyntaxContext.SqlSyntaxProvider.Format(indexDefinition);
Assert.AreEqual("CREATE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", actual);
}
[Test]
public void Format_SqlServer_NonClusteredIndexDefinition_UsingIsClusteredFalse_AddsClusteredDirective()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var indexDefinition = CreateIndexDefinition();
indexDefinition.IsClustered = false;
var actual = SqlSyntaxContext.SqlSyntaxProvider.Format(indexDefinition);
Assert.AreEqual("CREATE CLUSTERED INDEX [IX_A] ON [TheTable] ([A])", actual);
}
[Test]
public void CreateIndexBuilder_SqlServer_NonClustered_CreatesNonClusteredIndex()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var createExpression = new CreateIndexExpression { Index = { Name = "IX_A" } };
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().WithOptions().NonClustered();
Assert.AreEqual("CREATE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Unique_CreatesUniqueNonClusteredIndex()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var createExpression = new CreateIndexExpression { Index = { Name = "IX_A" } };
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().WithOptions().Unique();
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
[Test]
public void CreateIndexBuilder_SqlServer_Clustered_CreatesClusteredIndex()
{
SqlSyntaxContext.SqlSyntaxProvider = SqlServerSyntax.Provider;
var createExpression = new CreateIndexExpression { Index = { Name = "IX_A" } };
var builder = new CreateIndexBuilder(createExpression);
builder.OnTable("TheTable").OnColumn("A").Ascending().WithOptions().Clustered();
Assert.AreEqual("CREATE CLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
}
private static IndexDefinition CreateIndexDefinition()
{
return new IndexDefinition
{
ColumnName = "A",
Name = "IX_A",
TableName = "TheTable",
SchemaName = "dbo"
};
}
[TearDown]
public void TearDown()
{
SqlSyntaxContext.SqlSyntaxProvider = null;
}
}
}

View File

@@ -220,6 +220,7 @@
<Compile Include="Persistence\Repositories\TemplateRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\UserRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\UserTypeRepositoryTest.cs" />
<Compile Include="Persistence\SyntaxProvider\MySqlSyntaxProviderTests.cs" />
<Compile Include="PublishedContent\PublishedContentExtensionTests.cs" />
<Compile Include="PublishedContent\PublishedContentRequestEngineTests.cs" />
<Compile Include="PublishedContent\StronglyTypedModels\CallingMethodTests.cs" />
@@ -276,7 +277,7 @@
<Compile Include="Persistence\Querying\MediaTypeRepositorySqlClausesTest.cs" />
<Compile Include="Persistence\Repositories\ServerRegistrationRepositoryTest.cs" />
<Compile Include="Persistence\SchemaValidationTest.cs" />
<Compile Include="Persistence\SyntaxProvider\SqlSyntaxProviderTests.cs" />
<Compile Include="Persistence\SyntaxProvider\SqlCeSyntaxProviderTests.cs" />
<Compile Include="PublishedContent\DynamicXmlConverterTests.cs" />
<Compile Include="PublishedContent\DynamicXmlTests.cs" />
<Compile Include="PublishedContent\LegacyExamineBackedMediaTests.cs" />