diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbType.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbType.cs new file mode 100644 index 0000000000..00be5c51ab --- /dev/null +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbType.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations +{ + /// + /// Allows for specifying custom DB types that are not natively mapped. + /// + public struct SpecialDbType : IEquatable + { + private readonly string _dbType; + + public SpecialDbType(string dbType) + { + if (string.IsNullOrWhiteSpace(dbType)) + { + throw new ArgumentException($"'{nameof(dbType)}' cannot be null or whitespace.", nameof(dbType)); + } + + _dbType = dbType; + } + + public SpecialDbType(SpecialDbTypes specialDbTypes) + => _dbType = specialDbTypes.ToString(); + + public static SpecialDbType NTEXT { get; } = new SpecialDbType(SpecialDbTypes.NTEXT); + public static SpecialDbType NCHAR { get; } = new SpecialDbType(SpecialDbTypes.NCHAR); + public static SpecialDbType NVARCHARMAX { get; } = new SpecialDbType(SpecialDbTypes.NVARCHARMAX); + + public override bool Equals(object obj) => obj is SpecialDbType types && Equals(types); + public bool Equals(SpecialDbType other) => _dbType == other._dbType; + public override int GetHashCode() => 1038481724 + EqualityComparer.Default.GetHashCode(_dbType); + + public override string ToString() => _dbType.ToString(); + + // Make this directly castable to string + public static implicit operator string(SpecialDbType dbType) => dbType.ToString(); + + // direct equality operators with SpecialDbTypes enum + public static bool operator ==(SpecialDbTypes x, SpecialDbType y) => x.ToString() == y; + public static bool operator !=(SpecialDbTypes x, SpecialDbType y) => x.ToString() != y; + } +} diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypeAttribute.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypeAttribute.cs index 158a7ccb9b..d7fd2ff34f 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypeAttribute.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypeAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations { @@ -12,13 +12,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations public class SpecialDbTypeAttribute : Attribute { public SpecialDbTypeAttribute(SpecialDbTypes databaseType) - { - DatabaseType = databaseType; - } + => DatabaseType = new SpecialDbType(databaseType); + + public SpecialDbTypeAttribute(string databaseType) + => DatabaseType = new SpecialDbType(databaseType); /// - /// Gets or sets the for this column + /// Gets or sets the for this column /// - public SpecialDbTypes DatabaseType { get; private set; } + public SpecialDbType DatabaseType { get; private set; } } } diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypes.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypes.cs index 9d07395743..d867d6f682 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypes.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseAnnotations/SpecialDbTypes.cs @@ -1,13 +1,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations { /// - /// Enum with the two special types that has to be supported because - /// of the current umbraco db schema. + /// Known special DB types required for Umbraco. /// public enum SpecialDbTypes { NTEXT, NCHAR, - NVARCHARMAX + NVARCHARMAX, } } diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/ColumnDefinition.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/ColumnDefinition.cs index 2c22863ae5..dee560a40d 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/ColumnDefinition.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/ColumnDefinition.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Data; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; @@ -12,9 +12,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions //When DbType isn't set explicitly the Type will be used to find the right DbType in the SqlSyntaxProvider. //This type is typically used as part of an initial table creation public Type PropertyType { get; set; } - //Only used for special cases as part of an initial table creation - public bool HasSpecialDbType { get; set; } - public SpecialDbTypes DbType { get; set; } + + /// + /// Used for column types that cannot be natively mapped. + /// + public SpecialDbType? CustomDbType { get; set; } + public virtual int Seeding { get; set; } public virtual int Size { get; set; } public virtual int Precision { get; set; } diff --git a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs index 407672c995..34ad767b04 100644 --- a/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/DatabaseModelDefinitions/DefinitionFactory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using NPoco; @@ -75,8 +75,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions var databaseTypeAttribute = propertyInfo.FirstAttribute(); if (databaseTypeAttribute != null) { - definition.HasSpecialDbType = true; - definition.DbType = databaseTypeAttribute.DatabaseType; + definition.CustomDbType = databaseTypeAttribute.DatabaseType; } else { diff --git a/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs index 797400b7cc..942368f5cb 100644 --- a/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs @@ -24,15 +24,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence IEnumerable providerSpecificMapperFactories) { _getFactory = getFactory; - _embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x=>x.ProviderName); - _syntaxProviders = syntaxProviders.ToDictionary(x=>x.ProviderName); - _bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x=>x.ProviderName); - _providerSpecificMapperFactories = providerSpecificMapperFactories.ToDictionary(x=>x.ProviderName); + _embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x => x.ProviderName); + _syntaxProviders = syntaxProviders.ToDictionary(x => x.ProviderName); + _bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x => x.ProviderName); + _providerSpecificMapperFactories = providerSpecificMapperFactories.ToDictionary(x => x.ProviderName); } public DbProviderFactory CreateFactory(string providerName) { - if (string.IsNullOrEmpty(providerName)) return null; + if (string.IsNullOrEmpty(providerName)) + return null; return _getFactory(providerName); } @@ -40,7 +41,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence public ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName) { - if(!_syntaxProviders.TryGetValue(providerName, out var result)) + if (!_syntaxProviders.TryGetValue(providerName, out var result)) { throw new InvalidOperationException($"Unknown provider name \"{providerName}\""); } @@ -51,7 +52,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence public IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName) { - if(!_bulkSqlInsertProviders.TryGetValue(providerName, out var result)) + if (!_bulkSqlInsertProviders.TryGetValue(providerName, out var result)) { return new BasicBulkSqlInsertProvider(); } @@ -61,7 +62,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence public void CreateDatabase(string providerName) { - if(_embeddedDatabaseCreators.TryGetValue(providerName, out var creator)) + if (_embeddedDatabaseCreators.TryGetValue(providerName, out var creator)) { creator.Create(); } @@ -69,7 +70,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence public NPocoMapperCollection ProviderSpecificMappers(string providerName) { - if(_providerSpecificMapperFactories.TryGetValue(providerName, out var mapperFactory)) + if (_providerSpecificMapperFactories.TryGetValue(providerName, out var mapperFactory)) { return mapperFactory.Mappers; } diff --git a/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs b/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs index 71e22a4837..71e249a836 100644 --- a/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs +++ b/src/Umbraco.Infrastructure/Persistence/PocoDataDataReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -6,6 +6,7 @@ using NPoco; using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; +using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence { @@ -68,22 +69,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence foreach (var col in _columnDefinitions) { SqlDbType sqlDbType; - if (col.HasSpecialDbType) + if (col.CustomDbType.HasValue) { //get the SqlDbType from the 'special type' - switch (col.DbType) + switch (col.CustomDbType) { - case SpecialDbTypes.NTEXT: + case var x when x == SpecialDbType.NTEXT: sqlDbType = SqlDbType.NText; break; - case SpecialDbTypes.NCHAR: + case var x when x == SpecialDbType.NCHAR: sqlDbType = SqlDbType.NChar; break; - case SpecialDbTypes.NVARCHARMAX: + case var x when x == SpecialDbType.NVARCHARMAX: sqlDbType = SqlDbType.NVarChar; break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException("The custom DB type " + col.CustomDbType + " is not supported for bulk import statements."); } } else if (col.Type.HasValue) diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs index 6c551648b7..75d348df1a 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/ISqlSyntaxProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using System.Text.RegularExpressions; @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax string GetQuotedName(string name); bool DoesTableExist(IDatabase db, string tableName); string GetIndexType(IndexTypes indexTypes); - string GetSpecialDbType(SpecialDbTypes dbTypes); + string GetSpecialDbType(SpecialDbType dbType); string CreateTable { get; } string DropTable { get; } string AddColumn { get; } diff --git a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 24548fd36b..20cbb689c1 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -54,38 +54,36 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax public Regex AliasRegex { get; } - public string GetWildcardPlaceholder() - { - return "%"; - } + public string GetWildcardPlaceholder() => "%"; - public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})"; - public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})"; - public string DecimalColumnDefinitionFormat = "DECIMAL({0},{1})"; + public string StringLengthNonUnicodeColumnDefinitionFormat { get; } = "VARCHAR({0})"; + public string StringLengthUnicodeColumnDefinitionFormat { get; } = "NVARCHAR({0})"; + public string DecimalColumnDefinitionFormat { get; } = "DECIMAL({0},{1})"; - public string DefaultValueFormat = "DEFAULT ({0})"; - public int DefaultStringLength = 255; - public int DefaultDecimalPrecision = 20; - public int DefaultDecimalScale = 9; + public string DefaultValueFormat { get; } = "DEFAULT ({0})"; + public int DefaultStringLength { get; } = 255; + public int DefaultDecimalPrecision { get; } = 20; + public int DefaultDecimalScale { get; } = 9; //Set by Constructor - public string StringColumnDefinition; - public string StringLengthColumnDefinitionFormat; + public string StringColumnDefinition { get; } + public string StringLengthColumnDefinitionFormat { get; } - public string AutoIncrementDefinition = "AUTOINCREMENT"; - public string IntColumnDefinition = "INTEGER"; - public string LongColumnDefinition = "BIGINT"; - public string GuidColumnDefinition = "GUID"; - public string BoolColumnDefinition = "BOOL"; - public string RealColumnDefinition = "DOUBLE"; - public string DecimalColumnDefinition; - public string BlobColumnDefinition = "BLOB"; - public string DateTimeColumnDefinition = "DATETIME"; - public string TimeColumnDefinition = "DATETIME"; + public string AutoIncrementDefinition { get; protected set; } = "AUTOINCREMENT"; + public string IntColumnDefinition { get; } = "INTEGER"; + public string LongColumnDefinition { get; } = "BIGINT"; + public string GuidColumnDefinition { get; protected set; } = "GUID"; + public string BoolColumnDefinition { get; protected set; } = "BOOL"; + public string RealColumnDefinition { get; protected set; } = "DOUBLE"; + public string DecimalColumnDefinition { get; protected set; } + public string BlobColumnDefinition { get; protected set; } = "BLOB"; + public string DateTimeColumnDefinition { get; } = "DATETIME"; + public string TimeColumnDefinition { get; protected set; } = "DATETIME"; protected IList> ClauseOrder { get; } - protected DbTypes DbTypeMap = new DbTypes(); + protected DbTypes DbTypeMap { get; } = new DbTypes(); + protected void InitColumnTypeMap() { DbTypeMap.Set(DbType.String, StringColumnDefinition); @@ -100,8 +98,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax DbTypeMap.Set(DbType.DateTime, DateTimeColumnDefinition); DbTypeMap.Set(DbType.Time, TimeColumnDefinition); DbTypeMap.Set(DbType.Time, TimeColumnDefinition); - DbTypeMap.Set(DbType.Time, TimeColumnDefinition); - DbTypeMap.Set(DbType.Time, TimeColumnDefinition); + DbTypeMap.Set(DbType.DateTimeOffset, TimeColumnDefinition); + DbTypeMap.Set(DbType.DateTimeOffset, TimeColumnDefinition); DbTypeMap.Set(DbType.Byte, IntColumnDefinition); DbTypeMap.Set(DbType.Byte, IntColumnDefinition); @@ -193,17 +191,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax return indexType; } - public virtual string GetSpecialDbType(SpecialDbTypes dbTypes) + public virtual string GetSpecialDbType(SpecialDbType dbType) { - if (dbTypes == SpecialDbTypes.NCHAR) + if (dbType == SpecialDbType.NCHAR) { - return "NCHAR"; + return SpecialDbType.NCHAR; } - else if (dbTypes == SpecialDbTypes.NTEXT) + else if (dbType == SpecialDbType.NTEXT) { - return "NTEXT"; + return SpecialDbType.NTEXT; } - else if (dbTypes == SpecialDbTypes.NVARCHARMAX) + else if (dbType == SpecialDbType.NVARCHARMAX) { return "NVARCHAR(MAX)"; } @@ -470,14 +468,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax if (column.Type.HasValue == false && string.IsNullOrEmpty(column.CustomType) == false) return column.CustomType; - if (column.HasSpecialDbType) + if (column.CustomDbType.HasValue) { - if (column.Size != default(int)) + if (column.Size != default) { - return $"{GetSpecialDbType(column.DbType)}({column.Size})"; + return $"{GetSpecialDbType(column.CustomDbType.Value)}({column.Size})"; } - return GetSpecialDbType(column.DbType); + return GetSpecialDbType(column.CustomDbType.Value); } var type = column.Type.HasValue diff --git a/src/Umbraco.Persistence.SqlCe/SqlCeSyntaxProvider.cs b/src/Umbraco.Persistence.SqlCe/SqlCeSyntaxProvider.cs index e81b6135da..274c585fa2 100644 --- a/src/Umbraco.Persistence.SqlCe/SqlCeSyntaxProvider.cs +++ b/src/Umbraco.Persistence.SqlCe/SqlCeSyntaxProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -300,10 +300,14 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault() GetQuotedTableName(index.TableName), columns); } - public override string GetSpecialDbType(SpecialDbTypes dbTypes) + public override string GetSpecialDbType(SpecialDbType dbTypes) { - if (dbTypes == SpecialDbTypes.NVARCHARMAX) // SqlCE does not have nvarchar(max) for now + // SqlCE does not have nvarchar(max) for now + if (dbTypes == SpecialDbType.NVARCHARMAX) + { return "NTEXT"; + } + return base.GetSpecialDbType(dbTypes); } public override SqlDbType GetSqlDbType(DbType dbType)