diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs index 64ed9e3566..a1b9d2d8f0 100644 --- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs +++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerSyntaxProvider.cs @@ -272,6 +272,13 @@ where tbl.[name]=@0 and col.[name]=@1;", return !constraintName.IsNullOrWhiteSpace(); } + public override bool DoesPrimaryKeyExist(IDatabase db, string tableName, string primaryKeyName) + { + IEnumerable? keys = db.Fetch($"select * from sysobjects where xtype='pk' and parent_obj in (select id from sysobjects where name='{tableName}')") + .Where(x => x.Name == primaryKeyName); + return keys.FirstOrDefault() is not null; + } + public override bool DoesTableExist(IDatabase db, string tableName) { var result = @@ -453,4 +460,9 @@ _sqlInspector ??= new SqlInspectionUtilities(); } #endregion + + private class SqlPrimaryKey + { + public string Name { get; set; } = null!; + } } diff --git a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs index 3803d96490..af9147b29a 100644 --- a/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs +++ b/src/Umbraco.Cms.Persistence.Sqlite/Services/SqliteSyntaxProvider.cs @@ -174,6 +174,14 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase return false; } + public override bool DoesPrimaryKeyExist(IDatabase db, string tableName, string primaryKeyName) + { + IEnumerable items = db.Fetch($"select sql from sqlite_master where type = 'table' and name = '{tableName}'") + .Where(x => x.Contains($"CONSTRAINT {primaryKeyName} PRIMARY KEY")); + + return items.Any(); + } + public override string GetFieldNameForUpdate(Expression> fieldSelector, string? tableAlias = null) { var field = ExpressionHelper.FindProperty(fieldSelector).Item1 as PropertyInfo; diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs index 49775bcd0a..b548112d8f 100644 --- a/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs +++ b/src/Umbraco.Infrastructure/Migrations/MigrationBase_Extra.cs @@ -110,6 +110,11 @@ namespace Umbraco.Cms.Infrastructure.Migrations return indexes.Any(x => x.Item2.InvariantEquals(indexName)); } + protected bool PrimaryKeyExists(string tableName, string primaryKeyName) + { + return SqlSyntax.DoesPrimaryKeyExist(Context.Database, tableName, primaryKeyName); + } + protected bool ColumnExists(string tableName, string columnName) { ColumnInfo[]? columns = SqlSyntax.GetColumnsInSchema(Context.Database).Distinct().ToArray(); diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_5_0/AddPrimaryKeyConstrainToContentVersionCleanupDtos.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_5_0/AddPrimaryKeyConstrainToContentVersionCleanupDtos.cs index 331a6fced6..d1185bec46 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_5_0/AddPrimaryKeyConstrainToContentVersionCleanupDtos.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_5_0/AddPrimaryKeyConstrainToContentVersionCleanupDtos.cs @@ -10,18 +10,28 @@ public class AddPrimaryKeyConstrainToContentVersionCleanupDtos : MigrationBase protected override void Migrate() { - IEnumerable contentVersionCleanupPolicyDtos = - Database - .Fetch() - .OrderByDescending(x => x.Updated) - .DistinctBy(x => x.ContentTypeId); - + IEnumerable? contentVersionCleanupPolicyDtos = null; if (TableExists(ContentVersionCleanupPolicyDto.TableName)) { + if (PrimaryKeyExists(ContentVersionCleanupPolicyDto.TableName, "PK_umbracoContentVersionCleanupPolicy")) + { + return; + } + + contentVersionCleanupPolicyDtos = + Database + .Fetch() + .OrderByDescending(x => x.Updated) + .DistinctBy(x => x.ContentTypeId); + Delete.Table(ContentVersionCleanupPolicyDto.TableName).Do(); } Create.Table().Do(); - Database.InsertBatch(contentVersionCleanupPolicyDtos); + + if (contentVersionCleanupPolicyDtos is not null) + { + Database.InsertBatch(contentVersionCleanupPolicyDtos); + } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs index 128c59594b..4dd1a14fb5 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentVersionCleanupPolicyDto.cs @@ -1,4 +1,3 @@ -using System.Data; using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; @@ -13,7 +12,7 @@ internal class ContentVersionCleanupPolicyDto public const string TableName = Constants.DatabaseSchema.Tables.ContentVersionCleanupPolicy; [Column("contentTypeId")] - [PrimaryKeyColumn(AutoIncrement = false)] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_umbracoContentVersionCleanupPolicy")] [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] public int ContentTypeId { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index d9b76a4942..a71ccf5bed 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -188,6 +188,8 @@ public interface ISqlSyntaxProvider /// bool TryGetDefaultConstraint(IDatabase db, string? tableName, string columnName, [MaybeNullWhen(false)] out string constraintName); + bool DoesPrimaryKeyExist(IDatabase db, string tableName, string primaryKeyName) => throw new NotImplementedException(); + string GetFieldNameForUpdate(Expression> fieldSelector, string? tableAlias = null); /// diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 48b882d604..15cb68bfc5 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -209,6 +209,8 @@ public abstract class SqlSyntaxProviderBase : ISqlSyntaxProvider public abstract bool TryGetDefaultConstraint(IDatabase db, string? tableName, string columnName, [MaybeNullWhen(false)] out string constraintName); + public virtual bool DoesPrimaryKeyExist(IDatabase db, string tableName, string primaryKeyName) => throw new NotImplementedException(); + public virtual string GetFieldNameForUpdate( Expression> fieldSelector, string? tableAlias = null) => this.GetFieldName(fieldSelector, tableAlias);