From be2353aa0af81ad30988ecb6f44893cd992a8ff1 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 1 Dec 2025 11:51:57 +0100 Subject: [PATCH 1/3] Add migration to fix umbracoPropertyData column casing. --- .../Migrations/Upgrade/UmbracoPlan.cs | 3 ++ .../EnsureUmbracoPropertyDataColumnCasing.cs | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index 3e0ecb7f94f3..600ce902a2b7 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -141,6 +141,9 @@ protected virtual void DefinePlan() To("{8B2C830A-4FFB-4433-8337-8649B0BF52C8}"); To("{1C38D589-26BB-4A46-9ABE-E4A0DF548A87}"); + // To 17.1.0 + To("{BE5CA411-E12D-4455-A59E-F12A669E5363}"); + // To 18.0.0 // TODO (V18): Enable on 18 branch //// To("{74332C49-B279-4945-8943-F8F00B1F5949}"); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs new file mode 100644 index 000000000000..816689865458 --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs @@ -0,0 +1,35 @@ +using NPoco; +using Umbraco.Cms.Infrastructure.Persistence; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_17_1_0; + +/// +/// Ensures the propertyTypeId column in umbracoPropertyData has correct camel case naming. +/// +public class EnsureUmbracoPropertyDataColumnCasing : AsyncMigrationBase +{ + /// + /// Initializes a new instance of the class. + /// + public EnsureUmbracoPropertyDataColumnCasing(IMigrationContext context) + : base(context) + { + } + + /// + protected override async Task MigrateAsync() + { + if (DatabaseType == DatabaseType.SQLite) + { + return; + } + + // SQL Server is case sensitive for columns used in a SQL Bulk insert statement (which is used in publishing + // operations on umbracoPropertyData). + // Earlier versions of Umbraco used all lower case for the propertyTypeId column name (propertytypeid), whereas newer versions + // use camel case (propertyTypeId). + Sql sql = Database.SqlContext.Sql( + "EXEC sp_rename N'umbracoPropertyData.propertytypeid', N'propertyTypeId', N'COLUMN'"); + await Database.ExecuteAsync(sql); + } +} From abd19bd88ed347d0b46f12ff1eaa2828d26b4fc4 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 1 Dec 2025 12:23:19 +0100 Subject: [PATCH 2/3] Improve migration with column existence check and logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add ILogger to log when column is renamed - Check if column exists with incorrect casing before renaming - Use fluent Rename API instead of raw SQL - Add XML remarks documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../EnsureUmbracoPropertyDataColumnCasing.cs | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs index 816689865458..0a4ee74e455e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs @@ -1,35 +1,56 @@ +using Microsoft.Extensions.Logging; using NPoco; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Infrastructure.Persistence; +using static Umbraco.Cms.Core.Constants; +using ColumnInfo = Umbraco.Cms.Infrastructure.Persistence.SqlSyntax.ColumnInfo; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_17_1_0; /// /// Ensures the propertyTypeId column in umbracoPropertyData has correct camel case naming. /// +/// +/// SQL Server is case sensitive for columns used in a SQL Bulk insert statement(which is used in publishing +/// operations on umbracoPropertyData). +/// Earlier versions of Umbraco used all lower case for the propertyTypeId column name (propertytypeid), whereas newer versions +/// use camel case (propertyTypeId). +/// public class EnsureUmbracoPropertyDataColumnCasing : AsyncMigrationBase { + private readonly ILogger _logger; + /// /// Initializes a new instance of the class. /// - public EnsureUmbracoPropertyDataColumnCasing(IMigrationContext context) - : base(context) - { - } + public EnsureUmbracoPropertyDataColumnCasing(IMigrationContext context, ILogger logger) + : base(context) => _logger = logger; /// - protected override async Task MigrateAsync() + protected override Task MigrateAsync() { + // We only need to do this for SQL Server. if (DatabaseType == DatabaseType.SQLite) { - return; + return Task.CompletedTask; + } + + const string ColumnName = "propertyTypeId"; + ColumnInfo[] columns = [.. SqlSyntax.GetColumnsInSchema(Context.Database)]; + ColumnInfo? targetColumn = columns + .FirstOrDefault(x => x.TableName == DatabaseSchema.Tables.PropertyData && string.Equals(x.ColumnName, ColumnName.ToLower(), StringComparison.InvariantCulture)); + if (targetColumn is not null) + { + // The column exists with incorrect casing, we need to rename it. + Rename.Column(ColumnName.ToLower()) + .OnTable(DatabaseSchema.Tables.PropertyData) + .To(ColumnName) + .Do(); + + _logger.LogInformation("Renamed column {OldColumnName} to {NewColumnName} on table {TableName}", ColumnName.ToLower(), ColumnName, DatabaseSchema.Tables.PropertyData); } - // SQL Server is case sensitive for columns used in a SQL Bulk insert statement (which is used in publishing - // operations on umbracoPropertyData). - // Earlier versions of Umbraco used all lower case for the propertyTypeId column name (propertytypeid), whereas newer versions - // use camel case (propertyTypeId). - Sql sql = Database.SqlContext.Sql( - "EXEC sp_rename N'umbracoPropertyData.propertytypeid', N'propertyTypeId', N'COLUMN'"); - await Database.ExecuteAsync(sql); + return Task.CompletedTask; } } From 73d0ac4fece8d3ac18523e25f3de08ae509debdc Mon Sep 17 00:00:00 2001 From: kjac Date: Mon, 1 Dec 2025 17:29:49 +0100 Subject: [PATCH 3/3] Clarify what old and new column name really is --- .../V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs index 0a4ee74e455e..9aba6acd1ffc 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_17_1_0/EnsureUmbracoPropertyDataColumnCasing.cs @@ -36,19 +36,20 @@ protected override Task MigrateAsync() return Task.CompletedTask; } - const string ColumnName = "propertyTypeId"; + const string oldColumnName = "propertytypeid"; + const string newColumnName = "propertyTypeId"; ColumnInfo[] columns = [.. SqlSyntax.GetColumnsInSchema(Context.Database)]; ColumnInfo? targetColumn = columns - .FirstOrDefault(x => x.TableName == DatabaseSchema.Tables.PropertyData && string.Equals(x.ColumnName, ColumnName.ToLower(), StringComparison.InvariantCulture)); + .FirstOrDefault(x => x.TableName == DatabaseSchema.Tables.PropertyData && string.Equals(x.ColumnName, oldColumnName, StringComparison.InvariantCulture)); if (targetColumn is not null) { // The column exists with incorrect casing, we need to rename it. - Rename.Column(ColumnName.ToLower()) + Rename.Column(oldColumnName) .OnTable(DatabaseSchema.Tables.PropertyData) - .To(ColumnName) + .To(newColumnName) .Do(); - _logger.LogInformation("Renamed column {OldColumnName} to {NewColumnName} on table {TableName}", ColumnName.ToLower(), ColumnName, DatabaseSchema.Tables.PropertyData); + _logger.LogInformation("Renamed column {OldColumnName} to {NewColumnName} on table {TableName}", oldColumnName, newColumnName, DatabaseSchema.Tables.PropertyData); } return Task.CompletedTask;