diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs
index df5ed33bd3..4186eb0d73 100644
--- a/src/Umbraco.Core/DatabaseContext.cs
+++ b/src/Umbraco.Core/DatabaseContext.cs
@@ -579,10 +579,11 @@ namespace Umbraco.Core
message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
- var installedVersion = schemaResult.DetermineInstalledVersion();
+
+ var installedSchemaVersion = schemaResult.DetermineInstalledVersion();
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
- if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedVersion.Equals(new Version(0, 0, 0)))
+ if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedSchemaVersion.Equals(new Version(0, 0, 0)))
{
var helper = new DatabaseSchemaHelper(database, _logger, SqlSyntax);
helper.CreateDatabaseSchema(true, applicationContext);
@@ -634,14 +635,30 @@ namespace Umbraco.Core
var message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
- var installedVersion = schemaResult.DetermineInstalledVersion();
+
+ var installedSchemaVersion = schemaResult.DetermineInstalledVersion();
+ var installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
+
+ var targetVersion = UmbracoVersion.Current;
+ //In some cases - like upgrading from 7.2.6 -> 7.3, there will be no migration information in the database and therefore it will
+ // return a version of 0.0.0 and we don't necessarily want to run all migrations from 0 -> 7.3, so we'll just ensure that the
+ // migrations are run for the target version
+ if (installedMigrationVersion == new Version(0, 0, 0) && installedSchemaVersion > new Version(0, 0, 0))
+ {
+ //set the installedMigrationVersion to be one less than the target so the latest migrations are guaranteed to execute
+ installedMigrationVersion = targetVersion.SubtractRevision();
+ }
+
//DO the upgrade!
var currentVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
- ? installedVersion
- : new Version(GlobalSettings.ConfigurationStatus);
- var targetVersion = UmbracoVersion.Current;
+ //Take the minimum version between the detected schema version and the installed migration version
+ ? new[] {installedSchemaVersion, installedMigrationVersion}.Min()
+ //Take the minimum version between the installed migration version and the version specified in the config
+ : new[] { new Version(GlobalSettings.ConfigurationStatus), installedMigrationVersion }.Min();
+
+
var runner = new MigrationRunner(migrationEntryService, _logger, currentVersion, targetVersion, GlobalSettings.UmbracoMigrationName);
var upgraded = runner.Execute(database, true);
message = message + "
Upgrade completed!
";
diff --git a/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs b/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs
index 01feb74eaa..712acdfabe 100644
--- a/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs
@@ -8,7 +8,13 @@ namespace Umbraco.Core.Persistence.Factories
{
public MigrationEntry BuildEntity(MigrationDto dto)
{
- var model = new MigrationEntry(dto.Id, dto.CreateDate, dto.Name, Version.Parse(dto.Version));
+ Version parsed;
+ if (Version.TryParse(dto.Version, out parsed) == false)
+ {
+ throw new FormatException("Cannot parse the version string in the database to a real Version object: " + dto.Version);
+ }
+
+ var model = new MigrationEntry(dto.Id, dto.CreateDate, dto.Name, parsed);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
model.ResetDirtyProperties(false);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
index c2acbd3c97..bd99799e11 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
@@ -5,6 +5,7 @@ using System.Text;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Migrations.Initial
{
@@ -35,7 +36,20 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
internal IEnumerable DbIndexDefinitions { get; set; }
///
- /// Determines the version of the currently installed database.
+ /// Checks in the db which version is installed based on the migrations that have been run
+ ///
+ ///
+ ///
+ public Version DetermineInstalledVersionByMigrations(IMigrationEntryService migrationEntryService)
+ {
+ var allMigrations = migrationEntryService.GetAll(GlobalSettings.UmbracoMigrationName);
+ var mostrecent = allMigrations.OrderByDescending(x => x.Version).Select(x => x.Version).FirstOrDefault();
+
+ return mostrecent ?? new Version(0, 0, 0);
+ }
+
+ ///
+ /// Determines the version of the currently installed database by detecting the current database structure
///
///
/// A with Major and Minor values for
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
index 8d19cff387..412d97617c 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Migrations
[Obsolete("Use the ctor that specifies all dependencies instead")]
public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName)
- : this(logger, currentVersion, targetVersion, productName, Enumerable.Empty().ToArray())
+ : this(logger, currentVersion, targetVersion, productName, null)
{
}
@@ -53,7 +53,8 @@ namespace Umbraco.Core.Persistence.Migrations
_currentVersion = currentVersion;
_targetVersion = targetVersion;
_productName = productName;
- _migrations = migrations;
+ //ensure this is null if there aren't any
+ _migrations = migrations.Length == 0 ? null : migrations;
}
///
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs
index 9e0bb26ab9..46fde95005 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs
@@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("lastPasswordChangeDate")) == false)
Create.Column("lastPasswordChangeDate").OnTable("umbracoUser").AsDateTime().Nullable();
- if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("lastLoginDate")) == false);
+ if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("lastLoginDate")) == false)
Create.Column("lastLoginDate").OnTable("umbracoUser").AsDateTime().Nullable();
}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 2259044292..c3cc462faf 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -1313,6 +1313,7 @@
+
diff --git a/src/Umbraco.Core/VersionExtensions.cs b/src/Umbraco.Core/VersionExtensions.cs
new file mode 100644
index 0000000000..9d60c308cf
--- /dev/null
+++ b/src/Umbraco.Core/VersionExtensions.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Core
+{
+ internal static class VersionExtensions
+ {
+ public static Version SubtractRevision(this Version version)
+ {
+ var parts = new List(new[] {version.Major, version.Minor, version.Build, version.Revision});
+
+ //remove all prefixed zero parts
+ while (parts[0] <= 0)
+ {
+ parts.RemoveAt(0);
+ if (parts.Count == 0) break;
+ }
+
+ for (int index = 0; index < parts.Count; index++)
+ {
+ var part = parts[index];
+ if (part <= 0)
+ {
+ parts.RemoveAt(index);
+ index++;
+ }
+ else
+ {
+ //break when there isn't a zero part
+ break;
+ }
+ }
+
+ if (parts.Count == 0) throw new InvalidOperationException("Cannot subtract a revision from a zero version");
+
+ var lastNonZero = parts.FindLastIndex(i => i > 0);
+
+ //subtract 1 from the last non-zero
+ parts[lastNonZero] = parts[lastNonZero] - 1;
+
+ //the last non zero is actually the revision so we can just return
+ if (lastNonZero == (parts.Count -1))
+ {
+ return FromList(parts);
+ }
+
+ //the last non zero isn't the revision so the remaining zero's need to be replaced with int.max
+ for (var i = lastNonZero + 1; i < parts.Count; i++)
+ {
+ parts[i] = int.MaxValue;
+ }
+
+ return FromList(parts);
+ }
+
+ private static Version FromList(IList parts)
+ {
+ while (parts.Count < 4)
+ {
+ parts.Insert(0, 0);
+ }
+ return new Version(parts[0], parts[1], parts[2], parts[3]);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/XmlHelper.cs b/src/Umbraco.Core/XmlHelper.cs
index 5fcca2863e..1978c329ab 100644
--- a/src/Umbraco.Core/XmlHelper.cs
+++ b/src/Umbraco.Core/XmlHelper.cs
@@ -12,7 +12,7 @@ using Umbraco.Core.IO;
namespace Umbraco.Core
{
- ///
+ ///
/// The XmlHelper class contains general helper methods for working with xml in umbraco.
///
public class XmlHelper
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 1ab01e3d24..b8954e98f0 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -551,6 +551,7 @@
+
diff --git a/src/Umbraco.Tests/VersionExtensionTests.cs b/src/Umbraco.Tests/VersionExtensionTests.cs
new file mode 100644
index 0000000000..ca8fea0393
--- /dev/null
+++ b/src/Umbraco.Tests/VersionExtensionTests.cs
@@ -0,0 +1,30 @@
+using System;
+using NUnit.Framework;
+using Umbraco.Core;
+
+namespace Umbraco.Tests
+{
+ [TestFixture]
+ public class VersionExtensionTests
+ {
+ [TestCase(1, 0, 0, 0, "0.2147483647.2147483647.2147483647")]
+ [TestCase(1, 1, 0, 0, "1.0.2147483647.2147483647")]
+ [TestCase(1, 1, 1, 0, "1.1.0.2147483647")]
+ [TestCase(1, 1, 1, 1, "1.1.1.0")]
+ [TestCase(0, 1, 0, 0, "0.0.2147483647.2147483647")]
+ [TestCase(0, 1, 1, 0, "0.1.0.2147483647")]
+ [TestCase(0, 1, 1, 1, "0.1.1.0")]
+ [TestCase(0, 0, 1, 0, "0.0.0.2147483647")]
+ [TestCase(0, 0, 1, 1, "0.0.1.0")]
+ [TestCase(0, 0, 0, 1, "0.0.0.0")]
+ [TestCase(7, 3, 0, 0, "7.2.2147483647.2147483647")]
+ public void Subract_Revision(int major, int minor, int build, int rev, string outcome)
+ {
+ var version = new Version(major, minor, build, rev);
+
+ var result = version.SubtractRevision();
+
+ Assert.AreEqual(new Version(outcome), result);
+ }
+ }
+}
\ No newline at end of file