diff --git a/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs index 7526d83cd0..9902c8df30 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDocumentVersionRepository.cs @@ -7,17 +7,17 @@ public interface IDocumentVersionRepository : IRepository /// /// Gets a list of all historic content versions. /// - public IReadOnlyCollection? GetDocumentVersionsEligibleForCleanup(); + public IReadOnlyCollection GetDocumentVersionsEligibleForCleanup(); /// /// Gets cleanup policy override settings per content type. /// - public IReadOnlyCollection? GetCleanupPolicies(); + public IReadOnlyCollection GetCleanupPolicies(); /// /// Gets paginated content versions for given content id paginated. /// - public IEnumerable? GetPagedItemsByContentId(int contentId, long pageIndex, int pageSize, out long totalRecords, int? languageId = null); + public IEnumerable GetPagedItemsByContentId(int contentId, long pageIndex, int pageSize, out long totalRecords, int? languageId = null); /// /// Deletes multiple content versions by ID. diff --git a/src/Umbraco.Core/Security/ExternalLoginToken.cs b/src/Umbraco.Core/Security/ExternalLoginToken.cs index df986d176f..c2b93caa84 100644 --- a/src/Umbraco.Core/Security/ExternalLoginToken.cs +++ b/src/Umbraco.Core/Security/ExternalLoginToken.cs @@ -3,6 +3,8 @@ namespace Umbraco.Cms.Core.Security; /// public class ExternalLoginToken : IExternalLoginToken { + public const string TableName = Constants.DatabaseSchema.Tables.ExternalLoginToken; + /// /// Initializes a new instance of the class. /// diff --git a/src/Umbraco.Core/Services/ContentVersionService.cs b/src/Umbraco.Core/Services/ContentVersionService.cs index 5dddaad709..230aa22126 100644 --- a/src/Umbraco.Core/Services/ContentVersionService.cs +++ b/src/Umbraco.Core/Services/ContentVersionService.cs @@ -79,7 +79,7 @@ internal sealed class ContentVersionService : IContentVersionService return Task.FromResult(Attempt?, ContentVersionOperationStatus>.Fail(ContentVersionOperationStatus.InvalidSkipTake)); } - IEnumerable? versions = + IEnumerable versions = HandleGetPagedContentVersions( document.Id, pageNumber, @@ -87,11 +87,6 @@ internal sealed class ContentVersionService : IContentVersionService out var total, culture); - if (versions is null) - { - return Task.FromResult(Attempt?, ContentVersionOperationStatus>.Fail(ContentVersionOperationStatus.NotFound)); - } - return Task.FromResult(Attempt?, ContentVersionOperationStatus>.Succeed( ContentVersionOperationStatus.Success, new PagedModel(total, versions))); } @@ -152,8 +147,12 @@ internal sealed class ContentVersionService : IContentVersionService } } - private IEnumerable? HandleGetPagedContentVersions(int contentId, long pageIndex, - int pageSize, out long totalRecords, string? culture = null) + private IEnumerable HandleGetPagedContentVersions( + int contentId, + long pageIndex, + int pageSize, + out long totalRecords, + string? culture = null) { using (ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true)) { @@ -221,18 +220,20 @@ internal sealed class ContentVersionService : IContentVersionService */ using (ICoreScope scope = _scopeProvider.CreateCoreScope()) { - IReadOnlyCollection? allHistoricVersions = + IReadOnlyCollection allHistoricVersions = _documentVersionRepository.GetDocumentVersionsEligibleForCleanup(); - if (allHistoricVersions is null) + if (allHistoricVersions.Count == 0) { scope.Complete(); return Array.Empty(); } - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + + if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Discovered {count} candidate(s) for ContentVersion cleanup", allHistoricVersions.Count); } + versionsToDelete = new List(allHistoricVersions.Count); IEnumerable filteredContentVersions = @@ -245,7 +246,7 @@ internal sealed class ContentVersionService : IContentVersionService if (scope.Notifications.PublishCancelable( new ContentDeletingVersionsNotification(version.ContentId, messages, version.VersionId))) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Delete cancelled for ContentVersion [{versionId}]", version.VersionId); } @@ -260,7 +261,7 @@ internal sealed class ContentVersionService : IContentVersionService if (!versionsToDelete.Any()) { - if (_logger.IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug)) + if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("No remaining ContentVersions for cleanup"); } diff --git a/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs b/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs index 234dae683f..51813c3163 100644 --- a/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs +++ b/src/Umbraco.Core/Services/DefaultContentVersionCleanupPolicy.cs @@ -35,7 +35,7 @@ public class DefaultContentVersionCleanupPolicy : IContentVersionCleanupPolicy using (ICoreScope scope = _scopeProvider.CreateCoreScope()) { - var policyOverrides = _documentVersionRepository.GetCleanupPolicies()? + var policyOverrides = _documentVersionRepository.GetCleanupPolicies() .ToDictionary(x => x.ContentTypeId); foreach (ContentVersionMeta version in items) diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs index baf77377f8..acb86aea72 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs @@ -117,7 +117,10 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install { // found only 1 user == the default user with default password // however this always exists on uCloud, also need to check if there are other users too - result = _scopeAccessor.AmbientScope.Database.ExecuteScalar("SELECT COUNT(*) FROM umbracoUser"); + sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + .SelectCount() + .From(); + result = _scopeAccessor.AmbientScope.Database.ExecuteScalar(sql); has = result != 1; } scope.Complete(); diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index 4174196cad..0b1417f699 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -9,7 +9,9 @@ using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Infrastructure.Migrations.Upgrade; +using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Migrations.Install; @@ -1828,7 +1830,7 @@ internal sealed class DatabaseDataCreator }; _database.Insert(Constants.DatabaseSchema.Tables.Language, "id", false, dto); isDefault = false; - id++; + id += 1; } } else diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs index cfde523e28..9558b7e6c1 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Access)] +[TableName(TableName)] [PrimaryKey("id", AutoIncrement = false)] [ExplicitColumns] internal sealed class AccessDto { + public const string TableName = Constants.DatabaseSchema.Tables.Access; + [Column("id")] [PrimaryKeyColumn(Name = "PK_umbracoAccess", AutoIncrement = false)] public Guid Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessRuleDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessRuleDto.cs index ae0e654bd2..f6d97c72b0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessRuleDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessRuleDto.cs @@ -5,11 +5,12 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.AccessRule)] +[TableName(TableName)] [PrimaryKey("id", AutoIncrement = false)] [ExplicitColumns] internal sealed class AccessRuleDto { + public const string TableName = Constants.DatabaseSchema.Tables.AccessRule; [Column("id")] [PrimaryKeyColumn(Name = "PK_umbracoAccessRule", AutoIncrement = false)] public Guid Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/AuditEntryDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/AuditEntryDto.cs index e8d8a2052c..cc88e37f18 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/AuditEntryDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/AuditEntryDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.AuditEntry)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] internal sealed class AuditEntryDto { + public const string TableName = Constants.DatabaseSchema.Tables.AuditEntry; + [Column("id")] [PrimaryKeyColumn] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/CacheInstructionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/CacheInstructionDto.cs index bc4babef95..0c274c3195 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/CacheInstructionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/CacheInstructionDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.CacheInstruction)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] public class CacheInstructionDto { + public const string TableName = Constants.DatabaseSchema.Tables.CacheInstruction; + [Column("id")] [NullSetting(NullSetting = NullSettings.NotNull)] [PrimaryKeyColumn(AutoIncrement = true, Name = "PK_umbracoCacheInstruction")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ConsentDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ConsentDto.cs index e205869c56..6445f992e8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ConsentDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ConsentDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Consent)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] public class ConsentDto { + public const string TableName = Constants.DatabaseSchema.Tables.Consent; + [Column("id")] [PrimaryKeyColumn] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentDto.cs index 21e94349bd..d9a03c644f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentDto.cs @@ -17,17 +17,17 @@ public class ContentDto public int NodeId { get; set; } [Column("contentTypeId")] - [ForeignKey(typeof(ContentTypeDto), Column = "NodeId")] + [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] public int ContentTypeId { get; set; } [ResultColumn] - [Reference(ReferenceType.OneToOne, ColumnName = "NodeId")] + [Reference(ReferenceType.OneToOne, ColumnName = "nodeId")] public NodeDto NodeDto { get; set; } = null!; // although a content has many content versions, // they can only be loaded one by one (as several content), // so this here is a OneToOne reference [ResultColumn] - [Reference(ReferenceType.OneToOne, ReferenceMemberName = "NodeId")] + [Reference(ReferenceType.OneToOne, ReferenceMemberName = "nodeId")] public ContentVersionDto ContentVersionDto { get; set; } = null!; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs index a3a978f2e4..2ec2977671 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentNuDto.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.NodeData)] +[TableName(TableName)] [PrimaryKey("nodeId", AutoIncrement = false)] [ExplicitColumns] public class ContentNuDto diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentType2ContentTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentType2ContentTypeDto.cs index e4c4bf77de..a8d8a28864 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentType2ContentTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentType2ContentTypeDto.cs @@ -4,10 +4,12 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.ContentTypeTree)] +[TableName(TableName)] [ExplicitColumns] internal sealed class ContentType2ContentTypeDto { + public const string TableName = Constants.DatabaseSchema.Tables.ContentTypeTree; + [Column("parentContentTypeId")] [PrimaryKeyColumn(AutoIncrement = false, Clustered = true, Name = "PK_cmsContentType2ContentType", OnColumns = "parentContentTypeId, childContentTypeId")] [ForeignKey(typeof(NodeDto), Name = "FK_cmsContentType2ContentType_umbracoNode_parent")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeAllowedContentTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeAllowedContentTypeDto.cs index 5f0bf8f608..95d2feec3d 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeAllowedContentTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeAllowedContentTypeDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.ContentChildType)] +[TableName(TableName)] [PrimaryKey("Id", AutoIncrement = false)] [ExplicitColumns] internal sealed class ContentTypeAllowedContentTypeDto { + public const string TableName = Constants.DatabaseSchema.Tables.ContentChildType; + [Column("Id")] [ForeignKey(typeof(ContentTypeDto), Name = "FK_cmsContentTypeAllowedContentType_cmsContentType", Column = "nodeId")] [PrimaryKeyColumn(AutoIncrement = false, Clustered = true, Name = "PK_cmsContentTypeAllowedContentType", OnColumns = "Id, AllowedId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeTemplateDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeTemplateDto.cs index 4239937d21..fd85047891 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeTemplateDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ContentTypeTemplateDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.DocumentType)] +[TableName(TableName)] [PrimaryKey("contentTypeNodeId", AutoIncrement = false)] [ExplicitColumns] internal sealed class ContentTypeTemplateDto { + public const string TableName = Constants.DatabaseSchema.Tables.DocumentType; + [Column("contentTypeNodeId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsDocumentType", OnColumns = "contentTypeNodeId, templateNodeId")] [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs index 254ffc2e2a..9e8b4dee22 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DataTypeDto.cs @@ -4,11 +4,12 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.DataType)] +[TableName(TableName)] [PrimaryKey("nodeId", AutoIncrement = false)] [ExplicitColumns] public class DataTypeDto { + public const string TableName = Constants.DatabaseSchema.Tables.DataType; [Column("nodeId")] [PrimaryKeyColumn(AutoIncrement = false)] [ForeignKey(typeof(NodeDto))] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentDto.cs index e50ed28de6..b78538f349 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DocumentDto.cs @@ -1,4 +1,4 @@ -using NPoco; +using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; @@ -9,7 +9,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; [ExplicitColumns] public class DocumentDto { - private const string TableName = Constants.DatabaseSchema.Tables.Document; + public const string TableName = Constants.DatabaseSchema.Tables.Document; // Public constants to bind properties between DTOs diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs index 028eb0f52a..7a9257423f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/KeyValueDto.cs @@ -6,7 +6,7 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.KeyValue)] +[TableName(TableName)] [PrimaryKey("key", AutoIncrement = false)] [ExplicitColumns] internal sealed class KeyValueDto diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/LogViewerQueryDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/LogViewerQueryDto.cs index c71cb9b6b3..2420996879 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/LogViewerQueryDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/LogViewerQueryDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.LogViewerQuery)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] internal sealed class LogViewerQueryDto { + public const string TableName = Constants.DatabaseSchema.Tables.LogViewerQuery; + [Column("id")] [PrimaryKeyColumn] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/LongRunningOperationDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/LongRunningOperationDto.cs index 8b223b5c06..677eed7eda 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/LongRunningOperationDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/LongRunningOperationDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.LongRunningOperation)] +[TableName(TableName)] [PrimaryKey("id", AutoIncrement = false)] [ExplicitColumns] internal class LongRunningOperationDto { + public const string TableName = Constants.DatabaseSchema.Tables.LongRunningOperation; + [Column("id")] [PrimaryKeyColumn(Name = "PK_umbracoLongRunningOperation", AutoIncrement = false)] public Guid Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/Member2MemberGroupDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/Member2MemberGroupDto.cs index 99cb8d2a39..4925f2dd82 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/Member2MemberGroupDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/Member2MemberGroupDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Member2MemberGroup)] +[TableName(TableName)] [PrimaryKey("Member", AutoIncrement = false)] [ExplicitColumns] internal sealed class Member2MemberGroupDto { + public const string TableName = Constants.DatabaseSchema.Tables.Member2MemberGroup; + [Column("Member")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsMember2MemberGroup", OnColumns = "Member, MemberGroup")] [ForeignKey(typeof(MemberDto))] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs index f90824c51e..38e8e36271 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/NodeDto.cs @@ -34,7 +34,7 @@ public class NodeDto [Column(ParentIdColumnName)] [ForeignKey(typeof(NodeDto))] - [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_parentId_nodeObjectType", ForColumns = "parentID,nodeObjectType", IncludeColumns = "trashed,nodeUser,level,path,sortOrder,uniqueID,text,createDate")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_parentId_nodeObjectType", ForColumns = "parentId,nodeObjectType", IncludeColumns = "trashed,nodeUser,level,path,sortOrder,uniqueID,text,createDate")] public int ParentId { get; set; } // NOTE: This index is primarily for the nucache data lookup, see https://github.com/umbraco/Umbraco-CMS/pull/8365#issuecomment-673404177 @@ -48,7 +48,7 @@ public class NodeDto public string Path { get; set; } = null!; [Column(SortOrderColumnName)] - [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_ObjectType_trashed_sorted", ForColumns = "nodeObjectType,trashed,sortOrder,id", IncludeColumns = "uniqueID,parentID,level,path,nodeUser,text,createDate")] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_ObjectType_trashed_sorted", ForColumns = "nodeObjectType,trashed,sortOrder,id", IncludeColumns = "uniqueID,parentId,level,path,nodeUser,text,createDate")] public int SortOrder { get; set; } [Column(TrashedColumnName)] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs index 721ea6c507..943a8c1b21 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.PropertyType)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] internal class PropertyTypeDto { + public const string TableName = Constants.DatabaseSchema.Tables.PropertyType; + private string? _alias; [Column("id")] @@ -76,7 +78,7 @@ internal class PropertyTypeDto [Reference(ReferenceType.OneToOne, ColumnName = "DataTypeId")] public DataTypeDto DataTypeDto { get; set; } = null!; - [Column("UniqueID")] + [Column("UniqueId")] [NullSetting(NullSetting = NullSettings.NotNull)] [Constraint(Default = SystemMethods.NewGuid)] [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeUniqueID")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupReadOnlyDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupReadOnlyDto.cs index effe2acdf7..3269102562 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupReadOnlyDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeGroupReadOnlyDto.cs @@ -3,11 +3,13 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.PropertyTypeGroup)] +[TableName(TableName)] [PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] internal sealed class PropertyTypeGroupReadOnlyDto { + public const string TableName = Constants.DatabaseSchema.Tables.PropertyTypeGroup; + [Column("PropertyTypeGroupId")] public int? Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeReadOnlyDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeReadOnlyDto.cs index 14c24499e6..4c4e526693 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeReadOnlyDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/PropertyTypeReadOnlyDto.cs @@ -3,11 +3,13 @@ using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.PropertyType)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] internal sealed class PropertyTypeReadOnlyDto { + public const string TableName = Constants.DatabaseSchema.Tables.PropertyType; + [Column("PropertyTypeId")] public int? Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs index 49ab6c8aa4..e5af860721 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/RedirectUrlDto.cs @@ -4,7 +4,7 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.RedirectUrl)] +[TableName(TableName)] [PrimaryKey("id", AutoIncrement = false)] [ExplicitColumns] internal sealed class RedirectUrlDto @@ -28,7 +28,7 @@ internal sealed class RedirectUrlDto [Column("contentKey")] [NullSetting(NullSetting = NullSettings.NotNull)] - [ForeignKey(typeof(NodeDto), Column = "uniqueID")] + [ForeignKey(typeof(NodeDto), Column = "uniqueId")] public Guid ContentKey { get; set; } [Column("createDateUtc")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/RelationDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/RelationDto.cs index 72c0877c6a..5c15ca53fd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/RelationDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/RelationDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Relation)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] internal sealed class RelationDto { + public const string TableName = Constants.DatabaseSchema.Tables.Relation; + [Column("id")] [PrimaryKeyColumn] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/RelationTypeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/RelationTypeDto.cs index 3856d24ce2..20a43410b6 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/RelationTypeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/RelationTypeDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.RelationType)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] internal sealed class RelationTypeDto { + public const string TableName = Constants.DatabaseSchema.Tables.RelationType; + public const int NodeIdSeed = 10; [Column("id")] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ServerRegistrationDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ServerRegistrationDto.cs index 28a6fbe6ce..960697cf72 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ServerRegistrationDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ServerRegistrationDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Server)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] internal sealed class ServerRegistrationDto { + public const string TableName = Constants.DatabaseSchema.Tables.Server; + [Column("id")] [PrimaryKeyColumn(AutoIncrement = true)] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/TemplateDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/TemplateDto.cs index 636e152bed..e1462a47fa 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/TemplateDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/TemplateDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Template)] +[TableName(TableName)] [PrimaryKey("pk")] [ExplicitColumns] internal sealed class TemplateDto { + public const string TableName = Constants.DatabaseSchema.Tables.Template; + [Column("pk")] [PrimaryKeyColumn] public int PrimaryKey { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/User2ClientIdDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/User2ClientIdDto.cs index dc2399dc6a..9d433fb70a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/User2ClientIdDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/User2ClientIdDto.cs @@ -1,14 +1,16 @@ -using NPoco; +using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.User2ClientId)] +[TableName(TableName)] [PrimaryKey("userId", AutoIncrement = false)] [ExplicitColumns] public class User2ClientIdDto { + public const string TableName = Constants.DatabaseSchema.Tables.User2ClientId; + [Column("userId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_umbracoUser2ClientId", OnColumns = "userId, clientId")] [ForeignKey(typeof(UserDto))] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/User2NodeNotifyDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/User2NodeNotifyDto.cs index e25bad1f55..e980230d7f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/User2NodeNotifyDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/User2NodeNotifyDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.User2NodeNotify)] +[TableName(TableName)] [PrimaryKey("userId", AutoIncrement = false)] [ExplicitColumns] internal sealed class User2NodeNotifyDto { + public const string TableName = Constants.DatabaseSchema.Tables.User2NodeNotify; + [Column("userId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_umbracoUser2NodeNotify", OnColumns = "userId, nodeId, action")] [ForeignKey(typeof(UserDto))] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs index 3bc059ff21..fd0da99f39 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/User2UserGroupDto.cs @@ -4,10 +4,12 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.User2UserGroup)] +[TableName(TableName)] [ExplicitColumns] public class User2UserGroupDto { + public const string TableName = Constants.DatabaseSchema.Tables.User2UserGroup; + [Column("userId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_user2userGroup", OnColumns = "userId, userGroupId")] [ForeignKey(typeof(UserDto))] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2AppDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2AppDto.cs index 7e2099ae44..4b71e1b714 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2AppDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2AppDto.cs @@ -4,10 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.UserGroup2App)] +[TableName(TableName)] +[PrimaryKey("id", AutoIncrement = false)] [ExplicitColumns] public class UserGroup2AppDto { + public const string TableName = Constants.DatabaseSchema.Tables.UserGroup2App; + [Column("userGroupId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_userGroup2App", OnColumns = "userGroupId, app")] [ForeignKey(typeof(UserGroupDto))] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2GranularPermissionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2GranularPermissionDto.cs index 68f2e35bcb..e1cfc16380 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2GranularPermissionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2GranularPermissionDto.cs @@ -4,10 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission)] +[TableName(TableName)] +[PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] public class UserGroup2GranularPermissionDto { + public const string TableName = Constants.DatabaseSchema.Tables.UserGroup2GranularPermission; + [Column("id")] [PrimaryKeyColumn(Name = "PK_umbracoUserGroup2GranularPermissionDto", AutoIncrement = true)] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2LanguageDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2LanguageDto.cs index 1ecf84fafc..36ba42bb4e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2LanguageDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2LanguageDto.cs @@ -1,10 +1,10 @@ -using System.Data; +using System.Data; using NPoco; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Cms.Core.Constants.DatabaseSchema.Tables.UserGroup2Language)] +[TableName(TableName)] [ExplicitColumns] public class UserGroup2LanguageDto { diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2PermissionDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2PermissionDto.cs index 9f819a6058..ac095e6e42 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2PermissionDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroup2PermissionDto.cs @@ -1,13 +1,16 @@ -using NPoco; +using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.UserGroup2Permission)] +[TableName(TableName)] +[PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] public class UserGroup2PermissionDto { + public const string TableName = Constants.DatabaseSchema.Tables.UserGroup2Permission; + [Column("id")] [PrimaryKeyColumn(Name = "PK_userGroup2Permission", AutoIncrement = true)] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroupDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroupDto.cs index 794eceeff5..591b495db6 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroupDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserGroupDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.UserGroup)] +[TableName(TableName)] [PrimaryKey("id")] [ExplicitColumns] public class UserGroupDto { + public const string TableName = Constants.DatabaseSchema.Tables.UserGroup; + public UserGroupDto() { UserGroup2AppDtos = new List(); diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs index 4d54752e75..51ca08b842 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/UserStartNodeDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.UserStartNode)] +[TableName(TableName)] [PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] public class UserStartNodeDto : IEquatable { + public const string TableName = Constants.DatabaseSchema.Tables.UserStartNode; + public enum StartNodeTypeValue { Content = 1, diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2EventsDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2EventsDto.cs index 0278d22945..d534cf4c36 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2EventsDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2EventsDto.cs @@ -1,13 +1,15 @@ -using System.Data; +using System.Data; using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Webhook2Events)] +[TableName(TableName)] public class Webhook2EventsDto { + public const string TableName = Constants.DatabaseSchema.Tables.Webhook2Events; + [Column("webhookId")] [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_webhookEvent2WebhookDto", OnColumns = "webhookId, event")] [ForeignKey(typeof(WebhookDto), OnDelete = Rule.Cascade)] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2HeadersDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2HeadersDto.cs index 80a7724109..85d0395222 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2HeadersDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/Webhook2HeadersDto.cs @@ -1,15 +1,17 @@ -using System.Data; +using System.Data; using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Webhook2Headers)] +[TableName(TableName)] public class Webhook2HeadersDto { + public const string TableName = Constants.DatabaseSchema.Tables.Webhook2Headers; + [Column("webhookId")] - [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_heaeders2WebhookDto", OnColumns = "webhookId, key")] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_headers2WebhookDto", OnColumns = "webhookId, key")] [ForeignKey(typeof(WebhookDto), OnDelete = Rule.Cascade)] public int WebhookId { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookDto.cs index 0851d40aad..122a70c288 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookDto.cs @@ -5,11 +5,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.Webhook)] -[PrimaryKey("id")] +[TableName(TableName)] +[PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] internal sealed class WebhookDto { + public const string TableName = Constants.DatabaseSchema.Tables.Webhook; + [Column("id")] [PrimaryKeyColumn(AutoIncrement = true)] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookLogDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookLogDto.cs index c0f432a417..7c3601601f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookLogDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookLogDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.WebhookLog)] -[PrimaryKey("id")] +[TableName(TableName)] +[PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] internal sealed class WebhookLogDto { + public const string TableName = Constants.DatabaseSchema.Tables.WebhookLog; + [Column("id")] [PrimaryKeyColumn(AutoIncrement = true)] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookRequestDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookRequestDto.cs index 80739770a0..240fab3c11 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookRequestDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/WebhookRequestDto.cs @@ -1,14 +1,16 @@ -using NPoco; +using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos; -[TableName(Constants.DatabaseSchema.Tables.WebhookRequest)] -[PrimaryKey("id")] +[TableName(TableName)] +[PrimaryKey("id", AutoIncrement = true)] [ExplicitColumns] public class WebhookRequestDto { + public const string TableName = Constants.DatabaseSchema.Tables.WebhookRequest; + [Column("id")] [PrimaryKeyColumn(AutoIncrement = true)] public int Id { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs index 71880c95dc..241aa800bf 100644 --- a/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/NPocoSqlExtensions.cs @@ -92,6 +92,30 @@ namespace Umbraco.Extensions return sql; } + /// + /// Appends a OR IN clause to the Sql statement. + /// + /// The type of the first Dto. + /// The type of the second Dto. + /// The Sql statement. + /// An expression specifying the first field. + /// First values. + /// An expression specifying the second field. + /// Second values. + /// The Sql statement. + public static Sql WhereInOr( + this Sql sql, + Expression> field, + Expression> field2, + IEnumerable? values, + IEnumerable? values2) + { + var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(field); + var fieldName2 = sql.SqlContext.SqlSyntax.GetFieldName(field2); + sql.Where($"{fieldName} IN (@values) OR {fieldName2} IN (@values2)", new { values }, new { values2 }); + return sql; + } + /// /// Appends a WHERE IN clause to the Sql statement. /// @@ -141,7 +165,7 @@ namespace Umbraco.Extensions { var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(fieldSelector); var concat = sql.SqlContext.SqlSyntax.GetWildcardConcat(concatDefault); - var likeSelect = sql.SqlContext.SqlSyntax.GetConcat(valuesSql?.SQL ?? string.Empty, concat); + var likeSelect = sql.SqlContext.SqlSyntax.GetConcat($"({valuesSql?.SQL})", concat); sql.Where($"{fieldName} LIKE {likeSelect}", valuesSql?.Arguments); return sql; } @@ -702,6 +726,64 @@ namespace Umbraco.Extensions return sql.SqlContext.SqlSyntax.SelectTop(sql, count); } + /// + /// Adds a SQL SELECT statement to retrieve the maximum value of the specified field from the table associated + /// with the specified DTO type. + /// + /// The type of the Data Transfer Object (DTO) that represents the table from which the maximum value will be + /// selected. + /// The SQL query builder to which the SELECT statement will be appended. Cannot be . + /// An expression specifying the field for which the maximum value will be calculated. Cannot be . + /// A modified SQL query builder that includes the SELECT statement for the maximum value of the specified + /// field. + public static Sql SelectMax(this Sql sql, Expression> field) + { + ArgumentNullException.ThrowIfNull(sql); + ArgumentNullException.ThrowIfNull(field); + + return sql.Select($"MAX ({sql.SqlContext.SqlSyntax.GetFieldName(field)})"); + } + + /// + /// Adds a SQL SELECT statement to retrieve the maximum value of the specified field from the table associated + /// with the specified DTO type. + /// + /// The type of the Data Transfer Object (DTO) that represents the table from which the maximum value will be + /// selected. + /// The SQL query builder to which the SELECT statement will be appended. Cannot be . + /// An expression specifying the field for which the maximum value will be calculated. Cannot be . + /// COALESCE int value. + /// A modified SQL query builder that includes the SELECT statement for the maximum value of the specified + /// field or the coalesceValue. + public static Sql SelectMax(this Sql sql, Expression> field, int coalesceValue) + { + ArgumentNullException.ThrowIfNull(sql); + ArgumentNullException.ThrowIfNull(field); + + return sql.Select($"COALESCE(MAX ({sql.SqlContext.SqlSyntax.GetFieldName(field)}), {coalesceValue})"); + } + + /// + /// Adds a SQL SELECT statement to retrieve the sum of the values of the specified field from the table associated + /// with the specified DTO type. + /// + /// The type of the Data Transfer Object (DTO) that represents the table from which the maximum value will be + /// selected. + /// The SQL query builder to which the SELECT statement will be appended. Cannot be . + /// An expression specifying the field for which the maximum value will be calculated. Cannot be . + /// A modified SQL query builder that includes the SELECT statement for the maximum value of the specified + /// field. + public static Sql SelectSum(this Sql sql, Expression> field) + { + ArgumentNullException.ThrowIfNull(sql); + ArgumentNullException.ThrowIfNull(field); + + return sql.Select($"SUM ({sql.SqlContext.SqlSyntax.GetFieldName(field)})"); + } + /// /// Creates a SELECT COUNT(*) Sql statement. /// @@ -1246,6 +1328,19 @@ namespace Umbraco.Extensions return sql; } + /// + /// Deletes records from a table based on a predicate. + /// + /// Table definition. + /// SqlConext + /// A predicate to transform and append to the Sql statement (WHERE clause). + /// + public static Sql Delete(this Sql sql, Expression> predicate) + { + (string s, object[] a) = sql.SqlContext.VisitDto(predicate); + return sql.Delete().Where(s, a); + } + #endregion #region Update diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs index b20e9f34d0..bb231db73b 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditEntryRepository.cs @@ -107,7 +107,7 @@ internal sealed class AuditEntryRepository : EntityRepositoryBase - protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.AuditEntry}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(AuditEntryDto.TableName)}.id = @id"; /// protected override IEnumerable GetDeleteClauses() => @@ -160,7 +160,7 @@ internal sealed class AuditEntryRepository : EntityRepositoryBase sqlInsert = Sql($"INSERT INTO {Constants.DatabaseSchema.Tables.AuditEntry} ({cols})") + Sql sqlInsert = Sql($"INSERT INTO {QuoteTableName(AuditEntryDto.TableName)} ({cols})") .Append("VALUES (") .Append(sqlValues) .Append(")"); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs index 9ccaa4960c..8d3cb3fb96 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/AuditRepository.cs @@ -37,9 +37,12 @@ internal sealed class AuditRepository : EntityRepositoryBase, I { DateTime oldestPermittedLogEntry = DateTime.UtcNow.Subtract(new TimeSpan(0, maximumAgeOfLogsInMinutes, 0)); - Database.Execute( - "delete from umbracoLog where datestamp < @oldestPermittedLogEntry and logHeader in ('open','system')", - new { oldestPermittedLogEntry }); + var headers = new[] { "open", "system" }; + Sql sql = SqlContext.Sql() + .Delete() + .Where(c => c.Datestamp < oldestPermittedLogEntry) + .WhereIn(c => c.Header, headers); + Database.Execute(sql); } /// @@ -70,10 +73,7 @@ internal sealed class AuditRepository : EntityRepositoryBase, I AuditType[]? auditTypeFilter, IQuery? customFilter) { - if (auditTypeFilter == null) - { - auditTypeFilter = Array.Empty(); - } + auditTypeFilter ??= []; Sql sql = GetBaseQuery(false); @@ -84,7 +84,7 @@ internal sealed class AuditRepository : EntityRepositoryBase, I { foreach (Tuple filterClause in customFilter.GetWhereClauses()) { - sql.Where(filterClause.Item1, filterClause.Item2); + sql = sql.Where(filterClause.Item1, filterClause.Item2); } } @@ -92,13 +92,14 @@ internal sealed class AuditRepository : EntityRepositoryBase, I { foreach (AuditType type in auditTypeFilter) { - sql.Where("(logHeader=@0)", type.ToString()); + var typeString = type.ToString(); + sql = sql.Where(c => c.Header == typeString); } } sql = orderDirection == Direction.Ascending - ? sql.OrderBy("Datestamp") - : sql.OrderByDescending("Datestamp"); + ? sql.OrderBy(c => c.Datestamp) + : sql.OrderByDescending(c => c.Datestamp); // get page Page? page = Database.Page(pageIndex + 1, pageSize, sql); @@ -118,7 +119,7 @@ internal sealed class AuditRepository : EntityRepositoryBase, I protected override IAuditItem? PerformGet(int id) { Sql sql = GetBaseQuery(false); - sql.Where(GetBaseWhereClause(), new { id = id }); + sql.Where(GetBaseWhereClause(), new { id }); LogDto? dto = Database.First(sql); return dto == null @@ -158,7 +159,8 @@ internal sealed class AuditRepository : EntityRepositoryBase, I return sql; } - protected override string GetBaseWhereClause() => "umbracoLog.id = @id"; + protected override string GetBaseWhereClause() => + $"{QuoteTableName(LogDto.TableName)}.{QuoteColumnName("id")} = @id"; protected override IEnumerable GetDeleteClauses() => throw new NotImplementedException(); } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CacheInstructionRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CacheInstructionRepository.cs index c623a672d7..e6c9dba4f5 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CacheInstructionRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/CacheInstructionRepository.cs @@ -27,20 +27,42 @@ internal sealed class CacheInstructionRepository : ICacheInstructionRepository return 0; } - Sql? sql = AmbientScope.SqlContext.Sql().Select("COUNT(*)") + Sql? sql = AmbientScope.SqlContext.Sql().SelectCount() .From(); - return AmbientScope?.Database.ExecuteScalar(sql) ?? 0; + return AmbientScope.Database.ExecuteScalar(sql); } /// - public int CountPendingInstructions(int lastId) => - AmbientScope?.Database.ExecuteScalar( - "SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new { lastId }) ?? 0; + public int CountPendingInstructions(int lastId) + { + if (AmbientScope is null) + { + return 0; + } + + Sql? sql = AmbientScope.SqlContext.Sql() + .SelectSum(c => c.InstructionCount) + .From() + .Where(dto => dto.Id > lastId); + + return AmbientScope.Database.ExecuteScalar(sql); + } /// - public int GetMaxId() => - AmbientScope?.Database.ExecuteScalar("SELECT MAX(id) FROM umbracoCacheInstruction") ?? 0; + public int GetMaxId() + { + if (AmbientScope is null) + { + return 0; + } + + Sql sql = AmbientScope.SqlContext.Sql() + .SelectMax(c => c.Id) + .From(); + + return AmbientScope.Database.ExecuteScalar(sql); + } /// public bool Exists(int id) => AmbientScope?.Database.Exists(id) ?? false; @@ -71,12 +93,25 @@ internal sealed class CacheInstructionRepository : ICacheInstructionRepository /// public void DeleteInstructionsOlderThan(DateTime pruneDate) { + if (AmbientScope is null) + { + return; + } + // Using 2 queries is faster than convoluted joins. - var maxId = AmbientScope?.Database.ExecuteScalar("SELECT MAX(id) FROM umbracoCacheInstruction;"); - Sql deleteSql = - new Sql().Append( - @"DELETE FROM umbracoCacheInstruction WHERE utcStamp < @pruneDate AND id < @maxId", - new { pruneDate, maxId }); - AmbientScope?.Database.Execute(deleteSql); + Sql sql = AmbientScope.SqlContext.Sql() + .SelectMax(c => c.Id) + .From(); + var maxId = AmbientScope.Database.ExecuteScalar(sql); + if (maxId == 0) + { + return; // No instructions to delete. + } + + Sql? deleteSql = AmbientScope.SqlContext.Sql() + .Delete() + .Where(dto => dto.UtcStamp < pruneDate && dto.Id < maxId); + + AmbientScope.Database.Execute(deleteSql); } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentNavigationRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentNavigationRepository.cs index d184be555b..ce4592d129 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentNavigationRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentNavigationRepository.cs @@ -2,6 +2,7 @@ using NPoco; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; @@ -31,14 +32,16 @@ public class ContentNavigationRepository : INavigationRepository return Enumerable.Empty(); } + ISqlSyntaxProvider syntax = AmbientScope.SqlContext.SqlSyntax; + Sql sql = AmbientScope.SqlContext.Sql() .Select( - $"n.{NodeDto.IdColumnName} as {NodeDto.IdColumnName}", - $"n.{NodeDto.KeyColumnName} as {NodeDto.KeyColumnName}", - $"ctn.{NodeDto.KeyColumnName} as {NavigationDto.ContentTypeKeyColumnName}", - $"n.{NodeDto.ParentIdColumnName} as {NodeDto.ParentIdColumnName}", - $"n.{NodeDto.SortOrderColumnName} as {NodeDto.SortOrderColumnName}", - $"n.{NodeDto.TrashedColumnName} as {NodeDto.TrashedColumnName}") + $"n.{syntax.GetQuotedColumnName(NodeDto.IdColumnName)} as {syntax.GetQuotedColumnName(NodeDto.IdColumnName)}", + $"n.{syntax.GetQuotedColumnName(NodeDto.KeyColumnName)} as {syntax.GetQuotedColumnName(NodeDto.KeyColumnName)}", + $"ctn.{syntax.GetQuotedColumnName(NodeDto.KeyColumnName)} as {syntax.GetQuotedColumnName(NavigationDto.ContentTypeKeyColumnName)}", + $"n.{syntax.GetQuotedColumnName(NodeDto.ParentIdColumnName)} as {syntax.GetQuotedColumnName(NodeDto.ParentIdColumnName)}", + $"n.{syntax.GetQuotedColumnName(NodeDto.SortOrderColumnName)} as {syntax.GetQuotedColumnName(NodeDto.SortOrderColumnName)}", + $"n.{syntax.GetQuotedColumnName(NodeDto.TrashedColumnName)} as {syntax.GetQuotedColumnName(NodeDto.TrashedColumnName)}") .From("n") .InnerJoin("c").On((n, c) => n.NodeId == c.NodeId, "n", "c") .InnerJoin("ctn").On((c, ctn) => c.ContentTypeId == ctn.NodeId, "c", "ctn") diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 457271c1c9..32398b35f5 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -529,12 +529,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // needs to be an outer join since there's no guarantee that any of the nodes have values for this property Sql innerSql = Sql().Select($@"CASE - WHEN intValue IS NOT NULL THEN {sortedInt} - WHEN decimalValue IS NOT NULL THEN {sortedDecimal} - WHEN dateValue IS NOT NULL THEN {sortedDate} + WHEN {QuoteColumnName("intValue")} IS NOT NULL THEN {sortedInt} + WHEN {QuoteColumnName("decimalValue")} IS NOT NULL THEN {sortedDecimal} + WHEN {QuoteColumnName("dateValue")} IS NOT NULL THEN {sortedDate} ELSE {sortedString} - END AS customPropVal, - cver.nodeId AS customPropNodeId") + END AS {SqlSyntax.GetQuotedName("customPropVal")}, + cver.{QuoteColumnName("nodeId")} AS {SqlSyntax.GetQuotedName("customPropNodeId")}") .From("cver") .InnerJoin("opdata") .On((version, pdata) => version.Id == pdata.VersionId, "cver", "opdata") @@ -549,14 +549,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement var innerSqlString = ParameterHelper.ProcessParams(innerSql.SQL, innerSql.Arguments, argsList); // create the outer join complete sql fragment - var outerJoinTempTable = $@"LEFT OUTER JOIN ({innerSqlString}) AS customPropData - ON customPropData.customPropNodeId = {Constants.DatabaseSchema.Tables.Node}.id "; // trailing space is important! + var outerJoinTempTable = $@"LEFT OUTER JOIN ({innerSqlString}) AS {SqlSyntax.GetQuotedName("customPropData")} + ON {SqlSyntax.GetQuotedName("customPropData")}.{QuoteColumnName("customPropNodeId")} = {QuoteTableName(NodeDto.TableName)}.id "; // trailing space is important! // insert this just above the first WHERE var newSql = InsertBefore(sql.SQL, "WHERE", outerJoinTempTable); // see notes in ApplyOrdering: the field MUST be selected + aliased - newSql = InsertBefore(newSql, "FROM", ", customPropData.customPropVal AS ordering "); // trailing space is important! + newSql = InsertBefore(newSql, "FROM", $", {SqlSyntax.GetQuotedName("customPropData")}.{QuoteColumnName("customPropVal")} AS ordering "); // trailing space is important! // create the new sql sql = Sql(newSql, argsList.ToArray()); @@ -906,7 +906,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected string GetQuotedFieldName(string tableName, string fieldName) { - return SqlContext.SqlSyntax.GetQuotedTableName(tableName) + "." + SqlContext.SqlSyntax.GetQuotedColumnName(fieldName); + return QuoteTableName(tableName) + "." + QuoteColumnName(fieldName); } #region UnitOfWork Notification @@ -1015,7 +1015,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected virtual bool SortorderExists(int parentId, int sortOrder) { SqlTemplate? template = SqlContext.Templates.Get(Constants.SqlTemplates.VersionableRepository.SortOrderExists, tsql => tsql - .Select("sortOrder") + .Select(c => c.SortOrder) .From() .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType") && x.ParentId == SqlTemplate.Arg("parentId") && @@ -1030,7 +1030,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected virtual int GetNewChildSortOrder(int parentId, int first) { SqlTemplate? template = SqlContext.Templates.Get(Constants.SqlTemplates.VersionableRepository.GetSortOrder, tsql => tsql - .Select("MAX(sortOrder)") + .SelectMax(c => c.SortOrder) .From() .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType") && x.ParentId == SqlTemplate.Arg("parentId"))); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs index c6c00eb637..c01c0936d8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepository.cs @@ -1,3 +1,4 @@ +using System.Xml.Linq; using Microsoft.Extensions.Logging; using NPoco; using Umbraco.Cms.Core; @@ -47,8 +48,14 @@ internal sealed class ContentTypeRepository : ContentTypeRepositoryBase /// - public IEnumerable GetAllPropertyTypeAliases() => - Database.Fetch("SELECT DISTINCT Alias FROM cmsPropertyType ORDER BY Alias"); + public IEnumerable GetAllPropertyTypeAliases() + { + Sql sql = Sql() + .SelectDistinct(c => c.Alias) + .From() + .OrderBy(c => c.Alias); + return Database.Fetch(sql); + } /// /// Gets all content type aliases @@ -61,7 +68,7 @@ internal sealed class ContentTypeRepository : ContentTypeRepositoryBase GetAllContentTypeAliases(params Guid[] objectTypes) { Sql sql = Sql() - .Select("cmsContentType.alias") + .Select(c => c.Alias) .From() .InnerJoin() .On(dto => dto.NodeId, dto => dto.NodeId); @@ -177,15 +184,15 @@ internal sealed class ContentTypeRepository : ContentTypeRepositoryBase $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(NodeDto.TableName)}.id = @id"; protected override IEnumerable GetDeleteClauses() { var l = (List)base.GetDeleteClauses(); // we know it's a list - l.Add("DELETE FROM umbracoContentVersionCleanupPolicy WHERE contentTypeId = @id"); - l.Add("DELETE FROM cmsDocumentType WHERE contentTypeNodeId = @id"); - l.Add("DELETE FROM cmsContentType WHERE nodeId = @id"); - l.Add("DELETE FROM umbracoNode WHERE id = @id"); + l.Add($"DELETE FROM {QuoteTableName(ContentVersionCleanupPolicyDto.TableName)} WHERE {QuoteColumnName("contentTypeId")} = @id"); + l.Add($"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.DocumentType)} WHERE {QuoteColumnName("contentTypeNodeId")} = @id"); + l.Add($"DELETE FROM {QuoteTableName(ContentTypeDto.TableName)} WHERE {QuoteColumnName("nodeId")} = @id"); + l.Add($"DELETE FROM {QuoteTableName(NodeDto.TableName)} WHERE id = @id"); return l; } @@ -211,7 +218,7 @@ internal sealed class ContentTypeRepository : ContentTypeRepositoryBase sql = Sql() - .Select("DISTINCT " + Constants.DatabaseSchema.Tables.PropertyData + ".propertytypeid") + .SelectDistinct(c => c.PropertyTypeId) .From() .InnerJoin() .On(dto => dto.PropertyTypeId, dto => dto.Id) @@ -220,7 +227,7 @@ internal sealed class ContentTypeRepository : ContentTypeRepositoryBase(dto => dto.NodeId == entity.Id); // Delete all PropertyData where propertytypeid EXISTS in the subquery above - Database.Execute(SqlSyntax.GetDeleteSubquery(Constants.DatabaseSchema.Tables.PropertyData, "propertytypeid", sql)); + Database.Execute(SqlSyntax.GetDeleteSubquery(PropertyDataDto.TableName, "propertyTypeId", sql)); // delete all granular permissions for this content type Database.Delete(Sql().Where(dto => dto.UniqueId == entity.Key)); @@ -252,7 +259,10 @@ internal sealed class ContentTypeRepository : ContentTypeRepositoryBase("WHERE contentTypeNodeId = @Id", new { entity.Id }); + Sql sql = Sql() + .Delete() + .Where(x => x.ContentTypeNodeId == entity.Id); + Database.Execute(sql); // we could do it all in foreach if we assume that the default template is an allowed template?? var defaultTemplateId = entity.DefaultTemplateId; @@ -260,7 +270,9 @@ internal sealed class ContentTypeRepository : ContentTypeRepositoryBase("WHERE id = @ParentId", new { entity.ParentId }); + Sql sql = Sql() + .SelectAll() + .From() + .Where(x => x.NodeId == entity.ParentId); + NodeDto parent = Database.First(sql); entity.Path = string.Concat(parent.Path, ",", entity.Id); entity.Level = parent.Level + 1; - var maxSortOrder = - Database.ExecuteScalar( - "SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType", - new { entity.ParentId, NodeObjectType = NodeObjectTypeId }); + sql = Sql() + .SelectMax(c => c.SortOrder, 0) + .From() + .Where(x => x.ParentId == entity.ParentId && x.NodeObjectType == NodeObjectTypeId); + var maxSortOrder = Database.ExecuteScalar(sql); entity.SortOrder = maxSortOrder + 1; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index befaa6bc3a..74f6ff4c52 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -986,7 +986,7 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas .From() .InnerJoin().On(x => x.NodeId, x => x.NodeId) .Where(x => x.ContentTypeId == contentType.Id); - Sql? sqlInsertContentVersion = Sql($"INSERT INTO {SqlSyntax.GetQuotedTableName(ContentVersionCultureVariationDto.TableName)} ({cols})") + Sql? sqlInsertContentVersion = Sql($"INSERT INTO {QuoteTableName(ContentVersionCultureVariationDto.TableName)} ({cols})") .Append(sqlSelect2); Database.Execute(sqlInsertContentVersion); @@ -996,12 +996,12 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas x => x.Name, x => x.Available, x => x.LanguageId); Sql sqlSelectDocument = Sql().Select(x => x.NodeId, x => x.Edited, x => x.Published) .AndSelect(x => x.Text) - .Append($", 1, {defaultLanguageId}") // make Available + default language ID + .Append($", {SqlSyntax.ConvertIntegerToBoolean(1)}, {defaultLanguageId}") // make Available + default language ID .From() .InnerJoin().On(x => x.NodeId, x => x.NodeId) .InnerJoin().On(x => x.NodeId, x => x.NodeId) .Where(x => x.ContentTypeId == contentType.Id); - Sql sqlInsertDocumentCulture = Sql($"INSERT INTO {SqlSyntax.GetQuotedTableName(DocumentCultureVariationDto.TableName)} ({cols})").Append(sqlSelectDocument); + Sql sqlInsertDocumentCulture = Sql($"INSERT INTO {QuoteTableName(DocumentCultureVariationDto.TableName)} ({cols})").Append(sqlSelectDocument); Database.Execute(sqlInsertDocumentCulture); } @@ -1087,7 +1087,7 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas .Where(x => x.LanguageId.SqlNullableEquals(sourceLanguageId, -1)); var cols = Sql().ColumnsForInsert(x => x.Text, x => x.Group, x => x.LanguageId); - Sql? sqlInsertTags = Sql($"INSERT INTO {SqlSyntax.GetQuotedTableName(TagDto.TableName)} ({cols})").Append(sqlSelectTagsToInsert1); + Sql? sqlInsertTags = Sql($"INSERT INTO {QuoteTableName(TagDto.TableName)} ({cols})").Append(sqlSelectTagsToInsert1); Database.Execute(sqlInsertTags); @@ -1122,7 +1122,7 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas var relationColumnsToInsert = Sql().ColumnsForInsert(x => x.NodeId, x => x.PropertyTypeId, x => x.TagId); Sql? sqlInsertRelations = - Sql($"INSERT INTO {SqlSyntax.GetQuotedTableName(TagRelationshipDto.TableName)} ({relationColumnsToInsert})") + Sql($"INSERT INTO {QuoteTableName(TagRelationshipDto.TableName)} ({relationColumnsToInsert})") .Append(sqlSelectRelationsToInsert); Database.Execute(sqlInsertRelations); @@ -1241,7 +1241,7 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas .WhereIn(x => x.ContentTypeId, contentTypeIds); } - Sql? sqlInsert = Sql($"INSERT INTO {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} ({cols})").Append(sqlSelectData); + Sql? sqlInsert = Sql($"INSERT INTO {QuoteTableName(PropertyDataDto.TableName)} ({cols})").Append(sqlSelectData); Database.Execute(sqlInsert); @@ -1582,6 +1582,7 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas .WhereLike(c => c.Alias, $"{alias}{SqlSyntax.GetWildcardPlaceholder()}"); List aliases = Database.Fetch(sql); + var i = 1; string test; while (aliases.Contains(test = alias + i)) @@ -1617,7 +1618,7 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas public bool HasContentNodes(int id) { var sql = new Sql( - $"SELECT CASE WHEN EXISTS (SELECT * FROM {SqlSyntax.GetQuotedTableName(ContentDto.TableName)} WHERE {SqlSyntax.GetQuotedColumnName("contentTypeId")} = @id) THEN 1 ELSE 0 END", + $"SELECT CASE WHEN EXISTS (SELECT * FROM {QuoteTableName(ContentDto.TableName)} WHERE {QuoteColumnName("contentTypeId")} = @id) THEN 1 ELSE 0 END", new { id }); return Database.ExecuteScalar(sql) == 1; } @@ -1629,18 +1630,18 @@ internal abstract class ContentTypeRepositoryBase : EntityRepositoryBas // is included here just to be 100% sure since it has a FK on cmsPropertyType. var list = new List { - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.User2NodeNotify)} WHERE {SqlSyntax.GetQuotedColumnName("nodeId")} = @id", - $@"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission)} WHERE {SqlSyntax.GetQuotedColumnName("uniqueId")} IN - (SELECT {SqlSyntax.GetQuotedColumnName("uniqueId")} FROM {SqlSyntax.GetQuotedTableName(NodeDto.TableName)} WHERE id = @id)", - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.TagRelationship)} WHERE {SqlSyntax.GetQuotedColumnName("nodeId")} = @id", - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.ContentChildType)} WHERE {SqlSyntax.GetQuotedColumnName("Id")} = @id", - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.ContentChildType)} WHERE {SqlSyntax.GetQuotedColumnName("AllowedId")} = @id", - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.ContentTypeTree)} WHERE {SqlSyntax.GetQuotedColumnName("parentContentTypeId")} = @id", - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.ContentTypeTree)} WHERE {SqlSyntax.GetQuotedColumnName("childContentTypeId")} = @id", - $@"DELETE FROM {SqlSyntax.GetQuotedTableName(PropertyDataDto.TableName)} WHERE {SqlSyntax.GetQuotedColumnName("propertyTypeId")} IN - (SELECT id FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.PropertyType)} WHERE {SqlSyntax.GetQuotedColumnName("contentTypeId")} = @id)", - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.PropertyType)} WHERE {SqlSyntax.GetQuotedColumnName("contentTypeId")} = @id", - $"DELETE FROM {SqlSyntax.GetQuotedTableName(Constants.DatabaseSchema.Tables.PropertyTypeGroup)} WHERE {SqlSyntax.GetQuotedColumnName("contenttypeNodeId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.User2NodeNotify)} WHERE {QuoteColumnName("nodeId")} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission)} WHERE {QuoteColumnName("uniqueId")} IN + (SELECT {QuoteColumnName("uniqueId")} FROM {QuoteTableName(NodeDto.TableName)} WHERE id = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.TagRelationship)} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentChildType)} WHERE {QuoteColumnName("Id")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentChildType)} WHERE {QuoteColumnName("AllowedId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentTypeTree)} WHERE {QuoteColumnName("parentContentTypeId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentTypeTree)} WHERE {QuoteColumnName("childContentTypeId")} = @id", + $@"DELETE FROM {QuoteTableName(PropertyDataDto.TableName)} WHERE {QuoteColumnName("propertyTypeId")} IN + (SELECT id FROM {QuoteTableName(Constants.DatabaseSchema.Tables.PropertyType)} WHERE {QuoteColumnName("contentTypeId")} = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.PropertyType)} WHERE {QuoteColumnName("contentTypeId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.PropertyTypeGroup)} WHERE {QuoteColumnName("contenttypeNodeId")} = @id", }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs index 3e55ee442f..b97a200cbb 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs @@ -49,7 +49,7 @@ internal sealed class DataTypeRepository : EntityRepositoryBase, protected Guid NodeObjectTypeId => Constants.ObjectTypes.DataType; - public IDataType? Get(Guid key) => GetMany().FirstOrDefault(x=>x.Key == key); + public IDataType? Get(Guid key) => GetMany().FirstOrDefault(x => x.Key == key); public IEnumerable> Move(IDataType toMove, EntityContainer? container) { @@ -257,7 +257,7 @@ internal sealed class DataTypeRepository : EntityRepositoryBase, if (ids?.Any() ?? false) { - dataTypeSql.Where("umbracoNode.id in (@ids)", new { ids }); + dataTypeSql.WhereIn(w => w.NodeId, ids); } else { @@ -309,7 +309,7 @@ internal sealed class DataTypeRepository : EntityRepositoryBase, return sql; } - protected override string GetBaseWhereClause() => "umbracoNode.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName("umbracoNode")}.id = @id"; protected override IEnumerable GetDeleteClauses() => Array.Empty(); @@ -340,12 +340,17 @@ internal sealed class DataTypeRepository : EntityRepositoryBase, DataTypeDto dto = DataTypeFactory.BuildDto(entity, _serializer); // Logic for setting Path, Level and SortOrder - NodeDto? parent = Database.First("WHERE id = @ParentId", new { entity.ParentId }); + Sql sql = Sql() + .SelectAll() + .From() + .Where(x => x.NodeId == entity.ParentId); + NodeDto parent = Database.First(sql); var level = parent.Level + 1; - var sortOrder = - Database.ExecuteScalar( - "SELECT COUNT(*) FROM umbracoNode WHERE parentID = @ParentId AND nodeObjectType = @NodeObjectType", - new { entity.ParentId, NodeObjectType = NodeObjectTypeId }); + sql = Sql() + .SelectCount() + .From() + .Where(x => x.ParentId == entity.ParentId && x.NodeObjectType == NodeObjectTypeId); + var sortOrder = Database.ExecuteScalar(sql); // Create the (base) node data - umbracoNode NodeDto nodeDto = dto.NodeDto; @@ -355,7 +360,7 @@ internal sealed class DataTypeRepository : EntityRepositoryBase, var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto); // Update with new correct path - nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); + nodeDto.Path = string.Concat(nodeDto.Path, ",", nodeDto.NodeId); Database.Update(nodeDto); // Update entity with correct values @@ -392,13 +397,19 @@ internal sealed class DataTypeRepository : EntityRepositoryBase, // Look up parent to get and set the correct Path if ParentId has changed if (entity.IsPropertyDirty("ParentId")) { - NodeDto? parent = Database.First("WHERE id = @ParentId", new { entity.ParentId }); - entity.Path = string.Concat(parent.Path, ",", entity.Id); - entity.Level = parent.Level + 1; - var maxSortOrder = - Database.ExecuteScalar( - "SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType", - new { entity.ParentId, NodeObjectType = NodeObjectTypeId }); + Sql sql = Sql() + .SelectAll() + .From() + .Where(x => x.NodeId == entity.ParentId); + NodeDto? parent = Database.FirstOrDefault(sql); + entity.Path = string.Concat(parent?.Path ?? "-1", ",", entity.Id); + entity.Level = (parent?.Level ?? 0) + 1; + + sql = Sql() + .SelectMax(c => c.SortOrder, 0) + .From() + .Where(x => x.ParentId == entity.ParentId && x.NodeObjectType == NodeObjectTypeId); + var maxSortOrder = Database.ExecuteScalar(sql); entity.SortOrder = maxSortOrder + 1; } @@ -414,31 +425,44 @@ internal sealed class DataTypeRepository : EntityRepositoryBase, protected override void PersistDeletedItem(IDataType entity) { + Sql sql; + // Remove Notifications - Database.Delete("WHERE nodeId = @Id", new { entity.Id }); + sql = Sql().Delete(x => x.NodeId == entity.Id); + Database.Execute(sql); // Remove Permissions - Database.Delete("WHERE uniqueId = @Key", new { entity.Key }); + sql = Sql().Delete(x => x.UniqueId == entity.Key); + Database.Execute(sql); // Remove associated tags - Database.Delete("WHERE nodeId = @Id", new { entity.Id }); + sql = Sql().Delete(x => x.NodeId == entity.Id); + Database.Execute(sql); // PropertyTypes containing the DataType being deleted - List? propertyTypeDtos = - Database.Fetch("WHERE dataTypeId = @Id", new { entity.Id }); + sql = Sql() + .SelectAll() + .From() + .Where(x => x.DataTypeId == entity.Id); + List? propertyTypeDtos = Database.Fetch(sql); // Go through the PropertyTypes and delete referenced PropertyData before deleting the PropertyType foreach (PropertyTypeDto? dto in propertyTypeDtos) { - Database.Delete("WHERE propertytypeid = @Id", new { dto.Id }); - Database.Delete("WHERE id = @Id", new { dto.Id }); + sql = Sql().Delete(x => x.PropertyTypeId == dto.Id); + Database.Execute(sql); + + sql = Sql().Delete(x => x.Id == dto.Id); + Database.Execute(sql); } // Delete Content specific data - Database.Delete("WHERE nodeId = @Id", new { entity.Id }); + sql = Sql().Delete(x => x.NodeId == entity.Id); + Database.Execute(sql); // Delete (base) node data - Database.Delete("WHERE uniqueID = @Id", new { Id = entity.Key }); + sql = Sql().Delete(x => x.UniqueId == entity.Key); + Database.Execute(sql); entity.DeleteDate = DateTime.UtcNow; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs index 473f449557..47080a231d 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DictionaryRepository.cs @@ -22,6 +22,8 @@ internal sealed class DictionaryRepository : EntityRepositoryBase $"{QuoteTableName(DictionaryDto.TableName)}.{QuoteColumnName(columnName)}"; + public DictionaryRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, ILoggerFactory loggerFactory, ILanguageRepository languageRepository) : base(scopeAccessor, cache, logger) @@ -60,7 +62,7 @@ internal sealed class DictionaryRepository : EntityRepositoryBase GetDictionaryItemKeyMap() { - var columns = new[] { "key", "id" }.Select(x => (object)SqlSyntax.GetQuotedColumnName(x)).ToArray(); + var columns = new[] { "key", "id" }.Select(x => (object)QuotedColumn(x)).ToArray(); Sql sql = Sql().Select(columns).From(); return Database.Fetch(sql).ToDictionary(x => x.Key, x => x.Id); } @@ -186,6 +188,8 @@ internal sealed class DictionaryRepository : EntityRepositoryBase _languagesById; + private string QuotedColumn(string columnName) => $"{QuoteTableName(DictionaryDto.TableName)}.{QuoteColumnName(columnName)}"; + public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger) : base(scopeAccessor, cache, logger) @@ -201,7 +205,7 @@ internal sealed class DictionaryRepository : EntityRepositoryBase GetBaseQuery(bool isCount) => _dictionaryRepository.GetBaseQuery(isCount); protected override string GetBaseWhereClause() => - "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " = @id"; + $"{QuotedColumn("id")} = @id"; protected override IDictionaryItem ConvertToEntity(DictionaryDto dto) => ConvertFromDto(dto, _languagesById); @@ -209,7 +213,7 @@ internal sealed class DictionaryRepository : EntityRepositoryBase new { id }; protected override string GetWhereInClauseForGetAll() => - "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " in (@ids)"; + $"{QuotedColumn("id")} in (@ids)"; protected override IRepositoryCachePolicy CreateCachePolicy() { @@ -241,6 +245,8 @@ internal sealed class DictionaryRepository : EntityRepositoryBase _languagesById; + private string QuotedColumn(string columnName) => $"{QuoteTableName(DictionaryDto.TableName)}.{QuoteColumnName(columnName)}"; + public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger) : base(scopeAccessor, cache, logger) @@ -256,7 +262,7 @@ internal sealed class DictionaryRepository : EntityRepositoryBase GetBaseQuery(bool isCount) => _dictionaryRepository.GetBaseQuery(isCount); protected override string GetBaseWhereClause() => - "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " = @id"; + $"{QuotedColumn("key")} = @id"; protected override IDictionaryItem ConvertToEntity(DictionaryDto dto) => ConvertFromDto(dto, _languagesById); @@ -264,7 +270,7 @@ internal sealed class DictionaryRepository : EntityRepositoryBase new { id }; protected override string GetWhereInClauseForGetAll() => - "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " in (@ids)"; + $"{QuotedColumn("key")} IN (@ids)"; protected override IRepositoryCachePolicy CreateCachePolicy() { @@ -345,7 +351,7 @@ internal sealed class DictionaryRepository : EntityRepositoryBase $"{Constants.DatabaseSchema.Tables.DictionaryEntry}.pk = @id"; + protected override string GetBaseWhereClause() => $"{QuotedColumn("pk")} = @id"; protected override IEnumerable GetDeleteClauses() => new List(); @@ -421,8 +427,7 @@ internal sealed class DictionaryRepository : EntityRepositoryBase("WHERE UniqueId = @Id", new { Id = entity.Key }); - Database.Delete("WHERE id = @Id", new { Id = entity.Key }); + DeleteEntity(entity.Key); // Clear the cache entries that exist by uniqueid/item key IsolatedCache.Clear(RepositoryCacheKeys.GetKey(entity.ItemKey)); @@ -433,14 +438,17 @@ internal sealed class DictionaryRepository : EntityRepositoryBase? list = - Database.Fetch("WHERE parent = @ParentId", new { ParentId = parentId }); + Sql sql = SqlContext.Sql() + .Select() + .From() + .Where(c => c.Parent == parentId); + List? list = Database.Fetch(sql); + foreach (DictionaryDto? dto in list) { RecursiveDelete(dto.UniqueId); - Database.Delete("WHERE UniqueId = @Id", new { Id = dto.UniqueId }); - Database.Delete("WHERE id = @Id", new { Id = dto.UniqueId }); + DeleteEntity(dto.UniqueId); // Clear the cache entries that exist by uniqueid/item key IsolatedCache.Clear(RepositoryCacheKeys.GetKey(dto.Key)); @@ -448,6 +456,19 @@ internal sealed class DictionaryRepository : EntityRepositoryBase sql = SqlContext.Sql() + .Delete() + .Where(c => c.UniqueId == key); + Database.Execute(sql); + + sql = SqlContext.Sql() + .Delete() + .Where(c => c.UniqueId == key); + Database.Execute(sql); + } + private IDictionary GetLanguagesById() => _languageRepository .GetMany() .ToDictionary(language => language.Id); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs index 21d7986d4b..d38b9749c9 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs @@ -595,10 +595,9 @@ public class DocumentRepository : ContentRepositoryBase sql = GetBaseQuery(QueryType.Single) - .Where(x => x.NodeId == id) - .SelectTop(1); + .Where(x => x.NodeId == id); - DocumentDto? dto = Database.Fetch(sql).FirstOrDefault(); + DocumentDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : MapDtoToContent(dto); @@ -724,46 +723,48 @@ public class DocumentRepository : ContentRepositoryBase $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(NodeDto.TableName)}.id = @id"; protected override IEnumerable GetDeleteClauses() { + var nodeId = QuoteColumnName("nodeId"); + var uniqueId = QuoteColumnName("uniqueId"); + var umbracoNode = QuoteTableName(NodeDto.TableName); var list = new List - { - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentSchedule + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.RedirectUrl + - " WHERE contentKey IN (SELECT uniqueId FROM " + Constants.DatabaseSchema.Tables.Node + - " WHERE id = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.UserGroup2GranularPermission + " WHERE uniqueId IN (SELECT uniqueId FROM umbracoNode WHERE id = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.UserStartNode + " WHERE startNode = @id", - "UPDATE " + Constants.DatabaseSchema.Tables.UserGroup + - " SET startContentId = NULL WHERE startContentId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE parentId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE childId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.TagRelationship + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Domain + " WHERE domainRootStructureID = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentCultureVariation + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentVersion + " WHERE id IN (SELECT id FROM " + - Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE versionId IN (SELECT id FROM " + - Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersionCultureVariation + - " WHERE versionId IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + - " WHERE nodeId = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.AccessRule + " WHERE accessId IN (SELECT id FROM " + - Constants.DatabaseSchema.Tables.Access + - " WHERE nodeId = @id OR loginNodeId = @id OR noAccessNodeId = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Access + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Access + " WHERE loginNodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Access + " WHERE noAccessNodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentUrl + " WHERE uniqueId IN (SELECT uniqueId FROM " + Constants.DatabaseSchema.Tables.Node + - " WHERE id = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id", - + { + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentSchedule)} WHERE {nodeId} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.RedirectUrl)} WHERE {QuoteColumnName("contentKey")} IN + (SELECT {uniqueId} FROM {umbracoNode} WHERE id = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.User2NodeNotify )} WHERE {nodeId} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission)} WHERE {uniqueId} IN + (SELECT {uniqueId} FROM {umbracoNode} WHERE id = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.UserStartNode)} WHERE {QuoteColumnName("startNode")} = @id", + $@"UPDATE {QuoteTableName(Constants.DatabaseSchema.Tables.UserGroup)} + SET {QuoteColumnName("startContentId")} = NULL + WHERE {QuoteColumnName("startContentId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Relation)} WHERE {QuoteColumnName("parentId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Relation)} WHERE {QuoteColumnName("childId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.TagRelationship)} WHERE {nodeId} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Domain)} WHERE {QuoteColumnName("domainRootStructureID")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Document)} WHERE {nodeId} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.DocumentCultureVariation)} WHERE {nodeId} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} WHERE id IN + (SELECT id FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersion )} WHERE {nodeId} = @id)", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.PropertyData)} WHERE {QuoteColumnName("versionId")} IN + (SELECT id FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersion)} WHERE {nodeId} = @id)", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation)} WHERE {QuoteColumnName("versionId")} IN + (SELECT id FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersion)} WHERE {nodeId} = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersion)} WHERE {nodeId} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Content)} WHERE {nodeId} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.AccessRule)} WHERE {QuoteColumnName("accessId")} IN + (SELECT id FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Access)} + WHERE {nodeId} = @id OR {QuoteColumnName("loginNodeId")} = @id OR {QuoteColumnName("noAccessNodeId")} = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Access)} WHERE {nodeId} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Access)} WHERE {QuoteColumnName("loginNodeId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Access)} WHERE {QuoteColumnName("noAccessNodeId")} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.DocumentUrl)} WHERE {uniqueId} IN + (SELECT {uniqueId} FROM {umbracoNode} WHERE id = @id)", + $"DELETE FROM {umbracoNode} WHERE id = @id", }; return list; } @@ -881,10 +882,17 @@ public class DocumentRepository : ContentRepositoryBase("WHERE versionId = @versionId", new { versionId }); - Database.Delete("WHERE versionId = @versionId", new { versionId }); - Database.Delete("WHERE id = @versionId", new { versionId }); - Database.Delete("WHERE id = @versionId", new { versionId }); + Sql sql = Sql().Delete(x => x.VersionId == versionId); + Database.Execute(sql); + + sql = Sql().Delete(x => x.VersionId == versionId); + Database.Execute(sql); + + sql = Sql().Delete(x => x.Id == versionId); + Database.Execute(sql); + + sql = Sql().Delete(x => x.Id == versionId); + Database.Execute(sql); } #endregion @@ -1562,7 +1570,7 @@ public class DocumentRepository : ContentRepositoryBase sql = _outerRepo.GetBaseQuery(QueryType.Single) .Where(x => x.UniqueId == id); - DocumentDto? dto = Database.Fetch(sql.SelectTop(1)).FirstOrDefault(); + DocumentDto? dto = Database.FirstOrDefault(sql); if (dto == null) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs index 043f99eeb8..ba455bbf85 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentVersionRepository.cs @@ -1,10 +1,12 @@ using System; using System.Data; +using System.Text; using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; @@ -23,24 +25,17 @@ internal sealed class DocumentVersionRepository : IDocumentVersionRepository /// Never includes current published version.
/// Never includes versions marked as "preventCleanup".
/// - public IReadOnlyCollection? GetDocumentVersionsEligibleForCleanup() + public IReadOnlyCollection GetDocumentVersionsEligibleForCleanup() { - if (_scopeAccessor.AmbientScope is null) + IScope? ambientScope = _scopeAccessor.AmbientScope; + if (ambientScope is null) { return []; } - Sql query = _scopeAccessor.AmbientScope.SqlContext.Sql(); - - query.Select(@"umbracoDocument.nodeId as contentId, - umbracoContent.contentTypeId as contentTypeId, - umbracoContentVersion.id as versionId, - umbracoContentVersion.userId as userId, - umbracoContentVersion.versionDate as versionDate, - umbracoDocumentVersion.published as currentPublishedVersion, - umbracoContentVersion.[current] as currentDraftVersion, - umbracoContentVersion.preventCleanup as preventCleanup, - umbracoUser.userName as username") + ISqlSyntaxProvider syntax = ambientScope.SqlContext.SqlSyntax; + Sql query = ambientScope.SqlContext.Sql() + .Select(GetQuotedSelectColumns(syntax)) .From() .InnerJoin() .On(left => left.NodeId, right => right.NodeId) @@ -54,13 +49,26 @@ internal sealed class DocumentVersionRepository : IDocumentVersionRepository .Where(x => !x.PreventCleanup) // Never delete "pinned" versions .Where(x => !x.Published); // Never delete published version - List results = _scopeAccessor.AmbientScope.Database.Fetch(query); + List results = ambientScope.Database.Fetch(query); EnsureUtcDates(results); return results; } + private string GetQuotedSelectColumns(ISqlSyntaxProvider syntax) => + $@" +{syntax.ColumnWithAlias(ContentVersionDto.TableName, "id", "versionId")}, +{syntax.ColumnWithAlias(DocumentDto.TableName, "nodeId", "contentId")}, +{syntax.ColumnWithAlias(ContentDto.TableName, "contentTypeId", "contentTypeId")}, +{syntax.ColumnWithAlias(ContentVersionDto.TableName, "userId", "userId")}, +{syntax.ColumnWithAlias(ContentVersionDto.TableName, "versionDate", "versionDate")}, +{syntax.ColumnWithAlias(DocumentVersionDto.TableName ,"published", "currentPublishedVersion")}, +{syntax.ColumnWithAlias(ContentVersionDto.TableName, "current", "currentDraftVersion")}, +{syntax.ColumnWithAlias(ContentVersionDto.TableName, "preventCleanup", "preventCleanup")}, +{syntax.ColumnWithAlias(UserDto.TableName, "userName", "username")} +"; + /// - public IReadOnlyCollection? GetCleanupPolicies() + public IReadOnlyCollection GetCleanupPolicies() { if (_scopeAccessor.AmbientScope is null) { @@ -72,29 +80,22 @@ internal sealed class DocumentVersionRepository : IDocumentVersionRepository query.Select() .From(); - return _scopeAccessor.AmbientScope?.Database.Fetch(query); + return _scopeAccessor.AmbientScope.Database.Fetch(query); } /// - public IEnumerable? GetPagedItemsByContentId(int contentId, long pageIndex, int pageSize, out long totalRecords, int? languageId = null) + public IEnumerable GetPagedItemsByContentId(int contentId, long pageIndex, int pageSize, out long totalRecords, int? languageId = null) { - if (_scopeAccessor.AmbientScope is null) + IScope? ambientScope = _scopeAccessor.AmbientScope; + if (ambientScope is null) { totalRecords = 0; return []; } - Sql query = _scopeAccessor.AmbientScope.SqlContext.Sql(); - - query.Select(@"umbracoDocument.nodeId as contentId, - umbracoContent.contentTypeId as contentTypeId, - umbracoContentVersion.id as versionId, - umbracoContentVersion.userId as userId, - umbracoContentVersion.versionDate as versionDate, - umbracoDocumentVersion.published as currentPublishedVersion, - umbracoContentVersion.[current] as currentDraftVersion, - umbracoContentVersion.preventCleanup as preventCleanup, - umbracoUser.userName as username") + ISqlSyntaxProvider syntax = ambientScope.SqlContext.SqlSyntax; + Sql query = ambientScope.SqlContext.Sql() + .Select(GetQuotedSelectColumns(syntax)) .From() .InnerJoin() .On(left => left.NodeId, right => right.NodeId) @@ -111,12 +112,12 @@ internal sealed class DocumentVersionRepository : IDocumentVersionRepository // TODO: If there's not a better way to write this then we need a better way to write this. query = languageId.HasValue ? query.Where(x => x.LanguageId == languageId.Value) - : query.Where("umbracoContentVersionCultureVariation.languageId is null"); + : query.WhereNull(x => x.LanguageId); query = query.OrderByDescending(x => x.Id); Page page = - _scopeAccessor.AmbientScope.Database.Page(pageIndex + 1, pageSize, query); + ambientScope.Database.Page(pageIndex + 1, pageSize, query); totalRecords = page.TotalItems; @@ -185,22 +186,15 @@ internal sealed class DocumentVersionRepository : IDocumentVersionRepository /// public ContentVersionMeta? Get(int versionId) { - if (_scopeAccessor.AmbientScope is null) + IScope? ambientScope = _scopeAccessor.AmbientScope; + if (ambientScope is null) { return null; } - Sql query = _scopeAccessor.AmbientScope.SqlContext.Sql(); - - query.Select(@"umbracoDocument.nodeId as contentId, - umbracoContent.contentTypeId as contentTypeId, - umbracoContentVersion.id as versionId, - umbracoContentVersion.userId as userId, - umbracoContentVersion.versionDate as versionDate, - umbracoDocumentVersion.published as currentPublishedVersion, - umbracoContentVersion.[current] as currentDraftVersion, - umbracoContentVersion.preventCleanup as preventCleanup, - umbracoUser.userName as username") + ISqlSyntaxProvider syntax = ambientScope.SqlContext.SqlSyntax; + Sql query = ambientScope.SqlContext.Sql() + .Select(GetQuotedSelectColumns(syntax)) .From() .InnerJoin() .On(left => left.NodeId, right => right.NodeId) @@ -212,7 +206,7 @@ internal sealed class DocumentVersionRepository : IDocumentVersionRepository .On(left => left.Id, right => right.UserId) .Where(x => x.Id == versionId); - ContentVersionMeta result = _scopeAccessor.AmbientScope.Database.Single(query); + ContentVersionMeta result = ambientScope.Database.Single(query); result.EnsureUtc(); return result; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DomainRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DomainRepository.cs index 810dd5fe38..8cceca7d00 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DomainRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DomainRepository.cs @@ -62,7 +62,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo } else { - sql.Select($"{Constants.DatabaseSchema.Tables.Domain}.*, {Constants.DatabaseSchema.Tables.Language}.languageISOCode") + sql.Select($"{QuoteTableName(Constants.DatabaseSchema.Tables.Domain)}.*, {QuoteTableName(Constants.DatabaseSchema.Tables.Language)}.{QuoteColumnName("languageISOCode")}") .From() .LeftJoin() .On(dto => dto.DefaultLanguage, dto => dto.Id); @@ -72,18 +72,18 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo } protected override string GetBaseWhereClause() - => $"{Constants.DatabaseSchema.Tables.Domain}.id = @id"; + => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Domain)}.id = @id"; protected override IEnumerable GetDeleteClauses() => new [] { - $"DELETE FROM {Constants.DatabaseSchema.Tables.Domain} WHERE id = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Domain)} WHERE id = @id", }; protected override void PersistNewItem(IDomain entity) { var exists = Database.ExecuteScalar( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Domain} WHERE domainName = @domainName", + $"SELECT COUNT(*) FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Domain)} WHERE {QuoteColumnName("domainName")} = @domainName", new { domainName = entity.DomainName }); if (exists > 0) { @@ -93,7 +93,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo if (entity.RootContentId.HasValue) { var contentExists = Database.ExecuteScalar( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Content} WHERE nodeId = @id", + $"SELECT COUNT(*) FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Content)} WHERE {QuoteColumnName("nodeId")} = @id", new { id = entity.RootContentId.Value }); if (contentExists == 0) { @@ -104,7 +104,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo if (entity.LanguageId.HasValue) { var languageExists = Database.ExecuteScalar( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @id", + $"SELECT COUNT(*) FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Language)} WHERE id = @id", new { id = entity.LanguageId.Value }); if (languageExists == 0) { @@ -126,7 +126,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo if (entity.LanguageId.HasValue) { ((UmbracoDomain)entity).LanguageIsoCode = Database.ExecuteScalar( - $"SELECT languageISOCode FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @langId", + $"SELECT {QuoteColumnName("languageISOCode")} FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Language)} WHERE id = @langId", new { langId = entity.LanguageId }); } @@ -139,7 +139,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo // Ensure there is no other domain with the same name on another entity var exists = Database.ExecuteScalar( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Domain} WHERE domainName = @domainName AND umbracoDomain.id <> @id", + $"SELECT COUNT(*) FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Domain)} WHERE {QuoteColumnName("domainName")} = @domainName AND id <> @id", new { domainName = entity.DomainName, id = entity.Id }); if (exists > 0) { @@ -149,7 +149,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo if (entity.RootContentId.HasValue) { var contentExists = Database.ExecuteScalar( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Content} WHERE nodeId = @id", + $"SELECT COUNT(*) FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Content)} WHERE {QuoteColumnName("nodeId")} = @id", new { id = entity.RootContentId.Value }); if (contentExists == 0) { @@ -160,7 +160,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo if (entity.LanguageId.HasValue) { var languageExists = Database.ExecuteScalar( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @id", + $"SELECT COUNT(*) FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Language)} WHERE id = @id", new { id = entity.LanguageId.Value }); if (languageExists == 0) { @@ -176,7 +176,7 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo if (entity.WasPropertyDirty("LanguageId")) { ((UmbracoDomain)entity).LanguageIsoCode = Database.ExecuteScalar( - $"SELECT languageISOCode FROM {Constants.DatabaseSchema.Tables.Language} WHERE id = @langId", + $"SELECT {QuoteColumnName("languageISOCode")} FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Language)} WHERE id = @langId", new { langId = entity.LanguageId }); } @@ -187,6 +187,6 @@ internal sealed class DomainRepository : EntityRepositoryBase, IDo => isWildcard ? -1 : Database.ExecuteScalar( - $"SELECT COALESCE(MAX(sortOrder), -1) + 1 FROM {Constants.DatabaseSchema.Tables.Domain} WHERE domainRootStructureID = @rootContentId AND NOT (domainName = '' OR domainName LIKE '*%')", + $"SELECT COALESCE(MAX({QuoteColumnName("sortOrder")}), -1) + 1 FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Domain)} WHERE {QuoteColumnName("domainRootStructureID")} = @rootContentId AND NOT ({QuoteColumnName("domainName")} = '' OR {QuoteColumnName("domainName")} LIKE '*%')", new { rootContentId }); } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs index 117e8451fd..2ac35d914c 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityContainerRepository.cs @@ -37,7 +37,7 @@ internal class EntityContainerRepository : EntityRepositoryBase sql = GetBaseQuery(false).Where("UniqueId=@uniqueId", new { uniqueId = id }); + Sql sql = GetBaseQuery(false).Where(c => c.UniqueId == id); NodeDto? nodeDto = Database.Fetch(sql).FirstOrDefault(); return nodeDto == null ? null : CreateEntity(nodeDto); @@ -46,9 +46,7 @@ internal class EntityContainerRepository : EntityRepositoryBase Get(string name, int level) { Sql sql = GetBaseQuery(false) - .Where( - "text=@name AND level=@level AND nodeObjectType=@umbracoObjectTypeId", - new { name, level, umbracoObjectTypeId = NodeObjectTypeId }); + .Where(c => c.Text == name && c.Level == level && c.NodeObjectType == NodeObjectTypeId); return Database.Fetch(sql).Select(CreateEntity).WhereNotNull(); } @@ -61,7 +59,7 @@ internal class EntityContainerRepository : EntityRepositoryBase sql = GetBaseQuery(false) .Where(GetBaseWhereClause(), new { id, NodeObjectType = NodeObjectTypeId }); - NodeDto? nodeDto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); + NodeDto? nodeDto = Database.FirstOrDefault(sql); return nodeDto == null ? null : CreateEntity(nodeDto); } @@ -78,7 +76,7 @@ internal class EntityContainerRepository : EntityRepositoryBase sql = GetBaseQuery(false) - .Where("nodeObjectType=@umbracoObjectTypeId", new { umbracoObjectTypeId = NodeObjectTypeId }) + .Where(c => c.NodeObjectType == NodeObjectTypeId) .OrderBy(x => x.Level); return Database.Fetch(sql).Select(CreateEntity).WhereNotNull(); @@ -128,7 +126,7 @@ internal class EntityContainerRepository : EntityRepositoryBase "umbracoNode.id = @id and nodeObjectType = @NodeObjectType"; + protected override string GetBaseWhereClause() => $"id = @id and {QuoteColumnName("nodeObjectType")} = @NodeObjectType"; protected override IEnumerable GetDeleteClauses() => throw new NotImplementedException(); @@ -139,7 +137,7 @@ internal class EntityContainerRepository : EntityRepositoryBase("parent") .On( (node, parent) => node.ParentId == parent.NodeId, aliasRight: "parent") - .Where(dto => dto.Text == name && dto.NodeObjectType == NodeObjectTypeId) + .Where(dto => dto.Text == name && dto.NodeObjectType == NodeObjectTypeId) .Where(parent => parent.UniqueId == parentKey, alias: "parent")); return nodeDto is not null; @@ -175,14 +173,7 @@ internal class EntityContainerRepository : EntityRepositoryBase childDtos = Database.Fetch(Sql().SelectAll() .From() - .Where( - "parentID=@parentID AND (nodeObjectType=@containedObjectType OR nodeObjectType=@containerObjectType)", - new - { - parentID = entity.Id, - containedObjectType = entity.ContainedObjectType, - containerObjectType = entity.ContainerObjectType, - })); + .Where(c => c.ParentId == entity.Id && (c.NodeObjectType == entity.ContainedObjectType || c.NodeObjectType == entity.ContainerObjectType))); foreach (NodeDto childDto in childDtos) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs index 382e0a43e9..6bad48923a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepository.cs @@ -56,34 +56,73 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend return Database.ExecuteScalar(sql); } - public IEnumerable GetPagedResultsByQuery(IQuery query, ISet objectTypes, - long pageIndex, int pageSize, out long totalRecords, - IQuery? filter, Ordering? ordering) => + /// + /// Retrieves a paginated collection of entities that match the specified query and object types. + /// + /// The query used to filter the entities to be retrieved. + /// A set of object type GUIDs that specify the types of entities to include in the results. + /// The zero-based index of the page to retrieve. + /// The number of entities to include in each page. + /// When the method returns, contains the total number of records that match the query and object types. + /// An optional additional query to further filter the results. If , no additional filtering + /// is applied. + /// An optional ordering specification to determine the sort order of the results. If , the + /// default ordering is applied. + /// A collection of entities that match the specified query, object types, and optional filters, limited to the + /// specified page size. + public IEnumerable GetPagedResultsByQuery(IQuery query, ISet objectTypes, long pageIndex, int pageSize, out long totalRecords, IQuery? filter, Ordering? ordering) => GetPagedResultsByQuery(query, objectTypes.ToArray(), pageIndex, pageSize, out totalRecords, filter, ordering); - // get a page of entities - public IEnumerable GetPagedResultsByQuery(IQuery query, Guid[] objectTypes, - long pageIndex, int pageSize, out long totalRecords, - IQuery? filter, Ordering? ordering, Action>? sqlCustomization = null) + /// + /// Retrieves a paginated collection of entities based on the specified query and parameters. + /// + /// This method supports querying for content, media, and member entities. The results are + /// ordered by the specified if provided, and always include a fallback ordering by node + /// ID to ensure consistent results. + /// The query used to filter the entities. + /// An array of object type GUIDs to filter the entities by type. + /// The zero-based index of the page to retrieve. + /// The number of entities to include in each page. + /// When the method returns, contains the total number of records matching the query. + /// An optional additional query filter to further refine the results. Can be . + /// An optional ordering specification to determine the sort order of the results. Can be . + /// An optional action to customize the underlying SQL query. Can be . + /// A collection of entities that match the specified query and parameters. The collection will contain up to + /// entities, or fewer if there are not enough matching entities. + public IEnumerable GetPagedResultsByQuery( + IQuery query, + Guid[] objectTypes, + long pageIndex, + int pageSize, + out long totalRecords, + IQuery? filter, + Ordering? ordering, + Action>? sqlCustomization = null) { var isContent = objectTypes.Any(objectType => objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint); var isMedia = objectTypes.Any(objectType => objectType == Constants.ObjectTypes.Media); var isMember = objectTypes.Any(objectType => objectType == Constants.ObjectTypes.Member); - Sql sql = GetBaseWhere(isContent, isMedia, isMember, false, s => - { - sqlCustomization?.Invoke(s); - - if (filter != null) + Sql sql = GetBaseWhere( + isContent, + isMedia, + isMember, + false, + s => { - foreach (Tuple filterClause in filter.GetWhereClauses()) + sqlCustomization?.Invoke(s); + + if (filter != null) { - s.Where(filterClause.Item1, filterClause.Item2); + foreach (Tuple filterClause in filter.GetWhereClauses()) + { + s.Where(filterClause.Item1, filterClause.Item2); + } } - } - }, objectTypes); + }, + objectTypes); ordering ??= Ordering.ByDefault(); @@ -101,6 +140,7 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend var pageIndexToFetch = pageIndex + 1; IEnumerable dtos; Page? page = Database.Page(pageIndexToFetch, pageSize, sql); + dtos = page.Items; totalRecords = page.TotalItems; @@ -617,7 +657,7 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend protected Sql GetBase(bool isContent, bool isMedia, bool isMember, Action>? filter, Guid[] objectTypes, bool isCount = false) { Sql sql = Sql(); - + ISqlSyntaxProvider syntax = SqlContext.SqlSyntax; if (isCount) { sql.SelectCount(); @@ -632,14 +672,14 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend if (objectTypes.Length == 0) { - sql.Append(", COUNT(child.id) AS children"); + sql.Append($", COUNT(child.{syntax.GetQuotedColumnName("id")}) AS children"); } else { // The following is safe from SQL injection as we are dealing with GUIDs, not strings. // Upper-case is necessary for SQLite, and also works for SQL Server. - var objectTypesForInClause = string.Join("','", objectTypes.Select(x => x.ToString().ToUpperInvariant())); - sql.Append($", SUM(CASE WHEN child.nodeObjectType IN ('{objectTypesForInClause}') THEN 1 ELSE 0 END) AS children"); + var objectTypesForInClause = string.Join("','", objectTypes.Select(x => x.ToString().ToUpperInvariant())); + sql.Append($", SUM(CASE WHEN child.{syntax.GetQuotedColumnName("nodeObjectType")} IN ('{objectTypesForInClause}') THEN 1 ELSE 0 END) AS children"); } if (isContent || isMedia || isMember) @@ -676,7 +716,8 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend sql .LeftJoin() .On((left, right) => left.NodeId == right.NodeId && right.Current) - .LeftJoin().On((left, right) => left.NodeId == right.NodeId) + .LeftJoin() + .On((left, right) => left.NodeId == right.NodeId) .LeftJoin() .On((left, right) => left.ContentTypeId == right.NodeId) .LeftJoin("ContentTypeNode") @@ -825,7 +866,7 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend switch (runner.OrderBy?.ToUpperInvariant()) { case "NODEOBJECTTYPE": - orderBy = $"UPPER({SqlSyntax.GetQuotedColumn(NodeDto.TableName, "nodeObjectType")})"; + orderBy = SqlSyntax.OrderByGuid(NodeDto.TableName, "nodeObjectType"); break; case "PATH": orderBy = SqlSyntax.GetQuotedColumn(NodeDto.TableName, "path"); @@ -835,7 +876,7 @@ internal sealed class EntityRepository : RepositoryBase, IEntityRepositoryExtend orderingIncludesNodeId = true; break; default: - orderBy = runner.OrderBy ?? string.Empty; + orderBy = QuoteColumnName(runner.OrderBy) ?? string.Empty; break; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs index 78d820a6b2..dc8abbb11f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/EntityRepositoryBase.cs @@ -215,8 +215,7 @@ public abstract class EntityRepositoryBase : RepositoryBase, IRead protected virtual bool PerformExists(TId id) { - Sql sql = GetBaseQuery(true); - sql.Where(GetBaseWhereClause(), new { id }); + Sql sql = GetBaseQuery(true).Where(GetBaseWhereClause(), new { id }); var count = Database.ExecuteScalar(sql); return count == 1; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs index fd64293b7a..5f44f1a14d 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs @@ -53,8 +53,13 @@ internal sealed class ExternalLoginRepository : EntityRepositoryBase - public void DeleteUserLogins(Guid userOrMemberKey) => - Database.Delete("WHERE userOrMemberKey=@userOrMemberKey", new { userOrMemberKey }); + public void DeleteUserLogins(Guid userOrMemberKey) + { + Sql sql = SqlContext.Sql() + .Delete() + .Where(x => x.UserOrMemberKey == userOrMemberKey); + Database.Execute(sql); + } /// public void DeleteUserLoginsForRemovedProviders(IEnumerable currentLoginProviders) @@ -205,7 +210,7 @@ internal sealed class ExternalLoginRepository : EntityRepositoryBase sql = GetBaseQuery(false); sql.Where(GetBaseWhereClause(), new { id }); - ExternalLoginDto? dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); + ExternalLoginDto? dto = Database.FirstOrDefault(sql); if (dto == null) { return null; @@ -290,11 +295,13 @@ internal sealed class ExternalLoginRepository : EntityRepositoryBase $"{Constants.DatabaseSchema.Tables.ExternalLogin}.id = @id"; + private string QuotedTableName => QuoteTableName(ExternalLoginDto.TableName); + + protected override string GetBaseWhereClause() => $"{QuotedTableName}.id = @id"; protected override IEnumerable GetDeleteClauses() { - var list = new List { "DELETE FROM umbracoExternalLogin WHERE id = @id" }; + var list = new List { $"DELETE FROM {QuotedTableName} WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/IdKeyMapRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/IdKeyMapRepository.cs index fe7929ccd5..d3b5f898ed 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/IdKeyMapRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/IdKeyMapRepository.cs @@ -1,52 +1,102 @@ +using Microsoft.Extensions.Logging; +using NPoco; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Scoping; +using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; -public class IdKeyMapRepository : IIdKeyMapRepository +/// +/// Repository for mapping between integer IDs and GUID keys for Umbraco objects. +/// Provides methods to retrieve an ID for a given key and object type, and vice versa. +/// +public class IdKeyMapRepository(IScopeAccessor scopeAccessor) : IIdKeyMapRepository { - private readonly IScopeAccessor _scopeAccessor; - - public IdKeyMapRepository(IScopeAccessor scopeAccessor) => _scopeAccessor = scopeAccessor; - + /// + /// Retrieves the unique identifier (ID) for a specified key and object type. + /// + /// If is , the + /// query will not filter by object type. For other object types, the query will match the specified object type or + /// reserved object type. + /// The globally unique identifier (GUID) of the object to look up. + /// The type of the Umbraco object to filter the query. Use to ignore the + /// object type in the query. + /// The unique identifier (ID) of the object if found; otherwise, . public int? GetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType) { + if (scopeAccessor.AmbientScope is null) + { + return null; + } + + ISqlContext sqlContext = scopeAccessor.AmbientScope.SqlContext; + IUmbracoDatabase database = scopeAccessor.AmbientScope.Database; + Sql? sql; + // if it's unknown don't include the nodeObjectType in the query if (umbracoObjectType == UmbracoObjectTypes.Unknown) { - return _scopeAccessor.AmbientScope?.Database.ExecuteScalar( - "SELECT id FROM umbracoNode WHERE uniqueId=@id", new { id = key }); + sql = sqlContext.Sql() + .Select(c => c.NodeId) + .From() + .Where(n => n.UniqueId == key); + return database?.ExecuteScalar(sql); } - return _scopeAccessor.AmbientScope?.Database.ExecuteScalar( - "SELECT id FROM umbracoNode WHERE uniqueId=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)", - new - { - id = key, - type = GetNodeObjectTypeGuid(umbracoObjectType), - reservation = Constants.ObjectTypes.IdReservation, - }); + Guid type = GetNodeObjectTypeGuid(umbracoObjectType); + sql = sqlContext.Sql() + .Select(c => c.NodeId) + .From() + .Where(n => + n.UniqueId == key + && (n.NodeObjectType == type + || n.NodeObjectType == Constants.ObjectTypes.IdReservation)); + return database.ExecuteScalar(sql); } + /// + /// Retrieves the unique identifier (GUID) associated with a specified node ID and object type. + /// + /// If the is , the + /// query will only filter by the node ID. For other object types, the query will filter by both the node ID and + /// the object type, including reserved object types. + /// The ID of the node to retrieve the unique identifier for. + /// The type of the Umbraco object to filter by. If set to , the object + /// type is not included in the query. + /// The unique identifier (GUID) of the node if found; otherwise, . public Guid? GetIdForKey(int id, UmbracoObjectTypes umbracoObjectType) { + if (scopeAccessor.AmbientScope is null) + { + return null; + } + + ISqlContext sqlContext = scopeAccessor.AmbientScope.SqlContext; + IUmbracoDatabase database = scopeAccessor.AmbientScope.Database; + Sql sql; + // if it's unknown don't include the nodeObjectType in the query if (umbracoObjectType == UmbracoObjectTypes.Unknown) { - return _scopeAccessor.AmbientScope?.Database.ExecuteScalar( - "SELECT uniqueId FROM umbracoNode WHERE id=@id", new { id }); + sql = sqlContext.Sql() + .Select(c => c.UniqueId) + .From() + .Where(n => n.NodeId == id); + return database?.ExecuteScalar(sql); } - return _scopeAccessor.AmbientScope?.Database.ExecuteScalar( - "SELECT uniqueId FROM umbracoNode WHERE id=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)", - new - { - id, - type = GetNodeObjectTypeGuid(umbracoObjectType), - reservation = Constants.ObjectTypes.IdReservation, - }); + Guid type = GetNodeObjectTypeGuid(umbracoObjectType); + sql = sqlContext.Sql() + .Select(c => c.UniqueId) + .From() + .Where(n => + n.NodeId == id + && (n.NodeObjectType == type + || n.NodeObjectType == Constants.ObjectTypes.IdReservation)); + return database?.ExecuteScalar(sql); } private Guid GetNodeObjectTypeGuid(UmbracoObjectTypes umbracoObjectType) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs index 1d1a07802a..ad1bf0a1ea 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/KeyValueRepository.cs @@ -56,7 +56,7 @@ internal sealed class KeyValueRepository : EntityRepositoryBase Constants.DatabaseSchema.Tables.KeyValue + ".key = @id"; + protected override string GetBaseWhereClause() => QuoteTableName(Constants.DatabaseSchema.Tables.KeyValue) + ".key = @id"; protected override IEnumerable GetDeleteClauses() => Enumerable.Empty(); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs index cc78a4f648..5d7467d897 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LanguageRepository.cs @@ -241,23 +241,23 @@ internal sealed class LanguageRepository : EntityRepositoryBase, return sql; } - protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.Language}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Language)}.id = @id"; protected override IEnumerable GetDeleteClauses() { + var lIdWhere = $"WHERE {QuoteColumnName("languageId")} = @id"; var list = new List { // NOTE: There is no constraint between the Language and cmsDictionary/cmsLanguageText tables (?) // but we still need to remove them - "DELETE FROM " + Constants.DatabaseSchema.Tables.DictionaryValue + " WHERE languageId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE languageId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersionCultureVariation + " WHERE languageId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentCultureVariation + " WHERE languageId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.TagRelationship + " WHERE tagId IN (SELECT id FROM " + - Constants.DatabaseSchema.Tables.Tag + " WHERE languageId = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Tag + " WHERE languageId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentUrl + " WHERE languageId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Language + " WHERE id = @id", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.DictionaryValue)} {lIdWhere}", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.PropertyData)} {lIdWhere}", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation)} {lIdWhere}", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.DocumentCultureVariation)} {lIdWhere}", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.TagRelationship)} WHERE {QuoteColumnName("tagId")} IN (SELECT id FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.Tag)} {lIdWhere})", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.Tag)} {lIdWhere}", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.DocumentUrl)} {lIdWhere}", + $"DELETE FROM {SqlSyntax.GetQuotedName(Constants.DatabaseSchema.Tables.Language)} WHERE id = @id", }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LogViewerQueryRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LogViewerQueryRepository.cs index cd2cb7631e..8f9011a99a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LogViewerQueryRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LogViewerQueryRepository.cs @@ -29,10 +29,10 @@ internal sealed class LogViewerQueryRepository : EntityRepositoryBase PerformGetAll(params int[]? ids) { - Sql? sql = GetBaseQuery(false).Where($"{Constants.DatabaseSchema.Tables.LogViewerQuery}.id > 0"); - if (ids?.Any() ?? false) + Sql? sql = GetBaseQuery(false).Where(c => c.Id > 0); + if (ids?.Length > 0) { - sql.Where($"{Constants.DatabaseSchema.Tables.LogViewerQuery}.id in (@ids)", new { ids }); + sql.WhereIn(c => c.Id, ids); } return Database.Fetch(sql).Select(ConvertFromDto); @@ -49,19 +49,21 @@ internal sealed class LogViewerQueryRepository : EntityRepositoryBase $"{Constants.DatabaseSchema.Tables.LogViewerQuery}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.LogViewerQuery)}.id = @id"; protected override IEnumerable GetDeleteClauses() { - var list = new List { $"DELETE FROM {Constants.DatabaseSchema.Tables.LogViewerQuery} WHERE id = @id" }; + var list = new List { $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.LogViewerQuery)} WHERE id = @id" }; return list; } protected override void PersistNewItem(ILogViewerQuery entity) { - var exists = Database.ExecuteScalar( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.LogViewerQuery} WHERE name = @name", - new { name = entity.Name }); + Sql sql = Sql() + .SelectCount() + .From() + .Where(x => x.Name == entity.Name); + var exists = Database.ExecuteScalar(sql); if (exists > 0) { throw new DuplicateNameException($"The log query name '{entity.Name}' is already used"); @@ -79,10 +81,11 @@ internal sealed class LogViewerQueryRepository : EntityRepositoryBase( - $"SELECT COUNT(*) FROM {Constants.DatabaseSchema.Tables.LogViewerQuery} WHERE name = @name AND id <> @id", - new { name = entity.Name, id = entity.Id }); + Sql sql = Sql() + .SelectCount() + .From() + .Where(x => x.Name == entity.Name && x.Id != entity.Id); + var exists = Database.ExecuteScalar(sql); // ensure there is no other log query with the same name on another entity if (exists > 0) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LongRunningOperationRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LongRunningOperationRepository.cs index 1f02296be1..463a3e6f58 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LongRunningOperationRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LongRunningOperationRepository.cs @@ -1,6 +1,5 @@ -using System.Linq.Expressions; +using Microsoft.Extensions.Logging; using NPoco; -using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs index d08607ec09..9ff2e3e5a9 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs @@ -173,10 +173,9 @@ public class MediaRepository : ContentRepositoryBase sql = GetBaseQuery(QueryType.Single) - .Where(x => x.NodeId == id) - .SelectTop(1); + .Where(x => x.NodeId == id); - ContentDto? dto = Database.Fetch(sql).FirstOrDefault(); + ContentDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : MapDtoToContent(dto); @@ -260,28 +259,32 @@ public class MediaRepository : ContentRepositoryBase $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(NodeDto.TableName)}.id = @id"; protected override IEnumerable GetDeleteClauses() { + var nodeId = QuoteColumnName("nodeId"); + var uniqueId = QuoteColumnName("uniqueId"); + var umbracoNode = QuoteTableName(NodeDto.TableName); var list = new List { - "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.UserGroup2GranularPermission + " WHERE uniqueId IN (SELECT uniqueId FROM umbracoNode WHERE id = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.UserStartNode + " WHERE startNode = @id", - "UPDATE " + Constants.DatabaseSchema.Tables.UserGroup + - " SET startContentId = NULL WHERE startContentId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE parentId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Relation + " WHERE childId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.TagRelationship + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.MediaVersion + " WHERE id IN (SELECT id FROM " + - Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE versionId IN (SELECT id FROM " + - Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.User2NodeNotify)} WHERE {nodeId} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.UserGroup2GranularPermission)} WHERE {uniqueId} IN + (SELECT {uniqueId} FROM {umbracoNode} WHERE id = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.UserStartNode)} WHERE {QuoteColumnName("startNode")} = @id", + $@"UPDATE {QuoteTableName(Constants.DatabaseSchema.Tables.UserGroup)} + SET {QuoteColumnName("startContentId")} = NULL WHERE {QuoteColumnName("startContentId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Relation)} WHERE {QuoteColumnName("parentId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Relation)} WHERE {QuoteColumnName("childId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.TagRelationship)} WHERE {nodeId} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Document)} WHERE {nodeId} = @id", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.MediaVersion)} WHERE id IN + (SELECT id FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersion)} WHERE {nodeId} = @id)", + $@"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.PropertyData)} WHERE versionId IN + (SELECT id FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersion)} WHERE {nodeId} = @id)", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.ContentVersion)} WHERE {nodeId} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Content)} WHERE {nodeId} = @id", + $"DELETE FROM {umbracoNode} WHERE id = @id", }; return list; } @@ -324,19 +327,21 @@ public class MediaRepository : ContentRepositoryBase sql = GetBaseQuery(QueryType.Single, joinMediaVersion: true) - .Where(x => x.Path == umbracoFileValue) - .SelectTop(1); + .Where(x => x.Path == umbracoFileValue); - ContentDto? dto = Database.Fetch(sql).FirstOrDefault(); + ContentDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : MapDtoToContent(dto); } + /// + /// Nothing to do here, media has only one version which must not be deleted. + /// Base method is abstract so this must be implemented. + /// protected override void PerformDeleteVersion(int id, int versionId) { - Database.Delete("WHERE versionId = @versionId", new { versionId }); - Database.Delete("WHERE versionId = @versionId", new { versionId }); + // Nothing to do here, media has only one version which must not be deleted. } #endregion @@ -531,7 +536,7 @@ public class MediaRepository : ContentRepositoryBase sql = _outerRepo.GetBaseQuery(QueryType.Single) .Where(x => x.UniqueId == id); - ContentDto? dto = Database.Fetch(sql.SelectTop(1)).FirstOrDefault(); + ContentDto? dto = Database.FirstOrDefault(sql); if (dto == null) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs index 9c7e22fa4d..b89c6f57e5 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaTypeRepository.cs @@ -90,13 +90,13 @@ internal sealed class MediaTypeRepository : ContentTypeRepositoryBase $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Node)}.id = @id"; protected override IEnumerable GetDeleteClauses() { var l = (List)base.GetDeleteClauses(); // we know it's a list - l.Add("DELETE FROM cmsContentType WHERE nodeId = @id"); - l.Add("DELETE FROM umbracoNode WHERE id = @id"); + l.Add($"DELETE FROM {QuoteTableName("cmsContentType")} WHERE {QuoteColumnName("nodeId")} = @id"); + l.Add($"DELETE FROM {QuoteTableName("umbracoNode")} WHERE id = @id"); return l; } @@ -122,10 +122,11 @@ internal sealed class MediaTypeRepository : ContentTypeRepositoryBase("WHERE id = @ParentId", new { entity.ParentId }); entity.Path = string.Concat(parent.Path, ",", entity.Id); entity.Level = parent.Level + 1; - var maxSortOrder = - Database.ExecuteScalar( - "SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType", - new { entity.ParentId, NodeObjectType = NodeObjectTypeId }); + Sql sql = Sql() + .SelectMax(x => x.SortOrder, 0) + .From() + .Where(x => x.ParentId == entity.ParentId && x.NodeObjectType == NodeObjectTypeId); + var maxSortOrder = Database.ExecuteScalar(sql); entity.SortOrder = maxSortOrder + 1; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs index 9ae8df942a..541e2606b2 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs @@ -31,7 +31,7 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase sql = GetBaseQuery(false); sql.Where(x => x.UniqueId == uniqueId); - NodeDto? dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); + NodeDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : MemberGroupFactory.BuildEntity(dto); } @@ -79,7 +79,7 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase GetMemberGroupsForMember(int memberId) { Sql sql = Sql() - .Select("umbracoNode.*") + .SelectAll() .From() .InnerJoin() .On(dto => dto.NodeId, dto => dto.MemberGroup) @@ -94,14 +94,14 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase GetMemberGroupsForMember(string? username) { Sql? sql = Sql() - .Select("un.*") - .From("umbracoNode AS un") - .InnerJoin("cmsMember2MemberGroup") - .On("cmsMember2MemberGroup.MemberGroup = un.id") - .InnerJoin("cmsMember") - .On("cmsMember.nodeId = cmsMember2MemberGroup.Member") - .Where("un.nodeObjectType=@objectType", new { objectType = NodeObjectTypeId }) - .Where("cmsMember.LoginName=@loginName", new { loginName = username }); + .Select($"{QuoteTableName(NodeDto.TableName)}.*") + .From() + .InnerJoin() + .On((g, n) => g.MemberGroup == n.NodeId) + .InnerJoin() + .On((m, g) => m.NodeId == g.Member) + .Where(n => n.NodeObjectType == NodeObjectTypeId) + .Where(m => m.LoginName == username); return Database.Fetch(sql) .DistinctBy(dto => dto.NodeId) @@ -119,7 +119,7 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase sql = GetBaseQuery(false); sql.Where(GetBaseWhereClause(), new { id }); - NodeDto? dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); + NodeDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : MemberGroupFactory.BuildEntity(dto); } @@ -163,22 +163,24 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Node)}.id = @id"; protected override IEnumerable GetDeleteClauses() { - var list = new[] + Sql sql = Sql(); + + var inClause = $" IN (SELECT {QuoteTableName("umbracoUserGroup")}.{QuoteColumnName("key")} FROM {QuoteTableName("umbracoUserGroup")} WHERE id = @id)"; + return new List { - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @id", - "DELETE FROM umbracoUserGroup2Permission WHERE userGroupKey IN (SELECT [umbracoUserGroup].[Key] FROM umbracoUserGroup WHERE Id = @id)", - "DELETE FROM umbracoUserGroup2GranularPermission WHERE userGroupKey IN (SELECT [umbracoUserGroup].[Key] FROM umbracoUserGroup WHERE Id = @id)", - "DELETE FROM umbracoRelation WHERE parentId = @id", - "DELETE FROM umbracoRelation WHERE childId = @id", - "DELETE FROM cmsTagRelationship WHERE nodeId = @id", - "DELETE FROM cmsMember2MemberGroup WHERE MemberGroup = @id", - "DELETE FROM umbracoNode WHERE id = @id", + $"DELETE FROM {QuoteTableName("umbracoUser2NodeNotify")} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName("umbracoUserGroup2Permission")} WHERE {QuoteColumnName("userGroupKey")}{inClause}", + $"DELETE FROM {QuoteTableName("umbracoUserGroup2GranularPermission")} WHERE {QuoteColumnName("userGroupKey")}{inClause}", + $"DELETE FROM {QuoteTableName("umbracoRelation")} WHERE {QuoteColumnName("parentId")} = @id", + $"DELETE FROM {QuoteTableName("umbracoRelation")} WHERE {QuoteColumnName("childId")} = @id", + $"DELETE FROM {QuoteTableName("cmsTagRelationship")} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName("cmsMember2MemberGroup")} WHERE {QuoteColumnName("MemberGroup")} = @id", + $"DELETE FROM {QuoteTableName("umbracoNode")} WHERE id = @id", }; - return list; } protected override void PersistNewItem(IMemberGroup entity) @@ -218,10 +220,10 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase() .Where(dto => dto.NodeObjectType == NodeObjectTypeId) - .Where("umbracoNode." + SqlSyntax.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames }); + .WhereIn(n => n.Text, roleNames); IEnumerable existingRoles = Database.Fetch(existingSql).Select(x => x.Text); IEnumerable missingRoles = roleNames.Except(existingRoles, StringComparer.CurrentCultureIgnoreCase); - MemberGroup[] missingGroups = missingRoles.Select(x => new MemberGroup { Name = x }).ToArray(); + MemberGroup[] missingGroups = [.. missingRoles.Select(x => new MemberGroup { Name = x })]; EventMessages evtMsgs = _eventMessagesFactory.Get(); if (AmbientScope.Notifications.PublishCancelable(new MemberGroupSavingNotification(missingGroups, evtMsgs))) @@ -244,16 +246,18 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase delSql = Sql() + .Delete() + .WhereIn(x => x.Member, memberIds); + Database.Execute(delSql); - currentlyAssigned = Array.Empty(); + currentlyAssigned = []; } else { // get the groups that are currently assigned to any of these members Sql assignedSql = Sql() - .Select( - $"{SqlSyntax.GetQuotedColumnName("text")},{SqlSyntax.GetQuotedColumnName("Member")},{SqlSyntax.GetQuotedColumnName("MemberGroup")}") + .Select($"{QuoteColumnName("text")},{QuoteColumnName("Member")},{QuoteColumnName("MemberGroup")}") .From() .InnerJoin() .On(dto => dto.NodeId, dto => dto.MemberGroup) @@ -285,21 +289,18 @@ internal sealed class MemberGroupRepository : EntityRepositoryBase? existingSql = Sql() + Sql existingSql = Sql() .SelectAll() .From() .Where(dto => dto.NodeObjectType == NodeObjectTypeId) - .Where("umbracoNode." + SqlSyntax.GetQuotedColumnName("text") + " in (@names)", new { names = roleNames }); + .WhereIn(w => w.Text, roleNames); var existingRolesIds = Database.Fetch(existingSql).Select(x => x.NodeId).ToArray(); - Database.Execute( - "DELETE FROM cmsMember2MemberGroup WHERE Member IN (@memberIds) AND MemberGroup IN (@memberGroups)", - new - { - /*memberIds =*/ - memberIds, - memberGroups = existingRolesIds, - }); + Sql delSql = Sql() + .Delete() + .WhereIn(x => x.Member, memberIds) + .WhereIn(x => x.MemberGroup, existingRolesIds); + Database.Execute(delSql); } private sealed class AssignedRolesDto diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs index fc41bfdae9..9d7405536a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs @@ -504,10 +504,9 @@ public class MemberRepository : ContentRepositoryBase sql = GetBaseQuery(QueryType.Single) - .Where(x => x.NodeId == id) - .SelectTop(1); + .Where(x => x.NodeId == id); - MemberDto? dto = Database.Fetch(sql).FirstOrDefault(); + MemberDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : MapDtoToContent(dto); @@ -617,12 +616,12 @@ public class MemberRepository : ContentRepositoryBase "umbracoNode.id = @id"; + => $"{QuoteTableName("umbracoNode")}.id = @id"; // TODO: document/understand that one protected Sql GetNodeIdQueryWithPropertyData() => Sql() - .Select("DISTINCT(umbracoNode.id)") + .SelectDistinct(c => c.NodeId) .From() .InnerJoin().On((left, right) => left.NodeId == right.NodeId) .InnerJoin() @@ -641,27 +640,29 @@ public class MemberRepository : ContentRepositoryBase GetDeleteClauses() { - var list = new List - { - "DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @id", + var inClause = $"IN (SELECT {QuoteTableName("umbracoUserGroup")}.{QuoteColumnName("key")} FROM {QuoteTableName("umbracoUserGroup")} WHERE id = @id)"; - "DELETE FROM umbracoUserGroup2Permission WHERE userGroupKey IN (SELECT [umbracoUserGroup].[Key] FROM umbracoUserGroup WHERE Id = @id)", - "DELETE FROM umbracoUserGroup2GranularPermission WHERE userGroupKey IN (SELECT [umbracoUserGroup].[Key] FROM umbracoUserGroup WHERE Id = @id)", - "DELETE FROM umbracoRelation WHERE parentId = @id", - "DELETE FROM umbracoRelation WHERE childId = @id", - "DELETE FROM cmsTagRelationship WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + - " WHERE versionId IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + - " WHERE nodeId = @id)", - $"DELETE FROM {Constants.DatabaseSchema.Tables.ExternalLoginToken} WHERE externalLoginId = (SELECT id FROM {Constants.DatabaseSchema.Tables.ExternalLogin} WHERE userOrMemberKey = (SELECT uniqueId from {Constants.DatabaseSchema.Tables.Node} where id = @id))", - $"DELETE FROM {Constants.DatabaseSchema.Tables.ExternalLogin} WHERE userOrMemberKey = (SELECT uniqueId from {Constants.DatabaseSchema.Tables.Node} where id = @id)", - "DELETE FROM cmsMember2MemberGroup WHERE Member = @id", - "DELETE FROM cmsMember WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id", - "DELETE FROM umbracoNode WHERE id = @id" + return new List + { + $"DELETE FROM {QuoteTableName(User2NodeNotifyDto.TableName)} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName("umbracoUserGroup2Permission")} WHERE {QuoteColumnName("userGroupKey")} {inClause}", + $"DELETE FROM {QuoteTableName("umbracoUserGroup2GranularPermission")} WHERE {QuoteColumnName("userGroupKey")} {inClause}", + $"DELETE FROM {QuoteTableName("umbracoRelation")} WHERE {QuoteColumnName("parentId")} = @id", + $"DELETE FROM {QuoteTableName("umbracoRelation")} WHERE {QuoteColumnName("childId")} = @id", + $"DELETE FROM {QuoteTableName("cmsTagRelationship")} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName(PropertyDataDto.TableName)} WHERE {QuoteColumnName("versionId")}" + + $" IN (SELECT id FROM {QuoteTableName(ContentVersionDto.TableName)} WHERE {QuoteColumnName("nodeId")} = @id)", + $"DELETE FROM {QuoteTableName(ExternalLoginToken.TableName)} WHERE {QuoteColumnName("externalLoginId")} =" + + $" (SELECT id FROM {QuoteTableName(ExternalLoginDto.TableName)} WHERE {QuoteColumnName("userOrMemberKey")} =" + + $" (SELECT {QuoteColumnName("uniqueId")} from {QuoteTableName(NodeDto.TableName)} where id = @id))", + $"DELETE FROM {QuoteTableName(ExternalLoginDto.TableName)} WHERE {QuoteColumnName("userOrMemberKey")} =" + + $" (SELECT {QuoteColumnName("uniqueId")} from {QuoteTableName(NodeDto.TableName)} where id = @id)", + $"DELETE FROM {QuoteTableName("cmsMember2MemberGroup")} WHERE {QuoteColumnName("Member")} = @id", + $"DELETE FROM {QuoteTableName("cmsMember")} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName(ContentVersionDto.TableName)} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName(ContentDto.TableName)} WHERE {QuoteColumnName("nodeId")} = @id", + $"DELETE FROM {QuoteTableName("umbracoNode")} WHERE id = @id" }; - return list; } #endregion diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs index ffbe444f81..20b93f1259 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberTypeRepository.cs @@ -12,6 +12,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Factories; using Umbraco.Cms.Infrastructure.Persistence.Querying; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; +using static Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; @@ -108,7 +109,7 @@ internal sealed class MemberTypeRepository : ContentTypeRepositoryBase GetSubquery() { Sql sql = Sql() - .Select("DISTINCT(umbracoNode.id)") + .Select($"DISTINCT({QuoteTableName("umbracoNode")}.id)") .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) .LeftJoin().On(left => left.ContentTypeId, right => right.NodeId) @@ -121,14 +122,14 @@ internal sealed class MemberTypeRepository : ContentTypeRepositoryBase $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Node)}.id = @id"; protected override IEnumerable GetDeleteClauses() { var l = (List)base.GetDeleteClauses(); // we know it's a list - l.Add("DELETE FROM cmsMemberType WHERE NodeId = @id"); - l.Add("DELETE FROM cmsContentType WHERE nodeId = @id"); - l.Add("DELETE FROM umbracoNode WHERE id = @id"); + l.Add($"DELETE FROM {QuoteTableName("cmsMemberType")} WHERE NodeId = @id"); + l.Add($"DELETE FROM {QuoteTableName("cmsContentType")} WHERE nodeId = @id"); + l.Add($"DELETE FROM {QuoteTableName("umbracoNode")} WHERE id = @id"); return l; } @@ -175,16 +176,18 @@ internal sealed class MemberTypeRepository : ContentTypeRepositoryBase sql; // Look up parent to get and set the correct Path if ParentId has changed if (entity.IsPropertyDirty("ParentId")) { NodeDto? parent = Database.First("WHERE id = @ParentId", new { entity.ParentId }); entity.Path = string.Concat(parent.Path, ",", entity.Id); entity.Level = parent.Level + 1; - var maxSortOrder = - Database.ExecuteScalar( - "SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType", - new { entity.ParentId, NodeObjectType = NodeObjectTypeId }); + sql = Sql() + .SelectMax(c => c.SortOrder, 0) + .From() + .Where(x => x.ParentId == entity.ParentId && x.NodeObjectType == NodeObjectTypeId); + var maxSortOrder = Database.ExecuteScalar(sql); entity.SortOrder = maxSortOrder + 1; } @@ -192,7 +195,8 @@ internal sealed class MemberTypeRepository : ContentTypeRepositoryBase("WHERE NodeId = @Id", new { entity.Id }); + sql = Sql().Delete(c => c.NodeId == entity.Id); + _ = Database.Execute(sql); IEnumerable memberTypeDtos = ContentTypeFactory.BuildMemberPropertyTypeDtos(entity); foreach (MemberPropertyTypeDto memberTypeDto in memberTypeDtos) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/NotificationsRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/NotificationsRepository.cs index f9427ccb7f..ba6345c246 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/NotificationsRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/NotificationsRepository.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; @@ -26,9 +27,15 @@ public class NotificationsRepository : INotificationsRepository } var nodeIdsA = nodeIds.ToArray(); + + + ISqlSyntaxProvider syntax = AmbientScope.SqlContext.SqlSyntax; Sql sql = AmbientScope.SqlContext.Sql() - .Select( - "DISTINCT umbracoNode.id nodeId, umbracoUser.id userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action") + .Select($@"DISTINCT +{syntax.GetQuotedTableName("umbracoNode")}.id {syntax.GetQuotedName("nodeId")}, +{syntax.GetQuotedTableName("umbracoUser")}.id {syntax.GetQuotedName("userId")}, +{syntax.GetQuotedTableName("umbracoNode")}.{syntax.GetQuotedColumnName("nodeObjectType")}, +{syntax.GetQuotedTableName("umbracoUser2NodeNotify")}.action") .From() .InnerJoin().On(left => left.NodeId, right => right.NodeId) .InnerJoin().On(left => left.UserId, right => right.Id) @@ -37,8 +44,7 @@ public class NotificationsRepository : INotificationsRepository .Where(x => x.Action == action); // on the specified action if (nodeIdsA.Length > 0) { - sql - .WhereIn(x => x.NodeId, nodeIdsA); // for the specified nodes + sql.WhereIn(x => x.NodeId, nodeIdsA); // for the specified nodes } sql @@ -55,9 +61,13 @@ public class NotificationsRepository : INotificationsRepository return []; } + ISqlSyntaxProvider syntax = AmbientScope.SqlContext.SqlSyntax; Sql sql = AmbientScope.SqlContext.Sql() - .Select( - "DISTINCT umbracoNode.id AS nodeId, umbracoUser2NodeNotify.userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action") + .Select($@"DISTINCT +{syntax.GetQuotedTableName("umbracoNode")}.id {syntax.GetQuotedName("nodeId")}, +{syntax.GetQuotedTableName("umbracoUser2NodeNotify")}.{syntax.GetQuotedColumnName("userId")}, +{syntax.GetQuotedTableName("umbracoNode")}.{syntax.GetQuotedColumnName("nodeObjectType")}, +{syntax.GetQuotedTableName("umbracoUser2NodeNotify")}.action") .From() .InnerJoin() .On(dto => dto.NodeId, dto => dto.NodeId) @@ -90,9 +100,13 @@ public class NotificationsRepository : INotificationsRepository return []; } + ISqlSyntaxProvider syntax = AmbientScope.SqlContext.SqlSyntax; Sql sql = AmbientScope.SqlContext.Sql() - .Select( - "DISTINCT umbracoNode.id as nodeId, umbracoUser2NodeNotify.userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action") + .Select($@"DISTINCT +{syntax.GetQuotedTableName("umbracoNode")}.id {syntax.GetQuotedName("nodeId")}, +{syntax.GetQuotedTableName("umbracoUser2NodeNotify")}.{syntax.GetQuotedColumnName("userId")}, +{syntax.GetQuotedTableName("umbracoNode")}.{syntax.GetQuotedColumnName("nodeObjectType")}, +{syntax.GetQuotedTableName("umbracoUser2NodeNotify")}.action") .From() .InnerJoin() .On(dto => dto.NodeId, dto => dto.NodeId) @@ -105,18 +119,45 @@ public class NotificationsRepository : INotificationsRepository return dtos.Select(d => new Notification(d.NodeId, d.UserId, d.Action, d.NodeObjectType)).ToList(); } - public int DeleteNotifications(IEntity entity) => - AmbientScope?.Database.Delete("WHERE nodeId = @nodeId", new { nodeId = entity.Id }) ?? 0; + public int DeleteNotifications(IEntity entity) + { + if (AmbientScope == null) + { + return 0; + } - public int DeleteNotifications(IUser user) => - AmbientScope?.Database.Delete("WHERE userId = @userId", new { userId = user.Id }) ?? 0; + Sql sql = AmbientScope.SqlContext.Sql() + .Delete() + .Where(dto => dto.NodeId == entity.Id); + return AmbientScope.Database.Execute(sql); + } - public int DeleteNotifications(IUser user, IEntity entity) => + public int DeleteNotifications(IUser user) + { + if (AmbientScope == null) + { + return 0; + } + + Sql sql = AmbientScope.SqlContext.Sql() + .Delete() + .Where(dto => dto.UserId == user.Id); + return AmbientScope.Database.Execute(sql); + } + + public int DeleteNotifications(IUser user, IEntity entity) + { + if (AmbientScope == null) + { + return 0; + } // delete all settings on the node for this user - AmbientScope?.Database.Delete( - "WHERE userId = @userId AND nodeId = @nodeId", - new { userId = user.Id, nodeId = entity.Id }) ?? 0; + Sql sql = AmbientScope.SqlContext.Sql() + .Delete() + .Where(dto => dto.NodeId == entity.Id && dto.UserId == user.Id); + return AmbientScope.Database.Execute(sql); + } public bool TryCreateNotification(IUser user, IEntity entity, string action, [NotNullWhen(true)] out Notification? notification) { @@ -127,7 +168,7 @@ public class NotificationsRepository : INotificationsRepository } Sql? sql = AmbientScope.SqlContext.Sql() - .Select("DISTINCT nodeObjectType") + .SelectDistinct(c => c.NodeObjectType) .From() .Where(nodeDto => nodeDto.NodeId == entity.Id); Guid nodeType = AmbientScope.Database.ExecuteScalar(sql); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublicAccessRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublicAccessRepository.cs index aaeba86db6..a3080e6fb4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublicAccessRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublicAccessRepository.cs @@ -60,13 +60,15 @@ internal sealed class PublicAccessRepository : EntityRepositoryBase() .On(left => left.Id, right => right.AccessId); - protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.Access}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Access)}.id = @id"; protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoAccessRule WHERE accessId = @id", "DELETE FROM umbracoAccess WHERE id = @id", + $@"DELETE FROM {QuoteTableName("umbracoAccessRule")} + WHERE {QuoteColumnName("accessId")} = @id", + $"DELETE FROM {QuoteTableName("umbracoAccess")} WHERE id = @id", }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublishStatusRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublishStatusRepository.cs index 366c5c6a26..666743e23e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublishStatusRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PublishStatusRepository.cs @@ -7,7 +7,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; -public class PublishStatusRepository: IPublishStatusRepository +public class PublishStatusRepository : IPublishStatusRepository { private readonly IScopeAccessor _scopeAccessor; @@ -27,22 +27,22 @@ public class PublishStatusRepository: IPublishStatusRepository } } - private Sql GetBaseQuery() + private Sql GetBaseQuery() { + SqlSyntax.ISqlSyntaxProvider syntax = Database.SqlContext.SqlSyntax; Sql sql = Database.SqlContext.Sql() .Select( - $"n.{NodeDto.KeyColumnName}", - $"l.{LanguageDto.IsoCodeColumnName}", - $"ct.{ContentTypeDto.VariationsColumnName}", - $"d.{DocumentDto.PublishedColumnName}", - $"COALESCE(dcv.{DocumentCultureVariationDto.PublishedColumnName}, 0) as {PublishStatusDto.DocumentVariantPublishStatusColumnName}") + $"n.{syntax.GetQuotedColumnName(NodeDto.KeyColumnName)}", + $"l.{syntax.GetQuotedColumnName(LanguageDto.IsoCodeColumnName)}", + $"ct.{syntax.GetQuotedColumnName(ContentTypeDto.VariationsColumnName)}", + $"d.{syntax.GetQuotedColumnName(DocumentDto.PublishedColumnName)}", + $"dcv.{syntax.GetQuotedColumnName(DocumentCultureVariationDto.PublishedColumnName)} as {syntax.GetQuotedColumnName(PublishStatusDto.DocumentVariantPublishStatusColumnName)}") // COALESCE is not necessary as the column is not nullable .From("d") .InnerJoin("c").On((d, c) => d.NodeId == c.NodeId, "c", "d") .InnerJoin("ct").On((c, ct) => c.ContentTypeId == ct.NodeId, "c", "ct") .CrossJoin("l") - .LeftJoin("dcv").On((l, dcv, d) => l.Id == dcv.LanguageId && d.NodeId == dcv.NodeId , "l", "dcv", "d") - .InnerJoin("n").On((d, n) => n.NodeId == d.NodeId, "d", "n") - ; + .LeftJoin("dcv").On((l, dcv, d) => l.Id == dcv.LanguageId && d.NodeId == dcv.NodeId, "l", "dcv", "d") + .InnerJoin("n").On((d, n) => n.NodeId == d.NodeId, "d", "n"); return sql; } @@ -92,8 +92,8 @@ public class PublishStatusRepository: IPublishStatusRepository return databaseRecords .GroupBy(x => x.Key) .ToDictionary( - x=>x.Key, - x=> (ISet) x.Where(x=> IsPublished(x)).Select(y=>y.IsoCode).ToHashSet()); + x => x.Key, + x => (ISet)x.Where(x => IsPublished(x)).Select(y => y.IsoCode).ToHashSet()); } private static bool IsPublished(PublishStatusDto publishStatusDto) @@ -123,12 +123,12 @@ public class PublishStatusRepository: IPublishStatusRepository public string IsoCode { get; set; } = string.Empty; [Column(ContentTypeDto.VariationsColumnName)] - public byte ContentTypeVariation { get; set; } + public byte ContentTypeVariation { get; set; } [Column(DocumentDto.PublishedColumnName)] - public bool DocumentInvariantPublished { get; set; } + public bool DocumentInvariantPublished { get; set; } [Column(DocumentVariantPublishStatusColumnName)] - public bool DocumentVariantPublishStatus { get; set; } + public bool DocumentVariantPublishStatus { get; set; } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs index 2a78775c77..eb69ba5adf 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs @@ -28,10 +28,10 @@ internal sealed class RedirectUrlRepository : EntityRepositoryBase Database.Execute("DELETE FROM umbracoRedirectUrl"); + public void DeleteAll() => Database.Execute($"DELETE FROM {QuoteTableName("umbracoRedirectUrl")}"); public void DeleteContentUrls(Guid contentKey) => - Database.Execute("DELETE FROM umbracoRedirectUrl WHERE contentKey=@contentKey", new { contentKey }); + Database.Execute($"DELETE FROM {QuoteTableName("umbracoRedirectUrl")} WHERE {QuoteColumnName("contentKey")}=@contentKey", new { contentKey }); public void Delete(Guid id) => Database.Delete(id); @@ -133,8 +133,8 @@ internal sealed class RedirectUrlRepository : EntityRepositoryBase sql = GetBaseQuery(false) .Where( - string.Format("{0}.{1} LIKE @path", SqlSyntax.GetQuotedTableName("umbracoNode"), - SqlSyntax.GetQuotedColumnName("path")), new { path = "%," + rootContentId + ",%" }) + string.Format("{0}.{1} LIKE @path", QuoteTableName("umbracoNode"), + QuoteColumnName("path")), new { path = "%," + rootContentId + ",%" }) .OrderByDescending(x => x.CreateDateUtc); Page result = Database.Page(pageIndex + 1, pageSize, sql); total = Convert.ToInt32(result.TotalItems); @@ -147,8 +147,8 @@ internal sealed class RedirectUrlRepository : EntityRepositoryBase sql = GetBaseQuery(false) .Where( - string.Format("{0}.{1} LIKE @url", SqlSyntax.GetQuotedTableName("umbracoRedirectUrl"), - SqlSyntax.GetQuotedColumnName("Url")), + string.Format("{0}.{1} LIKE @url", QuoteTableName("umbracoRedirectUrl"), + QuoteColumnName("Url")), new { url = "%" + searchTerm.Trim().ToLowerInvariant() + "%" }) .OrderByDescending(x => x.CreateDateUtc); Page result = Database.Page(pageIndex + 1, pageSize, sql); @@ -166,7 +166,7 @@ internal sealed class RedirectUrlRepository : EntityRepositoryBase sql = GetBaseQuery(false).Where(x => x.Id == id); - RedirectUrlDto? dto = Database.Fetch(sql.SelectTop(1)).FirstOrDefault(); + RedirectUrlDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : Map(dto); } @@ -191,15 +191,17 @@ internal sealed class RedirectUrlRepository : EntityRepositoryBase sql = Sql(); if (isCount) { - sql.Select(@"COUNT(*) -FROM umbracoRedirectUrl -JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); + sql.Select($@"COUNT(*) +FROM {QuoteTableName("umbracoRedirectUrl")} +JOIN {QuoteTableName(NodeDto.TableName)} +ON {QuoteTableName("umbracoRedirectUrl")}.{QuoteColumnName("contentKey")}={QuoteTableName(NodeDto.TableName)}.{QuoteColumnName("uniqueId")}"); } else { - sql.Select(@"umbracoRedirectUrl.*, umbracoNode.id AS contentId -FROM umbracoRedirectUrl -JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); + sql.Select($@"{QuoteTableName("umbracoRedirectUrl")}.*, {QuoteTableName(NodeDto.TableName)}.id AS contentId +FROM {QuoteTableName("umbracoRedirectUrl")} +JOIN {QuoteTableName(NodeDto.TableName)} +ON {QuoteTableName("umbracoRedirectUrl")}.{QuoteColumnName("contentKey")}={QuoteTableName(NodeDto.TableName)}.{QuoteColumnName("uniqueId")}"); } return sql; @@ -209,7 +211,7 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID"); protected override IEnumerable GetDeleteClauses() { - var list = new List { "DELETE FROM umbracoRedirectUrl WHERE id = @id" }; + var list = new List { $"DELETE FROM {QuoteTableName("umbracoRedirectUrl")} WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs index 8b3dc4a891..4d9dcf834c 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationRepository.cs @@ -1,3 +1,4 @@ + using Microsoft.Extensions.Logging; using NPoco; using Umbraco.Cms.Core; @@ -7,7 +8,6 @@ using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.OperationStatus; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Factories; using Umbraco.Cms.Infrastructure.Persistence.Querying; @@ -173,9 +173,9 @@ internal sealed class RelationRepository : EntityRepositoryBase, public void DeleteByParent(int parentId, params string[] relationTypeAliases) { // HACK: SQLite - hard to replace this without provider specific repositories/another ORM. - if (Database.DatabaseType.IsSqlite()) + if (Database.DatabaseType.IsSqlServer() is false) { - Sql? query = Sql().Append(@"delete from umbracoRelation"); + Sql? query = Sql().Append($"DELETE FROM {QuoteTableName("umbracoRelation")}"); Sql subQuery = Sql().Select(x => x.Id) .From() @@ -198,10 +198,10 @@ internal sealed class RelationRepository : EntityRepositoryBase, SqlTemplate template = SqlContext.Templates.Get( Constants.SqlTemplates.RelationRepository.DeleteByParentIn, tsql => Sql().Delete() - .From() - .InnerJoin().On(x => x.RelationType, x => x.Id) - .Where(x => x.ParentId == SqlTemplate.Arg("parentId")) - .WhereIn(x => x.Alias, SqlTemplate.ArgIn("relationTypeAliases"))); + .From() + .InnerJoin().On(x => x.RelationType, x => x.Id) + .Where(x => x.ParentId == SqlTemplate.Arg("parentId")) + .WhereIn(x => x.Alias, SqlTemplate.ArgIn("relationTypeAliases"))); Sql sql = template.Sql(parentId, relationTypeAliases); @@ -212,9 +212,9 @@ internal sealed class RelationRepository : EntityRepositoryBase, SqlTemplate template = SqlContext.Templates.Get( Constants.SqlTemplates.RelationRepository.DeleteByParentAll, tsql => Sql().Delete() - .From() - .InnerJoin().On(x => x.RelationType, x => x.Id) - .Where(x => x.ParentId == SqlTemplate.Arg("parentId"))); + .From() + .InnerJoin().On(x => x.RelationType, x => x.Id) + .Where(x => x.ParentId == SqlTemplate.Arg("parentId"))); Sql sql = template.Sql(parentId); @@ -336,7 +336,7 @@ internal sealed class RelationRepository : EntityRepositoryBase, Sql sql = GetBaseQuery(false); sql.Where(GetBaseWhereClause(), new { id }); - RelationDto? dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); + RelationDto? dto = Database.FirstOrDefault(sql); if (dto == null) { return null; @@ -417,11 +417,13 @@ internal sealed class RelationRepository : EntityRepositoryBase, return sql; } - protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.Relation}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Relation)}.id = @id"; protected override IEnumerable GetDeleteClauses() { - var list = new List { "DELETE FROM umbracoRelation WHERE id = @id" }; + var list = new List { + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Relation)} WHERE id = @id" + }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationTypeRepository.cs index 8c84f2ae65..2c41302cd0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RelationTypeRepository.cs @@ -113,13 +113,14 @@ internal sealed class RelationTypeRepository : EntityRepositoryBase $"{Constants.DatabaseSchema.Tables.RelationType}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.RelationType)}.id = @id"; protected override IEnumerable GetDeleteClauses() { var list = new List { - "DELETE FROM umbracoRelation WHERE relType = @id", "DELETE FROM umbracoRelationType WHERE id = @id", + $"DELETE FROM {QuoteTableName("umbracoRelation")} WHERE {QuoteColumnName("relType")} = @id", + $"DELETE FROM {QuoteTableName("umbracoRelationType")} WHERE id = @id", }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBase.cs index 7a1f4a2677..ee20a8cccf 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RepositoryBase.cs @@ -77,4 +77,15 @@ public abstract class RepositoryBase : IRepository /// Creates a new query expression ///
protected IQuery Query() => SqlContext.Query(); + + + /// + /// Quotes a table name according to the registered SQL syntax provider. + /// + protected string QuoteTableName(string? tableName) => SqlSyntax.GetQuotedTableName(tableName); + + /// + /// Quotes a column name according to the registered SQL syntax provider. + /// + protected string QuoteColumnName(string? columnName) => SqlSyntax.GetQuotedColumnName(columnName); } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs index f5b6c43c91..2b6c54ab98 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ServerRegistrationRepository.cs @@ -25,12 +25,13 @@ internal sealed class ServerRegistrationRepository : EntityRepositoryBase( - "SET isActive=0, isSchedulingPublisher=0 WHERE lastNotifiedDate < @timeoutDate", new - { - /*timeoutDate =*/ - timeoutDate, - }); + Sql sql = Sql() + .Update(c => c + .Set(x => x.IsActive, false) + .Set(x => x.IsSchedulingPublisher, false)) + .Where(x => x.DateAccessed < timeoutDate); + Database.Execute(sql); + ClearCache(); } @@ -83,7 +84,7 @@ internal sealed class ServerRegistrationRepository : EntityRepositoryBase GetDeleteClauses() { - var list = new List { "DELETE FROM umbracoServer WHERE id = @id" }; + var list = new List { $"DELETE FROM {QuoteTableName("umbracoServer")} WHERE id = @id" }; return list; } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs index 8107abc3ef..91d737cf53 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs @@ -28,7 +28,7 @@ internal sealed class TagRepository : EntityRepositoryBase, ITagRepos protected override ITag? PerformGet(int id) { Sql sql = Sql().Select().From().Where(x => x.Id == id); - TagDto? dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); + TagDto? dto = Database.FirstOrDefault(sql); return dto == null ? null : TagFactory.BuildEntity(dto); } @@ -67,7 +67,8 @@ internal sealed class TagRepository : EntityRepositoryBase, ITagRepos { var list = new List { - "DELETE FROM cmsTagRelationship WHERE tagId = @id", "DELETE FROM cmsTags WHERE id = @id" + $"DELETE FROM {QuoteTableName("cmsTagRelationship")} WHERE {QuoteColumnName("tagId")} = @id", + $"DELETE FROM {QuoteTableName("cmsTags")} WHERE id = @id" }; return list; } @@ -109,9 +110,9 @@ internal sealed class TagRepository : EntityRepositoryBase, ITagRepos // replacing = clear all if (replaceTags) { - Sql sql0 = Sql().Delete() + Sql sql = Sql().Delete() .Where(x => x.NodeId == contentId && x.PropertyTypeId == propertyTypeId); - Database.Execute(sql0); + Database.Execute(sql); } // no tags? nothing else to do @@ -125,30 +126,35 @@ internal sealed class TagRepository : EntityRepositoryBase, ITagRepos // must coalesce languageId because equality of NULLs does not exist var tagSetSql = GetTagSet(tagsA); - var group = SqlSyntax.GetQuotedColumnName("group"); - + var cmsTags = QuoteTableName("cmsTags"); + var group = QuoteColumnName("group"); + var nodeId = QuoteColumnName("nodeId"); + var languageIdCol = QuoteColumnName("languageId"); + var cmsTagsLanguageIdCol = $"{QuoteTableName("cmsTags")}.{QuoteColumnName("languageId")}"; // insert tags // - Note we are checking in the subquery for the existence of the tag, so we don't insert duplicates, using a case-insensitive comparison (the // LOWER keyword is consistent across SQLite and SQLServer). This ensures consistent behavior across databases as by default, SQLServer will // perform a case-insensitive comparison, while SQLite will not. - var sql1 = $@"INSERT INTO cmsTags (tag, {group}, languageId) -SELECT tagSet.tag, tagSet.{group}, tagSet.languageId + var sql1 = $@"INSERT INTO {cmsTags} (tag, {group}, {languageIdCol}) +SELECT tagset.tag, tagset.{group}, tagset.languageid FROM {tagSetSql} -LEFT OUTER JOIN cmsTags ON (LOWER(tagSet.tag) = LOWER(cmsTags.tag) AND LOWER(tagSet.{group}) = LOWER(cmsTags.{group}) AND COALESCE(tagSet.languageId, -1) = COALESCE(cmsTags.languageId, -1)) -WHERE cmsTags.id IS NULL"; +LEFT OUTER JOIN {cmsTags} +ON (LOWER(tagset.tag) = LOWER({cmsTags}.tag) AND LOWER(tagset.{group}) = LOWER({cmsTags}.{group}) AND COALESCE(tagset.languageid, -1) = COALESCE({cmsTagsLanguageIdCol}, -1)) +WHERE {cmsTags}.id IS NULL"; // cmsTags.id is never null Database.Execute(sql1); // insert relations - var sql2 = $@"INSERT INTO cmsTagRelationship (nodeId, propertyTypeId, tagId) -SELECT {contentId}, {propertyTypeId}, tagSet2.Id + var sql2 = $@"INSERT INTO {QuoteTableName(TagRelationshipDto.TableName)} ({nodeId}, {QuoteColumnName("propertyTypeId")}, {QuoteColumnName("tagId")}) +SELECT {contentId}, {propertyTypeId}, tagset2.Id FROM ( - SELECT t.Id + SELECT t.{QuoteColumnName("Id")} FROM {tagSetSql} - INNER JOIN cmsTags as t ON (LOWER(tagSet.tag) = LOWER(t.tag) AND LOWER(tagSet.{group}) = LOWER(t.{group}) AND COALESCE(tagSet.languageId, -1) = COALESCE(t.languageId, -1)) -) AS tagSet2 -LEFT OUTER JOIN cmsTagRelationship r ON (tagSet2.id = r.tagId AND r.nodeId = {contentId} AND r.propertyTypeID = {propertyTypeId}) -WHERE r.tagId IS NULL"; + INNER JOIN {cmsTags} as t ON (LOWER(tagset.tag) = LOWER(t.tag) AND LOWER(tagset.{group}) = LOWER(t.{group}) AND COALESCE(tagset.languageid, -1) = COALESCE(t.{languageIdCol}, -1)) +) AS tagset2 +LEFT OUTER JOIN {QuoteTableName(TagRelationshipDto.TableName)} r +ON (tagset2.id = r.{QuoteColumnName("tagId")} AND r.{nodeId} = {contentId} AND r.{QuoteColumnName("propertyTypeID")} = {propertyTypeId}) +WHERE r.{QuoteColumnName("tagId")} IS NULL"; // cmsTagRelationship.tagId is never null Database.Execute(sql2); } @@ -158,29 +164,34 @@ WHERE r.tagId IS NULL"; public void Remove(int contentId, int propertyTypeId, IEnumerable tags) { var tagSetSql = GetTagSet(tags); - var group = SqlSyntax.GetQuotedColumnName("group"); + var group = QuoteColumnName("group"); + var nodeId = QuoteColumnName("nodeId"); + var cmsTags = QuoteTableName("cmsTags"); + var cmsTagsLanguageIdCol = $"{QuoteTableName("cmsTags")}.{QuoteColumnName("languageId")}"; var deleteSql = - $@"DELETE FROM cmsTagRelationship WHERE nodeId = {contentId} AND propertyTypeId = {propertyTypeId} AND tagId IN ( - SELECT id FROM cmsTags INNER JOIN {tagSetSql} ON ( - tagSet.tag = cmsTags.tag AND tagSet.{group} = cmsTags.{group} AND COALESCE(tagSet.languageId, -1) = COALESCE(cmsTags.languageId, -1) - ) - )"; - +$@"DELETE FROM {QuoteTableName(TagRelationshipDto.TableName)} WHERE {nodeId} = {contentId} AND {QuoteColumnName("propertyTypeId")} = {propertyTypeId} AND {QuoteColumnName("tagId")} IN +(SELECT id FROM {cmsTags} INNER JOIN {tagSetSql} +ON (tagset.tag = {cmsTags}.tag AND tagset.{group} = {cmsTags}.{group} AND COALESCE(tagset.languageid, -1) = COALESCE({cmsTagsLanguageIdCol}, -1)) +)"; Database.Execute(deleteSql); } /// - public void RemoveAll(int contentId, int propertyTypeId) => - Database.Execute( - "DELETE FROM cmsTagRelationship WHERE nodeId = @nodeId AND propertyTypeId = @propertyTypeId", - new {nodeId = contentId, propertyTypeId}); + public void RemoveAll(int contentId, int propertyTypeId) + { + Sql sql = Sql().Delete() + .Where(x => x.NodeId == contentId && x.PropertyTypeId == propertyTypeId); + Database.Execute(sql); + } /// - public void RemoveAll(int contentId) => - Database.Execute( - "DELETE FROM cmsTagRelationship WHERE nodeId = @nodeId", - new {nodeId = contentId}); + public void RemoveAll(int contentId) + { + Sql sql = Sql().Delete() + .Where(x => x.NodeId == contentId); + Database.Execute(sql); + } // this is a clever way to produce an SQL statement like this: // @@ -195,7 +206,7 @@ WHERE r.tagId IS NULL"; private string GetTagSet(IEnumerable tags) { var sql = new StringBuilder(); - var group = SqlSyntax.GetQuotedColumnName("group"); + var group = QuoteColumnName("group"); var first = true; sql.Append("("); @@ -235,11 +246,10 @@ WHERE r.tagId IS NULL"; { sql.Append("NULL"); } - - sql.Append(" AS languageId"); + sql.Append(" AS languageid"); } - sql.Append(") AS tagSet"); + sql.Append($") AS tagset"); return sql.ToString(); } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index 9aa212d058..eeb7f1899f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -90,25 +90,24 @@ internal sealed class TemplateRepository : EntityRepositoryBase, private IEnumerable GetAxisDefinitions(params TemplateDto[] templates) { - //look up the simple template definitions that have a master template assigned, this is used + // look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties Sql childIdsSql = SqlContext.Sql() - .Select("nodeId,alias,parentID") + .Select(t => t.NodeId, t => t.Alias) + .AndSelect(n => n.ParentId) .From() .InnerJoin() - .On(dto => dto.NodeId, dto => dto.NodeId) - //lookup axis's - .Where( - "umbracoNode." + SqlContext.SqlSyntax.GetQuotedColumnName("id") + - " IN (@parentIds) OR umbracoNode.parentID IN (@childIds)", - new - { - parentIds = templates.Select(x => x.NodeDto.ParentId), - childIds = templates.Select(x => x.NodeId) - }); + .On(left => left.NodeId, right => right.NodeId) + + // lookup axis's + .WhereInOr( + n => n.NodeId, + n2 => n2.ParentId, + templates.Select(x => x.NodeDto.ParentId), + templates.Select(x => x.NodeId)); IEnumerable childIds = Database.Fetch(childIdsSql) - .Select(x => new EntitySlim {Id = x.NodeId, ParentId = x.ParentId, Name = x.Alias}); + .Select(x => new EntitySlim { Id = x.NodeId, ParentId = x.ParentId, Name = x.Alias }); return childIds; } @@ -308,7 +307,7 @@ internal sealed class TemplateRepository : EntityRepositoryBase, if (ids?.Any() ?? false) { - sql.Where("umbracoNode.id in (@ids)", new {ids}); + sql.Where($"{QuoteTableName("umbracoNode")}.id in (@ids)", new { ids }); } else { @@ -326,7 +325,7 @@ internal sealed class TemplateRepository : EntityRepositoryBase, // later to populate the template item's properties IUmbracoEntity[] childIds = (ids?.Any() ?? false ? GetAxisDefinitions(dtos.ToArray()) - : dtos.Select(x => new EntitySlim {Id = x.NodeId, ParentId = x.NodeDto.ParentId, Name = x.Alias})) + : dtos.Select(x => new EntitySlim { Id = x.NodeId, ParentId = x.NodeDto.ParentId, Name = x.Alias })) .ToArray(); return dtos.Select(d => MapFromDto(d, childIds)); @@ -373,18 +372,20 @@ internal sealed class TemplateRepository : EntityRepositoryBase, return sql; } - protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.Node}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(Constants.DatabaseSchema.Tables.Node)}.id = @id"; protected override IEnumerable GetDeleteClauses() { + var nodeId = QuoteColumnName("nodeId"); + var umbracoNode = QuoteTableName(NodeDto.TableName); var list = new List { - "DELETE FROM " + Constants.DatabaseSchema.Tables.User2NodeNotify + " WHERE nodeId = @id", - "UPDATE " + Constants.DatabaseSchema.Tables.DocumentVersion + - " SET templateId = NULL WHERE templateId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentType + " WHERE templateNodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Template + " WHERE nodeId = @id", - "DELETE FROM " + Constants.DatabaseSchema.Tables.Node + " WHERE id = @id" + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.User2NodeNotify)} WHERE {nodeId} = @id", + $@"UPDATE {QuoteTableName(Constants.DatabaseSchema.Tables.DocumentVersion)} + SET {QuoteColumnName("templateId")} = NULL WHERE {QuoteColumnName("templateId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.DocumentType)} WHERE {QuoteColumnName("templateNodeId")} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.Template)} WHERE {nodeId} = @id", + $"DELETE FROM {umbracoNode} WHERE id = @id", }; return list; } @@ -474,7 +475,7 @@ internal sealed class TemplateRepository : EntityRepositoryBase, } //Get TemplateDto from db to get the Primary key of the entity - TemplateDto templateDto = Database.Single("WHERE nodeId = @Id", new {entity.Id}); + TemplateDto templateDto = Database.Single($"WHERE {QuoteColumnName("nodeId")} = @Id", new { entity.Id }); //Save updated entity to db template.UpdateDate = DateTime.UtcNow; @@ -538,14 +539,14 @@ internal sealed class TemplateRepository : EntityRepositoryBase, { foreach (var delete in deletes) { - Database.Execute(delete, new {id = GetEntityId(descendant)}); + Database.Execute(delete, new { id = GetEntityId(descendant) }); } } //now we can delete this one foreach (var delete in deletes) { - Database.Execute(delete, new {id = GetEntityId(entity)}); + Database.Execute(delete, new { id = GetEntityId(entity) }); } var viewName = string.Concat(entity.Alias, ".cshtml"); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs index fb66fd8265..2202a21629 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; @@ -67,21 +68,24 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return []; } + ISqlSyntaxProvider? sx = _scopeAccessor.AmbientScope.Database.SqlContext.SqlSyntax; + string[] columns = [ + sx.ColumnWithAlias("x", "otherId", "nodeId"), + sx.ColumnWithAlias("n", "uniqueId", "nodeKey"), + sx.ColumnWithAlias("n", "nodeObjectType", "nodeObjectType"), + sx.ColumnWithAlias("d", "published", "nodePublished"), + sx.ColumnWithAlias("ctn", "uniqueId", "contentTypeKey"), + sx.ColumnWithAlias("ct", "icon", "contentTypeIcon"), + sx.ColumnWithAlias("ct", "alias", "contentTypeAlias"), + sx.ColumnWithAlias("ctn", "text", "contentTypeName"), + sx.ColumnWithAlias("x", "alias", "relationTypeAlias"), + sx.ColumnWithAlias("x", "name", "relationTypeName"), + sx.ColumnWithAlias("x", "isDependency", "relationTypeIsDependency"), + sx.ColumnWithAlias("x", "dual", "relationTypeIsBidirectional") + ]; Sql innerUnionSql = GetInnerUnionSql(); - Sql sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct( - "[x].[otherId] as nodeId", - "[n].[uniqueId] as nodeKey", - "[n].[text] as nodeName", - "[n].[nodeObjectType] as nodeObjectType", - "[d].[published] as nodePublished", - "[ctn].[uniqueId] as contentTypeKey", - "[ct].[icon] as contentTypeIcon", - "[ct].[alias] as contentTypeAlias", - "[ctn].[text] as contentTypeName", - "[x].[alias] as relationTypeAlias", - "[x].[name] as relationTypeName", - "[x].[isDependency] as relationTypeIsDependency", - "[x].[dual] as relationTypeIsBidirectional") + Sql sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + .SelectDistinct(columns) .From("n") .InnerJoinNested(innerUnionSql, "x") .On((n, x) => n.NodeId == x.OtherId, "n", "x") @@ -150,31 +154,33 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return []; } + ISqlSyntaxProvider sx = _scopeAccessor.AmbientScope.Database.SqlContext.SqlSyntax; + string[] columns = [ + sx.ColumnWithAlias("x", "id", "nodeId"), + sx.ColumnWithAlias("n", "uniqueId", "nodeKey"), + sx.ColumnWithAlias("n", "text", "nodeName"), + sx.ColumnWithAlias("n", "nodeObjectType", "nodeObjectType"), + sx.ColumnWithAlias("ctn", "uniqueId", "contentTypeKey"), + sx.ColumnWithAlias("ct", "icon", "contentTypeIcon"), + sx.ColumnWithAlias("ct", "alias", "contentTypeAlias"), + sx.ColumnWithAlias("ctn", "text", "contentTypeName"), + sx.ColumnWithAlias("x", "alias", "relationTypeAlias"), + sx.ColumnWithAlias("x", "name", "relationTypeName"), + sx.ColumnWithAlias("x", "isDependency", "relationTypeIsDependency"), + sx.ColumnWithAlias("x", "dual", "relationTypeIsBidirectional") + ]; Sql innerUnionSql = GetInnerUnionSql(); - Sql sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct( - "[x].[id] as nodeId", - "[n].[uniqueId] as nodeKey", - "[n].[text] as nodeName", - "[n].[nodeObjectType] as nodeObjectType", - "[ctn].[uniqueId] as contentTypeKey", - "[ct].[icon] as contentTypeIcon", - "[ct].[alias] as contentTypeAlias", - "[ctn].[text] as contentTypeName", - "[x].[alias] as relationTypeAlias", - "[x].[name] as relationTypeName", - "[x].[isDependency] as relationTypeIsDependency", - "[x].[dual] as relationTypeIsBidirectional") + Sql? sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + .SelectDistinct(columns) .From("n") .InnerJoinNested(innerUnionSql, "x") .On((n, x) => n.NodeId == x.Id, "n", "x") .LeftJoin("c") .On((left, right) => left.NodeId == right.NodeId, aliasLeft: "n", aliasRight: "c") .LeftJoin("ct") - .On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", - aliasRight: "ct") + .On((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct") .LeftJoin("ctn") - .On((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", - aliasRight: "ctn"); + .On((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn"); if (keys.Any()) { sql = sql.Where(x => keys.Contains(x.UniqueId), "n"); @@ -265,39 +271,47 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return []; } + IUmbracoDatabase database = _scopeAccessor.AmbientScope.Database; + ISqlContext sqlContext = database.SqlContext; + SqlSyntax.ISqlSyntaxProvider syntax = _scopeAccessor.AmbientScope.Database.SqlContext.SqlSyntax; // Gets the path of the parent with ",%" added - Sql subsubQuery = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() - .Select(syntax?.GetConcat("[node].[path]", "',%'")) - .From("node") - .Where(x => x.UniqueId == parentKey, "node"); + Sql subsubQuery = sqlContext.Sql() + .Select(c => c.Path) + .From() + .Where(x => x.UniqueId == parentKey); // Gets the descendants of the parent node - Sql subQuery = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + Sql subQuery = sqlContext.Sql() .Select(x => x.NodeId) .From() .WhereLike(x => x.Path, subsubQuery); + ISqlSyntaxProvider sx = sqlContext.SqlSyntax; + string[] columns = [ + sx.ColumnWithAlias("x", "id", "nodeId"), + sx.ColumnWithAlias("n", "uniqueId", "nodeKey"), + sx.ColumnWithAlias("n", "text", "nodeName"), + sx.ColumnWithAlias("n", "nodeObjectType", "nodeObjectType"), + sx.ColumnWithAlias("d", "published", "nodePublished"), + sx.ColumnWithAlias("ctn", "uniqueId", "contentTypeKey"), + sx.ColumnWithAlias("ct", "icon", "contentTypeIcon"), + sx.ColumnWithAlias("ct", "alias", "contentTypeAlias"), + sx.ColumnWithAlias("ctn", "text", "contentTypeName"), + sx.ColumnWithAlias("x", "alias", "relationTypeAlias"), + sx.ColumnWithAlias("x", "name", "relationTypeName"), + sx.ColumnWithAlias("x", "isDependency", "relationTypeIsDependency"), + sx.ColumnWithAlias("x", "dual", "relationTypeIsBidirectional") + ]; Sql innerUnionSql = GetInnerUnionSql(); - Sql sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct( - "[x].[id] as nodeId", - "[n].[uniqueId] as nodeKey", - "[n].[text] as nodeName", - "[n].[nodeObjectType] as nodeObjectType", - "[d].[published] as nodePublished", - "[ctn].[uniqueId] as contentTypeKey", - "[ct].[icon] as contentTypeIcon", - "[ct].[alias] as contentTypeAlias", - "[ctn].[text] as contentTypeName", - "[x].[alias] as relationTypeAlias", - "[x].[name] as relationTypeName", - "[x].[isDependency] as relationTypeIsDependency", - "[x].[dual] as relationTypeIsBidirectional") + Sql sql = sqlContext.Sql() + .SelectDistinct(columns) .From("n") .InnerJoinNested(innerUnionSql, "x") .On((n, x) => n.NodeId == x.Id, "n", "x") - .LeftJoin("c").On( + .LeftJoin("c") + .On( (left, right) => left.NodeId == right.NodeId, aliasLeft: "n", aliasRight: "c") @@ -315,19 +329,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .On( (left, right) => left.NodeId == right.NodeId, aliasLeft: "n", - aliasRight: "d"); - sql = sql.WhereIn( - (Expression>)(x => x.NodeId), - subQuery, - "n"); + aliasRight: "d") + .WhereIn((Expression>)(x => x.NodeId), subQuery, "n"); if (filterMustBeIsDependency) { - sql = sql.Where(rt => rt.IsDependency, "x"); + sql.Where(rt => rt.IsDependency, "x"); } - // find the count before ordering - totalRecords = _scopeAccessor.AmbientScope?.Database.Count(sql!) ?? 0; + totalRecords = database?.Count(sql!) ?? 0; RelationItemDto[] pagedResult; @@ -335,7 +345,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement if (totalRecords > 0) { // Ordering is required for paging - sql = sql.OrderBy(x => x.Alias, "x"); + sql.OrderBy(x => x.Alias, "x"); pagedResult = _scopeAccessor.AmbientScope?.Database.SkipTake(skip, take, sql).ToArray() ?? @@ -356,17 +366,23 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement throw new InvalidOperationException("No Ambient Scope available"); } - Sql innerUnionSqlChild = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", - "[cn].trashed as [trashed]", - "[cn].nodeObjectType as [nodeObjectType]", - "[pn].uniqueId as otherKey," + - "[cr].childId as id", - "[cr].parentId as otherId", - "[rt].[alias]", - "[rt].[name]", - "[rt].[isDependency]", - "[rt].[dual]") + ISqlSyntaxProvider? sx = _scopeAccessor.AmbientScope.Database.SqlContext.SqlSyntax; + string[] columns = sx == null + ? [] + : [ + sx.ColumnWithAlias("cn", "uniqueId", "key"), + sx.ColumnWithAlias("cn", "trashed", "trashed"), + sx.ColumnWithAlias("cn", "nodeObjectType", "nodeObjectType"), + sx.ColumnWithAlias("pn", "uniqueId", "otherKey"), + sx.ColumnWithAlias("cr", "childId", "id"), + sx.ColumnWithAlias("cr", "parentId", "otherId"), + sx.ColumnWithAlias("rt", "alias", string.Empty), + sx.ColumnWithAlias("rt", "name", string.Empty), + sx.ColumnWithAlias("rt", "isDependency", string.Empty), + sx.ColumnWithAlias("rt", "dual", string.Empty) + ]; + Sql innerUnionSqlChild = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + .Select(columns) .From("cr") .InnerJoin("rt") .On((cr, rt) => rt.Dual == false && rt.Id == cr.RelationType, "cr", "rt") @@ -375,17 +391,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .InnerJoin("pn") .On((cr, pn) => cr.ParentId == pn.NodeId, "cr", "pn"); - Sql innerUnionSqlDualParent = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[pn].uniqueId as [key]", - "[pn].trashed as [trashed]", - "[pn].nodeObjectType as [nodeObjectType]", - "[cn].uniqueId as otherKey," + - "[dpr].parentId as id", - "[dpr].childId as otherId", - "[dprt].[alias]", - "[dprt].[name]", - "[dprt].[isDependency]", - "[dprt].[dual]") + columns = sx == null + ? [] + : [ + sx.ColumnWithAlias("pn", "uniqueId", "key"), + sx.ColumnWithAlias("pn", "trashed", "trashed"), + sx.ColumnWithAlias("pn", "nodeObjectType", "nodeObjectType"), + sx.ColumnWithAlias("cn", "uniqueId", "otherKey"), + sx.ColumnWithAlias("dpr", "parentId", "id"), + sx.ColumnWithAlias("dpr", "childId", "otherId"), + sx.ColumnWithAlias("dprt", "alias", string.Empty), + sx.ColumnWithAlias("dprt", "name", string.Empty), + sx.ColumnWithAlias("dprt", "isDependency", string.Empty), + sx.ColumnWithAlias("dprt", "dual", string.Empty) + ]; + Sql innerUnionSqlDualParent = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + .Select(columns) .From("dpr") .InnerJoin("dprt") .On( @@ -394,18 +415,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .On((dpr, cn) => dpr.ChildId == cn.NodeId, "dpr", "cn") .InnerJoin("pn") .On((dpr, pn) => dpr.ParentId == pn.NodeId, "dpr", "pn"); - - Sql innerUnionSql3 = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", - "[cn].trashed as [trashed]", - "[cn].nodeObjectType as [nodeObjectType]", - "[pn].uniqueId as otherKey," + - "[dcr].childId as id", - "[dcr].parentId as otherId", - "[dcrt].[alias]", - "[dcrt].[name]", - "[dcrt].[isDependency]", - "[dcrt].[dual]") + columns = sx == null + ? [] + : [ + sx.ColumnWithAlias("cn", "uniqueId", "key"), + sx.ColumnWithAlias("cn", "trashed", "trashed"), + sx.ColumnWithAlias("cn", "nodeObjectType", "nodeObjectType"), + sx.ColumnWithAlias("pn", "uniqueId", "otherKey"), + sx.ColumnWithAlias("dcr", "childId", "id"), + sx.ColumnWithAlias("dcr", "parentId", "otherId"), + sx.ColumnWithAlias("dcrt", "alias", string.Empty), + sx.ColumnWithAlias("dcrt", "name", string.Empty), + sx.ColumnWithAlias("dcrt", "isDependency", string.Empty), + sx.ColumnWithAlias("dcrt", "dual", string.Empty) + ]; + Sql innerUnionSql3 = _scopeAccessor.AmbientScope.Database.SqlContext.Sql() + .Select(columns) .From("dcr") .InnerJoin("dcrt") .On( diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TwoFactorLoginRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TwoFactorLoginRepository.cs index b5a049f0f4..1cc77f090c 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TwoFactorLoginRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TwoFactorLoginRepository.cs @@ -64,7 +64,7 @@ internal sealed class TwoFactorLoginRepository : EntityRepositoryBase - Constants.DatabaseSchema.Tables.TwoFactorLogin + ".id = @id"; + QuoteTableName(Constants.DatabaseSchema.Tables.TwoFactorLogin) + ".id = @id"; protected override IEnumerable GetDeleteClauses() => Enumerable.Empty(); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserGroupRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserGroupRepository.cs index 8168e6d142..99d2c7cd7a 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserGroupRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserGroupRepository.cs @@ -51,8 +51,11 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG // need to do a simple query to get the id - put this cache var id = IsolatedCache.GetCacheItem(GetByAliasCacheKey(alias), () => { - var groupId = - Database.ExecuteScalar("SELECT id FROM umbracoUserGroup WHERE userGroupAlias=@alias", new { alias }); + Sql sql = Sql() + .Select(x => x.Id) + .From() + .Where(x => x.Alias == alias); + var groupId = Database.ExecuteScalar(sql); if (groupId.HasValue == false) { throw new InvalidOperationException("No group found with alias " + alias); @@ -75,20 +78,10 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG { // Here we're building up a query that looks like this, a sub query is required because the resulting structure // needs to still contain all of the section rows per user group. - - // SELECT * - // FROM [umbracoUserGroup] - // LEFT JOIN [umbracoUserGroup2App] - // ON [umbracoUserGroup].[id] = [umbracoUserGroup2App].[user] - // WHERE umbracoUserGroup.id IN (SELECT umbracoUserGroup.id - // FROM [umbracoUserGroup] - // LEFT JOIN [umbracoUserGroup2App] - // ON [umbracoUserGroup].[id] = [umbracoUserGroup2App].[user] - // WHERE umbracoUserGroup2App.app = 'content') Sql sql = GetBaseQuery(QueryType.Many); Sql innerSql = GetBaseQuery(QueryType.Ids); - innerSql.Where("umbracoUserGroup2App.app = " + SqlSyntax.GetQuotedValue(sectionAlias)); - sql.Where($"umbracoUserGroup.id IN ({innerSql.SQL})"); + innerSql.Where(c => c.AppAlias == sectionAlias); + sql.WhereIn(c => c.Id, innerSql); AppendGroupBy(sql); return Database.Fetch(sql).Select(x => UserGroupFactory.BuildEntity(_shortStringHelper, x, _permissionMappers)); @@ -252,8 +245,13 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG /// Removes all users from a group ///
/// Id of group - private void RemoveAllUsersFromGroup(int groupId) => - Database.Delete("WHERE userGroupId = @groupId", new { groupId }); + private void RemoveAllUsersFromGroup(int groupId) + { + Sql sql = Sql() + .Delete() + .Where(x => x.UserGroupId == groupId); + Database.Execute(sql); + } /// /// Adds a set of users to a group @@ -399,7 +397,7 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG break; case QueryType.Single: case QueryType.Many: - sql.Select(r => r.Select(x => x.UserGroup2AppDtos), s => s.Append($", COUNT({sql.Columns(x => x.UserId)}) AS {SqlSyntax.GetQuotedColumnName("UserCount")}")); + sql.Select(r => r.Select(x => x.UserGroup2AppDtos), s => s.Append($", COUNT({sql.Columns(x => x.UserId)}) AS {SqlSyntax.GetQuotedName("UserCount")}")); addFrom = true; break; default: @@ -437,17 +435,22 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG x => x.DefaultPermissions) .AndBy(x => x.AppAlias, x => x.UserGroupId); - protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.UserGroup}.id = @id"; - + protected override string GetBaseWhereClause() => $"{QuoteTableName(UserGroupDto.TableName)}.id = @id"; protected override IEnumerable GetDeleteClauses() { + var userGroupId = QuoteColumnName("userGroupId"); + var userGroupKey = QuoteColumnName("userGroupKey"); + var key = QuoteColumnName("Key"); + var umbracoUserGroup = QuoteTableName(UserGroupDto.TableName); var list = new List { - "DELETE FROM umbracoUser2UserGroup WHERE userGroupId = @id", - "DELETE FROM umbracoUserGroup2App WHERE userGroupId = @id", - "DELETE FROM umbracoUserGroup2Permission WHERE userGroupKey IN (SELECT [umbracoUserGroup].[Key] FROM umbracoUserGroup WHERE Id = @id)", - "DELETE FROM umbracoUserGroup2GranularPermission WHERE userGroupKey IN (SELECT [umbracoUserGroup].[Key] FROM umbracoUserGroup WHERE Id = @id)", - "DELETE FROM umbracoUserGroup WHERE id = @id", + $"DELETE FROM {QuoteTableName("umbracoUser2UserGroup")} WHERE {userGroupId} = @id", + $"DELETE FROM {QuoteTableName("umbracoUserGroup2App")} WHERE {userGroupId} = @id", + $@"DELETE FROM {QuoteTableName("umbracoUserGroup2Permission")} WHERE {userGroupKey} IN + (SELECT {umbracoUserGroup}.{key} FROM {umbracoUserGroup} WHERE id = @id)", + $@"DELETE FROM {QuoteTableName("umbracoUserGroup2GranularPermission")} WHERE {userGroupKey} IN + (SELECT {umbracoUserGroup}.{key} FROM {umbracoUserGroup} WHERE id = @id)", + $"DELETE FROM {umbracoUserGroup} WHERE id = @id", }; return list; } @@ -490,7 +493,10 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG IUserGroup userGroup = entity; // First delete all - Database.Delete("WHERE UserGroupId = @UserGroupId", new { UserGroupId = userGroup.Id }); + Sql sql = Sql() + .Delete() + .Where(c => c.UserGroupId == userGroup.Id); + Database.Execute(sql); // Then re-add any associated with the group foreach (var app in userGroup.AllowedSections) @@ -505,7 +511,10 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG IUserGroup userGroup = entity; // First delete all - Database.Delete("WHERE UserGroupId = @UserGroupId", new { UserGroupId = userGroup.Id }); + Sql sql = Sql() + .Delete() + .Where(c => c.UserGroupId == userGroup.Id); + Database.Execute(sql); // Then re-add any associated with the group foreach (var language in userGroup.AllowedLanguages) @@ -522,7 +531,10 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG private void PersistPermissions(IUserGroup userGroup) { - Database.Delete("WHERE userGroupKey = @UserGroupKey", new { UserGroupKey = userGroup.Key }); + Sql sql = Sql() + .Delete() + .Where(c => c.UserGroupKey == userGroup.Key); + Database.Execute(sql); IEnumerable permissionDtos = userGroup.Permissions .Select(permission => new UserGroup2PermissionDto { UserGroupKey = userGroup.Key, Permission = permission }); @@ -531,14 +543,19 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG } private void PersistGranularPermissions(IUserGroup userGroup) { - Database.Delete("WHERE userGroupKey = @UserGroupKey", new { UserGroupKey = userGroup.Key }); + Sql sql = Sql() + .Delete() + .Where(c => c.UserGroupKey == userGroup.Key); + Database.Execute(sql); IEnumerable permissionDtos = userGroup.GranularPermissions .Select(permission => { var dto = new UserGroup2GranularPermissionDto { - UserGroupKey = userGroup.Key, Permission = permission.Permission, Context = permission.Context + UserGroupKey = userGroup.Key, + Permission = permission.Permission, + Context = permission.Context }; if (permission is INodeGranularPermission nodeGranularPermission) { @@ -608,7 +625,5 @@ public class UserGroupRepository : EntityRepositoryBase, IUserG List userGroupGranularPermissions = Database.Fetch(query); return userGroupGranularPermissions.GroupBy(x => x.UserGroupKey).ToDictionary(x => x.Key, x => x.ToList()); } - - #endregion } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs index 4c68bc5189..b005c9b881 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs @@ -18,6 +18,7 @@ using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Factories; using Umbraco.Cms.Infrastructure.Persistence.Mappers; using Umbraco.Cms.Infrastructure.Persistence.Querying; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; using IMapperCollection = Umbraco.Cms.Infrastructure.Persistence.Mappers.IMapperCollection; @@ -214,17 +215,23 @@ internal sealed class UserRepository : EntityRepositoryBase, IUserR public IDictionary GetUserStates() { // These keys in this query map to the `Umbraco.Core.Models.Membership.UserState` enum - var sql = @"SELECT -1 AS [Key], COUNT(id) AS [Value] FROM umbracoUser + var keyAlias = SqlSyntax.GetQuotedName("Key"); + var valueAlias = SqlSyntax.GetQuotedName("Value"); + var userTableName = QuoteTableName("umbracoUser"); + var sql = @$"SELECT -1 AS {keyAlias}, COUNT(id) AS {valueAlias} FROM {userTableName} UNION -SELECT 0 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 AND userNoConsole = 0 AND lastLoginDate IS NOT NULL +SELECT 0 AS {keyAlias}, COUNT(id) AS {valueAlias} FROM {userTableName} + WHERE {QuoteColumnName("userDisabled")} = 0 AND {QuoteColumnName("userNoConsole")} = 0 AND {QuoteColumnName("lastLoginDate")} IS NOT NULL UNION -SELECT 1 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 1 +SELECT 1 AS {keyAlias}, COUNT(id) AS {valueAlias} FROM {userTableName} WHERE {QuoteColumnName("userDisabled")} = 1 UNION -SELECT 2 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userNoConsole = 1 +SELECT 2 AS {keyAlias}, COUNT(id) AS {valueAlias} FROM {userTableName} WHERE {QuoteColumnName("userNoConsole")} = 1 UNION -SELECT 3 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE lastLoginDate IS NULL AND userDisabled = 1 AND invitedDate IS NOT NULL +SELECT 3 AS {keyAlias}, COUNT(id) AS {valueAlias} FROM {userTableName} + WHERE {QuoteColumnName("lastLoginDate")} IS NULL AND {QuoteColumnName("userDisabled")} = 1 AND {QuoteColumnName("invitedDate")} IS NOT NULL UNION -SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 AND userNoConsole = 0 AND lastLoginDate IS NULL"; +SELECT 4 AS {keyAlias}, COUNT(id) AS {valueAlias} FROM {userTableName} + WHERE {QuoteColumnName("userDisabled")} = 0 AND {QuoteColumnName("userNoConsole")} = 0 AND {QuoteColumnName("lastLoginDate")} IS NULL"; Dictionary? result = Database.Dictionary(sql); @@ -652,20 +659,22 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 .Select(columns) .From(); - protected override string GetBaseWhereClause() => $"{Constants.DatabaseSchema.Tables.User}.id = @id"; + protected override string GetBaseWhereClause() => $"{QuoteTableName(UserDto.TableName)}.id = @id"; protected override IEnumerable GetDeleteClauses() { + var userColName = QuoteColumnName("userId"); var list = new List { - $"DELETE FROM {Constants.DatabaseSchema.Tables.UserLogin} WHERE userId = @id", - $"DELETE FROM {Constants.DatabaseSchema.Tables.User2UserGroup} WHERE userId = @id", - $"DELETE FROM {Constants.DatabaseSchema.Tables.User2NodeNotify} WHERE userId = @id", - $"DELETE FROM {Constants.DatabaseSchema.Tables.User2ClientId} WHERE userId = @id", - $"DELETE FROM {Constants.DatabaseSchema.Tables.UserStartNode} WHERE userId = @id", - $"DELETE FROM {Constants.DatabaseSchema.Tables.ExternalLoginToken} WHERE externalLoginId = (SELECT id FROM {Constants.DatabaseSchema.Tables.ExternalLogin} WHERE userOrMemberKey = @key)", - $"DELETE FROM {Constants.DatabaseSchema.Tables.ExternalLogin} WHERE userOrMemberKey = @key", - $"DELETE FROM {Constants.DatabaseSchema.Tables.User} WHERE id = @id", + $"DELETE FROM {QuoteTableName(UserLoginDto.TableName)} WHERE {userColName} = @id", + $"DELETE FROM {QuoteTableName(User2UserGroupDto.TableName)} WHERE {userColName} = @id", + $"DELETE FROM {QuoteTableName(User2NodeNotifyDto.TableName)} WHERE {userColName} = @id", + $"DELETE FROM {QuoteTableName(User2ClientIdDto.TableName)} WHERE {userColName} = @id", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.UserStartNode)} WHERE {userColName} = @id", + @$"DELETE FROM {QuoteTableName(ExternalLoginTokenDto.TableName)} WHERE {QuoteColumnName("externalLoginId")} = + (SELECT id FROM {QuoteTableName(ExternalLoginDto.TableName)} WHERE {QuoteColumnName("userOrMemberKey")} = @key)", + $"DELETE FROM {QuoteTableName(ExternalLoginDto.TableName)} WHERE {QuoteColumnName("userOrMemberKey")} = @key", + $"DELETE FROM {QuoteTableName(Constants.DatabaseSchema.Tables.User)} WHERE id = @id", }; return list; } @@ -714,11 +723,13 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 if (entity.IsPropertyDirty("Groups")) { // lookup all assigned + Sql sql = SqlContext.Sql() + .SelectAll() + .From() + .WhereIn(x => x.Alias, entity.Groups.Select(x => x.Alias).ToArray()); List? assigned = entity.Groups == null || entity.Groups.Any() == false ? new List() - : Database.Fetch( - "SELECT * FROM umbracoUserGroup WHERE userGroupAlias IN (@aliases)", - new { aliases = entity.Groups.Select(x => x.Alias) }); + : Database.Fetch(sql); foreach (UserGroupDto? groupDto in assigned) { @@ -818,35 +829,43 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 if (entity.IsPropertyDirty("StartContentIds") || entity.IsPropertyDirty("StartMediaIds")) { + Sql sql = SqlContext.Sql() + .SelectAll() + .From() + .Where(x => x.UserId == entity.Id); List? assignedStartNodes = - Database.Fetch( - "SELECT * FROM umbracoUserStartNode WHERE userId = @userId", - new { userId = entity.Id }); + Database.Fetch(sql); + if (entity.IsPropertyDirty("StartContentIds")) { - AddingOrUpdateStartNodes(entity, assignedStartNodes, UserStartNodeDto.StartNodeTypeValue.Content, - entity.StartContentIds); + AddingOrUpdateStartNodes(entity, assignedStartNodes, UserStartNodeDto.StartNodeTypeValue.Content, entity.StartContentIds); } if (entity.IsPropertyDirty("StartMediaIds")) { - AddingOrUpdateStartNodes(entity, assignedStartNodes, UserStartNodeDto.StartNodeTypeValue.Media, - entity.StartMediaIds); + AddingOrUpdateStartNodes(entity, assignedStartNodes, UserStartNodeDto.StartNodeTypeValue.Media, entity.StartMediaIds); } } if (entity.IsPropertyDirty("Groups")) { - //lookup all assigned - List? assigned = entity.Groups == null || entity.Groups.Any() == false - ? new List() - : Database.Fetch( - "SELECT * FROM umbracoUserGroup WHERE userGroupAlias IN (@aliases)", - new { aliases = entity.Groups.Select(x => x.Alias) }); + // lookup all assigned + Sql sql = SqlContext.Sql() + .SelectAll() + .From() + .WhereIn( + x => x.Alias, + entity.Groups.Select(x => x.Alias).ToArray()); - //first delete all - // TODO: We could do this a nicer way instead of "Nuke and Pave" - Database.Delete("WHERE UserId = @UserId", new { UserId = entity.Id }); + List? assigned = entity.Groups == null || entity.Groups.Any() == false + ? [] + : Database.Fetch(sql); + + // first delete all + sql = SqlContext.Sql() + .Delete() + .Where(c => c.UserId == entity.Id); + Database.Execute(sql); foreach (UserGroupDto? groupDto in assigned) { @@ -868,16 +887,18 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 var assignedIds = current.Where(x => x.StartNodeType == (int)startNodeType).Select(x => x.StartNode).ToArray(); - //remove the ones not assigned to the entity + // remove the ones not assigned to the entity var toDelete = assignedIds.Except(entityStartIds).ToArray(); if (toDelete.Length > 0) { - Database.Delete( - "WHERE UserId = @UserId AND startNode IN (@startNodes)", - new { UserId = entity.Id, startNodes = toDelete }); + Sql sql = SqlContext.Sql() + .Delete() + .Where(x => x.UserId == entity.Id) + .WhereIn(x => x.StartNode, toDelete); + Database.Execute(sql); } - //add the ones not currently in the db + // add the ones not currently in the db var toAdd = entityStartIds.Except(assignedIds).ToArray(); foreach (var i in toAdd) { @@ -892,12 +913,13 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 public int GetCountByQuery(IQuery? query) { - Sql sqlClause = GetBaseQuery("umbracoUser.id"); + var userIdQuoted = SqlSyntax.GetQuotedColumn(UserDto.TableName, "id"); + Sql sqlClause = GetBaseQuery(userIdQuoted); var translator = new SqlTranslator(sqlClause, query); Sql subquery = translator.Translate(); //get the COUNT base query Sql? sql = GetBaseQuery(true) - .Append(new Sql("WHERE umbracoUser.id IN (" + subquery.SQL + ")", subquery.Arguments)); + .Append(new Sql($"WHERE {userIdQuoted} IN ({subquery.SQL})", subquery.Arguments)); return Database.ExecuteScalar(sql); } @@ -1060,23 +1082,16 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 } } + var userIdQuoted = SqlSyntax.GetQuotedColumn("umbracoUser", "id"); if (includeUserGroups != null && includeUserGroups.Length > 0) { - const string subQuery = @"AND (umbracoUser.id IN (SELECT DISTINCT umbracoUser.id - FROM umbracoUser - INNER JOIN umbracoUser2UserGroup ON umbracoUser2UserGroup.userId = umbracoUser.id - INNER JOIN umbracoUserGroup ON umbracoUserGroup.id = umbracoUser2UserGroup.userGroupId - WHERE umbracoUserGroup.userGroupAlias IN (@userGroups)))"; + string subQuery = GetSuQueryInExclude("IN"); filterSql?.Append(subQuery, new { userGroups = includeUserGroups }); } if (excludeUserGroups != null && excludeUserGroups.Length > 0) { - const string subQuery = @"AND (umbracoUser.id NOT IN (SELECT DISTINCT umbracoUser.id - FROM umbracoUser - INNER JOIN umbracoUser2UserGroup ON umbracoUser2UserGroup.userId = umbracoUser.id - INNER JOIN umbracoUserGroup ON umbracoUserGroup.id = umbracoUser2UserGroup.userGroupId - WHERE umbracoUserGroup.userGroupAlias IN (@userGroups)))"; + string subQuery = GetSuQueryInExclude("NOT IN"); filterSql?.Append(subQuery, new { userGroups = excludeUserGroups }); } @@ -1167,6 +1182,19 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 return pagedResult.Items.Select(x => UserFactory.BuildEntity(_globalSettings, x, _permissionMappers)); } + private string GetSuQueryInExclude(string inOrNotIn) + { + var userIdQuoted = SqlSyntax.GetQuotedColumn("umbracoUser", "id"); + // this is used to get the correct query for the in or not in clause + return @$"AND ({userIdQuoted} {inOrNotIn} (SELECT DISTINCT {userIdQuoted} + FROM {QuoteTableName("umbracoUser")} + INNER JOIN {QuoteTableName("umbracoUser2UserGroup")} + ON {SqlSyntax.GetQuotedColumn("umbracoUser2UserGroup", "userId")} = {userIdQuoted} + INNER JOIN {QuoteTableName("umbracoUserGroup")} + ON {SqlSyntax.GetQuotedColumn("umbracoUserGroup", "id")} = {SqlSyntax.GetQuotedColumn("umbracoUser2UserGroup", "userGroupId")} + WHERE {SqlSyntax.GetQuotedColumn("umbracoUserGroup", "userGroupAlias")} IN (@userGroups)))"; + } + public IEnumerable GetAllClientIds() => Database.Fetch(SqlContext.Sql() .Select(d => d.ClientId) @@ -1286,7 +1314,7 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 // Delete the OpenIddict tokens for the users associated with the removed providers. // The following is safe from SQL injection as we are dealing with GUIDs, not strings. var userKeysForInClause = string.Join("','", userKeysAssociatedWithRemovedProviders.Select(x => x.ToString())); - Database.Execute("DELETE FROM umbracoOpenIddictTokens WHERE Subject IN ('" + userKeysForInClause + "')"); + Database.Execute($"DELETE FROM {QuoteTableName("umbracoOpenIddictTokens")} WHERE {QuoteColumnName("Subject")} IN ('{userKeysForInClause}')"); } #endregion diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/WebhookRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/WebhookRepository.cs index 978043eeaa..49d38ac709 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/WebhookRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/WebhookRepository.cs @@ -3,6 +3,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Cms.Infrastructure.Persistence.Factories; +using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Extensions; @@ -146,9 +147,26 @@ public class WebhookRepository : IWebhookRepository private void DeleteManyToOneReferences(int webhookId) { - _scopeAccessor.AmbientScope?.Database.Delete("WHERE webhookId = @webhookId", new { webhookId }); - _scopeAccessor.AmbientScope?.Database.Delete("WHERE webhookId = @webhookId", new { webhookId }); - _scopeAccessor.AmbientScope?.Database.Delete("WHERE webhookId = @webhookId", new { webhookId }); + IUmbracoDatabase? database = _scopeAccessor.AmbientScope?.Database; + if (database is null) + { + return; + } + + Sql sql = database.SqlContext.Sql() + .Delete() + .Where(x => x.WebhookId == webhookId); + database.Execute(sql); + + sql = database.SqlContext.Sql() + .Delete() + .Where(x => x.WebhookId == webhookId); + database.Execute(sql); + + sql = database.SqlContext.Sql() + .Delete() + .Where(x => x.WebhookId == webhookId); + database.Execute(sql); } private void InsertManyToOneReferences(IWebhook webhook) @@ -176,9 +194,29 @@ public class WebhookRepository : IWebhookRepository private async Task DtoToEntity(WebhookDto dto) { - List? webhookEntityKeyDtos = await _scopeAccessor.AmbientScope?.Database.FetchAsync("WHERE webhookId = @webhookId", new { webhookId = dto.Id })!; - List? event2WebhookDtos = await _scopeAccessor.AmbientScope?.Database.FetchAsync("WHERE webhookId = @webhookId", new { webhookId = dto.Id })!; - List? headersWebhookDtos = await _scopeAccessor.AmbientScope?.Database.FetchAsync("WHERE webhookId = @webhookId", new { webhookId = dto.Id })!; + List? webhookEntityKeyDtos = null; + List? event2WebhookDtos = null; + List? headersWebhookDtos = null; + IUmbracoDatabase? database = _scopeAccessor.AmbientScope?.Database; + if (database is not null) + { + Sql sql = database.SqlContext.Sql() + .Select() + .From() + .Where(x => x.WebhookId == dto.Id); + webhookEntityKeyDtos = await database.FetchAsync(sql); + sql = database.SqlContext.Sql() + .Select() + .From() + .Where(x => x.WebhookId == dto.Id); + event2WebhookDtos = await database.FetchAsync(sql); + sql = database.SqlContext.Sql() + .Select() + .From() + .Where(x => x.WebhookId == dto.Id); + headersWebhookDtos = await database.FetchAsync(sql); + } + Webhook entity = WebhookFactory.BuildEntity(dto, webhookEntityKeyDtos, event2WebhookDtos, headersWebhookDtos); return entity; diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ColumnInfo.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ColumnInfo.cs index 26aea8965f..9aac684bbd 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ColumnInfo.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ColumnInfo.cs @@ -8,7 +8,7 @@ public class ColumnInfo ColumnName = columnName; Ordinal = ordinal; ColumnDefault = columnDefault; - IsNullable = isNullable.Equals("YES"); + IsNullable = isNullable?.Equals("YES") ?? false; DataType = dataType; } @@ -17,7 +17,7 @@ public class ColumnInfo TableName = tableName; ColumnName = columnName; Ordinal = ordinal; - IsNullable = isNullable.Equals("YES"); + IsNullable = isNullable?.Equals("YES") ?? false; DataType = dataType; } diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 2593f52337..a27c666d53 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -74,6 +74,8 @@ public interface ISqlSyntaxProvider string ConvertUniqueIdentifierToString => throw new NotImplementedException(); + string ConvertIntegerToBoolean(int value); + /// /// Returns the default isolation level for the database /// @@ -103,12 +105,14 @@ public interface ISqlSyntaxProvider string GetConcat(params string[] args); - string GetColumn(DatabaseType dbType, string tableName, string columnName, string columnAlias, string? referenceName = null, bool forInsert = false); + string GetColumn(DatabaseType dbType, string tableName, string columnName, string? columnAlias, string? referenceName = null, bool forInsert = false); string GetQuotedTableName(string? tableName); string GetQuotedColumnName(string? columnName); + string OrderByGuid(string tableName, string columnName); + string GetQuotedName(string? name); bool DoesTableExist(IDatabase db, string tableName); @@ -143,6 +147,8 @@ public interface ISqlSyntaxProvider string FormatTableRename(string? oldName, string? newName); + string ColumnWithAlias(string tableNameOrAlias, string columnName, string columnAlias = ""); + void HandleCreateTable(IDatabase database, TableDefinition tableDefinition, bool skipKeysAndIndexes = false); Sql SelectTop(Sql sql, int top); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index df56d36da6..9cefd32b5b 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -11,6 +11,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Persistence; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; +using Umbraco.Cms.Infrastructure.Persistence.Dtos; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; @@ -147,6 +148,8 @@ public abstract class SqlSyntaxProviderBase : ISqlSyntaxProvider public virtual string GetQuotedColumnName(string? columnName) => $"\"{columnName}\""; + public virtual string OrderByGuid(string tableName, string columnName) => $"UPPER({this.GetQuotedColumn(tableName, columnName)})"; + public virtual string GetQuotedName(string? name) => $"\"{name}\""; public virtual string GetQuotedValue(string value) => $"'{value}'"; @@ -192,7 +195,7 @@ public abstract class SqlSyntaxProviderBase : ISqlSyntaxProvider return "NVARCHAR"; } - public virtual string GetColumn(DatabaseType dbType, string tableName, string columnName, string columnAlias, string? referenceName = null, bool forInsert = false) + public virtual string GetColumn(DatabaseType dbType, string tableName, string columnName, string? columnAlias, string? referenceName = null, bool forInsert = false) { tableName = GetQuotedTableName(tableName); columnName = GetQuotedColumnName(columnName); @@ -429,6 +432,29 @@ public abstract class SqlSyntaxProviderBase : ISqlSyntaxProvider public virtual string FormatTableRename(string? oldName, string? newName) => string.Format(RenameTable, GetQuotedTableName(oldName), GetQuotedTableName(newName)); + public virtual string ColumnWithAlias(string tableNameOrAlias, string columnName, string columnAlias = "") + { + var quotedColumnName = GetQuotedColumnName(columnName); + var columnPrefix = GetColumnPrefix(tableNameOrAlias); + var asAppendix = string.IsNullOrEmpty(columnAlias) + ? string.Empty + : $" AS {GetQuotedName(columnAlias)}"; + + return $"{columnPrefix}{quotedColumnName}{asAppendix}"; + } + + private string GetColumnPrefix(string? tableNameOrAlias) + { + if (string.IsNullOrEmpty(tableNameOrAlias)) + { + return string.Empty; + } + + // Always quote the identifier to avoid ambiguity between table names and aliases. + var quoted = GetQuotedTableName(tableNameOrAlias.Trim()); + return $"{quoted}."; + } + public abstract Sql SelectTop(Sql sql, int top); public abstract void HandleCreateTable( @@ -486,6 +512,8 @@ public abstract class SqlSyntaxProviderBase : ISqlSyntaxProvider public virtual string ConvertUniqueIdentifierToString => "CONVERT(nvarchar(36), {0})"; + public virtual string ConvertIntegerToBoolean(int value) => value == 0 ? "0" : "1"; + private DbTypes InitColumnTypeMap() { var dbTypeMap = new DbTypesFactory(); diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntaxExtensions.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntaxExtensions.cs index 4ab8968389..57ab9a6eda 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntaxExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntaxExtensions.cs @@ -19,9 +19,7 @@ public static class SqlSyntaxExtensions /// An expression specifying the field. /// An optional table alias. /// - public static string GetFieldName( - this ISqlSyntaxProvider sqlSyntax, - Expression> fieldSelector, string? tableAlias = null) + public static string GetFieldName(this ISqlSyntaxProvider sqlSyntax, Expression> fieldSelector, string? tableAlias = null) { var field = ExpressionHelper.FindProperty(fieldSelector).Item1 as PropertyInfo; var fieldName = field?.GetColumnName(); diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs index 7fd3f614f5..9237db2655 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseExtensions.cs @@ -37,7 +37,7 @@ internal static class UmbracoDatabaseExtensions // create the wildcard where clause ISqlSyntaxProvider sqlSyntax = database.SqlContext.SqlSyntax; var whereParam = sqlSyntax.GetStringColumnWildcardComparison( - sqlSyntax.GetQuotedColumnName("key"), + sqlSyntax.GetColumn(database.SqlContext.DatabaseType, KeyValueDto.TableName, "key", null), 0, TextColumnType.NVarchar); diff --git a/src/Umbraco.Web.UI/appsettings.Development.template.json b/src/Umbraco.Web.UI/appsettings.Development.template.json index 93768f2d42..5b8bc438a9 100644 --- a/src/Umbraco.Web.UI/appsettings.Development.template.json +++ b/src/Umbraco.Web.UI/appsettings.Development.template.json @@ -32,7 +32,7 @@ "Smtp": { //"From": "your@email.here", //"Host": "localhost", - // "Port": "25" + //"Port": 25 } }, "Hosting": { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs index 05adc7f5c9..c0f1bb54ec 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs @@ -249,6 +249,37 @@ internal sealed class MediaRepositoryTest : UmbracoIntegrationTest } } + [Test] + public void DeleteVersions() + { + // Arrange + var provider = ScopeProvider; + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository(provider, out var mediaTypeRepository); + + // Act + var media = repository.Get(_testFile.Id); + + int initialCount = repository.GetAllVersions(media.Id).Count(); + repository.DeleteVersions(media.Id, DateTime.Now); + int initialDeleteCount = repository.GetAllVersions(media.Id).Count(); + + media.Name = $"Test File Updated"; + repository.Save(media); + + int updatedCount = repository.GetAllVersions(media.Id).Count(); + repository.DeleteVersions(media.Id, DateTime.Now); + int updatedDeleteCount = repository.GetAllVersions(media.Id).Count(); + + // Assert + Assert.That(initialCount == 1); + Assert.That(initialDeleteCount == initialCount); + Assert.That(updatedCount == 1); // media has no unpublished state and therefore only one Version + Assert.That(updatedDeleteCount == initialCount); + } + } + [Test] public void GetMedia() { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs index d4ad005b85..b0b94e0340 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/SyntaxProvider/SqlServerSyntaxProviderTests.cs @@ -48,7 +48,7 @@ internal sealed class SqlServerSyntaxProviderTests : UmbracoIntegrationTest { var mediaObjectType = Constants.ObjectTypes.Media; var subQuery = SqlContext.Sql() - .Select("DISTINCT cmsContentNu.nodeId") + .SelectDistinct(c => c.NodeId) .From() .InnerJoin() .On(left => left.NodeId, right => right.NodeId) @@ -66,11 +66,16 @@ internal sealed class SqlServerSyntaxProviderTests : UmbracoIntegrationTest return SqlContext.SqlSyntax.GetQuotedColumnName(x); } - Assert.AreEqual( - @$"DELETE FROM {t("cmsContentNu")} WHERE {c("nodeId")} IN (SELECT {c("nodeId")} FROM (SELECT DISTINCT cmsContentNu.nodeId FROM {t("cmsContentNu")} INNER JOIN {t("umbracoNode")} ON {t("cmsContentNu")}.{c("nodeId")} = {t("umbracoNode")}.{c("id")} WHERE (({t("umbracoNode")}.{c("nodeObjectType")} = @0))) x)".Replace(Environment.NewLine, " ") - .Replace("\n", " ").Replace("\r", " "), - sqlOutput.SQL.Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " ")); + string n(string x) + { + return SqlContext.SqlSyntax.GetQuotedName(x); + } + var expectedSql = @$"DELETE FROM {t("cmsContentNu")} WHERE {c("nodeId")} IN (SELECT {c("nodeId")} FROM (SELECT DISTINCT {t("cmsContentNu")}.{c("nodeId")} AS {n("NodeId")} FROM {t("cmsContentNu")} INNER JOIN {t("umbracoNode")} ON {t("cmsContentNu")}.{c("nodeId")} = {t("umbracoNode")}.{c("id")} WHERE (({t("umbracoNode")}.{c("nodeObjectType")} = @0))) x)".Replace(Environment.NewLine, " ") + .Replace("\n", " ").Replace("\r", " "); + var sqlOutputSql = sqlOutput.SQL.Replace(Environment.NewLine, " ").Replace("\n", " ").Replace("\r", " "); + + Assert.AreEqual(expectedSql, sqlOutputSql); Assert.AreEqual(1, sqlOutput.Arguments.Length); Assert.AreEqual(mediaObjectType, sqlOutput.Arguments[0]); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs index 62306f4175..6e19ff735e 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Persistence/Querying/ContentTypeRepositorySqlClausesTest.cs @@ -188,12 +188,12 @@ public class ContentTypeRepositorySqlClausesTest : BaseUsingSqlSyntax string expectedSQL = @"DELETE FROM [umbracoUserGroup2GranularPermission] WHERE (([umbracoUserGroup2GranularPermission].[uniqueId] = @0)) -AND ([umbracoUserGroup2GranularPermission].[permission] LIKE CONCAT((SELECT - CONVERT(nvarchar(36), [cmsPropertyType].[UniqueID]) +AND ([umbracoUserGroup2GranularPermission].[permission] LIKE CONCAT(((SELECT + CONVERT(nvarchar(36), [cmsPropertyType].[UniqueId]) FROM [cmsPropertyType] WHERE (([cmsPropertyType].[id] = @1)) -),'|%'))".Replace("\r", string.Empty); +)),'|%'))".Replace("\r", string.Empty); var typedSql = sql.SQL; Assert.That(typedSql, Is.EqualTo(expectedSQL)); }