diff --git a/src/DapperMatic/DataAnnotations/DxColumnAttribute.cs b/src/DapperMatic/DataAnnotations/DxColumnAttribute.cs
index 967ff7e..f60af8b 100644
--- a/src/DapperMatic/DataAnnotations/DxColumnAttribute.cs
+++ b/src/DapperMatic/DataAnnotations/DxColumnAttribute.cs
@@ -45,6 +45,10 @@ public DxColumnAttribute(
}
public string? ColumnName { get; }
+
+ ///
+ /// /// Format of provider data types: {mysql:varchar(255),sqlserver:nvarchar(255)}
+ ///
public string? ProviderDataType { get; }
public int? Length { get; }
public int? Precision { get; }
diff --git a/src/DapperMatic/DataAnnotations/DxPrimaryKeyConstraintAttribute.cs b/src/DapperMatic/DataAnnotations/DxPrimaryKeyConstraintAttribute.cs
index b088004..ccf81cb 100644
--- a/src/DapperMatic/DataAnnotations/DxPrimaryKeyConstraintAttribute.cs
+++ b/src/DapperMatic/DataAnnotations/DxPrimaryKeyConstraintAttribute.cs
@@ -2,10 +2,7 @@
namespace DapperMatic.DataAnnotations;
-[AttributeUsage(
- AttributeTargets.Property | AttributeTargets.Class,
- AllowMultiple = true
-)]
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = true)]
public class DxPrimaryKeyConstraintAttribute : Attribute
{
public DxPrimaryKeyConstraintAttribute() { }
@@ -21,6 +18,11 @@ public DxPrimaryKeyConstraintAttribute(string constraintName, params string[] co
Columns = columnNames.Select(columnName => new DxOrderedColumn(columnName)).ToArray();
}
+ public DxPrimaryKeyConstraintAttribute(string[] columnNames)
+ {
+ Columns = columnNames.Select(columnName => new DxOrderedColumn(columnName)).ToArray();
+ }
+
public string? ConstraintName { get; }
public DxOrderedColumn[]? Columns { get; }
}
diff --git a/src/DapperMatic/DbConnectionExtensions.cs b/src/DapperMatic/DbConnectionExtensions.cs
index 235ff23..5a37cdc 100644
--- a/src/DapperMatic/DbConnectionExtensions.cs
+++ b/src/DapperMatic/DbConnectionExtensions.cs
@@ -30,12 +30,15 @@ public static async Task GetDatabaseVersionAsync(
return await Database(db).GetDatabaseVersionAsync(db, tx, cancellationToken);
}
- public static IProviderTypeMap GetProviderTypeMap(this IDbConnection db)
+ public static IDbProviderTypeMap GetProviderTypeMap(this IDbConnection db)
{
return Database(db).ProviderTypeMap;
}
- public static (Type dotnetType, int? length, int? precision, int? scale, bool? autoIncrementing, Type[] allSupportedTypes) GetDotnetTypeFromSqlType(this IDbConnection db, string sqlType)
+ public static DbProviderDotnetTypeDescriptor GetDotnetTypeFromSqlType(
+ this IDbConnection db,
+ string sqlType
+ )
{
return Database(db).GetDotnetTypeFromSqlType(sqlType);
}
diff --git a/src/DapperMatic/DbProviderSqlType.cs b/src/DapperMatic/DbProviderSqlType.cs
new file mode 100644
index 0000000..78575fe
--- /dev/null
+++ b/src/DapperMatic/DbProviderSqlType.cs
@@ -0,0 +1,74 @@
+namespace DapperMatic.Providers;
+
+///
+/// The provider SQL type.
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+///
+public class DbProviderSqlType(
+ DbProviderSqlTypeAffinity affinity,
+ string name,
+ Type? recommendedDotnetType = null,
+ string? aliasOf = null,
+ string? formatWithLength = null,
+ string? formatWithPrecision = null,
+ string? formatWithPrecisionAndScale = null,
+ int? defaultLength = null,
+ int? defaultPrecision = null,
+ int? defaultScale = null,
+ bool canUseToAutoIncrement = false,
+ bool autoIncrementsAutomatically = false,
+ double? minValue = null,
+ double? maxValue = null,
+ bool includesTimeZone = false,
+ bool isDateOnly = false,
+ bool isTimeOnly = false,
+ bool isYearOnly = false,
+ bool isFixedLength = false,
+ bool isGuidOnly = false,
+ bool isUnicode = false
+)
+{
+ public DbProviderSqlTypeAffinity Affinity { get; init; } = affinity;
+ public string Name { get; init; } = name;
+ public Type? RecommendedDotnetType { get; init; } = recommendedDotnetType;
+ public string? AliasOf { get; set; } = aliasOf;
+ public string? FormatWithLength { get; init; } = formatWithLength;
+ public string? FormatWithPrecision { get; init; } = formatWithPrecision;
+ public string? FormatWithPrecisionAndScale { get; init; } = formatWithPrecisionAndScale;
+ public int? DefaultLength { get; set; } = defaultLength;
+ public int? DefaultPrecision { get; set; } = defaultPrecision;
+ public int? DefaultScale { get; set; } = defaultScale;
+ public bool CanUseToAutoIncrement { get; init; } = canUseToAutoIncrement;
+ public bool AutoIncrementsAutomatically { get; init; } = autoIncrementsAutomatically;
+ public double? MinValue { get; init; } = minValue;
+ public double? MaxValue { get; init; } = maxValue;
+ public bool IncludesTimeZone { get; init; } = includesTimeZone;
+ public bool IsDateOnly { get; init; } = isDateOnly;
+ public bool IsTimeOnly { get; init; } = isTimeOnly;
+ public bool IsYearOnly { get; init; } = isYearOnly;
+ public bool IsFixedLength { get; init; } = isFixedLength;
+ public bool IsGuidOnly { get; init; } = isGuidOnly;
+ public bool IsUnicode { get; set; } = isUnicode;
+}
+
+public static class DbProviderSqlTypeExtensions
+{
+ public static bool SupportsLength(this DbProviderSqlType providerSqlType) =>
+ !string.IsNullOrWhiteSpace(providerSqlType.FormatWithLength);
+
+ public static bool SupportsPrecision(this DbProviderSqlType providerSqlType) =>
+ !string.IsNullOrWhiteSpace(providerSqlType.FormatWithPrecision);
+
+ public static bool SupportsPrecisionAndScale(this DbProviderSqlType providerSqlType) =>
+ !string.IsNullOrWhiteSpace(providerSqlType.FormatWithPrecisionAndScale);
+}
diff --git a/src/DapperMatic/Providers/ProviderSqlTypeAffinity.cs b/src/DapperMatic/DbProviderSqlTypeAffinity.cs
similarity index 60%
rename from src/DapperMatic/Providers/ProviderSqlTypeAffinity.cs
rename to src/DapperMatic/DbProviderSqlTypeAffinity.cs
index 6b063cd..d4c9802 100644
--- a/src/DapperMatic/Providers/ProviderSqlTypeAffinity.cs
+++ b/src/DapperMatic/DbProviderSqlTypeAffinity.cs
@@ -1,6 +1,6 @@
-namespace DapperMatic.Providers;
+namespace DapperMatic;
-public enum ProviderSqlTypeAffinity
+public enum DbProviderSqlTypeAffinity
{
Integer,
Real,
@@ -11,4 +11,4 @@ public enum ProviderSqlTypeAffinity
Geometry,
RangeType,
Other
-}
\ No newline at end of file
+}
diff --git a/src/DapperMatic/DbProviderType.cs b/src/DapperMatic/DbProviderType.cs
index 0e6e4cc..8f41292 100644
--- a/src/DapperMatic/DbProviderType.cs
+++ b/src/DapperMatic/DbProviderType.cs
@@ -1,3 +1,6 @@
+using System.Collections.Concurrent;
+using System.Data;
+
namespace DapperMatic;
public enum DbProviderType
@@ -7,3 +10,51 @@ public enum DbProviderType
MySql,
PostgreSql
}
+
+public static class DbProviderTypeExtensions
+{
+ private static readonly ConcurrentDictionary ProviderTypes = new();
+
+ public static DbProviderType GetDbProviderType(this IDbConnection db)
+ {
+ var type = db.GetType();
+ if (ProviderTypes.TryGetValue(type, out var dbType))
+ {
+ return dbType;
+ }
+
+ dbType = ToDbProviderType(type.FullName!);
+ ProviderTypes.TryAdd(type, dbType);
+
+ return dbType;
+ }
+
+ private static DbProviderType ToDbProviderType(string provider)
+ {
+ if (provider.Contains("sqlite", StringComparison.OrdinalIgnoreCase))
+ return DbProviderType.Sqlite;
+
+ if (
+ provider.Contains("mysql", StringComparison.OrdinalIgnoreCase)
+ || provider.Contains("maria", StringComparison.OrdinalIgnoreCase)
+ )
+ return DbProviderType.MySql;
+
+ if (
+ provider.Contains("postgres", StringComparison.OrdinalIgnoreCase)
+ || provider.Contains("npgsql", StringComparison.OrdinalIgnoreCase)
+ || provider.Contains("pg", StringComparison.OrdinalIgnoreCase)
+ )
+ return DbProviderType.PostgreSql;
+
+ if (
+ provider.Contains("sqlserver", StringComparison.OrdinalIgnoreCase)
+ || provider.Contains("mssql", StringComparison.OrdinalIgnoreCase)
+ || provider.Contains("localdb", StringComparison.OrdinalIgnoreCase)
+ || provider.Contains("sqlclient", StringComparison.OrdinalIgnoreCase)
+ )
+ return DbProviderType.SqlServer;
+
+ throw new NotSupportedException($"Db type {provider} is not supported.");
+ }
+}
diff --git a/src/DapperMatic/DbProviderTypeExtensions.cs b/src/DapperMatic/DbProviderTypeExtensions.cs
deleted file mode 100644
index 3b4cc4c..0000000
--- a/src/DapperMatic/DbProviderTypeExtensions.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System.Collections.Concurrent;
-using System.Data;
-
-namespace DapperMatic;
-
-public static class DbProviderTypeExtensions
-{
- private static readonly ConcurrentDictionary ProviderTypes = new();
-
- public static DbProviderType GetDbProviderType(this IDbConnection db)
- {
- var type = db.GetType();
- if (ProviderTypes.TryGetValue(type, out var dbType))
- {
- return dbType;
- }
-
- dbType = ToDbProviderType(type.FullName!);
- ProviderTypes.TryAdd(type, dbType);
-
- return dbType;
- }
-
- private static DbProviderType ToDbProviderType(string provider)
- {
- if (
- string.IsNullOrWhiteSpace(provider)
- || provider.Contains("sqlite", StringComparison.OrdinalIgnoreCase)
- )
- return DbProviderType.Sqlite;
-
- if (
- provider.Contains("mysql", StringComparison.OrdinalIgnoreCase)
- || provider.Contains("maria", StringComparison.OrdinalIgnoreCase)
- )
- return DbProviderType.MySql;
-
- if (
- provider.Contains("postgres", StringComparison.OrdinalIgnoreCase)
- || provider.Contains("npgsql", StringComparison.OrdinalIgnoreCase)
- || provider.Contains("pg", StringComparison.OrdinalIgnoreCase)
- )
- return DbProviderType.PostgreSql;
-
- if (
- provider.Contains("sqlserver", StringComparison.OrdinalIgnoreCase)
- || provider.Contains("mssql", StringComparison.OrdinalIgnoreCase)
- || provider.Contains("localdb", StringComparison.OrdinalIgnoreCase)
- || provider.Contains("sqlclient", StringComparison.OrdinalIgnoreCase)
- )
- return DbProviderType.SqlServer;
-
- throw new NotSupportedException($"Cache type {provider} is not supported.");
- }
-}
diff --git a/src/DapperMatic/ExtensionMethods.cs b/src/DapperMatic/ExtensionMethods.cs
index 2f7cdc6..b5afe8f 100644
--- a/src/DapperMatic/ExtensionMethods.cs
+++ b/src/DapperMatic/ExtensionMethods.cs
@@ -9,6 +9,24 @@ namespace DapperMatic;
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public static partial class ExtensionMethods
{
+ public static string GetFriendlyName(this Type type)
+ {
+ if (type == null)
+ return "(Unknown Type)";
+
+ if (!type.IsGenericType)
+ return type.Name;
+
+ var genericTypeName = type.GetGenericTypeDefinition().Name;
+ var friendlyGenericTypeName = genericTypeName[..genericTypeName.LastIndexOf("`")];
+
+ var genericArguments = type.GetGenericArguments();
+ var genericArgumentNames = genericArguments.Select(GetFriendlyName).ToArray();
+ var genericTypeArgumentsString = string.Join(", ", genericArgumentNames);
+
+ return $"{friendlyGenericTypeName}<{genericTypeArgumentsString}>";
+ }
+
public static TValue? GetFieldValue(this object instance, string name)
{
var type = instance.GetType();
@@ -72,6 +90,22 @@ public static int[] ExtractNumbers(this string input)
return [.. numbers];
}
+ public static string DiscardLengthPrecisionAndScaleFromSqlTypeName(this string sqlTypeName)
+ {
+ // extract the type name from the sql type name where a sqlTypeName might be "time(5, 2) without time zone" and the return value would be "time without time zone",
+ // it could also be "time ( 122, 2 ) without time zone" and the return value would be "time without time zone
+ var openIndex = sqlTypeName.IndexOf('(');
+ var closeIndex = sqlTypeName.IndexOf(')');
+ var txt = (
+ openIndex > 0 && closeIndex > 0
+ ? sqlTypeName.Remove(openIndex, closeIndex - openIndex + 1)
+ : sqlTypeName
+ ).Trim();
+ while (txt.Contains(" "))
+ txt = txt.Replace(" ", " ");
+ return txt;
+ }
+
public static string ToQuotedIdentifier(
this string prefix,
char[] quoteChar,
diff --git a/src/DapperMatic/Interfaces/IDatabaseMethods.cs b/src/DapperMatic/Interfaces/IDatabaseMethods.cs
index af263a8..c7a3154 100644
--- a/src/DapperMatic/Interfaces/IDatabaseMethods.cs
+++ b/src/DapperMatic/Interfaces/IDatabaseMethods.cs
@@ -16,7 +16,7 @@ public interface IDatabaseMethods
IDatabaseViewMethods
{
DbProviderType ProviderType { get; }
- IProviderTypeMap ProviderTypeMap { get; }
+ IDbProviderTypeMap ProviderTypeMap { get; }
bool SupportsSchemas { get; }
@@ -39,9 +39,8 @@ Task GetDatabaseVersionAsync(
CancellationToken cancellationToken = default
);
- (Type dotnetType, int? length, int? precision, int? scale, bool? isAutoIncrementing, Type[] allSupportedTypes)
- GetDotnetTypeFromSqlType(string sqlType);
- string GetSqlTypeFromDotnetType(Type type, int? length, int? precision, int? scale, bool? autoIncrementing);
+ DbProviderDotnetTypeDescriptor GetDotnetTypeFromSqlType(string sqlType);
+ string GetSqlTypeFromDotnetType(DbProviderDotnetTypeDescriptor descriptor);
string NormalizeName(string name);
}
diff --git a/src/DapperMatic/Models/DxColumn.cs b/src/DapperMatic/Models/DxColumn.cs
index 7ecbc0d..ee47f5d 100644
--- a/src/DapperMatic/Models/DxColumn.cs
+++ b/src/DapperMatic/Models/DxColumn.cs
@@ -1,4 +1,6 @@
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Text.Json;
namespace DapperMatic.Models;
@@ -16,7 +18,7 @@ public DxColumn(
string tableName,
string columnName,
Type dotnetType,
- string? providerDataType = null,
+ Dictionary? providerDataTypes = null,
int? length = null,
int? precision = null,
int? scale = null,
@@ -38,13 +40,8 @@ public DxColumn(
TableName = tableName;
ColumnName = columnName;
DotnetType = dotnetType;
- ProviderDataType = providerDataType;
- Length =
- dotnetType == typeof(string)
- && string.IsNullOrWhiteSpace(providerDataType)
- && !length.HasValue
- ? 255 /* a sensible default */
- : length;
+ ProviderDataTypes = providerDataTypes ?? [];
+ Length = length;
Precision = precision;
Scale = scale;
CheckExpression = checkExpression;
@@ -73,7 +70,7 @@ public DxColumn(
///
/// The provider data type should include the length, precision, and scale if applicable.
///
- public string? ProviderDataType { get; set; }
+ public Dictionary ProviderDataTypes { get; } = new();
public int? Length { get; set; }
public int? Precision { get; set; }
public int? Scale { get; set; }
@@ -82,6 +79,7 @@ public DxColumn(
public bool IsNullable { get; set; }
public bool IsPrimaryKey { get; set; }
public bool IsAutoIncrement { get; set; }
+ public bool IsUnicode { get; set; }
///
/// Is either part of a single column unique constraint or a single column unique index.
@@ -193,7 +191,7 @@ public string GetTypeCategory()
// ToString override to display column definition
public override string ToString()
{
- return $"{ColumnName} ({ProviderDataType}) {(IsNullable ? "NULL" : "NOT NULL")}"
+ return $"{ColumnName} ({JsonSerializer.Serialize(ProviderDataTypes)}) {(IsNullable ? "NULL" : "NOT NULL")}"
+ $"{(IsPrimaryKey ? " PRIMARY KEY" : "")}"
+ $"{(IsUnique ? " UNIQUE" : "")}"
+ $"{(IsIndexed ? " INDEXED" : "")}"
@@ -202,4 +200,17 @@ public override string ToString()
+ $"{(!string.IsNullOrWhiteSpace(CheckExpression) ? $" CHECK ({CheckExpression})" : "")}"
+ $"{(!string.IsNullOrWhiteSpace(DefaultExpression) ? $" DEFAULT {(DefaultExpression.Contains(' ') ? $"({DefaultExpression})" : DefaultExpression)}" : "")}";
}
+
+ public string? GetProviderDataType(DbProviderType providerType)
+ {
+ return ProviderDataTypes.TryGetValue(providerType, out var providerDataType)
+ ? providerDataType
+ : null;
+ }
+
+ public DxColumn SetProviderDataType(DbProviderType providerType, string providerDataType)
+ {
+ ProviderDataTypes[providerType] = providerDataType;
+ return this;
+ }
}
diff --git a/src/DapperMatic/Models/DxTableFactory.cs b/src/DapperMatic/Models/DxTableFactory.cs
index d9b578d..fb4fec7 100644
--- a/src/DapperMatic/Models/DxTableFactory.cs
+++ b/src/DapperMatic/Models/DxTableFactory.cs
@@ -1,5 +1,4 @@
using System.Collections.Concurrent;
-using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Reflection;
using DapperMatic.DataAnnotations;
@@ -188,7 +187,7 @@ ca is System.ComponentModel.DataAnnotations.Schema.TableAttribute ta
var paType = pa.GetType();
return pa is DxPrimaryKeyConstraintAttribute
// EF Core
- || pa is KeyAttribute
+ || pa is System.ComponentModel.DataAnnotations.KeyAttribute
// ServiceStack.OrmLite
|| paType.Name == "PrimaryKeyAttribute";
});
@@ -203,12 +202,57 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
|| paType.Name == "RequiredAttribute";
});
+ // Format of provider data types: {mysql:varchar(255),sqlserver:nvarchar(255)}
+ var providerDataTypes = new Dictionary();
+ if (!string.IsNullOrWhiteSpace(columnAttribute?.ProviderDataType))
+ {
+ var pdts = columnAttribute
+ .ProviderDataType.Trim('{', '}', '[', ']', ' ')
+ .Split([',', ';']);
+ foreach (var pdt in pdts)
+ {
+ var pdtParts = pdt.Split(':');
+ if (
+ pdtParts.Length == 2
+ && !string.IsNullOrWhiteSpace(pdtParts[0])
+ && !string.IsNullOrWhiteSpace(pdtParts[1])
+ )
+ {
+ if (
+ pdtParts[0].Contains("mysql", StringComparison.OrdinalIgnoreCase)
+ || pdtParts[0].Contains("maria", StringComparison.OrdinalIgnoreCase)
+ )
+ {
+ providerDataTypes.Add(DbProviderType.MySql, pdtParts[1]);
+ }
+ else if (
+ pdtParts[0].Contains("pg", StringComparison.OrdinalIgnoreCase)
+ || pdtParts[0].Contains("postgres", StringComparison.OrdinalIgnoreCase)
+ )
+ {
+ providerDataTypes.Add(DbProviderType.PostgreSql, pdtParts[1]);
+ }
+ else if (pdtParts[0].Contains("sqlite", StringComparison.OrdinalIgnoreCase))
+ {
+ providerDataTypes.Add(DbProviderType.Sqlite, pdtParts[1]);
+ }
+ else if (
+ pdtParts[0].Contains("sqlserver", StringComparison.OrdinalIgnoreCase)
+ || pdtParts[0].Contains("mssql", StringComparison.OrdinalIgnoreCase)
+ )
+ {
+ providerDataTypes.Add(DbProviderType.SqlServer, pdtParts[1]);
+ }
+ }
+ }
+ }
+
var column = new DxColumn(
schemaName,
tableName,
columnName,
property.PropertyType,
- columnAttribute?.ProviderDataType,
+ providerDataTypes.Count != 0 ? providerDataTypes : null,
columnAttribute?.Length,
columnAttribute?.Precision,
columnAttribute?.Scale,
@@ -238,14 +282,16 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
if (column.Length == null)
{
- var stringLengthAttribute = property.GetCustomAttribute();
+ var stringLengthAttribute =
+ property.GetCustomAttribute();
if (stringLengthAttribute != null)
{
column.Length = stringLengthAttribute.MaximumLength;
}
else
{
- var maxLengthAttribute = property.GetCustomAttribute();
+ var maxLengthAttribute =
+ property.GetCustomAttribute();
if (maxLengthAttribute != null)
{
column.Length = maxLengthAttribute.Length;
@@ -314,7 +360,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
columnName,
!string.IsNullOrWhiteSpace(columnCheckConstraintAttribute.ConstraintName)
? columnCheckConstraintAttribute.ConstraintName
- : ProviderUtils.GenerateCheckConstraintName(tableName, columnName),
+ : DbProviderUtils.GenerateCheckConstraintName(tableName, columnName),
columnCheckConstraintAttribute.Expression
);
checkConstraints.Add(checkConstraint);
@@ -333,7 +379,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
columnName,
!string.IsNullOrWhiteSpace(columnDefaultConstraintAttribute.ConstraintName)
? columnDefaultConstraintAttribute.ConstraintName
- : ProviderUtils.GenerateDefaultConstraintName(tableName, columnName),
+ : DbProviderUtils.GenerateDefaultConstraintName(tableName, columnName),
columnDefaultConstraintAttribute.Expression
);
defaultConstraints.Add(defaultConstraint);
@@ -351,7 +397,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
tableName,
!string.IsNullOrWhiteSpace(columnUniqueConstraintAttribute.ConstraintName)
? columnUniqueConstraintAttribute.ConstraintName
- : ProviderUtils.GenerateUniqueConstraintName(tableName, columnName),
+ : DbProviderUtils.GenerateUniqueConstraintName(tableName, columnName),
[new(columnName)]
);
uniqueConstraints.Add(uniqueConstraint);
@@ -368,7 +414,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
tableName,
!string.IsNullOrWhiteSpace(columnIndexAttribute.IndexName)
? columnIndexAttribute.IndexName
- : ProviderUtils.GenerateIndexName(tableName, columnName),
+ : DbProviderUtils.GenerateIndexName(tableName, columnName),
[new(columnName)],
isUnique: columnIndexAttribute.IsUnique
);
@@ -393,7 +439,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
&& !string.IsNullOrWhiteSpace(n)
)
? n
- : ProviderUtils.GenerateIndexName(tableName, columnName);
+ : DbProviderUtils.GenerateIndexName(tableName, columnName);
var index = new DxIndex(
schemaName,
tableName,
@@ -430,7 +476,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
columnForeignKeyConstraintAttribute.ConstraintName
)
? columnForeignKeyConstraintAttribute.ConstraintName
- : ProviderUtils.GenerateForeignKeyConstraintName(
+ : DbProviderUtils.GenerateForeignKeyConstraintName(
tableName,
columnName,
referencedTableName,
@@ -471,7 +517,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
};
var onDelete = DxForeignKeyAction.NoAction;
var onUpdate = DxForeignKeyAction.NoAction;
- var constraintName = ProviderUtils.GenerateForeignKeyConstraintName(
+ var constraintName = DbProviderUtils.GenerateForeignKeyConstraintName(
tableName,
columnName,
referencedTableName,
@@ -533,7 +579,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
if (primaryKey != null && string.IsNullOrWhiteSpace(primaryKey.ConstraintName))
{
- primaryKey.ConstraintName = ProviderUtils.GeneratePrimaryKeyConstraintName(
+ primaryKey.ConstraintName = DbProviderUtils.GeneratePrimaryKeyConstraintName(
tableName,
primaryKey.Columns.Select(c => c.ColumnName).ToArray()
);
@@ -548,7 +594,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
var constraintName = !string.IsNullOrWhiteSpace(cca.ConstraintName)
? cca.ConstraintName
- : ProviderUtils.GenerateCheckConstraintName(tableName, $"{ccaId++}");
+ : DbProviderUtils.GenerateCheckConstraintName(tableName, $"{ccaId++}");
checkConstraints.Add(
new DxCheckConstraint(schemaName, tableName, null, constraintName, cca.Expression)
@@ -563,7 +609,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
var constraintName = !string.IsNullOrWhiteSpace(uca.ConstraintName)
? uca.ConstraintName
- : ProviderUtils.GenerateUniqueConstraintName(
+ : DbProviderUtils.GenerateUniqueConstraintName(
tableName,
uca.Columns.Select(c => c.ColumnName).ToArray()
);
@@ -593,7 +639,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
var indexName = !string.IsNullOrWhiteSpace(cia.IndexName)
? cia.IndexName
- : ProviderUtils.GenerateIndexName(
+ : DbProviderUtils.GenerateIndexName(
tableName,
cia.Columns.Select(c => c.ColumnName).ToArray()
);
@@ -634,7 +680,7 @@ pa is System.ComponentModel.DataAnnotations.RequiredAttribute
var constraintName = !string.IsNullOrWhiteSpace(cfk.ConstraintName)
? cfk.ConstraintName
- : ProviderUtils.GenerateForeignKeyConstraintName(
+ : DbProviderUtils.GenerateForeignKeyConstraintName(
tableName,
cfk.SourceColumnNames,
cfk.ReferencedTableName,
diff --git a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Columns.cs b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Columns.cs
index 2a42a5b..c8e1a39 100644
--- a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Columns.cs
+++ b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Columns.cs
@@ -16,7 +16,7 @@ public virtual async Task DoesColumnExistAsync(
)
{
return await GetColumnAsync(db, schemaName, tableName, columnName, tx, cancellationToken)
- .ConfigureAwait(false) != null;
+ .ConfigureAwait(false) != null;
}
public virtual async Task CreateColumnIfNotExistsAsync(
@@ -175,7 +175,12 @@ public virtual async Task CreateColumnIfNotExistsAsync(
tableName,
columnName,
dotnetType,
- providerDataType,
+ providerDataType == null
+ ? null
+ : new Dictionary
+ {
+ { ProviderType, providerDataType }
+ },
length,
precision,
scale,
@@ -408,10 +413,10 @@ await DoesColumnExistAsync(
await ExecuteAsync(
db,
$"""
- ALTER TABLE {schemaQualifiedTableName}
- RENAME COLUMN {columnName}
- TO {newColumnName}
- """,
+ ALTER TABLE {schemaQualifiedTableName}
+ RENAME COLUMN {columnName}
+ TO {newColumnName}
+ """,
tx: tx
)
.ConfigureAwait(false);
diff --git a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Strings.cs b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Strings.cs
index 67eed0c..a72d65f 100644
--- a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Strings.cs
+++ b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Strings.cs
@@ -149,13 +149,13 @@ Version dbVersion
)
)
{
- var pkConstraintName = ProviderUtils.GeneratePrimaryKeyConstraintName(
+ var pkConstraintName = DbProviderUtils.GeneratePrimaryKeyConstraintName(
tableName,
columnName
);
var pkInlineSql = SqlInlinePrimaryKeyColumnConstraint(
+ column,
pkConstraintName,
- column.IsAutoIncrement,
out var useTableConstraint
);
if (!string.IsNullOrWhiteSpace(pkInlineSql))
@@ -190,7 +190,7 @@ [new DxOrderedColumn(columnName)]
)
)
{
- var defConstraintName = ProviderUtils.GenerateDefaultConstraintName(
+ var defConstraintName = DbProviderUtils.GenerateDefaultConstraintName(
tableName,
columnName
);
@@ -223,7 +223,10 @@ [new DxOrderedColumn(columnName)]
)
)
{
- var ckConstraintName = ProviderUtils.GenerateCheckConstraintName(tableName, columnName);
+ var ckConstraintName = DbProviderUtils.GenerateCheckConstraintName(
+ tableName,
+ columnName
+ );
var ckInlineSql = SqlInlineCheckColumnConstraint(
ckConstraintName,
column.CheckExpression,
@@ -257,7 +260,7 @@ out var useTableConstraint
)
)
{
- var ucConstraintName = ProviderUtils.GenerateUniqueConstraintName(
+ var ucConstraintName = DbProviderUtils.GenerateUniqueConstraintName(
tableName,
columnName
);
@@ -293,7 +296,7 @@ [new DxOrderedColumn(columnName)]
)
)
{
- var fkConstraintName = ProviderUtils.GenerateForeignKeyConstraintName(
+ var fkConstraintName = DbProviderUtils.GenerateForeignKeyConstraintName(
tableName,
columnName,
NormalizeName(column.ReferencedTableName),
@@ -338,7 +341,7 @@ [new DxOrderedColumn(column.ReferencedColumnName)],
)
)
{
- var indexName = ProviderUtils.GenerateIndexName(tableName, columnName);
+ var indexName = DbProviderUtils.GenerateIndexName(tableName, columnName);
tableConstraints.Indexes.Add(
new DxIndex(
schemaName,
@@ -355,37 +358,48 @@ [new DxOrderedColumn(columnName)],
protected virtual string SqlInlineColumnNameAndType(DxColumn column, Version dbVersion)
{
- var columnType = string.IsNullOrWhiteSpace(column.ProviderDataType)
- ? GetSqlTypeFromDotnetType(
- column.DotnetType,
- column.Length,
- column.Precision,
- column.Scale
- )
- : column.ProviderDataType;
+ var descriptor = new DbProviderDotnetTypeDescriptor(
+ column.DotnetType,
+ column.Length,
+ column.Precision,
+ column.Scale,
+ column.IsAutoIncrement,
+ column.IsUnicode
+ );
+
+ var columnType = string.IsNullOrWhiteSpace(column.GetProviderDataType(ProviderType))
+ ? GetSqlTypeFromDotnetType(descriptor)
+ : column.GetProviderDataType(ProviderType);
+
+ if (string.IsNullOrWhiteSpace(columnType))
+ throw new InvalidOperationException(
+ $"Could not determine the SQL type for column {column.ColumnName} of type {column.DotnetType.Name}"
+ );
// set the type on the column so that it can be used in other methods
- column.ProviderDataType = columnType;
+ column.SetProviderDataType(ProviderType, columnType);
return $"{NormalizeName(column.ColumnName)} {columnType}";
}
protected virtual string SqlInlineColumnNullable(DxColumn column)
{
- return column.IsNullable ? " NULL" : " NOT NULL";
+ return column.IsNullable && !column.IsUnique && !column.IsPrimaryKey
+ ? " NULL"
+ : " NOT NULL";
}
protected virtual string SqlInlinePrimaryKeyColumnConstraint(
+ DxColumn column,
string constraintName,
- bool isAutoIncrement,
out bool useTableConstraint
)
{
useTableConstraint = false;
- return $"CONSTRAINT {NormalizeName(constraintName)} PRIMARY KEY {(isAutoIncrement ? SqlInlinePrimaryKeyAutoIncrementColumnConstraint() : "")}".Trim();
+ return $"CONSTRAINT {NormalizeName(constraintName)} PRIMARY KEY {(column.IsAutoIncrement ? SqlInlinePrimaryKeyAutoIncrementColumnConstraint(column) : "")}".Trim();
}
- protected virtual string SqlInlinePrimaryKeyAutoIncrementColumnConstraint()
+ protected virtual string SqlInlinePrimaryKeyAutoIncrementColumnConstraint(DxColumn column)
{
return "IDENTITY(1,1)";
}
@@ -448,7 +462,7 @@ DxPrimaryKeyConstraint primaryKeyConstraint
var pkColumnNames = primaryKeyConstraint.Columns.Select(c => c.ColumnName).ToArray();
var pkConstrainName = !string.IsNullOrWhiteSpace(primaryKeyConstraint.ConstraintName)
? primaryKeyConstraint.ConstraintName
- : ProviderUtils.GeneratePrimaryKeyConstraintName(
+ : DbProviderUtils.GeneratePrimaryKeyConstraintName(
table.TableName,
pkColumnNames.ToArray()
);
@@ -461,11 +475,11 @@ protected virtual string SqlInlineCheckTableConstraint(DxTable table, DxCheckCon
var ckConstraintName = !string.IsNullOrWhiteSpace(check.ConstraintName)
? check.ConstraintName
: string.IsNullOrWhiteSpace(check.ColumnName)
- ? ProviderUtils.GenerateCheckConstraintName(
+ ? DbProviderUtils.GenerateCheckConstraintName(
table.TableName,
DateTime.Now.Ticks.ToString()
)
- : ProviderUtils.GenerateCheckConstraintName(table.TableName, check.ColumnName);
+ : DbProviderUtils.GenerateCheckConstraintName(table.TableName, check.ColumnName);
return $"CONSTRAINT {NormalizeName(ckConstraintName)} CHECK ({check.Expression})";
}
@@ -494,7 +508,7 @@ bool supportsOrderedKeysInConstraints
{
var ucConstraintName = !string.IsNullOrWhiteSpace(uc.ConstraintName)
? uc.ConstraintName
- : ProviderUtils.GenerateUniqueConstraintName(
+ : DbProviderUtils.GenerateUniqueConstraintName(
table.TableName,
uc.Columns.Select(c => NormalizeName(c.ColumnName)).ToArray()
);
diff --git a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Tables.cs b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Tables.cs
index 45a5609..490c2c6 100644
--- a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Tables.cs
+++ b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.Tables.cs
@@ -145,7 +145,7 @@ [.. table.Indexes]
if (table.PrimaryKeyConstraint == null && table.Columns.Count(c => c.IsPrimaryKey) > 1)
{
var pkColumns = table.Columns.Where(c => c.IsPrimaryKey).ToArray();
- var pkConstraintName = ProviderUtils.GeneratePrimaryKeyConstraintName(
+ var pkConstraintName = DbProviderUtils.GeneratePrimaryKeyConstraintName(
table.TableName,
pkColumns.Select(c => c.ColumnName).ToArray()
);
@@ -185,7 +185,7 @@ [.. table.Indexes]
)
)
{
- var fkConstraintName = ProviderUtils.GenerateForeignKeyConstraintName(
+ var fkConstraintName = DbProviderUtils.GenerateForeignKeyConstraintName(
tableName,
column.ColumnName,
column.ReferencedTableName,
@@ -257,7 +257,17 @@ [new DxOrderedColumn(column.ReferencedColumnName)]
}
sql.AppendLine();
- sql.Append(");");
+ sql.Append(")");
+
+ // TODO: for MySQL, we need to add the ENGINE=InnoDB; at the end of the CREATE TABLE statement
+ if (ProviderType == DbProviderType.MySql)
+ {
+ sql.Append(
+ " DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB"
+ );
+ }
+
+ sql.Append(';');
var sqlStatement = sql.ToString();
diff --git a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.cs b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.cs
index a43db66..23234e2 100644
--- a/src/DapperMatic/Providers/Base/DatabaseMethodsBase.cs
+++ b/src/DapperMatic/Providers/Base/DatabaseMethodsBase.cs
@@ -12,7 +12,7 @@ public abstract partial class DatabaseMethodsBase : IDatabaseMethods
{
public abstract DbProviderType ProviderType { get; }
- public abstract IProviderTypeMap ProviderTypeMap { get; }
+ public abstract IDbProviderTypeMap ProviderTypeMap { get; }
protected abstract string DefaultSchema { get; }
@@ -38,51 +38,74 @@ public virtual Task SupportsDefaultConstraintsAsync(
private ILogger Logger => DxLogger.CreateLogger(GetType());
- public virtual (Type dotnetType, int? length, int? precision, int? scale, bool? isAutoIncrementing, Type[]
- allSupportedTypes) GetDotnetTypeFromSqlType(string sqlType)
+ public virtual DbProviderDotnetTypeDescriptor GetDotnetTypeFromSqlType(string sqlType)
{
if (
- !ProviderTypeMap.TryGetRecommendedDotnetTypeMatchingSqlType(
+ !ProviderTypeMap.TryGetDotnetTypeDescriptorMatchingFullSqlTypeName(
sqlType,
- out var providerDataType
- ) || !providerDataType.HasValue
+ out var dotnetTypeDescriptor
+ )
+ || dotnetTypeDescriptor == null
)
throw new NotSupportedException($"SQL type {sqlType} is not supported.");
- return providerDataType.Value;
+ return dotnetTypeDescriptor;
}
- public string GetSqlTypeFromDotnetType(
- Type type,
- int? length = null,
- int? precision = null,
- int? scale = null,
- bool? autoIncrementing = null
- )
+ public string GetSqlTypeFromDotnetType(DbProviderDotnetTypeDescriptor descriptor)
{
+ var tmb = ProviderTypeMap as DbProviderTypeMapBase;
+
if (
- !ProviderTypeMap.TryGetRecommendedSqlTypeMatchingDotnetType(
- type,
- length,
- precision,
- scale,
- autoIncrementing,
+ !ProviderTypeMap.TryGetProviderSqlTypeMatchingDotnetType(
+ descriptor,
out var providerDataType
)
|| providerDataType == null
)
- throw new NotSupportedException($"No provider data type found for .NET type {type}.");
+ {
+ if (tmb != null)
+ return tmb.SqTypeForUnknownDotnetType;
+
+ throw new NotSupportedException(
+ $"No provider data type found for .NET type {descriptor}."
+ );
+ }
+
+ var length = descriptor.Length;
+ var precision = descriptor.Precision;
+ var scale = descriptor.Scale;
if (providerDataType.SupportsLength())
{
- length ??= providerDataType.DefaultLength;
+ if (!length.HasValue && descriptor.DotnetType == typeof(Guid))
+ length = 36;
+ if (!length.HasValue && descriptor.DotnetType == typeof(char))
+ length = 1;
+ if (!length.HasValue)
+ length = providerDataType.DefaultLength;
+
if (length.HasValue)
{
+ if (
+ tmb != null
+ && length >= 8000
+ && providerDataType.Affinity == DbProviderSqlTypeAffinity.Text
+ )
+ return tmb.SqTypeForStringLengthMax;
+
+ if (
+ tmb != null
+ && length >= 8000
+ && providerDataType.Affinity == DbProviderSqlTypeAffinity.Binary
+ )
+ return tmb.SqTypeForBinaryLengthMax;
+
if (!string.IsNullOrWhiteSpace(providerDataType.FormatWithLength))
return string.Format(providerDataType.FormatWithLength, length);
}
}
-
+
if (providerDataType.SupportsPrecision())
{
precision ??= providerDataType.DefaultPrecision;
@@ -105,10 +128,8 @@ out var providerDataType
return providerDataType.Name;
}
- internal static readonly ConcurrentDictionary<
- string,
- (string sql, object? parameters)
- > LastSqls = new();
+ internal readonly ConcurrentDictionary LastSqls =
+ new();
public abstract Task GetDatabaseVersionAsync(
IDbConnection db,
@@ -126,7 +147,7 @@ public string GetLastSql(IDbConnection db)
return LastSqls.TryGetValue(db.ConnectionString, out var sql) ? sql : ("", null);
}
- private static void SetLastSql(IDbConnection db, string sql, object? param = null)
+ private void SetLastSql(IDbConnection db, string sql, object? param = null)
{
LastSqls.AddOrUpdate(db.ConnectionString, (sql, param), (_, _) => (sql, param));
}
@@ -143,7 +164,7 @@ protected virtual async Task> QueryAsync(
try
{
Log(
- LogLevel.Information,
+ LogLevel.Debug,
"[{provider}] Executing SQL query: {sql}, with parameters {parameters}",
ProviderType,
sql,
@@ -161,10 +182,12 @@ await db.QueryAsync(sql, param, tx, commandTimeout, commandType)
Log(
LogLevel.Error,
ex,
- "An error occurred while executing SQL query: {sql}, with parameters {parameters}.\n{message}",
+ "An error occurred while executing {provider} SQL query with map {providerMap}: \n{message}\n{sql}, with parameters {parameters}.",
+ ProviderType,
+ ProviderTypeMap.GetType().Name,
+ ex.Message,
sql,
- param == null ? "{}" : JsonSerializer.Serialize(param),
- ex.Message
+ param == null ? "{}" : JsonSerializer.Serialize(param)
);
throw;
}
@@ -182,7 +205,7 @@ await db.QueryAsync(sql, param, tx, commandTimeout, commandType)
try
{
Log(
- LogLevel.Information,
+ LogLevel.Debug,
"[{provider}] Executing SQL scalar: {sql}, with parameters {parameters}",
ProviderType,
sql,
@@ -203,10 +226,12 @@ await db.QueryAsync(sql, param, tx, commandTimeout, commandType)
Log(
LogLevel.Error,
ex,
- "An error occurred while executing SQL scalar query: {sql}, with parameters {parameters}.\n{message}",
+ "An error occurred while executing {provider} SQL scalar query with map {providerMap}: \n{message}\n{sql}, with parameters {parameters}.",
+ ProviderType,
+ ProviderTypeMap.GetType().Name,
+ ex.Message,
sql,
- param == null ? "{}" : JsonSerializer.Serialize(param),
- ex.Message
+ param == null ? "{}" : JsonSerializer.Serialize(param)
);
throw;
}
@@ -224,7 +249,7 @@ protected virtual async Task ExecuteAsync(
try
{
Log(
- LogLevel.Information,
+ LogLevel.Debug,
"[{provider}] Executing SQL statement: {sql}, with parameters {parameters}",
ProviderType,
sql,
@@ -239,10 +264,12 @@ protected virtual async Task ExecuteAsync(
Log(
LogLevel.Error,
ex,
- "An error occurred while executing SQL statement: {sql}, with parameters {parameters}.\n{message}",
+ "An error occurred while executing {provider} SQL statement with map {providerMap}: \n{message}\n{sql}, with parameters {parameters}.",
+ ProviderType,
+ ProviderTypeMap.GetType().Name,
+ ex.Message,
sql,
- param == null ? "{}" : JsonSerializer.Serialize(param),
- ex.Message
+ param == null ? "{}" : JsonSerializer.Serialize(param)
);
throw;
}
diff --git a/src/DapperMatic/Providers/DbProviderDotnetTypeDescriptor.cs b/src/DapperMatic/Providers/DbProviderDotnetTypeDescriptor.cs
new file mode 100644
index 0000000..514bf99
--- /dev/null
+++ b/src/DapperMatic/Providers/DbProviderDotnetTypeDescriptor.cs
@@ -0,0 +1,72 @@
+using System.Text;
+
+namespace DapperMatic.Providers;
+
+// The struct to store the parameters:
+public class DbProviderDotnetTypeDescriptor
+{
+ public DbProviderDotnetTypeDescriptor(
+ Type dotnetType,
+ int? length = null,
+ int? precision = null,
+ int? scale = null,
+ bool? autoIncrement = false,
+ bool? unicode = false,
+ Type[]? otherSupportedTypes = null
+ )
+ {
+ DotnetType =
+ (
+ dotnetType.IsGenericType
+ && dotnetType.GetGenericTypeDefinition() == typeof(Nullable<>)
+ )
+ ? Nullable.GetUnderlyingType(dotnetType)!
+ : dotnetType;
+ Length = length;
+ Precision = precision;
+ Scale = scale;
+ AutoIncrement = autoIncrement;
+ Unicode = unicode;
+ this.otherSupportedTypes = otherSupportedTypes ?? [];
+ }
+
+ ///
+ /// Non-nullable type used to determine or map to a recommended sql type
+ ///
+ public Type DotnetType { get; init; }
+ public int? Length { get; init; }
+ public int? Precision { get; init; }
+ public int? Scale { get; init; }
+ public bool? AutoIncrement { get; init; }
+ public bool? Unicode { get; init; }
+
+ private Type[] otherSupportedTypes = [];
+ public Type[] AllSupportedTypes
+ {
+ get => [DotnetType, .. otherSupportedTypes.Where(t => t != DotnetType).Distinct()];
+ set => otherSupportedTypes = value;
+ }
+
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append(DotnetType.GetFriendlyName());
+ if (Length.HasValue)
+ {
+ sb.Append($" LENGTH({Length})");
+ }
+ if (Precision.HasValue)
+ {
+ sb.Append($" PRECISION({Precision})");
+ }
+ if (AutoIncrement.HasValue)
+ {
+ sb.Append(" AUTO_INCREMENT");
+ }
+ if (Unicode.HasValue)
+ {
+ sb.Append(" UNICODE");
+ }
+ return sb.ToString();
+ }
+}
diff --git a/src/DapperMatic/Providers/DbProviderTypeMapBase.cs b/src/DapperMatic/Providers/DbProviderTypeMapBase.cs
new file mode 100644
index 0000000..06b14ed
--- /dev/null
+++ b/src/DapperMatic/Providers/DbProviderTypeMapBase.cs
@@ -0,0 +1,363 @@
+using System.Collections.Concurrent;
+
+namespace DapperMatic.Providers;
+
+public abstract class DbProviderTypeMapBase : IDbProviderTypeMap
+{
+ // ReSharper disable once MemberCanBePrivate.Global
+ // ReSharper disable once CollectionNeverUpdated.Global
+ public static readonly ConcurrentDictionary> TypeMaps =
+ new();
+
+ protected abstract DbProviderType ProviderType { get; }
+ protected abstract DbProviderSqlType[] ProviderSqlTypes { get; }
+
+ private Dictionary? _lookup = null;
+ protected virtual Dictionary ProviderSqlTypeLookup =>
+ _lookup ??= ProviderSqlTypes.ToDictionary(t => t.Name, StringComparer.OrdinalIgnoreCase);
+
+ public abstract string SqTypeForStringLengthMax { get; }
+ public abstract string SqTypeForBinaryLengthMax { get; }
+ public abstract string SqlTypeForJson { get; }
+ public virtual string SqTypeForUnknownDotnetType => SqTypeForStringLengthMax;
+ protected virtual int DefaultLength { get; set; } = 255;
+
+ public virtual bool UseIntegersForEnumTypes { get; set; } = false;
+
+ public virtual bool TryGetDotnetTypeDescriptorMatchingFullSqlTypeName(
+ string fullSqlType,
+ out DbProviderDotnetTypeDescriptor? descriptor
+ )
+ {
+ descriptor = new DbProviderDotnetTypeDescriptor(typeof(string));
+
+ // Prioritize any custom mappings
+ if (TypeMaps.TryGetValue(ProviderType, out var additionalTypeMaps))
+ {
+ foreach (var typeMap in additionalTypeMaps)
+ {
+ if (
+ typeMap.TryGetDotnetTypeDescriptorMatchingFullSqlTypeName(
+ fullSqlType,
+ out var rdt
+ )
+ )
+ {
+ descriptor = rdt;
+ return true;
+ }
+ }
+ }
+
+ if (
+ !TryGetProviderSqlTypeFromFullSqlTypeName(fullSqlType, out var providerSqlType)
+ || providerSqlType == null
+ )
+ return false;
+
+ // perform some detective reasoning to pinpoint a recommended type
+ var numbers = fullSqlType.ExtractNumbers();
+ var isAutoIncrementing = providerSqlType.AutoIncrementsAutomatically;
+ var unicode = providerSqlType.IsUnicode;
+
+ switch (providerSqlType.Affinity)
+ {
+ case DbProviderSqlTypeAffinity.Binary:
+ descriptor = new(typeof(byte[]));
+ break;
+ case DbProviderSqlTypeAffinity.Boolean:
+ descriptor = new(
+ typeof(bool),
+ otherSupportedTypes:
+ [
+ typeof(short),
+ typeof(int),
+ typeof(long),
+ typeof(ushort),
+ typeof(uint),
+ typeof(ulong),
+ typeof(string)
+ ]
+ );
+ break;
+ case DbProviderSqlTypeAffinity.DateTime:
+ if (providerSqlType.IsDateOnly == true)
+ descriptor = new(
+ typeof(DateOnly),
+ otherSupportedTypes: [typeof(DateOnly), typeof(DateTime), typeof(string)]
+ );
+ else if (providerSqlType.IsTimeOnly == true)
+ descriptor = new(
+ typeof(TimeOnly),
+ otherSupportedTypes: [typeof(TimeOnly), typeof(DateTime), typeof(string)]
+ );
+ else if (providerSqlType.IsYearOnly == true)
+ descriptor = new(
+ typeof(int),
+ otherSupportedTypes:
+ [
+ typeof(short),
+ typeof(long),
+ typeof(ushort),
+ typeof(uint),
+ typeof(ulong),
+ typeof(string)
+ ]
+ );
+ else if (providerSqlType.IncludesTimeZone == true)
+ descriptor = new DbProviderDotnetTypeDescriptor(
+ typeof(DateTimeOffset),
+ otherSupportedTypes: [typeof(DateTime), typeof(string)]
+ );
+ else
+ descriptor = new(
+ typeof(DateTime),
+ otherSupportedTypes: [typeof(DateTimeOffset), typeof(string)]
+ );
+ break;
+ case DbProviderSqlTypeAffinity.Integer:
+ int? intPrecision = numbers.Length > 0 ? numbers[0] : null;
+ if (providerSqlType.MinValue.HasValue && providerSqlType.MinValue == 0)
+ {
+ if (providerSqlType.MaxValue.HasValue)
+ {
+ if (providerSqlType.MaxValue.Value <= ushort.MaxValue)
+ descriptor = new(
+ typeof(ushort),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes:
+ [
+ typeof(short),
+ typeof(int),
+ typeof(long),
+ typeof(uint),
+ typeof(ulong),
+ typeof(string)
+ ]
+ );
+ else if (providerSqlType.MaxValue.Value <= uint.MaxValue)
+ descriptor = new(
+ typeof(uint),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes:
+ [
+ typeof(int),
+ typeof(long),
+ typeof(ulong),
+ typeof(string)
+ ]
+ );
+ else if (providerSqlType.MaxValue.Value <= ulong.MaxValue)
+ descriptor = new(
+ typeof(ulong),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes: [typeof(long), typeof(string)]
+ );
+ }
+ descriptor ??= new(
+ typeof(uint),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes:
+ [
+ typeof(int),
+ typeof(long),
+ typeof(ulong),
+ typeof(string)
+ ]
+ );
+ }
+ if (descriptor == null)
+ {
+ if (providerSqlType.MaxValue.HasValue)
+ {
+ if (providerSqlType.MaxValue.Value <= short.MaxValue)
+ descriptor = new(
+ typeof(short),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes: [typeof(int), typeof(long), typeof(string)]
+ );
+ else if (providerSqlType.MaxValue.Value <= int.MaxValue)
+ descriptor = new(
+ typeof(int),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes: [typeof(long), typeof(string)]
+ );
+ else if (providerSqlType.MaxValue.Value <= long.MaxValue)
+ descriptor = new(
+ typeof(long),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes: [typeof(string)]
+ );
+ }
+ descriptor ??= new(
+ typeof(int),
+ precision: intPrecision,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes: [typeof(long), typeof(string)]
+ );
+ }
+ break;
+ case DbProviderSqlTypeAffinity.Real:
+ int? precision = numbers.Length > 0 ? numbers[0] : null;
+ int? scale = numbers.Length > 1 ? numbers[1] : null;
+ descriptor = new(
+ typeof(decimal),
+ precision: precision,
+ scale: scale,
+ autoIncrement: isAutoIncrementing,
+ otherSupportedTypes:
+ [
+ typeof(decimal),
+ typeof(float),
+ typeof(double),
+ typeof(string)
+ ]
+ );
+ break;
+ case DbProviderSqlTypeAffinity.Text:
+ int? length = numbers.Length > 0 ? numbers[0] : null;
+ if (length >= 8000)
+ length = int.MaxValue;
+ descriptor = new(
+ typeof(string),
+ length: length,
+ unicode: unicode,
+ otherSupportedTypes: [typeof(string)]
+ );
+ break;
+ case DbProviderSqlTypeAffinity.Geometry:
+ case DbProviderSqlTypeAffinity.RangeType:
+ case DbProviderSqlTypeAffinity.Other:
+ default:
+ if (
+ providerSqlType.Name.Contains("json", StringComparison.OrdinalIgnoreCase)
+ || providerSqlType.Name.Contains("xml", StringComparison.OrdinalIgnoreCase)
+ )
+ descriptor = new(
+ typeof(string),
+ length: int.MaxValue,
+ unicode: unicode,
+ otherSupportedTypes: [typeof(string)]
+ );
+ else
+ descriptor = new(
+ typeof(object),
+ otherSupportedTypes: [typeof(object), typeof(string)]
+ );
+ break;
+ }
+
+ return descriptor != null;
+ }
+
+ public virtual bool TryGetProviderSqlTypeMatchingDotnetType(
+ DbProviderDotnetTypeDescriptor descriptor,
+ out DbProviderSqlType? providerSqlType
+ )
+ {
+ providerSqlType = null;
+
+ // Prioritize any custom mappings
+ if (TypeMaps.TryGetValue(ProviderType, out var additionalTypeMaps))
+ {
+ foreach (var typeMap in additionalTypeMaps)
+ {
+ if (typeMap.TryGetProviderSqlTypeMatchingDotnetType(descriptor, out var rdt))
+ {
+ providerSqlType = rdt;
+ return true;
+ }
+ }
+ }
+
+ var dotnetType = descriptor.DotnetType;
+
+ // Enums become strings, or integers if UseIntegersForEnumTypes is true
+ if (dotnetType.IsEnum)
+ {
+ return TryGetProviderSqlTypeMatchingEnumType(
+ dotnetType,
+ descriptor.Length,
+ ref providerSqlType
+ );
+ }
+
+ // char becomes string(1)
+ if (dotnetType == typeof(char) && (descriptor.Length == null || descriptor.Length == 1))
+ {
+ return TryGetProviderSqlTypeMatchingDotnetType(
+ new DbProviderDotnetTypeDescriptor(typeof(string), 1, unicode: descriptor.Unicode),
+ out providerSqlType
+ );
+ }
+
+ if (TryGetProviderSqlTypeMatchingDotnetTypeInternal(descriptor, out providerSqlType))
+ return true;
+
+ return providerSqlType != null;
+ }
+
+ protected abstract bool TryGetProviderSqlTypeMatchingDotnetTypeInternal(
+ DbProviderDotnetTypeDescriptor descriptor,
+ out DbProviderSqlType? providerSqlType
+ );
+
+ protected virtual bool TryGetProviderSqlTypeFromFullSqlTypeName(
+ string fullSqlType,
+ out DbProviderSqlType? providerSqlType
+ )
+ {
+ // perform some detective reasoning to pinpoint a recommended type
+ var numbers = fullSqlType.ExtractNumbers();
+
+ // try to find a sql provider type match by removing the length, precision, and scale
+ // from the sql type name and converting it to an alpha only representation of the type
+ var fullSqlTypeAlpha = fullSqlType
+ .DiscardLengthPrecisionAndScaleFromSqlTypeName()
+ .ToAlpha("[]");
+
+ providerSqlType = ProviderSqlTypes.FirstOrDefault(t =>
+ t.Name.DiscardLengthPrecisionAndScaleFromSqlTypeName()
+ .ToAlpha("[]")
+ .Equals(fullSqlTypeAlpha, StringComparison.OrdinalIgnoreCase)
+ );
+
+ return providerSqlType != null;
+ }
+
+ protected virtual bool TryGetProviderSqlTypeMatchingEnumType(
+ Type dotnetType,
+ int? length,
+ ref DbProviderSqlType? providerSqlType
+ )
+ {
+ if (UseIntegersForEnumTypes)
+ {
+ return TryGetProviderSqlTypeMatchingDotnetType(
+ new DbProviderDotnetTypeDescriptor(typeof(int), length),
+ out providerSqlType
+ );
+ }
+
+ if (length == null)
+ {
+ var maxEnumNameLength = Enum.GetNames(dotnetType).Max(m => m.Length);
+ var x = 64;
+ while (x < maxEnumNameLength)
+ x *= 2;
+ length = x;
+ }
+
+ return TryGetProviderSqlTypeMatchingDotnetType(
+ new DbProviderDotnetTypeDescriptor(typeof(string), length),
+ out providerSqlType
+ );
+ }
+}
diff --git a/src/DapperMatic/Providers/ProviderUtils.cs b/src/DapperMatic/Providers/DbProviderUtils.cs
similarity index 97%
rename from src/DapperMatic/Providers/ProviderUtils.cs
rename to src/DapperMatic/Providers/DbProviderUtils.cs
index 4f7d463..04b776e 100644
--- a/src/DapperMatic/Providers/ProviderUtils.cs
+++ b/src/DapperMatic/Providers/DbProviderUtils.cs
@@ -2,7 +2,7 @@
namespace DapperMatic.Providers;
-public static partial class ProviderUtils
+public static partial class DbProviderUtils
{
public static string GenerateCheckConstraintName(string tableName, string columnName)
{
@@ -54,6 +54,7 @@ string[] refColumnNames
[GeneratedRegex(@"\d+(\.\d+)+")]
private static partial Regex VersionPatternRegex();
+
private static readonly Regex VersionPattern = VersionPatternRegex();
internal static Version ExtractVersionFromVersionString(string versionString)
diff --git a/src/DapperMatic/Providers/IDbProviderTypeMap.cs b/src/DapperMatic/Providers/IDbProviderTypeMap.cs
new file mode 100644
index 0000000..b555beb
--- /dev/null
+++ b/src/DapperMatic/Providers/IDbProviderTypeMap.cs
@@ -0,0 +1,14 @@
+namespace DapperMatic.Providers;
+
+public interface IDbProviderTypeMap
+{
+ bool TryGetDotnetTypeDescriptorMatchingFullSqlTypeName(
+ string fullSqlType,
+ out DbProviderDotnetTypeDescriptor? descriptor
+ );
+
+ bool TryGetProviderSqlTypeMatchingDotnetType(
+ DbProviderDotnetTypeDescriptor descriptor,
+ out DbProviderSqlType? providerSqlType
+ );
+}
diff --git a/src/DapperMatic/Providers/IProviderTypeMap.cs b/src/DapperMatic/Providers/IProviderTypeMap.cs
deleted file mode 100644
index 79e5a8b..0000000
--- a/src/DapperMatic/Providers/IProviderTypeMap.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace DapperMatic.Providers;
-
-public interface IProviderTypeMap
-{
- bool TryGetRecommendedDotnetTypeMatchingSqlType(
- string fullSqlType,
- out (Type dotnetType, int? length, int? precision, int? scale, bool? isAutoIncrementing, Type[] allSupportedTypes)? recommendedDotnetType
- );
-
- bool TryGetRecommendedSqlTypeMatchingDotnetType(
- Type dotnetType,
- int? length,
- int? precision,
- int? scale,
- bool? autoIncrement,
- out ProviderSqlType? recommendedSqlType
- );
-}
\ No newline at end of file
diff --git a/src/DapperMatic/Providers/MySql/MySqlMethods.Strings.cs b/src/DapperMatic/Providers/MySql/MySqlMethods.Strings.cs
index 06e2c73..4d507d6 100644
--- a/src/DapperMatic/Providers/MySql/MySqlMethods.Strings.cs
+++ b/src/DapperMatic/Providers/MySql/MySqlMethods.Strings.cs
@@ -12,11 +12,6 @@ public partial class MySqlMethods
protected override string SqlInlineColumnNameAndType(DxColumn column, Version dbVersion)
{
- if (column.DotnetType == typeof(Guid) && string.IsNullOrWhiteSpace(column.ProviderDataType))
- {
- column.ProviderDataType = "varchar(36)";
- }
-
var nameAndType = base.SqlInlineColumnNameAndType(column, dbVersion);
if (
@@ -32,11 +27,12 @@ protected override string SqlInlineColumnNameAndType(DxColumn column, Version db
|| dbVersion.Major == 11;
// || (dbVersion.Major == 10 && dbVersion < new Version(10, 5, 25));
- if (!doNotAddUtf8Mb4)
+ if (!doNotAddUtf8Mb4 && column.IsUnicode)
{
// make it unicode by default
nameAndType += " CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci";
}
+
return nameAndType;
}
@@ -44,19 +40,19 @@ protected override string SqlInlineColumnNameAndType(DxColumn column, Version db
// MySQL DOES NOT ALLOW a named constraint in the column definition, so we HAVE to create
// the primary key constraint in the table constraints section
protected override string SqlInlinePrimaryKeyColumnConstraint(
+ DxColumn column,
string constraintName,
- bool isAutoIncrement,
out bool useTableConstraint
)
{
useTableConstraint = true;
- return isAutoIncrement ? "AUTO_INCREMENT" : "";
+ return column.IsAutoIncrement ? "AUTO_INCREMENT" : "";
// the following code doesn't work because MySQL doesn't allow named constraints in the column definition
- // return $"CONSTRAINT {NormalizeName(constraintName)} {(isAutoIncrement ? $"{SqlInlinePrimaryKeyAutoIncrementColumnConstraint()} " : "")}PRIMARY KEY".Trim();
+ // return $"CONSTRAINT {NormalizeName(constraintName)} {(column.IsAutoIncrement ? $"{SqlInlinePrimaryKeyAutoIncrementColumnConstraint(column)} " : "")}PRIMARY KEY".Trim();
}
- protected override string SqlInlinePrimaryKeyAutoIncrementColumnConstraint()
+ protected override string SqlInlinePrimaryKeyAutoIncrementColumnConstraint(DxColumn column)
{
return "AUTO_INCREMENT";
}
diff --git a/src/DapperMatic/Providers/MySql/MySqlMethods.Tables.cs b/src/DapperMatic/Providers/MySql/MySqlMethods.Tables.cs
index 4d29eb5..8a3a8c1 100644
--- a/src/DapperMatic/Providers/MySql/MySqlMethods.Tables.cs
+++ b/src/DapperMatic/Providers/MySql/MySqlMethods.Tables.cs
@@ -167,7 +167,7 @@ string columns_desc_csv
DefaultSchema,
c.table_name,
c.column_name,
- ProviderUtils.GenerateDefaultConstraintName(c.table_name, c.column_name),
+ DbProviderUtils.GenerateDefaultConstraintName(c.table_name, c.column_name),
c.column_default.Trim('(', ')')
);
})
@@ -182,7 +182,7 @@ string columns_desc_csv
return new DxPrimaryKeyConstraint(
DefaultSchema,
t.table_name,
- ProviderUtils.GeneratePrimaryKeyConstraintName(t.table_name, columnNames),
+ DbProviderUtils.GeneratePrimaryKeyConstraintName(t.table_name, columnNames),
columnNames
.Select(
(c, i) =>
@@ -457,16 +457,17 @@ string check_expression
)
?.i;
- var (dotnetType, _, _, _, _, _) = GetDotnetTypeFromSqlType(
- tableColumn.data_type_complete
- );
+ var dotnetTypeDescriptor = GetDotnetTypeFromSqlType(tableColumn.data_type_complete);
var column = new DxColumn(
tableColumn.schema_name,
tableColumn.table_name,
tableColumn.column_name,
- dotnetType,
- tableColumn.data_type_complete,
+ dotnetTypeDescriptor.DotnetType,
+ new Dictionary
+ {
+ { ProviderType, tableColumn.data_type_complete }
+ },
tableColumn.max_length.HasValue
? (
tableColumn.max_length.Value > int.MaxValue
diff --git a/src/DapperMatic/Providers/MySql/MySqlMethods.cs b/src/DapperMatic/Providers/MySql/MySqlMethods.cs
index 8733324..bfeb0a0 100644
--- a/src/DapperMatic/Providers/MySql/MySqlMethods.cs
+++ b/src/DapperMatic/Providers/MySql/MySqlMethods.cs
@@ -8,7 +8,7 @@ public partial class MySqlMethods : DatabaseMethodsBase, IDatabaseMethods
{
public override DbProviderType ProviderType => DbProviderType.MySql;
- public override IProviderTypeMap ProviderTypeMap => MySqlProviderTypeMap.Instance.Value;
+ public override IDbProviderTypeMap ProviderTypeMap => MySqlProviderTypeMap.Instance.Value;
protected override string DefaultSchema => "";
@@ -21,7 +21,7 @@ public override async Task SupportsCheckConstraintsAsync(
var versionStr =
await ExecuteScalarAsync(db, "SELECT VERSION()", tx: tx).ConfigureAwait(false)
?? "";
- var version = ProviderUtils.ExtractVersionFromVersionString(versionStr);
+ var version = DbProviderUtils.ExtractVersionFromVersionString(versionStr);
return (
versionStr.Contains("MariaDB", StringComparison.OrdinalIgnoreCase)
&& version > new Version(10, 2, 1)
@@ -50,7 +50,7 @@ public override async Task GetDatabaseVersionAsync(
var sql = @"SELECT VERSION()";
var versionString =
await ExecuteScalarAsync(db, sql, tx: tx).ConfigureAwait(false) ?? "";
- return ProviderUtils.ExtractVersionFromVersionString(versionString);
+ return DbProviderUtils.ExtractVersionFromVersionString(versionString);
}
public override char[] QuoteChars => ['`'];
diff --git a/src/DapperMatic/Providers/MySql/MySqlProviderTypeMap.cs b/src/DapperMatic/Providers/MySql/MySqlProviderTypeMap.cs
index 7821124..80fd4df 100644
--- a/src/DapperMatic/Providers/MySql/MySqlProviderTypeMap.cs
+++ b/src/DapperMatic/Providers/MySql/MySqlProviderTypeMap.cs
@@ -1,112 +1,435 @@
+using System.Collections;
+using System.Collections.Immutable;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+
namespace DapperMatic.Providers.MySql;
-public class MySqlProviderTypeMap : ProviderTypeMapBase
+public sealed class MySqlProviderTypeMap : DbProviderTypeMapBase
{
- internal static readonly Lazy Instance =
- new(() => new MySqlProviderTypeMap());
-
- private MySqlProviderTypeMap() : base()
- {
- }
-
- protected override DbProviderType ProviderType => DbProviderType.MySql;
-
- ///
- /// IMPORTANT!! The order within an affinity group matters, as the first possible match will be used as the recommended sql type for a dotnet type
- ///
- protected override ProviderSqlType[] ProviderSqlTypes =>
- [
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_tinyint, formatWithPrecision: "tinyint({0})",
- defaultPrecision: 4, canUseToAutoIncrement: true, minValue: -128, maxValue: 128),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_tinyint_unsigned,
- formatWithPrecision: "tinyint({0}) unsigned", defaultPrecision: 4, canUseToAutoIncrement: true, minValue: 0,
- maxValue: 255),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_smallint, formatWithPrecision: "smallint({0})",
- defaultPrecision: 5, canUseToAutoIncrement: true, minValue: -32768, maxValue: 32767),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_smallint_unsigned,
- formatWithPrecision: "smallint({0}) unsigned", defaultPrecision: 5, canUseToAutoIncrement: true,
- minValue: 0, maxValue: 65535),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_mediumint, formatWithPrecision: "mediumint({0})",
- defaultPrecision: 7, canUseToAutoIncrement: true, minValue: -8388608, maxValue: 8388607),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_mediumint_unsigned,
- formatWithPrecision: "mediumint({0}) unsigned", defaultPrecision: 7, canUseToAutoIncrement: true,
- minValue: 0, maxValue: 16777215),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_integer, formatWithPrecision: "integer({0})",
- defaultPrecision: 11, canUseToAutoIncrement: true, minValue: -2147483648, maxValue: 2147483647),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_integer_unsigned,
- formatWithPrecision: "integer({0}) unsigned", defaultPrecision: 11, canUseToAutoIncrement: true,
- minValue: 0, maxValue: 4294967295),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_int, aliasOf: "integer", formatWithPrecision: "int({0})",
- defaultPrecision: 11, canUseToAutoIncrement: true, minValue: -2147483648, maxValue: 2147483647),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_int_unsigned, formatWithPrecision: "int({0}) unsigned",
- defaultPrecision: 11, canUseToAutoIncrement: true, minValue: 0, maxValue: 4294967295),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_bigint, formatWithPrecision: "bigint({0})",
- defaultPrecision: 19, canUseToAutoIncrement: true, minValue: -Math.Pow(2, 63),
- maxValue: Math.Pow(2, 63) - 1),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_bigint_unsigned,
- formatWithPrecision: "bigint({0}) unsigned", defaultPrecision: 19, canUseToAutoIncrement: true, minValue: 0,
- maxValue: Math.Pow(2, 64) - 1),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_serial, aliasOf: "bigint unsigned",
- canUseToAutoIncrement: true, autoIncrementsAutomatically: true, minValue: 0, maxValue: Math.Pow(2, 64) - 1),
- new(ProviderSqlTypeAffinity.Integer, MySqlTypes.sql_bit, formatWithPrecision: "bit({0})", defaultPrecision: 1,
- minValue: 0, maxValue: long.MaxValue),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_decimal, formatWithPrecision: "decimal({0})",
- formatWithPrecisionAndScale: "decimal({0},{1})", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_dec, aliasOf: "decimal", formatWithPrecision: "dec({0})",
- formatWithPrecisionAndScale: "dec({0},{1})", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_numeric, formatWithPrecision: "numeric({0})",
- formatWithPrecisionAndScale: "numeric({0},{1})", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_fixed, aliasOf: "decimal", formatWithPrecision: "fixed({0})",
- formatWithPrecisionAndScale: "fixed({0},{1})", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_float, formatWithPrecision: "float({0})",
- formatWithPrecisionAndScale: "float({0},{1})", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_real, aliasOf: "double",
- formatWithPrecisionAndScale: "real({0},{1})", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_double_precision, aliasOf: "double",
- formatWithPrecisionAndScale: "double precision({0},{1})", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_double_precision_unsigned, aliasOf: "double unsigned",
- formatWithPrecisionAndScale: "double precision({0},{1}) unsigned", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_double, formatWithPrecisionAndScale: "double({0},{1})",
- defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, MySqlTypes.sql_double_unsigned,
- formatWithPrecisionAndScale: "double({0},{1}) unsigned", defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Boolean, MySqlTypes.sql_bool, aliasOf: "tinyint(1)"),
- new(ProviderSqlTypeAffinity.Boolean, MySqlTypes.sql_boolean, aliasOf: "tinyint(1)"),
- new(ProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_datetime),
- new(ProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_timestamp),
- new(ProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_time, formatWithPrecision: "time({0})",
- defaultPrecision: 6, isTimeOnly: true),
- new(ProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_date, isDateOnly: true),
- new(ProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_year, isYearOnly: true),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_char, formatWithLength: "char({0})", defaultLength: 255,
- isFixedLength: true),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_varchar, formatWithLength: "varchar({0})",
- defaultLength: 8000),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_long_varchar, aliasOf: "mediumtext"),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_tinytext),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_text, isMaxStringLengthType: true),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_mediumtext),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_longtext),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_enum),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_set),
- new(ProviderSqlTypeAffinity.Text, MySqlTypes.sql_json),
- new(ProviderSqlTypeAffinity.Binary, MySqlTypes.sql_blob),
- new(ProviderSqlTypeAffinity.Binary, MySqlTypes.sql_tinyblob),
- new(ProviderSqlTypeAffinity.Binary, MySqlTypes.sql_mediumblob),
- new(ProviderSqlTypeAffinity.Binary, MySqlTypes.sql_longblob),
- new(ProviderSqlTypeAffinity.Binary, MySqlTypes.sql_binary, formatWithLength: "binary({0})", defaultLength: 255,
- isFixedLength: true),
- new(ProviderSqlTypeAffinity.Binary, MySqlTypes.sql_varbinary, formatWithLength: "varbinary({0})",
- defaultLength: 8000),
- new(ProviderSqlTypeAffinity.Binary, MySqlTypes.sql_long_varbinary, aliasOf: "mediumblob"),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_geometry),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_point),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_linestring),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_polygon),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_multipoint),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_multilinestring),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_multipolygon),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_geomcollection),
- new(ProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_geometrycollection, aliasOf: "geomcollection")
- ];
-}
\ No newline at end of file
+ internal static readonly Lazy Instance =
+ new(() => new MySqlProviderTypeMap());
+
+ private MySqlProviderTypeMap()
+ : base() { }
+
+ protected override DbProviderType ProviderType => DbProviderType.MySql;
+
+ public override string SqTypeForStringLengthMax => "text(65535)";
+
+ public override string SqTypeForBinaryLengthMax => "blob(65535)";
+
+ public override string SqlTypeForJson => "text(65535)";
+
+ ///
+ /// IMPORTANT!! The order within an affinity group matters, as the first possible match will be used as the recommended sql type for a dotnet type
+ ///
+ protected override DbProviderSqlType[] ProviderSqlTypes =>
+ [
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_tinyint,
+ formatWithPrecision: "tinyint({0})",
+ defaultPrecision: 4,
+ canUseToAutoIncrement: true,
+ minValue: -128,
+ maxValue: 128
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_tinyint_unsigned,
+ formatWithPrecision: "tinyint({0}) unsigned",
+ defaultPrecision: 4,
+ canUseToAutoIncrement: true,
+ minValue: 0,
+ maxValue: 255
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_smallint,
+ formatWithPrecision: "smallint({0})",
+ defaultPrecision: 5,
+ canUseToAutoIncrement: true,
+ minValue: -32768,
+ maxValue: 32767
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_smallint_unsigned,
+ formatWithPrecision: "smallint({0}) unsigned",
+ defaultPrecision: 5,
+ canUseToAutoIncrement: true,
+ minValue: 0,
+ maxValue: 65535
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_mediumint,
+ formatWithPrecision: "mediumint({0})",
+ defaultPrecision: 7,
+ canUseToAutoIncrement: true,
+ minValue: -8388608,
+ maxValue: 8388607
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_mediumint_unsigned,
+ formatWithPrecision: "mediumint({0}) unsigned",
+ defaultPrecision: 7,
+ canUseToAutoIncrement: true,
+ minValue: 0,
+ maxValue: 16777215
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_integer,
+ formatWithPrecision: "integer({0})",
+ defaultPrecision: 11,
+ canUseToAutoIncrement: true,
+ minValue: -2147483648,
+ maxValue: 2147483647
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_integer_unsigned,
+ formatWithPrecision: "integer({0}) unsigned",
+ defaultPrecision: 11,
+ canUseToAutoIncrement: true,
+ minValue: 0,
+ maxValue: 4294967295
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_int,
+ aliasOf: "integer",
+ formatWithPrecision: "int({0})",
+ defaultPrecision: 11,
+ canUseToAutoIncrement: true,
+ minValue: -2147483648,
+ maxValue: 2147483647
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_int_unsigned,
+ formatWithPrecision: "int({0}) unsigned",
+ defaultPrecision: 11,
+ canUseToAutoIncrement: true,
+ minValue: 0,
+ maxValue: 4294967295
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_bigint,
+ formatWithPrecision: "bigint({0})",
+ defaultPrecision: 19,
+ canUseToAutoIncrement: true,
+ minValue: -Math.Pow(2, 63),
+ maxValue: Math.Pow(2, 63) - 1
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_bigint_unsigned,
+ formatWithPrecision: "bigint({0}) unsigned",
+ defaultPrecision: 19,
+ canUseToAutoIncrement: true,
+ minValue: 0,
+ maxValue: Math.Pow(2, 64) - 1
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_serial,
+ aliasOf: "bigint unsigned",
+ canUseToAutoIncrement: true,
+ autoIncrementsAutomatically: true,
+ minValue: 0,
+ maxValue: Math.Pow(2, 64) - 1
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ MySqlTypes.sql_bit,
+ formatWithPrecision: "bit({0})",
+ defaultPrecision: 1,
+ minValue: 0,
+ maxValue: long.MaxValue
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_decimal,
+ formatWithPrecision: "decimal({0})",
+ formatWithPrecisionAndScale: "decimal({0},{1})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_dec,
+ aliasOf: "decimal",
+ formatWithPrecision: "dec({0})",
+ formatWithPrecisionAndScale: "dec({0},{1})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_numeric,
+ formatWithPrecision: "numeric({0})",
+ formatWithPrecisionAndScale: "numeric({0},{1})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_fixed,
+ aliasOf: "decimal",
+ formatWithPrecision: "fixed({0})",
+ formatWithPrecisionAndScale: "fixed({0},{1})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_float
+ // formatWithPrecision: "float({0})",
+ // formatWithPrecisionAndScale: "float({0},{1})",
+ // defaultPrecision: 12,
+ // defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_real
+ // aliasOf: "double",
+ // formatWithPrecisionAndScale: "real({0},{1})",
+ // defaultPrecision: 12,
+ // defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_double_precision
+ // aliasOf: "double",
+ // formatWithPrecisionAndScale: "double precision({0},{1})",
+ // defaultPrecision: 12,
+ // defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_double_precision_unsigned,
+ aliasOf: "double unsigned"
+ // formatWithPrecisionAndScale: "double precision({0},{1}) unsigned",
+ // defaultPrecision: 12,
+ // defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_double
+ // formatWithPrecisionAndScale: "double({0},{1})",
+ // defaultPrecision: 12,
+ // defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ MySqlTypes.sql_double_unsigned
+ // formatWithPrecisionAndScale: "double({0},{1}) unsigned",
+ // defaultPrecision: 12,
+ // defaultScale: 2
+ ),
+ new(DbProviderSqlTypeAffinity.Boolean, MySqlTypes.sql_bool, aliasOf: "tinyint(1)"),
+ new(DbProviderSqlTypeAffinity.Boolean, MySqlTypes.sql_boolean, aliasOf: "tinyint(1)"),
+ new(DbProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_datetime),
+ new(DbProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_timestamp),
+ new(
+ DbProviderSqlTypeAffinity.DateTime,
+ MySqlTypes.sql_time,
+ formatWithPrecision: "time({0})",
+ defaultPrecision: 6,
+ isTimeOnly: true
+ ),
+ new(DbProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_date, isDateOnly: true),
+ new(DbProviderSqlTypeAffinity.DateTime, MySqlTypes.sql_year, isYearOnly: true),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ MySqlTypes.sql_char,
+ formatWithLength: "char({0})",
+ defaultLength: DefaultLength,
+ isFixedLength: true
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ MySqlTypes.sql_varchar,
+ formatWithLength: "varchar({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ MySqlTypes.sql_text,
+ formatWithLength: "text({0})",
+ defaultLength: 65535
+ ),
+ new(DbProviderSqlTypeAffinity.Text, MySqlTypes.sql_long_varchar, aliasOf: "mediumtext"),
+ new(DbProviderSqlTypeAffinity.Text, MySqlTypes.sql_tinytext),
+ new(DbProviderSqlTypeAffinity.Text, MySqlTypes.sql_mediumtext),
+ new(DbProviderSqlTypeAffinity.Text, MySqlTypes.sql_longtext),
+ new(DbProviderSqlTypeAffinity.Text, MySqlTypes.sql_enum),
+ new(DbProviderSqlTypeAffinity.Text, MySqlTypes.sql_set),
+ new(DbProviderSqlTypeAffinity.Text, MySqlTypes.sql_json),
+ new(
+ DbProviderSqlTypeAffinity.Binary,
+ MySqlTypes.sql_blob,
+ formatWithLength: "blob({0})",
+ defaultLength: 65535
+ ),
+ new(DbProviderSqlTypeAffinity.Binary, MySqlTypes.sql_tinyblob),
+ new(DbProviderSqlTypeAffinity.Binary, MySqlTypes.sql_mediumblob),
+ new(DbProviderSqlTypeAffinity.Binary, MySqlTypes.sql_longblob),
+ new(
+ DbProviderSqlTypeAffinity.Binary,
+ MySqlTypes.sql_varbinary,
+ formatWithLength: "varbinary({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Binary,
+ MySqlTypes.sql_binary,
+ formatWithLength: "binary({0})",
+ defaultLength: DefaultLength,
+ isFixedLength: true
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Binary,
+ MySqlTypes.sql_long_varbinary,
+ aliasOf: "mediumblob"
+ ),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_geometry),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_point),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_linestring),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_polygon),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_multipoint),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_multilinestring),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_multipolygon),
+ new(DbProviderSqlTypeAffinity.Geometry, MySqlTypes.sql_geomcollection),
+ new(
+ DbProviderSqlTypeAffinity.Geometry,
+ MySqlTypes.sql_geometrycollection,
+ aliasOf: "geomcollection"
+ )
+ ];
+
+ protected override bool TryGetProviderSqlTypeMatchingDotnetTypeInternal(
+ DbProviderDotnetTypeDescriptor descriptor,
+ out DbProviderSqlType? providerSqlType
+ )
+ {
+ providerSqlType = null;
+
+ var dotnetType = descriptor.DotnetType;
+
+ // handle well-known types first
+ providerSqlType = dotnetType.IsGenericType
+ ? null
+ : dotnetType switch
+ {
+ Type t when t == typeof(bool) => ProviderSqlTypeLookup[MySqlTypes.sql_boolean],
+ Type t when t == typeof(byte)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_smallint_unsigned],
+ Type t when t == typeof(ReadOnlyMemory)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_smallint],
+ Type t when t == typeof(sbyte) => ProviderSqlTypeLookup[MySqlTypes.sql_smallint],
+ Type t when t == typeof(short) => ProviderSqlTypeLookup[MySqlTypes.sql_smallint],
+ Type t when t == typeof(ushort)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_smallint_unsigned],
+ Type t when t == typeof(int) => ProviderSqlTypeLookup[MySqlTypes.sql_int],
+ Type t when t == typeof(uint) => ProviderSqlTypeLookup[MySqlTypes.sql_int_unsigned],
+ Type t when t == typeof(long) => ProviderSqlTypeLookup[MySqlTypes.sql_bigint],
+ Type t when t == typeof(ulong)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_bigint_unsigned],
+ Type t when t == typeof(float) => ProviderSqlTypeLookup[MySqlTypes.sql_float],
+ Type t when t == typeof(double) => ProviderSqlTypeLookup[MySqlTypes.sql_double],
+ Type t when t == typeof(decimal) => ProviderSqlTypeLookup[MySqlTypes.sql_decimal],
+ Type t when t == typeof(char) => ProviderSqlTypeLookup[MySqlTypes.sql_varchar],
+ Type t when t == typeof(string)
+ => descriptor.Length.GetValueOrDefault(255) < 8000
+ ? ProviderSqlTypeLookup[MySqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t when t == typeof(char[])
+ => descriptor.Length.GetValueOrDefault(255) < 8000
+ ? ProviderSqlTypeLookup[MySqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t when t == typeof(ReadOnlyMemory[])
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[MySqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t when t == typeof(Stream)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[MySqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t when t == typeof(TextReader)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[MySqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t when t == typeof(byte[]) => ProviderSqlTypeLookup[MySqlTypes.sql_blob],
+ Type t when t == typeof(object)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[MySqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t when t == typeof(object[])
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[MySqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t when t == typeof(Guid) => ProviderSqlTypeLookup[MySqlTypes.sql_varchar],
+ Type t when t == typeof(DateTime) => ProviderSqlTypeLookup[MySqlTypes.sql_datetime],
+ Type t when t == typeof(DateTimeOffset)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_timestamp],
+ Type t when t == typeof(TimeSpan) => ProviderSqlTypeLookup[MySqlTypes.sql_bigint],
+ Type t when t == typeof(DateOnly) => ProviderSqlTypeLookup[MySqlTypes.sql_date],
+ Type t when t == typeof(TimeOnly) => ProviderSqlTypeLookup[MySqlTypes.sql_time],
+ Type t when t == typeof(BitArray) || t == typeof(BitVector32)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_varbinary],
+ Type t
+ when t == typeof(ImmutableDictionary)
+ || t == typeof(Dictionary)
+ || t == typeof(IDictionary)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ Type t
+ when t == typeof(JsonNode)
+ || t == typeof(JsonObject)
+ || t == typeof(JsonArray)
+ || t == typeof(JsonValue)
+ || t == typeof(JsonDocument)
+ || t == typeof(JsonElement)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle generic types
+ providerSqlType = !dotnetType.IsGenericType
+ ? null
+ : dotnetType.GetGenericTypeDefinition() switch
+ {
+ Type t
+ when t == typeof(Dictionary<,>)
+ || t == typeof(IDictionary<,>)
+ || t == typeof(List<>)
+ || t == typeof(IList<>)
+ || t == typeof(Collection<>)
+ || t == typeof(ICollection<>)
+ || t == typeof(IEnumerable<>)
+ => ProviderSqlTypeLookup[MySqlTypes.sql_text],
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle POCO type
+ if (dotnetType.IsClass || dotnetType.IsInterface)
+ {
+ providerSqlType = ProviderSqlTypeLookup[MySqlTypes.sql_text];
+ }
+
+ return providerSqlType != null;
+ }
+}
diff --git a/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Strings.cs b/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Strings.cs
index f11e3e8..b1e1628 100644
--- a/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Strings.cs
+++ b/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Strings.cs
@@ -33,20 +33,31 @@ protected override string SqlDropSchema(string schemaName)
protected override string SqlInlineColumnNullable(DxColumn column)
{
+ // serial columns are implicitly NOT NULL
if (
column.IsNullable
- && (column.ProviderDataType ?? "").Contains(
+ && (column.GetProviderDataType(ProviderType) ?? "").Contains(
"serial",
StringComparison.OrdinalIgnoreCase
)
)
return "";
- return column.IsNullable ? " NULL" : " NOT NULL";
+ return column.IsNullable && !column.IsUnique && !column.IsPrimaryKey
+ ? " NULL"
+ : " NOT NULL";
}
- protected override string SqlInlinePrimaryKeyAutoIncrementColumnConstraint()
+ protected override string SqlInlinePrimaryKeyAutoIncrementColumnConstraint(DxColumn column)
{
+ if (
+ (column.GetProviderDataType(ProviderType) ?? "").Contains(
+ "serial",
+ StringComparison.OrdinalIgnoreCase
+ )
+ )
+ return string.Empty;
+
return "GENERATED BY DEFAULT AS IDENTITY";
}
diff --git a/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Tables.cs b/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Tables.cs
index 1ae2f8d..7781459 100644
--- a/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Tables.cs
+++ b/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.Tables.cs
@@ -399,22 +399,24 @@ int column_ordinal
)
?.i;
- var (dotnetType, length, precision, scale, autoIncrementing, otherSupportedTypes) =
- GetDotnetTypeFromSqlType(
- tableColumn.data_type.Length < tableColumn.data_type_ext.Length
- ? tableColumn.data_type_ext
- : tableColumn.data_type
- );
+ var dotnetTypeDescriptor = GetDotnetTypeFromSqlType(
+ tableColumn.data_type.Length < tableColumn.data_type_ext.Length
+ ? tableColumn.data_type_ext
+ : tableColumn.data_type
+ );
var column = new DxColumn(
tableColumn.schema_name,
tableColumn.table_name,
tableColumn.column_name,
- dotnetType,
- tableColumn.data_type,
- length,
- precision,
- scale,
+ dotnetTypeDescriptor.DotnetType,
+ new Dictionary
+ {
+ { ProviderType, tableColumn.data_type }
+ },
+ dotnetTypeDescriptor.Length,
+ dotnetTypeDescriptor.Precision,
+ dotnetTypeDescriptor.Scale,
tableCheckConstraints
.FirstOrDefault(c =>
!string.IsNullOrWhiteSpace(c.ColumnName)
diff --git a/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.cs b/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.cs
index 82cb6bf..b4f6139 100644
--- a/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.cs
+++ b/src/DapperMatic/Providers/PostgreSql/PostgreSqlMethods.cs
@@ -8,7 +8,7 @@ public partial class PostgreSqlMethods : DatabaseMethodsBase, IDatabaseMethods
{
public override DbProviderType ProviderType => DbProviderType.PostgreSql;
- public override IProviderTypeMap ProviderTypeMap => PostgreSqlProviderTypeMap.Instance.Value;
+ public override IDbProviderTypeMap ProviderTypeMap => PostgreSqlProviderTypeMap.Instance.Value;
private static string _defaultSchema = "public";
protected override string DefaultSchema => _defaultSchema;
@@ -39,7 +39,7 @@ public override async Task GetDatabaseVersionAsync(
const string sql = "SELECT VERSION()";
var versionString =
await ExecuteScalarAsync(db, sql, tx: tx).ConfigureAwait(false) ?? "";
- return ProviderUtils.ExtractVersionFromVersionString(versionString);
+ return DbProviderUtils.ExtractVersionFromVersionString(versionString);
}
public override char[] QuoteChars => ['"'];
diff --git a/src/DapperMatic/Providers/PostgreSql/PostgreSqlProviderTypeMap.cs b/src/DapperMatic/Providers/PostgreSql/PostgreSqlProviderTypeMap.cs
index b4d9a09..02755c3 100644
--- a/src/DapperMatic/Providers/PostgreSql/PostgreSqlProviderTypeMap.cs
+++ b/src/DapperMatic/Providers/PostgreSql/PostgreSqlProviderTypeMap.cs
@@ -1,8 +1,15 @@
+using System.Collections;
+using System.Collections.Immutable;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+
namespace DapperMatic.Providers.PostgreSql;
// https://www.npgsql.org/doc/types/basic.html#read-mappings
// https://www.npgsql.org/doc/types/basic.html#write-mappings
-public sealed class PostgreSqlProviderTypeMap : ProviderTypeMapBase
+public sealed class PostgreSqlProviderTypeMap : DbProviderTypeMapBase
{
internal static readonly Lazy Instance =
new(() => new PostgreSqlProviderTypeMap());
@@ -12,62 +19,68 @@ private PostgreSqlProviderTypeMap()
protected override DbProviderType ProviderType => DbProviderType.PostgreSql;
+ public override string SqTypeForStringLengthMax => "text";
+
+ public override string SqTypeForBinaryLengthMax => "bytea";
+
+ public override string SqlTypeForJson => "jsonb";
+
///
/// IMPORTANT!! The order within an affinity group matters, as the first possible match will be used as the recommended sql type for a dotnet type
///
- protected override ProviderSqlType[] ProviderSqlTypes =>
+ protected override DbProviderSqlType[] ProviderSqlTypes =>
[
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_smallint,
canUseToAutoIncrement: true,
minValue: -32768,
maxValue: 32767
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_int2,
canUseToAutoIncrement: true,
minValue: -32768,
maxValue: 32767
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_integer,
canUseToAutoIncrement: true,
minValue: -2147483648,
maxValue: 2147483647
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_int,
canUseToAutoIncrement: true,
minValue: -2147483648,
maxValue: 2147483647
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_int4,
canUseToAutoIncrement: true,
minValue: -2147483648,
maxValue: 2147483647
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_int8,
canUseToAutoIncrement: true,
minValue: -9223372036854775808,
maxValue: 9223372036854775807
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_bigint,
canUseToAutoIncrement: true,
minValue: -9223372036854775808,
maxValue: 9223372036854775807
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_smallserial,
canUseToAutoIncrement: true,
autoIncrementsAutomatically: true,
@@ -75,7 +88,7 @@ private PostgreSqlProviderTypeMap()
maxValue: 32767
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_serial2,
canUseToAutoIncrement: true,
autoIncrementsAutomatically: true,
@@ -83,7 +96,7 @@ private PostgreSqlProviderTypeMap()
maxValue: 32767
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_serial,
canUseToAutoIncrement: true,
autoIncrementsAutomatically: true,
@@ -91,7 +104,7 @@ private PostgreSqlProviderTypeMap()
maxValue: 2147483647
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_serial4,
canUseToAutoIncrement: true,
autoIncrementsAutomatically: true,
@@ -99,7 +112,7 @@ private PostgreSqlProviderTypeMap()
maxValue: 2147483647
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_bigserial,
canUseToAutoIncrement: true,
autoIncrementsAutomatically: true,
@@ -107,7 +120,7 @@ private PostgreSqlProviderTypeMap()
maxValue: 9223372036854775807
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
PostgreSqlTypes.sql_serial8,
canUseToAutoIncrement: true,
autoIncrementsAutomatically: true,
@@ -115,31 +128,31 @@ private PostgreSqlProviderTypeMap()
maxValue: 9223372036854775807
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
PostgreSqlTypes.sql_real,
minValue: float.MinValue,
maxValue: float.MaxValue
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
PostgreSqlTypes.sql_double_precision,
minValue: double.MinValue,
maxValue: double.MaxValue
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
PostgreSqlTypes.sql_float4,
minValue: float.MinValue,
maxValue: float.MaxValue
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
PostgreSqlTypes.sql_float8,
minValue: double.MinValue,
maxValue: double.MaxValue
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
PostgreSqlTypes.sql_money,
formatWithPrecision: "money({0})",
defaultPrecision: 19,
@@ -147,7 +160,7 @@ private PostgreSqlProviderTypeMap()
maxValue: 92233720368547758.07
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
PostgreSqlTypes.sql_numeric,
formatWithPrecision: "numeric({0})",
formatWithPrecisionAndScale: "numeric({0},{1})",
@@ -155,7 +168,7 @@ private PostgreSqlProviderTypeMap()
defaultScale: 2
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
PostgreSqlTypes.sql_decimal,
formatWithPrecision: "decimal({0})",
formatWithPrecisionAndScale: "decimal({0},{1})",
@@ -163,169 +176,469 @@ private PostgreSqlProviderTypeMap()
defaultScale: 2
),
new(
- ProviderSqlTypeAffinity.Boolean,
+ DbProviderSqlTypeAffinity.Boolean,
PostgreSqlTypes.sql_bool,
canUseToAutoIncrement: false
),
- new(ProviderSqlTypeAffinity.Boolean, PostgreSqlTypes.sql_boolean),
- new(ProviderSqlTypeAffinity.DateTime, PostgreSqlTypes.sql_date, isDateOnly: true),
- new(ProviderSqlTypeAffinity.DateTime, PostgreSqlTypes.sql_interval),
+ new(DbProviderSqlTypeAffinity.Boolean, PostgreSqlTypes.sql_boolean),
+ new(DbProviderSqlTypeAffinity.DateTime, PostgreSqlTypes.sql_date, isDateOnly: true),
+ new(DbProviderSqlTypeAffinity.DateTime, PostgreSqlTypes.sql_interval),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_time_without_timezone,
formatWithPrecision: "time({0}) without timezone",
defaultPrecision: 6
),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_time,
formatWithPrecision: "time({0})",
defaultPrecision: 6,
isTimeOnly: true
),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_time_with_time_zone,
formatWithPrecision: "time({0}) with time zone",
defaultPrecision: 6
),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_timetz,
formatWithPrecision: "timetz({0})",
defaultPrecision: 6,
isTimeOnly: true
),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_timestamp_without_time_zone,
formatWithPrecision: "timestamp({0}) without time zone",
defaultPrecision: 6
),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_timestamp,
formatWithPrecision: "timestamp({0})",
defaultPrecision: 6
),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_timestamp_with_time_zone,
formatWithPrecision: "timestamp({0}) with time zone",
defaultPrecision: 6
),
new(
- ProviderSqlTypeAffinity.DateTime,
+ DbProviderSqlTypeAffinity.DateTime,
PostgreSqlTypes.sql_timestamptz,
formatWithPrecision: "timestamptz({0})",
defaultPrecision: 6
),
new(
- ProviderSqlTypeAffinity.Text,
- PostgreSqlTypes.sql_bit,
- formatWithPrecision: "bit({0})",
- defaultPrecision: 1,
- minValue: 0,
- maxValue: 1
- ),
- new(
- ProviderSqlTypeAffinity.Text,
- PostgreSqlTypes.sql_bit_varying,
- formatWithPrecision: "bit varying({0})",
- defaultPrecision: 63
- ),
- new(
- ProviderSqlTypeAffinity.Text,
- PostgreSqlTypes.sql_varbit,
- formatWithPrecision: "varbit({0})",
- defaultPrecision: 63
+ DbProviderSqlTypeAffinity.Text,
+ PostgreSqlTypes.sql_varchar,
+ formatWithLength: "varchar({0})",
+ defaultLength: DefaultLength
),
new(
- ProviderSqlTypeAffinity.Text,
+ DbProviderSqlTypeAffinity.Text,
PostgreSqlTypes.sql_character_varying,
formatWithLength: "character varying({0})",
- defaultLength: 255
+ defaultLength: DefaultLength
),
new(
- ProviderSqlTypeAffinity.Text,
- PostgreSqlTypes.sql_varchar,
- formatWithLength: "varchar({0})",
- defaultLength: 255
- ),
- new(
- ProviderSqlTypeAffinity.Text,
+ DbProviderSqlTypeAffinity.Text,
PostgreSqlTypes.sql_character,
formatWithLength: "character({0})",
- defaultLength: 1
+ defaultLength: DefaultLength
),
new(
- ProviderSqlTypeAffinity.Text,
+ DbProviderSqlTypeAffinity.Text,
PostgreSqlTypes.sql_char,
formatWithLength: "char({0})",
- defaultLength: 1
+ defaultLength: DefaultLength
),
new(
- ProviderSqlTypeAffinity.Text,
+ DbProviderSqlTypeAffinity.Text,
PostgreSqlTypes.sql_bpchar,
formatWithLength: "bpchar({0})",
- defaultLength: 1
- ),
- new(ProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_text),
- new(ProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_name),
- new(ProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_uuid, isGuidOnly: true),
- new(ProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_json),
- new(ProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_jsonb),
- new(ProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_jsonpath),
- new(ProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_xml),
- new(ProviderSqlTypeAffinity.Binary, PostgreSqlTypes.sql_bytea),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_box),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_circle),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_geography),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_geometry),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_line),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_lseg),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_path),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_point),
- new(ProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_polygon),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_datemultirange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_daterange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int4multirange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int4range),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int8multirange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int8range),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_nummultirange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_numrange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tsmultirange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tsrange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tstzmultirange),
- new(ProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tstzrange),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_cidr),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_citext),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_hstore),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_inet),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_int2vector),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_lquery),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_ltree),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_ltxtquery),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_macaddr),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_macaddr8),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_oid),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_oidvector),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_pg_lsn),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_pg_snapshot),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_refcursor),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regclass),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regcollation),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regconfig),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regdictionary),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regnamespace),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regrole),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regtype),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_tid),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_tsquery),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_tsvector),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_txid_snapshot),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_xid),
- new(ProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_xid8),
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ PostgreSqlTypes.sql_bit,
+ formatWithPrecision: "bit({0})",
+ defaultPrecision: 1,
+ minValue: 0,
+ maxValue: 1
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ PostgreSqlTypes.sql_bit_varying,
+ formatWithPrecision: "bit varying({0})",
+ defaultPrecision: 63
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ PostgreSqlTypes.sql_varbit,
+ formatWithPrecision: "varbit({0})",
+ defaultPrecision: 63
+ ),
+ new(DbProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_text),
+ new(DbProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_name),
+ new(DbProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_uuid, isGuidOnly: true),
+ new(DbProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_json),
+ new(DbProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_jsonb),
+ new(DbProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_jsonpath),
+ new(DbProviderSqlTypeAffinity.Text, PostgreSqlTypes.sql_xml),
+ new(DbProviderSqlTypeAffinity.Binary, PostgreSqlTypes.sql_bytea),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_box),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_circle),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_geography),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_geometry),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_line),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_lseg),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_path),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_point),
+ new(DbProviderSqlTypeAffinity.Geometry, PostgreSqlTypes.sql_polygon),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_datemultirange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_daterange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int4multirange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int4range),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int8multirange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_int8range),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_nummultirange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_numrange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tsmultirange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tsrange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tstzmultirange),
+ new(DbProviderSqlTypeAffinity.RangeType, PostgreSqlTypes.sql_tstzrange),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_cidr),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_citext),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_hstore),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_inet),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_int2vector),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_lquery),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_ltree),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_ltxtquery),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_macaddr),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_macaddr8),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_oid),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_oidvector),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_pg_lsn),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_pg_snapshot),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_refcursor),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regclass),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regcollation),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regconfig),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regdictionary),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regnamespace),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regrole),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_regtype),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_tid),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_tsquery),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_tsvector),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_txid_snapshot),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_xid),
+ new(DbProviderSqlTypeAffinity.Other, PostgreSqlTypes.sql_xid8),
];
+
+ protected override bool TryGetProviderSqlTypeMatchingDotnetTypeInternal(
+ DbProviderDotnetTypeDescriptor descriptor,
+ out DbProviderSqlType? providerSqlType
+ )
+ {
+ providerSqlType = null;
+
+ var dotnetType = descriptor.DotnetType;
+
+ // handle well-known types first
+ providerSqlType = dotnetType.IsGenericType
+ ? null
+ : dotnetType switch
+ {
+ Type t when t == typeof(bool) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_boolean],
+ Type t when t == typeof(byte) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int2],
+ Type t when t == typeof(ReadOnlyMemory)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int2],
+ Type t when t == typeof(sbyte) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int2],
+ // it's no longer recommended to use SERIAL auto-incrementing columns
+ // Type t when t == typeof(short)
+ // => descriptor.AutoIncrement.GetValueOrDefault(false)
+ // ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_serial2]
+ // : ProviderSqlTypeLookup[PostgreSqlTypes.sql_int2],
+ // Type t when t == typeof(ushort)
+ // => descriptor.AutoIncrement.GetValueOrDefault(false)
+ // ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_serial2]
+ // : ProviderSqlTypeLookup[PostgreSqlTypes.sql_int2],
+ // Type t when t == typeof(int)
+ // => descriptor.AutoIncrement.GetValueOrDefault(false)
+ // ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_serial4]
+ // : ProviderSqlTypeLookup[PostgreSqlTypes.sql_int4],
+ // Type t when t == typeof(uint)
+ // => descriptor.AutoIncrement.GetValueOrDefault(false)
+ // ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_serial4]
+ // : ProviderSqlTypeLookup[PostgreSqlTypes.sql_int4],
+ // Type t when t == typeof(long)
+ // => descriptor.AutoIncrement.GetValueOrDefault(false)
+ // ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_serial8]
+ // : ProviderSqlTypeLookup[PostgreSqlTypes.sql_int8],
+ // Type t when t == typeof(ulong)
+ // => descriptor.AutoIncrement.GetValueOrDefault(false)
+ // ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_serial8]
+ // : ProviderSqlTypeLookup[PostgreSqlTypes.sql_int8],
+ Type t when t == typeof(short)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int2],
+ Type t when t == typeof(ushort) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int2],
+ Type t when t == typeof(int) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int4],
+ Type t when t == typeof(uint) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int4],
+ Type t when t == typeof(long) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int8],
+ Type t when t == typeof(ulong) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int8],
+ Type t when t == typeof(float) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_float4],
+ Type t when t == typeof(double)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_float8],
+ Type t when t == typeof(decimal)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_numeric],
+ Type t when t == typeof(char) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar],
+ Type t when t == typeof(string)
+ => descriptor.Length.GetValueOrDefault(255) < 8000
+ ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[PostgreSqlTypes.sql_text],
+ Type t when t == typeof(char[])
+ => descriptor.Length.GetValueOrDefault(255) < 8000
+ ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[PostgreSqlTypes.sql_text],
+ Type t when t == typeof(ReadOnlyMemory[])
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[PostgreSqlTypes.sql_text],
+ Type t when t == typeof(Stream)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[PostgreSqlTypes.sql_text],
+ Type t when t == typeof(TextReader)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[PostgreSqlTypes.sql_text],
+ Type t when t == typeof(byte[]) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_bytea],
+ Type t when t == typeof(object)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[PostgreSqlTypes.sql_text],
+ Type t when t == typeof(object[])
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[PostgreSqlTypes.sql_varchar]
+ : ProviderSqlTypeLookup[PostgreSqlTypes.sql_text],
+ Type t when t == typeof(Guid) => ProviderSqlTypeLookup[PostgreSqlTypes.sql_uuid],
+ Type t when t == typeof(DateTime)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_timestamp],
+ Type t when t == typeof(DateTimeOffset)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_timestamptz],
+ Type t when t == typeof(TimeSpan)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_interval],
+ Type t when t == typeof(DateOnly)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_date],
+ Type t when t == typeof(TimeOnly)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_time],
+ Type t when t == typeof(BitArray) || t == typeof(BitVector32)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_varbit],
+ Type t
+ when t == typeof(ImmutableDictionary)
+ || t == typeof(Dictionary)
+ || t == typeof(IDictionary)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_hstore],
+ Type t
+ when t == typeof(JsonNode)
+ || t == typeof(JsonObject)
+ || t == typeof(JsonArray)
+ || t == typeof(JsonValue)
+ || t == typeof(JsonDocument)
+ || t == typeof(JsonElement)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_jsonb],
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle generic types
+ providerSqlType = !dotnetType.IsGenericType
+ ? null
+ : dotnetType.GetGenericTypeDefinition() switch
+ {
+ Type t
+ when t == typeof(Dictionary<,>)
+ || t == typeof(IDictionary<,>)
+ || t == typeof(List<>)
+ || t == typeof(IList<>)
+ || t == typeof(Collection<>)
+ || t == typeof(ICollection<>)
+ || t == typeof(IEnumerable<>)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_jsonb],
+ Type t when t.Name.StartsWith("NpgsqlRange")
+ => dotnetType.GetGenericArguments().First() switch
+ {
+ Type at when at == typeof(DateOnly)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_daterange],
+ Type at when at == typeof(int)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int4range],
+ Type at when at == typeof(long)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_int8range],
+ Type at when at == typeof(decimal)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_numrange],
+ Type at when at == typeof(float)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_numrange],
+ Type at when at == typeof(double)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_numrange],
+ Type at when at == typeof(DateTime)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_tsrange],
+ Type at when at == typeof(DateTimeOffset)
+ => ProviderSqlTypeLookup[PostgreSqlTypes.sql_tstzrange],
+ _ => null
+ },
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // Handle Npgsql types
+ switch (dotnetType.FullName)
+ {
+ case "System.Net.IPAddress":
+ case "NpgsqlTypes.NpgsqlInet":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_inet];
+ break;
+ case "NpgsqlTypes.NpgsqlCidr":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_cidr];
+ break;
+ case "System.Net.NetworkInformation.PhysicalAddress":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_macaddr8];
+ // providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_macaddr];
+ break;
+ case "NpgsqlTypes.NpgsqlPoint":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_point];
+ break;
+ case "NpgsqlTypes.NpgsqlLSeg":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_lseg];
+ break;
+ case "NpgsqlTypes.NpgsqlPath":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_path];
+ break;
+ case "NpgsqlTypes.NpgsqlPolygon":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_polygon];
+ break;
+ case "NpgsqlTypes.NpgsqlLine":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_line];
+ break;
+ case "NpgsqlTypes.NpgsqlCircle":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_circle];
+ break;
+ case "NpgsqlTypes.NpgsqlBox":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_box];
+ break;
+ case "NetTopologySuite.Geometries.Geometry":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_geometry];
+ break;
+ case "NpgsqlTypes.NpgsqlInterval":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_interval];
+ break;
+ case "NpgsqlTypes.NpgsqlTid":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_tid];
+ break;
+ case "NpgsqlTypes.NpgsqlTsQuery":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_tsquery];
+ break;
+ case "NpgsqlTypes.NpgsqlTsVector":
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_tsvector];
+ break;
+ }
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle array types
+ var elementType = dotnetType.IsArray ? dotnetType.GetElementType() : null;
+ if (
+ elementType != null
+ && TryGetProviderSqlTypeMatchingDotnetTypeInternal(
+ new DbProviderDotnetTypeDescriptor(elementType),
+ out var elementProviderSqlType
+ )
+ && elementProviderSqlType != null
+ )
+ {
+ switch (elementProviderSqlType.Name)
+ {
+ case PostgreSqlTypes.sql_tsrange:
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_tsmultirange];
+ break;
+ case PostgreSqlTypes.sql_numrange:
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_nummultirange];
+ break;
+ case PostgreSqlTypes.sql_daterange:
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_datemultirange];
+ break;
+ case PostgreSqlTypes.sql_int4range:
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_int4multirange];
+ break;
+ case PostgreSqlTypes.sql_int8range:
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_int8multirange];
+ break;
+ case PostgreSqlTypes.sql_tstzrange:
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_tstzmultirange];
+ break;
+ default:
+ // in postgresql, we can have array types that end with [] or ARRA
+ providerSqlType = new DbProviderSqlType(
+ DbProviderSqlTypeAffinity.Other,
+ $"{elementProviderSqlType.Name}[]"
+ );
+ break;
+ }
+ }
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle POCO type
+ if (dotnetType.IsClass || dotnetType.IsInterface)
+ {
+ providerSqlType = ProviderSqlTypeLookup[PostgreSqlTypes.sql_jsonb];
+ }
+
+ return providerSqlType != null;
+ }
+
+ protected override bool TryGetProviderSqlTypeFromFullSqlTypeName(
+ string fullSqlType,
+ out DbProviderSqlType? providerSqlType
+ )
+ {
+ if (base.TryGetProviderSqlTypeFromFullSqlTypeName(fullSqlType, out providerSqlType))
+ return true;
+
+ providerSqlType = null;
+
+ // PostgreSql (unlike other providers) supports array types for (almost) all its types
+ if (fullSqlType.EndsWith("[]", StringComparison.OrdinalIgnoreCase))
+ {
+ var elementTypeName = fullSqlType.Substring(0, fullSqlType.Length - 2);
+ if (
+ TryGetProviderSqlTypeFromFullSqlTypeName(
+ elementTypeName,
+ out var elementProviderSqlType
+ )
+ && elementProviderSqlType != null
+ )
+ {
+ providerSqlType = new DbProviderSqlType(
+ DbProviderSqlTypeAffinity.Other,
+ $"{elementProviderSqlType.Name}[]"
+ );
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/src/DapperMatic/Providers/PostgreSql/PostgreSqlTypes.cs b/src/DapperMatic/Providers/PostgreSql/PostgreSqlTypes.cs
index 85b0e8c..d710ff6 100644
--- a/src/DapperMatic/Providers/PostgreSql/PostgreSqlTypes.cs
+++ b/src/DapperMatic/Providers/PostgreSql/PostgreSqlTypes.cs
@@ -19,7 +19,7 @@ public static class PostgreSqlTypes
public const string sql_int8 = "int8";
public const string sql_bigserial = "bigserial";
public const string sql_serial8 = "serial8";
-
+
// real
public const string sql_float4 = "float4";
public const string sql_real = "real";
@@ -28,7 +28,7 @@ public static class PostgreSqlTypes
public const string sql_money = "money";
public const string sql_numeric = "numeric";
public const string sql_decimal = "decimal";
-
+
// bool
public const string sql_bool = "bool";
public const string sql_boolean = "boolean";
@@ -44,7 +44,7 @@ public static class PostgreSqlTypes
public const string sql_timestamp = "timestamp";
public const string sql_timestamp_with_time_zone = "timestamp with time zone";
public const string sql_timestamptz = "timestamptz";
-
+
// text
public const string sql_bit = "bit";
public const string sql_bit_varying = "bit varying";
@@ -61,10 +61,10 @@ public static class PostgreSqlTypes
public const string sql_jsonb = "jsonb";
public const string sql_jsonpath = "jsonpath";
public const string sql_xml = "xml";
-
+
// binary
public const string sql_bytea = "bytea";
-
+
// geometry
public const string sql_box = "box";
public const string sql_circle = "circle";
@@ -119,4 +119,4 @@ public static class PostgreSqlTypes
public const string sql_txid_snapshot = "txid_snapshot";
public const string sql_xid = "xid";
public const string sql_xid8 = "xid8";
-}
\ No newline at end of file
+}
diff --git a/src/DapperMatic/Providers/ProviderSqlType.cs b/src/DapperMatic/Providers/ProviderSqlType.cs
deleted file mode 100644
index 0a84aca..0000000
--- a/src/DapperMatic/Providers/ProviderSqlType.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-namespace DapperMatic.Providers;
-
-///
-/// The provider SQL type.
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-public class ProviderSqlType(
- ProviderSqlTypeAffinity affinity,
- string name,
- Type? recommendedDotnetType = null,
- string? aliasOf = null,
- string? formatWithLength = null,
- string? formatWithPrecision = null,
- string? formatWithPrecisionAndScale = null,
- int? defaultLength = null,
- int? defaultPrecision = null,
- int? defaultScale = null,
- bool canUseToAutoIncrement = false,
- bool autoIncrementsAutomatically = false,
- double? minValue = null,
- double? maxValue = null,
- bool includesTimeZone = false,
- bool isDateOnly = false,
- bool isTimeOnly = false,
- bool isYearOnly = false,
- bool isMaxStringLengthType = false,
- bool isFixedLength = false,
- bool isGuidOnly = false)
-{
- public ProviderSqlTypeAffinity Affinity { get; init; } = affinity;
- public string Name { get; init; } = name;
- public Type? RecommendedDotnetType { get; init; } = recommendedDotnetType;
- public string? AliasOf { get; set; } = aliasOf;
- public string? FormatWithLength { get; init; } = formatWithLength;
- public string? FormatWithPrecision { get; init; } = formatWithPrecision;
- public string? FormatWithPrecisionAndScale { get; init; } = formatWithPrecisionAndScale;
- public int? DefaultLength { get; set; } = defaultLength;
- public int? DefaultPrecision { get; set; } = defaultPrecision;
- public int? DefaultScale { get; set; } = defaultScale;
- public bool CanUseToAutoIncrement { get; init; } = canUseToAutoIncrement;
- public bool AutoIncrementsAutomatically { get; init; } = autoIncrementsAutomatically;
- public double? MinValue { get; init; } = minValue;
- public double? MaxValue { get; init; } = maxValue;
- public bool IncludesTimeZone { get; init; } = includesTimeZone;
- public bool IsDateOnly { get; init; } = isDateOnly;
- public bool IsTimeOnly { get; init; } = isTimeOnly;
- public bool IsYearOnly { get; init; } = isYearOnly;
- public bool IsMaxStringLengthType { get; init; } = isMaxStringLengthType;
- public bool IsFixedLength { get; init; } = isFixedLength;
- public bool IsGuidOnly { get; init; } = isGuidOnly;
-}
-
-
-public static class ProviderSqlTypeExtensions
-{
- public static bool SupportsLength(this ProviderSqlType providerSqlType) =>
- !string.IsNullOrWhiteSpace(providerSqlType.FormatWithLength);
-
- public static bool SupportsPrecision(this ProviderSqlType providerSqlType) =>
- !string.IsNullOrWhiteSpace(providerSqlType.FormatWithPrecision);
-
- public static bool SupportsPrecisionAndScale(this ProviderSqlType providerSqlType) =>
- !string.IsNullOrWhiteSpace(providerSqlType.FormatWithPrecisionAndScale);
-}
\ No newline at end of file
diff --git a/src/DapperMatic/Providers/ProviderTypeMapBase.cs b/src/DapperMatic/Providers/ProviderTypeMapBase.cs
deleted file mode 100644
index addcc24..0000000
--- a/src/DapperMatic/Providers/ProviderTypeMapBase.cs
+++ /dev/null
@@ -1,992 +0,0 @@
-using System.Collections.Concurrent;
-using System.Collections.ObjectModel;
-
-namespace DapperMatic.Providers;
-
-public abstract class ProviderTypeMapBase : IProviderTypeMap
-{
- // ReSharper disable once MemberCanBePrivate.Global
- // ReSharper disable once CollectionNeverUpdated.Global
- public static readonly ConcurrentDictionary> TypeMaps =
- new();
-
- protected abstract DbProviderType ProviderType { get; }
- protected abstract ProviderSqlType[] ProviderSqlTypes { get; }
-
- public virtual bool TryGetRecommendedDotnetTypeMatchingSqlType(
- string fullSqlType,
- out (
- Type dotnetType,
- int? length,
- int? precision,
- int? scale,
- bool? isAutoIncrementing,
- Type[] allSupportedTypes
- )? recommendedDotnetType
- )
- {
- recommendedDotnetType = null;
-
- if (TypeMaps.TryGetValue(ProviderType, out var additionalTypeMaps))
- {
- foreach (var typeMap in additionalTypeMaps)
- {
- if (typeMap.TryGetRecommendedDotnetTypeMatchingSqlType(fullSqlType, out var rdt))
- {
- recommendedDotnetType = rdt;
- return true;
- }
- }
- }
-
- // perform some detective reasoning to pinpoint a recommended type
- var numbers = fullSqlType.ExtractNumbers();
-
- // try to find a sql provider type match
- var fullSqlTypeAlpha = fullSqlType.ToAlpha();
- var sqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.ToAlpha().Equals(fullSqlTypeAlpha, StringComparison.OrdinalIgnoreCase)
- );
- if (sqlType == null)
- return false;
-
- var isAutoIncrementing = sqlType.AutoIncrementsAutomatically;
-
- switch (sqlType.Affinity)
- {
- case ProviderSqlTypeAffinity.Binary:
- recommendedDotnetType = (typeof(byte[]), null, null, null, null, [typeof(byte[])]);
- break;
- case ProviderSqlTypeAffinity.Boolean:
- recommendedDotnetType = (
- typeof(bool),
- null,
- null,
- null,
- null,
- [
- typeof(bool),
- typeof(short),
- typeof(int),
- typeof(long),
- typeof(ushort),
- typeof(uint),
- typeof(ulong),
- typeof(string)
- ]
- );
- break;
- case ProviderSqlTypeAffinity.DateTime:
- if (sqlType.IsDateOnly == true)
- recommendedDotnetType = (
- typeof(DateOnly),
- null,
- null,
- null,
- null,
- [typeof(DateOnly), typeof(DateTime), typeof(string)]
- );
- else if (sqlType.IsTimeOnly == true)
- recommendedDotnetType = (
- typeof(TimeOnly),
- null,
- null,
- null,
- null,
- [typeof(TimeOnly), typeof(DateTime), typeof(string)]
- );
- else if (sqlType.IsYearOnly == true)
- recommendedDotnetType = (
- typeof(int),
- null,
- null,
- null,
- null,
- [
- typeof(short),
- typeof(int),
- typeof(long),
- typeof(ushort),
- typeof(uint),
- typeof(ulong),
- typeof(string)
- ]
- );
- else if (sqlType.IncludesTimeZone == true)
- recommendedDotnetType = (
- typeof(DateTimeOffset),
- null,
- null,
- null,
- null,
- [typeof(DateTimeOffset), typeof(DateTime), typeof(string)]
- );
- else
- recommendedDotnetType = (
- typeof(DateTime),
- null,
- null,
- null,
- null,
- [typeof(DateTime), typeof(DateTimeOffset), typeof(string)]
- );
- break;
- case ProviderSqlTypeAffinity.Integer:
- int? intPrecision = numbers.Length > 0 ? numbers[0] : null;
- if (sqlType.MinValue.HasValue && sqlType.MinValue == 0)
- {
- if (sqlType.MaxValue.HasValue)
- {
- if (sqlType.MaxValue.Value <= ushort.MaxValue)
- recommendedDotnetType = (
- typeof(ushort),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [
- typeof(short),
- typeof(int),
- typeof(long),
- typeof(ushort),
- typeof(uint),
- typeof(ulong),
- typeof(string)
- ]
- );
- else if (sqlType.MaxValue.Value <= uint.MaxValue)
- recommendedDotnetType = (
- typeof(uint),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [
- typeof(int),
- typeof(long),
- typeof(uint),
- typeof(ulong),
- typeof(string)
- ]
- );
- else if (sqlType.MaxValue.Value <= ulong.MaxValue)
- recommendedDotnetType = (
- typeof(ulong),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [typeof(long), typeof(ulong), typeof(string)]
- );
- }
- if (recommendedDotnetType == null)
- {
- recommendedDotnetType = (
- typeof(uint),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [typeof(int), typeof(long), typeof(uint), typeof(ulong), typeof(string)]
- );
- }
- }
- if (recommendedDotnetType == null)
- {
- if (sqlType.MaxValue.HasValue)
- {
- if (sqlType.MaxValue.Value <= short.MaxValue)
- recommendedDotnetType = (
- typeof(short),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [typeof(short), typeof(int), typeof(long), typeof(string)]
- );
- else if (sqlType.MaxValue.Value <= int.MaxValue)
- recommendedDotnetType = (
- typeof(int),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [typeof(int), typeof(long), typeof(string)]
- );
- else if (sqlType.MaxValue.Value <= long.MaxValue)
- recommendedDotnetType = (
- typeof(long),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [typeof(long), typeof(string)]
- );
- }
- if (recommendedDotnetType == null)
- {
- recommendedDotnetType = (
- typeof(int),
- null,
- intPrecision,
- null,
- isAutoIncrementing,
- [typeof(int), typeof(long), typeof(string)]
- );
- }
- }
- break;
- case ProviderSqlTypeAffinity.Real:
- int? precision = numbers.Length > 0 ? numbers[0] : null;
- int? scale = numbers.Length > 1 ? numbers[1] : null;
- recommendedDotnetType = (
- typeof(decimal),
- null,
- precision,
- scale,
- isAutoIncrementing,
- [typeof(decimal), typeof(float), typeof(double), typeof(string)]
- );
- break;
- case ProviderSqlTypeAffinity.Text:
- int? length = numbers.Length > 0 ? numbers[0] : null;
- if (length > 8000)
- length = int.MaxValue;
- recommendedDotnetType = (
- typeof(string),
- null,
- length,
- null,
- null,
- [typeof(string)]
- );
- break;
- case ProviderSqlTypeAffinity.Geometry:
- case ProviderSqlTypeAffinity.RangeType:
- case ProviderSqlTypeAffinity.Other:
- if (
- sqlType.Name.Contains("json", StringComparison.OrdinalIgnoreCase)
- || sqlType.Name.Contains("xml", StringComparison.OrdinalIgnoreCase)
- )
- recommendedDotnetType = (
- typeof(string),
- null,
- null,
- null,
- null,
- [typeof(string)]
- );
- else
- recommendedDotnetType = (
- typeof(object),
- null,
- null,
- null,
- null,
- [typeof(object), typeof(string)]
- );
- break;
- }
-
- return recommendedDotnetType != null;
- }
-
- public virtual bool TryGetRecommendedSqlTypeMatchingDotnetType(
- Type dotnetType,
- int? length,
- int? precision,
- int? scale,
- bool? autoIncrement,
- out ProviderSqlType? recommendedSqlType
- )
- {
- recommendedSqlType = null;
-
- if (TypeMaps.TryGetValue(ProviderType, out var additionalTypeMaps))
- {
- foreach (var typeMap in additionalTypeMaps)
- {
- if (
- typeMap.TryGetRecommendedSqlTypeMatchingDotnetType(
- dotnetType,
- length,
- precision,
- scale,
- autoIncrement,
- out var rdt
- )
- )
- {
- recommendedSqlType = rdt;
- return true;
- }
- }
- }
-
- if (ProviderType == DbProviderType.PostgreSql)
- {
- // Handle well-known types
- var typeName = dotnetType.Name;
- switch (typeName)
- {
- case "IPAddress":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("inet", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlCidr4":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("cidr", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "PhysicalAddress":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("macaddr", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlTsQuery":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("tsquery", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlTsVector":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("tsvector", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlPoint":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("point", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlLSeg":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("lseg", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlPath":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("path", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlPolygon":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("polygon", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlLine":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("line", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlCircle":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("circle", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "NpgsqlBox":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("box", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case "PostgisGeometry":
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("geometry", StringComparison.OrdinalIgnoreCase)
- );
- break;
- }
-
- if (
- dotnetType == typeof(Dictionary)
- || dotnetType == typeof(IDictionary)
- )
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Name.Equals("hstore", StringComparison.OrdinalIgnoreCase)
- );
-
- if (recommendedSqlType != null)
- return true;
- }
-
- // the dotnetType could be a nullable type, so we need to check for that
- // and get the underlying type
- if (dotnetType.IsGenericType && dotnetType.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- dotnetType = Nullable.GetUnderlyingType(dotnetType)!;
- }
-
- // We're trying to find the right type to use as a lookup type
- // IDictionary<,> Dictionary<,> IEnumerable<> ICollection<> List<> object[]
- if (dotnetType.IsArray)
- {
- // dotnetType = dotnetType.GetElementType()!;
- dotnetType = typeof(object[]);
- }
- else if (
- dotnetType.IsGenericType
- && dotnetType.GetGenericTypeDefinition() == typeof(List<>)
- )
- {
- // dotnetType = dotnetType.GetGenericArguments()[0];
- dotnetType = typeof(List<>);
- }
- else if (
- dotnetType.IsGenericType
- && dotnetType.GetGenericTypeDefinition() == typeof(IDictionary<,>)
- )
- {
- // dotnetType = dotnetType.GetGenericArguments()[1];
- dotnetType = typeof(IDictionary<,>);
- }
- else if (
- dotnetType.IsGenericType
- && dotnetType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
- )
- {
- // dotnetType = dotnetType.GetGenericArguments()[1];
- dotnetType = typeof(Dictionary<,>);
- }
- else if (
- dotnetType.IsGenericType
- && dotnetType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
- )
- {
- // dotnetType = dotnetType.GetGenericArguments()[0];
- dotnetType = typeof(IEnumerable<>);
- }
- else if (
- dotnetType.IsGenericType
- && dotnetType.GetGenericTypeDefinition() == typeof(ICollection<>)
- )
- {
- // dotnetType = dotnetType.GetGenericArguments()[0];
- dotnetType = typeof(ICollection<>);
- }
- else if (
- dotnetType.IsGenericType
- && dotnetType.GetGenericTypeDefinition() == typeof(IList<>)
- )
- {
- // dotnetType = dotnetType.GetGenericArguments()[0];
- dotnetType = typeof(IList<>);
- }
- else if (dotnetType.IsGenericType)
- {
- // could probably just stick with this, but the above
- // is more explicit for now
- dotnetType = dotnetType.GetGenericTypeDefinition();
- }
-
- // WARNING!! The following showcases why the order within each affinity group of the provider sql types matters, as the recommended type
- // is going to be the first match for the given scenario
- switch (dotnetType)
- {
- case not null when dotnetType == typeof(sbyte):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(sbyte.MinValue) <= sbyte.MinValue
- && t.MaxValue.GetValueOrDefault(sbyte.MaxValue) >= sbyte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(sbyte.MaxValue) >= sbyte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(sbyte.MinValue) <= sbyte.MinValue
- && t.MaxValue.GetValueOrDefault(sbyte.MaxValue) >= sbyte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(sbyte.MaxValue) >= sbyte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(sbyte.MinValue) <= sbyte.MinValue
- && t.MaxValue.GetValueOrDefault(sbyte.MaxValue) >= sbyte.MaxValue
- );
- break;
- case not null when dotnetType == typeof(byte):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(byte.MinValue) <= byte.MinValue
- && t.MaxValue.GetValueOrDefault(byte.MaxValue) >= byte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(byte.MaxValue) >= byte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(byte.MinValue) <= byte.MinValue
- && t.MaxValue.GetValueOrDefault(byte.MaxValue) >= byte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(byte.MaxValue) >= byte.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(byte.MinValue) <= byte.MinValue
- && t.MaxValue.GetValueOrDefault(byte.MaxValue) >= byte.MaxValue
- );
- break;
- case not null when dotnetType == typeof(short):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(short.MinValue) <= short.MinValue
- && t.MaxValue.GetValueOrDefault(short.MaxValue) >= short.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(short.MaxValue) >= short.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(short.MinValue) <= short.MinValue
- && t.MaxValue.GetValueOrDefault(short.MaxValue) >= short.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(short.MaxValue) >= short.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(short.MinValue) <= short.MinValue
- && t.MaxValue.GetValueOrDefault(short.MaxValue) >= short.MaxValue
- );
- break;
- case not null when dotnetType == typeof(int):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(int.MinValue) <= int.MinValue
- && t.MaxValue.GetValueOrDefault(int.MaxValue) >= int.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(int.MaxValue) >= int.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(int.MinValue) <= int.MinValue
- && t.MaxValue.GetValueOrDefault(int.MaxValue) >= int.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(int.MaxValue) >= int.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(int.MinValue) <= int.MinValue
- && t.MaxValue.GetValueOrDefault(int.MaxValue) >= int.MaxValue
- );
- break;
- case not null when dotnetType == typeof(long):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(long.MinValue) <= long.MinValue
- && t.MaxValue.GetValueOrDefault(long.MaxValue) >= long.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(long.MaxValue) >= long.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(long.MinValue) <= long.MinValue
- && t.MaxValue.GetValueOrDefault(long.MaxValue) >= long.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(long.MaxValue) >= long.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(long.MinValue) <= long.MinValue
- && t.MaxValue.GetValueOrDefault(long.MaxValue) >= long.MaxValue
- );
- break;
- case not null when dotnetType == typeof(ushort):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(ushort.MinValue) <= ushort.MinValue
- && t.MaxValue.GetValueOrDefault(ushort.MaxValue) >= ushort.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(ushort.MaxValue) >= ushort.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(ushort.MinValue) <= ushort.MinValue
- && t.MaxValue.GetValueOrDefault(ushort.MaxValue) >= ushort.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(ushort.MaxValue) >= ushort.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(0) == 0
- && t.MaxValue.GetValueOrDefault(ushort.MaxValue) >= ushort.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MaxValue.GetValueOrDefault(ushort.MaxValue) >= ushort.MaxValue
- );
- break;
- case not null when dotnetType == typeof(uint):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(uint.MinValue) <= uint.MinValue
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) >= uint.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) >= uint.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(uint.MinValue) <= uint.MinValue
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) >= uint.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) >= uint.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(0) == 0
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) >= uint.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) >= uint.MaxValue
- );
- break;
- case not null when dotnetType == typeof(ulong):
- if (autoIncrement.GetValueOrDefault(false))
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MinValue.GetValueOrDefault(ulong.MinValue) <= ulong.MinValue
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.AutoIncrementsAutomatically
- && t.MaxValue.GetValueOrDefault(long.MaxValue) >= long.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MinValue.GetValueOrDefault(ulong.MinValue) <= ulong.MinValue
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.CanUseToAutoIncrement
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer && t.CanUseToAutoIncrement
- );
- else
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(0) == 0
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- );
- break;
- case not null when dotnetType == typeof(bool):
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Boolean
- );
- break;
- case not null when dotnetType == typeof(decimal):
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.MinValue.GetValueOrDefault((double)decimal.MinValue)
- <= (double)decimal.MinValue
- && t.MaxValue.GetValueOrDefault((double)decimal.MaxValue)
- >= (double)decimal.MaxValue
- );
- break;
- case not null when dotnetType == typeof(double):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Equals("double", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Contains("double", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.MinValue.GetValueOrDefault(double.MinValue) <= double.MinValue
- && t.MaxValue.GetValueOrDefault(double.MaxValue) >= double.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Equals("float", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Equals("numeric", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Equals("decimal", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case not null when dotnetType == typeof(float):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Equals("float", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Contains("float", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.MinValue.GetValueOrDefault(float.MinValue) <= float.MinValue
- && t.MaxValue.GetValueOrDefault(float.MaxValue) >= float.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Equals("numeric", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Real
- && t.Name.Equals("decimal", StringComparison.OrdinalIgnoreCase)
- );
- break;
- case not null when dotnetType == typeof(DateTime):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime && t.IncludesTimeZone != true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime
- );
- break;
- case not null when dotnetType == typeof(DateTimeOffset):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime && t.IncludesTimeZone == true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime
- );
- break;
- case not null when dotnetType == typeof(DateOnly):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime && t.IsDateOnly == true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime
- );
- break;
- case not null when dotnetType == typeof(TimeOnly):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime && t.IsTimeOnly == true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.DateTime
- );
- break;
- case not null when dotnetType == typeof(TimeSpan):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MinValue.GetValueOrDefault(0) == 0
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MaxValue.GetValueOrDefault(ulong.MaxValue) >= ulong.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) > uint.MaxValue
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Integer
- && t.MaxValue.GetValueOrDefault(uint.MaxValue) >= uint.MaxValue
- );
- break;
- case not null when dotnetType == typeof(byte[]):
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Binary
- );
- break;
- case not null when dotnetType == typeof(Guid):
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text && t.IsGuidOnly == true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- && !string.IsNullOrWhiteSpace(t.FormatWithLength)
- && t.IsFixedLength == true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- && !string.IsNullOrWhiteSpace(t.FormatWithLength)
- && t.IsFixedLength != true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text && t.IsFixedLength != true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- );
- break;
- case not null when dotnetType == typeof(string):
- case not null when dotnetType == typeof(char[]):
- if (length.HasValue && length.Value > 8000)
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- && t.IsMaxStringLengthType == true
- && t.IsFixedLength != true
- );
- if (recommendedSqlType == null && length.HasValue && length.Value <= 8000)
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- && !string.IsNullOrWhiteSpace(t.FormatWithLength)
- && t.IsFixedLength != true
- );
- if (recommendedSqlType == null)
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text && t.IsFixedLength != true
- );
- break;
- case not null when dotnetType == typeof(Dictionary<,>):
- case not null when dotnetType == typeof(IDictionary<,>):
- case not null when dotnetType == typeof(IEnumerable<>):
- case not null when dotnetType == typeof(ICollection<>):
- case not null when dotnetType == typeof(List<>):
- case not null when dotnetType == typeof(IList<>):
- case not null when dotnetType == typeof(object[]):
- case not null when dotnetType == typeof(object):
- case not null when dotnetType.IsClass:
- recommendedSqlType =
- ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- && t.Name.Contains("json", StringComparison.OrdinalIgnoreCase)
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- && t.IsMaxStringLengthType == true
- && t.IsFixedLength != true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text
- && !string.IsNullOrWhiteSpace(t.FormatWithLength)
- && t.IsFixedLength != true
- )
- ?? ProviderSqlTypes.FirstOrDefault(t =>
- t.Affinity == ProviderSqlTypeAffinity.Text && t.IsFixedLength != true
- );
- break;
- }
-
- if (recommendedSqlType == null)
- {
- // couldn't find the appropriate type, so we'll just use the first one that matches the requested type
- // if such exists (NOT IDEAL!!)
- recommendedSqlType = ProviderSqlTypes.FirstOrDefault(t =>
- t.RecommendedDotnetType == dotnetType
- );
- }
-
- return recommendedSqlType != null;
- }
-}
diff --git a/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Strings.cs b/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Strings.cs
index 5a61489..a535bb9 100644
--- a/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Strings.cs
+++ b/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Strings.cs
@@ -1,3 +1,5 @@
+using DapperMatic.Models;
+
namespace DapperMatic.Providers.SqlServer;
public partial class SqlServerMethods
@@ -41,26 +43,26 @@ string newTableName
protected override (string sql, object parameters) SqlGetViewNames(
string? schemaName,
- string? viewNameFilter = null)
+ string? viewNameFilter = null
+ )
{
var where = string.IsNullOrWhiteSpace(viewNameFilter) ? "" : ToLikeString(viewNameFilter);
- var sql =
- $"""
-
- SELECT
- v.[name] AS ViewName
- FROM sys.objects v
- INNER JOIN sys.sql_modules m ON v.object_id = m.object_id
- WHERE
- v.[type] = 'V'
- AND v.is_ms_shipped = 0
- AND SCHEMA_NAME(v.schema_id) = @schemaName
- {(string.IsNullOrWhiteSpace(where) ? "" : " AND v.[name] LIKE @where")}
- ORDER BY
- SCHEMA_NAME(v.schema_id),
- v.[name]
- """;
+ var sql = $"""
+
+ SELECT
+ v.[name] AS ViewName
+ FROM sys.objects v
+ INNER JOIN sys.sql_modules m ON v.object_id = m.object_id
+ WHERE
+ v.[type] = 'V'
+ AND v.is_ms_shipped = 0
+ AND SCHEMA_NAME(v.schema_id) = @schemaName
+ {(string.IsNullOrWhiteSpace(where) ? "" : " AND v.[name] LIKE @where")}
+ ORDER BY
+ SCHEMA_NAME(v.schema_id),
+ v.[name]
+ """;
return (sql, new { schemaName = NormalizeSchemaName(schemaName), where });
}
@@ -72,24 +74,23 @@ protected override (string sql, object parameters) SqlGetViews(
{
var where = string.IsNullOrWhiteSpace(viewNameFilter) ? "" : ToLikeString(viewNameFilter);
- var sql =
- $"""
-
- SELECT
- SCHEMA_NAME(v.schema_id) AS SchemaName,
- v.[name] AS ViewName,
- m.definition AS Definition
- FROM sys.objects v
- INNER JOIN sys.sql_modules m ON v.object_id = m.object_id
- WHERE
- v.[type] = 'V'
- AND v.is_ms_shipped = 0
- AND SCHEMA_NAME(v.schema_id) = @schemaName
- {(string.IsNullOrWhiteSpace(where) ? "" : " AND v.[name] LIKE @where")}
- ORDER BY
- SCHEMA_NAME(v.schema_id),
- v.[name]
- """;
+ var sql = $"""
+
+ SELECT
+ SCHEMA_NAME(v.schema_id) AS SchemaName,
+ v.[name] AS ViewName,
+ m.definition AS Definition
+ FROM sys.objects v
+ INNER JOIN sys.sql_modules m ON v.object_id = m.object_id
+ WHERE
+ v.[type] = 'V'
+ AND v.is_ms_shipped = 0
+ AND SCHEMA_NAME(v.schema_id) = @schemaName
+ {(string.IsNullOrWhiteSpace(where) ? "" : " AND v.[name] LIKE @where")}
+ ORDER BY
+ SCHEMA_NAME(v.schema_id),
+ v.[name]
+ """;
return (sql, new { schemaName = NormalizeSchemaName(schemaName), where });
}
@@ -109,12 +110,14 @@ protected override string NormalizeViewDefinition(string definition)
if (i == definition.Length - 2)
break;
- if (!WhiteSpaceCharacters.Contains(definition[i - 1])
+ if (
+ !WhiteSpaceCharacters.Contains(definition[i - 1])
|| char.ToUpperInvariant(definition[i]) != 'A'
|| char.ToUpperInvariant(definition[i + 1]) != 'S'
- || !WhiteSpaceCharacters.Contains(definition[i + 2]))
+ || !WhiteSpaceCharacters.Contains(definition[i + 2])
+ )
continue;
-
+
indexOfAs = i;
break;
}
diff --git a/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Tables.cs b/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Tables.cs
index a10dfa1..24a6e40 100644
--- a/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Tables.cs
+++ b/src/DapperMatic/Providers/SqlServer/SqlServerMethods.Tables.cs
@@ -389,14 +389,17 @@ string default_expression
)
?.i;
- var (dotnetType, _, _, _, _, _) = GetDotnetTypeFromSqlType(tableColumn.data_type);
+ var dotnetTypeDescriptor = GetDotnetTypeFromSqlType(tableColumn.data_type);
var column = new DxColumn(
tableColumn.schema_name,
tableColumn.table_name,
tableColumn.column_name,
- dotnetType,
- tableColumn.data_type,
+ dotnetTypeDescriptor.DotnetType,
+ new Dictionary
+ {
+ { ProviderType, tableColumn.data_type }
+ },
tableColumn.max_length,
tableColumn.numeric_precision,
tableColumn.numeric_scale,
diff --git a/src/DapperMatic/Providers/SqlServer/SqlServerMethods.cs b/src/DapperMatic/Providers/SqlServer/SqlServerMethods.cs
index f81661e..e493182 100644
--- a/src/DapperMatic/Providers/SqlServer/SqlServerMethods.cs
+++ b/src/DapperMatic/Providers/SqlServer/SqlServerMethods.cs
@@ -8,7 +8,7 @@ public partial class SqlServerMethods : DatabaseMethodsBase, IDatabaseMethods
{
public override DbProviderType ProviderType => DbProviderType.SqlServer;
- public override IProviderTypeMap ProviderTypeMap => SqlServerProviderTypeMap.Instance.Value;
+ public override IDbProviderTypeMap ProviderTypeMap => SqlServerProviderTypeMap.Instance.Value;
private static string _defaultSchema = "dbo";
protected override string DefaultSchema => _defaultSchema;
@@ -36,7 +36,7 @@ public override async Task GetDatabaseVersionAsync(
const string sql = "SELECT SERVERPROPERTY('Productversion')";
var versionString =
await ExecuteScalarAsync(db, sql, tx: tx).ConfigureAwait(false) ?? "";
- return ProviderUtils.ExtractVersionFromVersionString(versionString);
+ return DbProviderUtils.ExtractVersionFromVersionString(versionString);
}
public override char[] QuoteChars => ['[', ']'];
diff --git a/src/DapperMatic/Providers/SqlServer/SqlServerProviderTypeMap.cs b/src/DapperMatic/Providers/SqlServer/SqlServerProviderTypeMap.cs
index a6f6799..d8de819 100644
--- a/src/DapperMatic/Providers/SqlServer/SqlServerProviderTypeMap.cs
+++ b/src/DapperMatic/Providers/SqlServer/SqlServerProviderTypeMap.cs
@@ -1,6 +1,13 @@
+using System.Collections;
+using System.Collections.Immutable;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+
namespace DapperMatic.Providers.SqlServer;
-public sealed class SqlServerProviderTypeMap : ProviderTypeMapBase
+public sealed class SqlServerProviderTypeMap : DbProviderTypeMapBase
{
internal static readonly Lazy Instance =
new(() => new SqlServerProviderTypeMap());
@@ -8,43 +15,49 @@ public sealed class SqlServerProviderTypeMap : ProviderTypeMapBase
private SqlServerProviderTypeMap()
: base() { }
- protected override DbProviderType ProviderType => DbProviderType.Sqlite;
+ protected override DbProviderType ProviderType => DbProviderType.SqlServer;
+
+ public override string SqTypeForStringLengthMax => "nvarchar(max)";
+
+ public override string SqTypeForBinaryLengthMax => "varbinary(max)";
+
+ public override string SqlTypeForJson => "nvarchar(max)";
///
/// IMPORTANT!! The order within an affinity group matters, as the first possible match will be used as the recommended sql type for a dotnet type
///
- protected override ProviderSqlType[] ProviderSqlTypes =>
+ protected override DbProviderSqlType[] ProviderSqlTypes =>
[
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
SqlServerTypes.sql_tinyint,
canUseToAutoIncrement: true,
minValue: -128,
maxValue: 128
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
SqlServerTypes.sql_smallint,
canUseToAutoIncrement: true,
minValue: -32768,
maxValue: 32767
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
SqlServerTypes.sql_int,
canUseToAutoIncrement: true,
minValue: -2147483648,
maxValue: 2147483647
),
new(
- ProviderSqlTypeAffinity.Integer,
+ DbProviderSqlTypeAffinity.Integer,
SqlServerTypes.sql_bigint,
canUseToAutoIncrement: true,
minValue: -9223372036854775808,
maxValue: 9223372036854775807
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
SqlServerTypes.sql_decimal,
formatWithPrecision: "decimal({0})",
formatWithPrecisionAndScale: "decimal({0},{1})",
@@ -52,7 +65,7 @@ private SqlServerProviderTypeMap()
defaultScale: 0
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
SqlServerTypes.sql_numeric,
formatWithPrecision: "numeric({0})",
formatWithPrecisionAndScale: "numeric({0},{1})",
@@ -60,92 +73,214 @@ private SqlServerProviderTypeMap()
defaultScale: 0
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
SqlServerTypes.sql_float,
formatWithPrecision: "float({0})",
defaultPrecision: 53,
defaultScale: 0
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
SqlServerTypes.sql_real,
formatWithPrecision: "real({0})",
defaultPrecision: 24,
defaultScale: 0
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
SqlServerTypes.sql_money,
formatWithPrecision: "money({0})",
defaultPrecision: 19,
defaultScale: 4
),
new(
- ProviderSqlTypeAffinity.Real,
+ DbProviderSqlTypeAffinity.Real,
SqlServerTypes.sql_smallmoney,
formatWithPrecision: "smallmoney({0})",
defaultPrecision: 10,
defaultScale: 4
),
- new(ProviderSqlTypeAffinity.Boolean, SqlServerTypes.sql_bit),
- new(ProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_date, isDateOnly: true),
- new(ProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_datetime),
- new(ProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_smalldatetime),
- new(ProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_datetime2),
- new(ProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_datetimeoffset),
- new(ProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_time, isTimeOnly: true),
- new(ProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_timestamp),
+ new(DbProviderSqlTypeAffinity.Boolean, SqlServerTypes.sql_bit),
+ new(DbProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_date, isDateOnly: true),
+ new(DbProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_datetime),
+ new(DbProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_smalldatetime),
+ new(DbProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_datetime2),
+ new(DbProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_datetimeoffset),
+ new(DbProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_time, isTimeOnly: true),
+ new(DbProviderSqlTypeAffinity.DateTime, SqlServerTypes.sql_timestamp),
new(
- ProviderSqlTypeAffinity.Text,
- SqlServerTypes.sql_char,
- formatWithLength: "char({0})",
- defaultLength: 255
+ DbProviderSqlTypeAffinity.Text,
+ SqlServerTypes.sql_nvarchar,
+ formatWithLength: "nvarchar({0})",
+ defaultLength: DefaultLength
),
new(
- ProviderSqlTypeAffinity.Text,
+ DbProviderSqlTypeAffinity.Text,
SqlServerTypes.sql_varchar,
formatWithLength: "varchar({0})",
- defaultLength: 255
+ defaultLength: DefaultLength
),
- new(ProviderSqlTypeAffinity.Text, SqlServerTypes.sql_text),
+ new(DbProviderSqlTypeAffinity.Text, SqlServerTypes.sql_ntext),
+ new(DbProviderSqlTypeAffinity.Text, SqlServerTypes.sql_text),
new(
- ProviderSqlTypeAffinity.Text,
+ DbProviderSqlTypeAffinity.Text,
SqlServerTypes.sql_nchar,
formatWithLength: "nchar({0})",
- defaultLength: 255
+ defaultLength: DefaultLength
),
new(
- ProviderSqlTypeAffinity.Text,
- SqlServerTypes.sql_nvarchar,
- formatWithLength: "nvarchar({0})",
- defaultLength: 255
+ DbProviderSqlTypeAffinity.Text,
+ SqlServerTypes.sql_char,
+ formatWithLength: "char({0})",
+ defaultLength: DefaultLength
),
- new(ProviderSqlTypeAffinity.Text, SqlServerTypes.sql_ntext),
new(
- ProviderSqlTypeAffinity.Text,
+ DbProviderSqlTypeAffinity.Text,
SqlServerTypes.sql_uniqueidentifier,
isGuidOnly: true
),
new(
- ProviderSqlTypeAffinity.Binary,
+ DbProviderSqlTypeAffinity.Binary,
+ SqlServerTypes.sql_varbinary,
+ formatWithLength: "varbinary({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Binary,
SqlServerTypes.sql_binary,
formatWithLength: "binary({0})",
- defaultLength: 1024
+ defaultLength: DefaultLength
),
- new(
- ProviderSqlTypeAffinity.Binary,
- SqlServerTypes.sql_varbinary,
- formatWithLength: "varbinary({0})",
- defaultLength: 1024
- ),
- new(ProviderSqlTypeAffinity.Binary, SqlServerTypes.sql_image),
- new(ProviderSqlTypeAffinity.Geometry, SqlServerTypes.sql_geometry),
- new(ProviderSqlTypeAffinity.Geometry, SqlServerTypes.sql_geography),
- new(ProviderSqlTypeAffinity.Geometry, SqlServerTypes.sql_hierarchyid),
- new(ProviderSqlTypeAffinity.Other, SqlServerTypes.sql_variant),
- new(ProviderSqlTypeAffinity.Other, SqlServerTypes.sql_xml),
- new(ProviderSqlTypeAffinity.Other, SqlServerTypes.sql_cursor),
- new(ProviderSqlTypeAffinity.Other, SqlServerTypes.sql_table),
- new(ProviderSqlTypeAffinity.Other, SqlServerTypes.sql_json)
+ new(DbProviderSqlTypeAffinity.Binary, SqlServerTypes.sql_image),
+ new(DbProviderSqlTypeAffinity.Geometry, SqlServerTypes.sql_geometry),
+ new(DbProviderSqlTypeAffinity.Geometry, SqlServerTypes.sql_geography),
+ new(DbProviderSqlTypeAffinity.Geometry, SqlServerTypes.sql_hierarchyid),
+ new(DbProviderSqlTypeAffinity.Other, SqlServerTypes.sql_variant),
+ new(DbProviderSqlTypeAffinity.Other, SqlServerTypes.sql_xml),
+ new(DbProviderSqlTypeAffinity.Other, SqlServerTypes.sql_cursor),
+ new(DbProviderSqlTypeAffinity.Other, SqlServerTypes.sql_table),
+ new(DbProviderSqlTypeAffinity.Other, SqlServerTypes.sql_json)
];
+
+ protected override bool TryGetProviderSqlTypeMatchingDotnetTypeInternal(
+ DbProviderDotnetTypeDescriptor descriptor,
+ out DbProviderSqlType? providerSqlType
+ )
+ {
+ providerSqlType = null;
+
+ var dotnetType = descriptor.DotnetType;
+
+ // handle well-known types first
+ providerSqlType = dotnetType.IsGenericType
+ ? null
+ : dotnetType switch
+ {
+ Type t when t == typeof(bool) => ProviderSqlTypeLookup[SqlServerTypes.sql_bit],
+ Type t when t == typeof(byte) => ProviderSqlTypeLookup[SqlServerTypes.sql_smallint],
+ Type t when t == typeof(ReadOnlyMemory)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_smallint],
+ Type t when t == typeof(sbyte)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_smallint],
+ Type t when t == typeof(short)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_smallint],
+ Type t when t == typeof(ushort)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_smallint],
+ Type t when t == typeof(int) => ProviderSqlTypeLookup[SqlServerTypes.sql_int],
+ Type t when t == typeof(uint) => ProviderSqlTypeLookup[SqlServerTypes.sql_int],
+ Type t when t == typeof(long) => ProviderSqlTypeLookup[SqlServerTypes.sql_bigint],
+ Type t when t == typeof(ulong) => ProviderSqlTypeLookup[SqlServerTypes.sql_bigint],
+ Type t when t == typeof(float) => ProviderSqlTypeLookup[SqlServerTypes.sql_float],
+ Type t when t == typeof(double) => ProviderSqlTypeLookup[SqlServerTypes.sql_float],
+ Type t when t == typeof(decimal)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_decimal],
+ Type t when t == typeof(char)
+ => descriptor.Unicode.GetValueOrDefault(true)
+ ? ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar]
+ : ProviderSqlTypeLookup[SqlServerTypes.sql_varchar],
+ Type t when t == typeof(string)
+ => descriptor.Unicode.GetValueOrDefault(true)
+ ? ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar]
+ : ProviderSqlTypeLookup[SqlServerTypes.sql_varchar],
+ Type t when t == typeof(char[])
+ => descriptor.Unicode.GetValueOrDefault(true)
+ ? ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar]
+ : ProviderSqlTypeLookup[SqlServerTypes.sql_varchar],
+ Type t when t == typeof(ReadOnlyMemory[])
+ => descriptor.Unicode.GetValueOrDefault(true)
+ ? ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar]
+ : ProviderSqlTypeLookup[SqlServerTypes.sql_varchar],
+ Type t when t == typeof(Stream)
+ => descriptor.Unicode.GetValueOrDefault(true)
+ ? ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar]
+ : ProviderSqlTypeLookup[SqlServerTypes.sql_varchar],
+ Type t when t == typeof(TextReader)
+ => descriptor.Unicode.GetValueOrDefault(true)
+ ? ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar]
+ : ProviderSqlTypeLookup[SqlServerTypes.sql_varchar],
+ Type t when t == typeof(byte[])
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_varbinary],
+ Type t when t == typeof(object)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar],
+ Type t when t == typeof(object[])
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar],
+ Type t when t == typeof(Guid)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_uniqueidentifier],
+ Type t when t == typeof(DateTime)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_datetime],
+ Type t when t == typeof(DateTimeOffset)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_datetimeoffset],
+ Type t when t == typeof(TimeSpan)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_bigint],
+ Type t when t == typeof(DateOnly) => ProviderSqlTypeLookup[SqlServerTypes.sql_date],
+ Type t when t == typeof(TimeOnly) => ProviderSqlTypeLookup[SqlServerTypes.sql_time],
+ Type t when t == typeof(BitArray) || t == typeof(BitVector32)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_varbinary],
+ Type t
+ when t == typeof(ImmutableDictionary)
+ || t == typeof(Dictionary)
+ || t == typeof(IDictionary)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar],
+ Type t
+ when t == typeof(JsonNode)
+ || t == typeof(JsonObject)
+ || t == typeof(JsonArray)
+ || t == typeof(JsonValue)
+ || t == typeof(JsonDocument)
+ || t == typeof(JsonElement)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar],
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle generic types
+ providerSqlType = !dotnetType.IsGenericType
+ ? null
+ : dotnetType.GetGenericTypeDefinition() switch
+ {
+ Type t
+ when t == typeof(Dictionary<,>)
+ || t == typeof(IDictionary<,>)
+ || t == typeof(List<>)
+ || t == typeof(IList<>)
+ || t == typeof(Collection<>)
+ || t == typeof(ICollection<>)
+ || t == typeof(IEnumerable<>)
+ => ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar],
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle POCO type
+ if (dotnetType.IsClass || dotnetType.IsInterface)
+ {
+ providerSqlType = ProviderSqlTypeLookup[SqlServerTypes.sql_nvarchar];
+ }
+
+ return providerSqlType != null;
+ }
}
diff --git a/src/DapperMatic/Providers/Sqlite/SqliteMethods.Strings.cs b/src/DapperMatic/Providers/Sqlite/SqliteMethods.Strings.cs
index 50c55c1..44ded9c 100644
--- a/src/DapperMatic/Providers/Sqlite/SqliteMethods.Strings.cs
+++ b/src/DapperMatic/Providers/Sqlite/SqliteMethods.Strings.cs
@@ -15,12 +15,13 @@ protected override string SqlInlineColumnNameAndType(DxColumn column, Version db
// https://www.sqlite.org/autoinc.html
if (column.IsAutoIncrement)
{
- column.ProviderDataType = SqliteTypes.sql_integer;
+ column.SetProviderDataType(ProviderType, SqliteTypes.sql_integer);
}
+
return base.SqlInlineColumnNameAndType(column, dbVersion);
}
- protected override string SqlInlinePrimaryKeyAutoIncrementColumnConstraint()
+ protected override string SqlInlinePrimaryKeyAutoIncrementColumnConstraint(DxColumn column)
{
return "AUTOINCREMENT";
}
diff --git a/src/DapperMatic/Providers/Sqlite/SqliteMethods.cs b/src/DapperMatic/Providers/Sqlite/SqliteMethods.cs
index a72a274..357cf35 100644
--- a/src/DapperMatic/Providers/Sqlite/SqliteMethods.cs
+++ b/src/DapperMatic/Providers/Sqlite/SqliteMethods.cs
@@ -8,7 +8,7 @@ public partial class SqliteMethods : DatabaseMethodsBase, IDatabaseMethods
{
public override DbProviderType ProviderType => DbProviderType.Sqlite;
- public override IProviderTypeMap ProviderTypeMap => SqliteProviderTypeMap.Instance.Value;
+ public override IDbProviderTypeMap ProviderTypeMap => SqliteProviderTypeMap.Instance.Value;
protected override string DefaultSchema => "";
@@ -24,7 +24,7 @@ public override async Task GetDatabaseVersionAsync(
const string sql = "SELECT sqlite_version()";
var versionString =
await ExecuteScalarAsync(db, sql, tx: tx).ConfigureAwait(false) ?? "";
- return ProviderUtils.ExtractVersionFromVersionString(versionString);
+ return DbProviderUtils.ExtractVersionFromVersionString(versionString);
}
public override char[] QuoteChars => ['"'];
diff --git a/src/DapperMatic/Providers/Sqlite/SqliteProviderTypeMap.cs b/src/DapperMatic/Providers/Sqlite/SqliteProviderTypeMap.cs
index 750c725..d19d9b7 100644
--- a/src/DapperMatic/Providers/Sqlite/SqliteProviderTypeMap.cs
+++ b/src/DapperMatic/Providers/Sqlite/SqliteProviderTypeMap.cs
@@ -1,77 +1,330 @@
+using System.Collections;
+using System.Collections.Immutable;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+
namespace DapperMatic.Providers.Sqlite;
-public sealed class SqliteProviderTypeMap : ProviderTypeMapBase
+public sealed class SqliteProviderTypeMap : DbProviderTypeMapBase
{
internal static readonly Lazy Instance =
new(() => new SqliteProviderTypeMap());
- private SqliteProviderTypeMap() : base()
- {
- }
+ private SqliteProviderTypeMap()
+ : base() { }
protected override DbProviderType ProviderType => DbProviderType.Sqlite;
+ public override string SqTypeForStringLengthMax => "text";
+
+ public override string SqTypeForBinaryLengthMax => "blob";
+
+ public override string SqlTypeForJson => "text";
+
///
/// IMPORTANT!! The order within an affinity group matters, as the first possible match will be used as the recommended sql type for a dotnet type
///
- protected override ProviderSqlType[] ProviderSqlTypes =>
- [
- new(ProviderSqlTypeAffinity.Integer, SqliteTypes.sql_integer, formatWithPrecision: "integer({0})",
- defaultPrecision: 11, canUseToAutoIncrement: true, minValue: -2147483648, maxValue: 2147483647),
- new(ProviderSqlTypeAffinity.Integer, SqliteTypes.sql_int, aliasOf: "integer", formatWithPrecision: "int({0})",
- defaultPrecision: 11, canUseToAutoIncrement: true, minValue: -2147483648, maxValue: 2147483647),
- new(ProviderSqlTypeAffinity.Integer, SqliteTypes.sql_tinyint, formatWithPrecision: "tinyint({0})",
- defaultPrecision: 4, canUseToAutoIncrement: true, minValue: -128, maxValue: 128),
- new(ProviderSqlTypeAffinity.Integer, SqliteTypes.sql_smallint, formatWithPrecision: "smallint({0})",
- defaultPrecision: 5, canUseToAutoIncrement: true, minValue: -32768, maxValue: 32767),
- new(ProviderSqlTypeAffinity.Integer, SqliteTypes.sql_mediumint, formatWithPrecision: "mediumint({0})",
- defaultPrecision: 7, canUseToAutoIncrement: true, minValue: -8388608, maxValue: 8388607),
- new(ProviderSqlTypeAffinity.Integer, SqliteTypes.sql_bigint, formatWithPrecision: "bigint({0})",
- defaultPrecision: 19, canUseToAutoIncrement: true, minValue: -9223372036854775808,
- maxValue: 9223372036854775807),
- new(ProviderSqlTypeAffinity.Integer, SqliteTypes.sql_unsigned_big_int, formatWithPrecision: "unsigned big int({0})",
- defaultPrecision: 20, canUseToAutoIncrement: true, minValue: 0, maxValue: 18446744073709551615),
- new(ProviderSqlTypeAffinity.Real, SqliteTypes.sql_real, formatWithPrecision: "real({0})",
- defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, SqliteTypes.sql_double, formatWithPrecision: "double({0})",
- defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, SqliteTypes.sql_float, formatWithPrecision: "float({0})",
- defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, SqliteTypes.sql_numeric, formatWithPrecision: "numeric({0})",
- defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Real, SqliteTypes.sql_decimal, formatWithPrecision: "decimal({0})",
- defaultPrecision: 12, defaultScale: 2),
- new(ProviderSqlTypeAffinity.Boolean, SqliteTypes.sql_bool, formatWithPrecision: "bool({0})",
- defaultPrecision: 1),
- new(ProviderSqlTypeAffinity.Boolean, SqliteTypes.sql_boolean, formatWithPrecision: "boolean({0})",
- defaultPrecision: 1),
- new(ProviderSqlTypeAffinity.DateTime, SqliteTypes.sql_date, formatWithPrecision: "date({0})",
- defaultPrecision: 10, isDateOnly: true),
- new(ProviderSqlTypeAffinity.DateTime, SqliteTypes.sql_datetime, formatWithPrecision: "datetime({0})",
- defaultPrecision: 19),
- new(ProviderSqlTypeAffinity.DateTime, SqliteTypes.sql_timestamp, formatWithPrecision: "timestamp({0})",
- defaultPrecision: 19),
- new(ProviderSqlTypeAffinity.DateTime, SqliteTypes.sql_time, formatWithPrecision: "time({0})",
- defaultPrecision: 8, isTimeOnly: true),
- new(ProviderSqlTypeAffinity.DateTime, SqliteTypes.sql_year, formatWithPrecision: "year({0})",
- defaultPrecision: 4),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_char, formatWithPrecision: "char({0})",
- defaultPrecision: 1),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_nchar, formatWithPrecision: "nchar({0})",
- defaultPrecision: 1),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_varchar, formatWithPrecision: "varchar({0})",
- defaultPrecision: 255),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_nvarchar, formatWithPrecision: "nvarchar({0})",
- defaultPrecision: 255),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_varying_character, formatWithPrecision: "varying character({0})",
- defaultPrecision: 255),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_native_character, formatWithPrecision: "native character({0})",
- defaultPrecision: 255),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_text, formatWithPrecision: "text({0})",
- defaultPrecision: 65535),
- new(ProviderSqlTypeAffinity.Text, SqliteTypes.sql_clob, formatWithPrecision: "clob({0})",
- defaultPrecision: 65535),
- new(ProviderSqlTypeAffinity.Binary, SqliteTypes.sql_blob, formatWithPrecision: "blob({0})",
- defaultPrecision: 65535),
- ];
-}
\ No newline at end of file
+ protected override DbProviderSqlType[] ProviderSqlTypes =>
+ [
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ SqliteTypes.sql_integer,
+ formatWithPrecision: "integer({0})",
+ defaultPrecision: 11,
+ canUseToAutoIncrement: true,
+ minValue: -2147483648,
+ maxValue: 2147483647
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ SqliteTypes.sql_int,
+ aliasOf: "integer",
+ formatWithPrecision: "int({0})",
+ defaultPrecision: 11,
+ canUseToAutoIncrement: true,
+ minValue: -2147483648,
+ maxValue: 2147483647
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ SqliteTypes.sql_tinyint,
+ formatWithPrecision: "tinyint({0})",
+ defaultPrecision: 4,
+ canUseToAutoIncrement: true,
+ minValue: -128,
+ maxValue: 128
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ SqliteTypes.sql_smallint,
+ formatWithPrecision: "smallint({0})",
+ defaultPrecision: 5,
+ canUseToAutoIncrement: true,
+ minValue: -32768,
+ maxValue: 32767
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ SqliteTypes.sql_mediumint,
+ formatWithPrecision: "mediumint({0})",
+ defaultPrecision: 7,
+ canUseToAutoIncrement: true,
+ minValue: -8388608,
+ maxValue: 8388607
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ SqliteTypes.sql_bigint,
+ formatWithPrecision: "bigint({0})",
+ defaultPrecision: 19,
+ canUseToAutoIncrement: true,
+ minValue: -9223372036854775808,
+ maxValue: 9223372036854775807
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Integer,
+ SqliteTypes.sql_unsigned_big_int,
+ formatWithPrecision: "unsigned big int({0})",
+ defaultPrecision: 20,
+ canUseToAutoIncrement: true,
+ minValue: 0,
+ maxValue: 18446744073709551615
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ SqliteTypes.sql_real,
+ formatWithPrecision: "real({0})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ SqliteTypes.sql_double,
+ formatWithPrecision: "double({0})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ SqliteTypes.sql_float,
+ formatWithPrecision: "float({0})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ SqliteTypes.sql_numeric,
+ formatWithPrecision: "numeric({0})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Real,
+ SqliteTypes.sql_decimal,
+ formatWithPrecision: "decimal({0})",
+ defaultPrecision: 12,
+ defaultScale: 2
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Boolean,
+ SqliteTypes.sql_bool,
+ formatWithPrecision: "bool({0})",
+ defaultPrecision: 1
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Boolean,
+ SqliteTypes.sql_boolean,
+ formatWithPrecision: "boolean({0})",
+ defaultPrecision: 1
+ ),
+ new(
+ DbProviderSqlTypeAffinity.DateTime,
+ SqliteTypes.sql_date,
+ formatWithPrecision: "date({0})",
+ defaultPrecision: 10,
+ isDateOnly: true
+ ),
+ new(
+ DbProviderSqlTypeAffinity.DateTime,
+ SqliteTypes.sql_datetime,
+ formatWithPrecision: "datetime({0})",
+ defaultPrecision: 19
+ ),
+ new(
+ DbProviderSqlTypeAffinity.DateTime,
+ SqliteTypes.sql_timestamp,
+ formatWithPrecision: "timestamp({0})",
+ defaultPrecision: 19
+ ),
+ new(
+ DbProviderSqlTypeAffinity.DateTime,
+ SqliteTypes.sql_time,
+ formatWithPrecision: "time({0})",
+ defaultPrecision: 8,
+ isTimeOnly: true
+ ),
+ new(
+ DbProviderSqlTypeAffinity.DateTime,
+ SqliteTypes.sql_year,
+ formatWithPrecision: "year({0})",
+ defaultPrecision: 4
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ SqliteTypes.sql_nvarchar,
+ formatWithLength: "nvarchar({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ SqliteTypes.sql_varchar,
+ formatWithLength: "varchar({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ SqliteTypes.sql_nchar,
+ formatWithLength: "nchar({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ SqliteTypes.sql_char,
+ formatWithLength: "char({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ SqliteTypes.sql_varying_character,
+ formatWithLength: "varying character({0})",
+ defaultLength: DefaultLength
+ ),
+ new(
+ DbProviderSqlTypeAffinity.Text,
+ SqliteTypes.sql_native_character,
+ formatWithLength: "native character({0})",
+ defaultLength: DefaultLength
+ ),
+ new(DbProviderSqlTypeAffinity.Text, SqliteTypes.sql_text),
+ new(DbProviderSqlTypeAffinity.Text, SqliteTypes.sql_clob),
+ new(DbProviderSqlTypeAffinity.Binary, SqliteTypes.sql_blob),
+ ];
+
+ protected override bool TryGetProviderSqlTypeMatchingDotnetTypeInternal(
+ DbProviderDotnetTypeDescriptor descriptor,
+ out DbProviderSqlType? providerSqlType
+ )
+ {
+ providerSqlType = null;
+
+ var dotnetType = descriptor.DotnetType;
+
+ // handle well-known types first
+ providerSqlType = dotnetType.IsGenericType
+ ? null
+ : dotnetType switch
+ {
+ Type t when t == typeof(bool) => ProviderSqlTypeLookup[SqliteTypes.sql_boolean],
+ Type t when t == typeof(byte) => ProviderSqlTypeLookup[SqliteTypes.sql_smallint],
+ Type t when t == typeof(ReadOnlyMemory)
+ => ProviderSqlTypeLookup[SqliteTypes.sql_smallint],
+ Type t when t == typeof(sbyte) => ProviderSqlTypeLookup[SqliteTypes.sql_smallint],
+ Type t when t == typeof(short) => ProviderSqlTypeLookup[SqliteTypes.sql_smallint],
+ Type t when t == typeof(ushort) => ProviderSqlTypeLookup[SqliteTypes.sql_smallint],
+ Type t when t == typeof(int) => ProviderSqlTypeLookup[SqliteTypes.sql_int],
+ Type t when t == typeof(uint) => ProviderSqlTypeLookup[SqliteTypes.sql_int],
+ Type t when t == typeof(long) => ProviderSqlTypeLookup[SqliteTypes.sql_bigint],
+ Type t when t == typeof(ulong) => ProviderSqlTypeLookup[SqliteTypes.sql_bigint],
+ Type t when t == typeof(float) => ProviderSqlTypeLookup[SqliteTypes.sql_float],
+ Type t when t == typeof(double) => ProviderSqlTypeLookup[SqliteTypes.sql_double],
+ Type t when t == typeof(decimal) => ProviderSqlTypeLookup[SqliteTypes.sql_decimal],
+ Type t when t == typeof(char) => ProviderSqlTypeLookup[SqliteTypes.sql_varchar],
+ Type t when t == typeof(string)
+ => descriptor.Length.GetValueOrDefault(255) < 8000
+ ? ProviderSqlTypeLookup[SqliteTypes.sql_varchar]
+ : ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t when t == typeof(char[])
+ => descriptor.Length.GetValueOrDefault(255) < 8000
+ ? ProviderSqlTypeLookup[SqliteTypes.sql_varchar]
+ : ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t when t == typeof(ReadOnlyMemory[])
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[SqliteTypes.sql_varchar]
+ : ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t when t == typeof(Stream)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[SqliteTypes.sql_varchar]
+ : ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t when t == typeof(TextReader)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[SqliteTypes.sql_varchar]
+ : ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t when t == typeof(byte[]) => ProviderSqlTypeLookup[SqliteTypes.sql_blob],
+ Type t when t == typeof(object)
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[SqliteTypes.sql_varchar]
+ : ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t when t == typeof(object[])
+ => descriptor.Length.GetValueOrDefault(int.MaxValue) < 8000
+ ? ProviderSqlTypeLookup[SqliteTypes.sql_varchar]
+ : ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t when t == typeof(Guid) => ProviderSqlTypeLookup[SqliteTypes.sql_varchar],
+ Type t when t == typeof(DateTime)
+ => ProviderSqlTypeLookup[SqliteTypes.sql_datetime],
+ Type t when t == typeof(DateTimeOffset)
+ => ProviderSqlTypeLookup[SqliteTypes.sql_timestamp],
+ Type t when t == typeof(TimeSpan) => ProviderSqlTypeLookup[SqliteTypes.sql_bigint],
+ Type t when t == typeof(DateOnly) => ProviderSqlTypeLookup[SqliteTypes.sql_date],
+ Type t when t == typeof(TimeOnly) => ProviderSqlTypeLookup[SqliteTypes.sql_time],
+ Type t when t == typeof(BitArray) || t == typeof(BitVector32)
+ => ProviderSqlTypeLookup[SqliteTypes.sql_blob],
+ Type t
+ when t == typeof(ImmutableDictionary)
+ || t == typeof(Dictionary)
+ || t == typeof(IDictionary)
+ => ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ Type t
+ when t == typeof(JsonNode)
+ || t == typeof(JsonObject)
+ || t == typeof(JsonArray)
+ || t == typeof(JsonValue)
+ || t == typeof(JsonDocument)
+ || t == typeof(JsonElement)
+ => ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle generic types
+ providerSqlType = !dotnetType.IsGenericType
+ ? null
+ : dotnetType.GetGenericTypeDefinition() switch
+ {
+ Type t
+ when t == typeof(Dictionary<,>)
+ || t == typeof(IDictionary<,>)
+ || t == typeof(List<>)
+ || t == typeof(IList<>)
+ || t == typeof(Collection<>)
+ || t == typeof(ICollection<>)
+ || t == typeof(IEnumerable<>)
+ => ProviderSqlTypeLookup[SqliteTypes.sql_text],
+ _ => null
+ };
+
+ if (providerSqlType != null)
+ return true;
+
+ // handle POCO type
+ if (dotnetType.IsClass || dotnetType.IsInterface)
+ {
+ providerSqlType = ProviderSqlTypeLookup[SqliteTypes.sql_text];
+ }
+
+ return providerSqlType != null;
+ }
+}
diff --git a/src/DapperMatic/Providers/Sqlite/SqliteSqlParser.cs b/src/DapperMatic/Providers/Sqlite/SqliteSqlParser.cs
index 6d612ea..febb8ab 100644
--- a/src/DapperMatic/Providers/Sqlite/SqliteSqlParser.cs
+++ b/src/DapperMatic/Providers/Sqlite/SqliteSqlParser.cs
@@ -135,10 +135,11 @@ thirdChild.Children[1] is SqlWordClause sw2
// if we don't recognize the column data type, we skip it
if (
- !providerTypeMap.TryGetRecommendedDotnetTypeMatchingSqlType(
+ !providerTypeMap.TryGetDotnetTypeDescriptorMatchingFullSqlTypeName(
columnDataType,
- out var providerDataType
- ) || !providerDataType.HasValue
+ out var dotnetTypeDescriptor
+ )
+ || dotnetTypeDescriptor == null
)
continue;
@@ -146,8 +147,11 @@ out var providerDataType
null,
tableName,
columnName,
- providerDataType.Value.dotnetType,
- columnDataType,
+ dotnetTypeDescriptor.DotnetType,
+ new Dictionary
+ {
+ { DbProviderType.Sqlite, columnDataType }
+ },
length,
precision,
scale
@@ -196,7 +200,7 @@ out var providerDataType
// add the default constraint to the table
var defaultConstraintName =
inlineConstraintName
- ?? ProviderUtils.GenerateDefaultConstraintName(
+ ?? DbProviderUtils.GenerateDefaultConstraintName(
tableName,
columnName
);
@@ -218,7 +222,7 @@ out var providerDataType
// add the default constraint to the table
var uniqueConstraintName =
inlineConstraintName
- ?? ProviderUtils.GenerateUniqueConstraintName(
+ ?? DbProviderUtils.GenerateUniqueConstraintName(
tableName,
columnName
);
@@ -247,7 +251,7 @@ [new DxOrderedColumn(column.ColumnName)]
// add the default constraint to the table
var checkConstraintName =
inlineConstraintName
- ?? ProviderUtils.GenerateCheckConstraintName(
+ ?? DbProviderUtils.GenerateCheckConstraintName(
tableName,
columnName
);
@@ -269,7 +273,7 @@ [new DxOrderedColumn(column.ColumnName)]
// add the default constraint to the table
var pkConstraintName =
inlineConstraintName
- ?? ProviderUtils.GeneratePrimaryKeyConstraintName(
+ ?? DbProviderUtils.GeneratePrimaryKeyConstraintName(
tableName,
columnName
);
@@ -323,7 +327,7 @@ [new DxOrderedColumn(column.ColumnName, columnOrder)]
var constraintName =
inlineConstraintName
- ?? ProviderUtils.GenerateForeignKeyConstraintName(
+ ?? DbProviderUtils.GenerateForeignKeyConstraintName(
tableName,
columnName,
referencedTableName,
@@ -433,7 +437,7 @@ [new DxOrderedColumn(referenceColumnName)]
null,
tableName,
inlineConstraintName
- ?? ProviderUtils.GeneratePrimaryKeyConstraintName(
+ ?? DbProviderUtils.GeneratePrimaryKeyConstraintName(
tableName,
pkColumnNames
),
@@ -472,7 +476,7 @@ [new DxOrderedColumn(referenceColumnName)]
null,
tableName,
inlineConstraintName
- ?? ProviderUtils.GenerateUniqueConstraintName(
+ ?? DbProviderUtils.GenerateUniqueConstraintName(
tableName,
ucColumnNames
),
@@ -502,7 +506,7 @@ [new DxOrderedColumn(referenceColumnName)]
// add the default constraint to the table
var checkConstraintName =
inlineConstraintName
- ?? ProviderUtils.GenerateCheckConstraintName(
+ ?? DbProviderUtils.GenerateCheckConstraintName(
tableName,
table.CheckConstraints.Count > 0
? $"{table.CheckConstraints.Count}"
@@ -564,7 +568,7 @@ [new DxOrderedColumn(referenceColumnName)]
var constraintName =
inlineConstraintName
- ?? ProviderUtils.GenerateForeignKeyConstraintName(
+ ?? DbProviderUtils.GenerateForeignKeyConstraintName(
tableName,
fkSourceColumnNames,
referencedTableName,
diff --git a/tests/DapperMatic.Tests/DatabaseMethodsTests.Columns.cs b/tests/DapperMatic.Tests/DatabaseMethodsTests.Columns.cs
index 56d6e7a..dffd37d 100644
--- a/tests/DapperMatic.Tests/DatabaseMethodsTests.Columns.cs
+++ b/tests/DapperMatic.Tests/DatabaseMethodsTests.Columns.cs
@@ -280,302 +280,5 @@ await db.CreateTableIfNotExistsAsync(
}
await db.DropTableIfExistsAsync(schemaName, tableName);
-
- // Output.WriteLine("Column Exists: {0}.{1}", tableName, columnName);
- // exists = await db.DoesColumnExistAsync(schemaName, tableName, columnName);
- // Assert.True(exists);
-
- // Output.WriteLine("Dropping columnName: {0}.{1}", tableName, columnName);
- // await db.DropColumnIfExistsAsync(schemaName, tableName, columnName);
-
- // Output.WriteLine("Column Exists: {0}.{1}", tableName, columnName);
- // exists = await db.DoesColumnExistAsync(schemaName, tableName, columnName);
- // Assert.False(exists);
-
- // // try adding a columnName of all the supported types
- // var columnCount = 1;
- // var addColumns = new List
- // {
- // new(schemaName, tableName2, "intid" + columnCount++, typeof(int)),
- // new(
- // schemaName,
- // tableName2,
- // "intpkid" + columnCount++,
- // typeof(int),
- // isPrimaryKey: true,
- // isAutoIncrement: supportsMultipleIdentityColumns ? true : false
- // ),
- // new(schemaName, tableName2, "intucid" + columnCount++, typeof(int), isUnique: true),
- // new(
- // schemaName,
- // tableName2,
- // "id" + columnCount++,
- // typeof(int),
- // isUnique: true,
- // isIndexed: true
- // ),
- // new(schemaName, tableName2, "intixid" + columnCount++, typeof(int), isIndexed: true),
- // new(
- // schemaName,
- // tableName2,
- // "colWithFk" + columnCount++,
- // typeof(int),
- // isForeignKey: true,
- // referencedTableName: tableName,
- // referencedColumnName: "id",
- // onDelete: DxForeignKeyAction.Cascade,
- // onUpdate: DxForeignKeyAction.Cascade
- // ),
- // new(
- // schemaName,
- // tableName2,
- // "createdDateColumn" + columnCount++,
- // typeof(DateTime),
- // defaultExpression: defaultDateTimeSql
- // ),
- // new(
- // schemaName,
- // tableName2,
- // "newidColumn" + columnCount++,
- // typeof(Guid),
- // defaultExpression: defaultGuidSql
- // ),
- // new(schemaName, tableName2, "bigintColumn" + columnCount++, typeof(long)),
- // new(schemaName, tableName2, "binaryColumn" + columnCount++, typeof(byte[])),
- // new(schemaName, tableName2, "bitColumn" + columnCount++, typeof(bool)),
- // new(schemaName, tableName2, "charColumn" + columnCount++, typeof(string), length: 10),
- // new(schemaName, tableName2, "dateColumn" + columnCount++, typeof(DateTime)),
- // new(schemaName, tableName2, "datetimeColumn" + columnCount++, typeof(DateTime)),
- // new(schemaName, tableName2, "datetime2Column" + columnCount++, typeof(DateTime)),
- // new(
- // schemaName,
- // tableName2,
- // "datetimeoffsetColumn" + columnCount++,
- // typeof(DateTimeOffset)
- // ),
- // new(
- // schemaName,
- // tableName2,
- // "decimalColumn" + columnCount++,
- // typeof(decimal),
- // precision: 16,
- // scale: 3
- // ),
- // new(
- // schemaName,
- // tableName2,
- // "decimalColumnWithPrecision" + columnCount++,
- // typeof(decimal),
- // precision: 10
- // ),
- // new(
- // schemaName,
- // tableName2,
- // "decimalColumnWithPrecisionAndScale" + columnCount++,
- // typeof(decimal),
- // precision: 10,
- // scale: 5
- // ),
- // new(schemaName, tableName2, "floatColumn" + columnCount++, typeof(double)),
- // new(schemaName, tableName2, "imageColumn" + columnCount++, typeof(byte[])),
- // new(schemaName, tableName2, "intColumn" + columnCount++, typeof(int)),
- // new(schemaName, tableName2, "moneyColumn" + columnCount++, typeof(decimal)),
- // new(schemaName, tableName2, "ncharColumn" + columnCount++, typeof(string), length: 10),
- // new(
- // schemaName,
- // tableName2,
- // "ntextColumn" + columnCount++,
- // typeof(string),
- // length: int.MaxValue
- // ),
- // new(schemaName, tableName2, "floatColumn2" + columnCount++, typeof(float)),
- // new(schemaName, tableName2, "doubleColumn2" + columnCount++, typeof(double)),
- // new(schemaName, tableName2, "guidArrayColumn" + columnCount++, typeof(Guid[])),
- // new(schemaName, tableName2, "intArrayColumn" + columnCount++, typeof(int[])),
- // new(schemaName, tableName2, "longArrayColumn" + columnCount++, typeof(long[])),
- // new(schemaName, tableName2, "doubleArrayColumn" + columnCount++, typeof(double[])),
- // new(schemaName, tableName2, "decimalArrayColumn" + columnCount++, typeof(decimal[])),
- // new(schemaName, tableName2, "stringArrayColumn" + columnCount++, typeof(string[])),
- // new(
- // schemaName,
- // tableName2,
- // "stringDictionaryArrayColumn" + columnCount++,
- // typeof(Dictionary)
- // ),
- // new(
- // schemaName,
- // tableName2,
- // "objectDitionaryArrayColumn" + columnCount++,
- // typeof(Dictionary)
- // )
- // };
- // await db.DropTableIfExistsAsync(schemaName, tableName2);
- // await db.CreateTableIfNotExistsAsync(schemaName, tableName2, [addColumns[0]]);
- // foreach (var col in addColumns.Skip(1))
- // {
- // await db.CreateColumnIfNotExistsAsync(col);
- // var columns = await db.GetColumnsAsync(schemaName, tableName2);
- // // immediately do a check to make sure column was created as expected
- // var column = await db.GetColumnAsync(schemaName, tableName2, col.ColumnName);
- // Assert.NotNull(column);
-
- // if (!string.IsNullOrWhiteSpace(schemaName) && db.SupportsSchemas())
- // {
- // Assert.Equal(schemaName, column.SchemaName, true);
- // }
-
- // try
- // {
- // Assert.Equal(col.IsIndexed, column.IsIndexed);
- // Assert.Equal(col.IsUnique, column.IsUnique);
- // Assert.Equal(col.IsPrimaryKey, column.IsPrimaryKey);
- // Assert.Equal(col.IsAutoIncrement, column.IsAutoIncrement);
- // Assert.Equal(col.IsNullable, column.IsNullable);
- // Assert.Equal(col.IsForeignKey, column.IsForeignKey);
- // if (col.IsForeignKey)
- // {
- // Assert.Equal(col.ReferencedTableName, column.ReferencedTableName, true);
- // Assert.Equal(col.ReferencedColumnName, column.ReferencedColumnName, true);
- // Assert.Equal(col.OnDelete, column.OnDelete);
- // Assert.Equal(col.OnUpdate, column.OnUpdate);
- // }
- // Assert.Equal(col.DotnetType, column.DotnetType);
- // Assert.Equal(col.Length, column.Length);
- // Assert.Equal(col.Precision, column.Precision);
- // Assert.Equal(col.Scale ?? 0, column.Scale ?? 0);
- // }
- // catch (Exception ex)
- // {
- // Output.WriteLine("Error validating column {0}: {1}", col.ColumnName, ex.Message);
- // column = await db.GetColumnAsync(schemaName, tableName2, col.ColumnName);
- // }
-
- // Assert.NotNull(column?.ProviderDataType);
- // Assert.NotEmpty(column.ProviderDataType);
- // if (!string.IsNullOrWhiteSpace(col.ProviderDataType))
- // {
- // if (
- // !col.ProviderDataType.Equals(
- // column.ProviderDataType,
- // StringComparison.OrdinalIgnoreCase
- // )
- // )
- // {
- // // then we want to make sure that the new provider data type in the database is more complete than the one we provided
- // // sometimes, if you tell a database to create a column with a type of "decimal", it will actually create it as "decimal(11)" or something similar
- // // in our case here, too, when creating a numeric(10, 5) column, the database might create it as decimal(10, 5)
- // // so we CAN'T just compare the two strings directly
- // // Assert.True(col.ProviderDataType.Length < column.ProviderDataType.Length);
-
- // // sometimes, it's tricky to know what the database will do, so we just want to make sure that the database type is at least as specific as the one we provided
- // if (col.Length.HasValue)
- // Assert.Equal(col.Length, column.Length);
- // if (col.Precision.HasValue)
- // Assert.Equal(col.Precision, column.Precision);
- // if (col.Scale.HasValue)
- // Assert.Equal(col.Scale, column.Scale);
- // }
- // }
- // }
-
- // var actualColumns = await db.GetColumnsAsync(schemaName, tableName2);
- // Output.WriteLine(JsonConvert.SerializeObject(actualColumns, Formatting.Indented));
- // var columnNames = await db.GetColumnNamesAsync(schemaName, tableName2);
- // var expectedColumnNames = addColumns
- // .OrderBy(c => c.ColumnName.ToLowerInvariant())
- // .Select(c => c.ColumnName.ToLowerInvariant())
- // .ToArray();
- // var actualColumnNames = columnNames
- // .OrderBy(s => s.ToLowerInvariant())
- // .Select(s => s.ToLowerInvariant())
- // .ToArray();
- // Output.WriteLine("Expected columns: {0}", string.Join(", ", expectedColumnNames));
- // Output.WriteLine("Actual columns: {0}", string.Join(", ", actualColumnNames));
- // Output.WriteLine("Expected columns count: {0}", expectedColumnNames.Length);
- // Output.WriteLine("Actual columns count: {0}", actualColumnNames.Length);
- // Output.WriteLine(
- // "Expected not in actual: {0}",
- // string.Join(", ", expectedColumnNames.Except(actualColumnNames))
- // );
- // Output.WriteLine(
- // "Actual not in expected: {0}",
- // string.Join(", ", actualColumnNames.Except(expectedColumnNames))
- // );
- // Assert.Equal(expectedColumnNames.Length, actualColumnNames.Length);
- // // Assert.Same(expectedColumnNames, actualColumnNames);
-
- // // validate that:
- // // - all columns are of the expected types
- // // - all indexes are created correctly
- // // - all foreign keys are created correctly
- // // - all default values are set correctly
- // // - all column lengths are set correctly
- // // - all column scales are set correctly
- // // - all column precision is set correctly
- // // - all columns are nullable or not nullable as specified
- // // - all columns are unique or not unique as specified
- // // - all columns are indexed or not indexed as specified
- // // - all columns are foreign key or not foreign key as specified
- // var table = await db.GetTableAsync(schemaName, tableName2);
- // Assert.NotNull(table);
-
- // foreach (var column in table.Columns)
- // {
- // var originalColumn = addColumns.SingleOrDefault(c =>
- // c.ColumnName.Equals(column.ColumnName, StringComparison.OrdinalIgnoreCase)
- // );
- // Assert.NotNull(originalColumn);
- // }
-
- // // general count tests
- // // some providers like MySQL create unique constraints for unique indexes, and vice-versa, so we can't just count the unique indexes
- // Assert.Equal(
- // addColumns.Count(c => !c.IsIndexed && c.IsUnique),
- // dbType == DbProviderType.MySql
- // ? table.UniqueConstraints.Count / 2
- // : table.UniqueConstraints.Count
- // );
- // Assert.Equal(
- // addColumns.Count(c => c.IsIndexed && !c.IsUnique),
- // table.Indexes.Count(c => !c.IsUnique)
- // );
- // var expectedUniqueIndexes = addColumns.Where(c => c.IsIndexed && c.IsUnique).ToArray();
- // var actualUniqueIndexes = table.Indexes.Where(c => c.IsUnique).ToArray();
- // Assert.Equal(
- // expectedUniqueIndexes.Length,
- // dbType == DbProviderType.MySql
- // ? actualUniqueIndexes.Length / 2
- // : actualUniqueIndexes.Length
- // );
- // Assert.Equal(addColumns.Count(c => c.IsForeignKey), table.ForeignKeyConstraints.Count());
- // Assert.Equal(
- // addColumns.Count(c => c.DefaultExpression != null),
- // table.DefaultConstraints.Count()
- // );
- // Assert.Equal(
- // addColumns.Count(c => c.CheckExpression != null),
- // table.CheckConstraints.Count()
- // );
- // Assert.Equal(addColumns.Count(c => c.IsNullable), table.Columns.Count(c => c.IsNullable));
- // Assert.Equal(
- // addColumns.Count(c => c.IsPrimaryKey && c.IsAutoIncrement),
- // table.Columns.Count(c => c.IsPrimaryKey && c.IsAutoIncrement)
- // );
- // Assert.Equal(addColumns.Count(c => c.IsUnique), table.Columns.Count(c => c.IsUnique));
-
- // var indexedColumnsExpected = addColumns.Where(c => c.IsIndexed).ToArray();
- // var uniqueColumnsNonIndexed = addColumns.Where(c => c.IsUnique && !c.IsIndexed).ToArray();
-
- // var indexedColumnsActual = table.Columns.Where(c => c.IsIndexed).ToArray();
-
- // Assert.Equal(
- // dbType == DbProviderType.MySql
- // ? indexedColumnsExpected.Length + uniqueColumnsNonIndexed.Length
- // : indexedColumnsExpected.Length,
- // indexedColumnsActual.Length
- // );
-
- // await db.DropTableIfExistsAsync(schemaName, tableName2);
- // await db.DropTableIfExistsAsync(schemaName, tableName);
}
}
diff --git a/tests/DapperMatic.Tests/DatabaseMethodsTests.DefaultConstraints.cs b/tests/DapperMatic.Tests/DatabaseMethodsTests.DefaultConstraints.cs
index 2a4965b..1ebb890 100644
--- a/tests/DapperMatic.Tests/DatabaseMethodsTests.DefaultConstraints.cs
+++ b/tests/DapperMatic.Tests/DatabaseMethodsTests.DefaultConstraints.cs
@@ -24,7 +24,7 @@ [new DxColumn(schemaName, testTableName, testColumnName, typeof(int))]
);
// in MySQL, default constraints are not named, so this MUST use the ProviderUtils method which is what DapperMatic uses internally
- var constraintName = ProviderUtils.GenerateDefaultConstraintName(
+ var constraintName = DbProviderUtils.GenerateDefaultConstraintName(
testTableName,
testColumnName
);
diff --git a/tests/DapperMatic.Tests/DatabaseMethodsTests.TableFactory.cs b/tests/DapperMatic.Tests/DatabaseMethodsTests.TableFactory.cs
new file mode 100644
index 0000000..97afec5
--- /dev/null
+++ b/tests/DapperMatic.Tests/DatabaseMethodsTests.TableFactory.cs
@@ -0,0 +1,179 @@
+using System.Collections.ObjectModel;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using Dapper;
+using DapperMatic.DataAnnotations;
+using DapperMatic.Models;
+using DapperMatic.Providers;
+using Microsoft.Data.SqlClient.DataClassification;
+
+namespace DapperMatic.Tests;
+
+public abstract partial class DatabaseMethodsTests
+{
+ [Theory]
+ [InlineData(typeof(TestDao1))]
+ [InlineData(typeof(TestDao2))]
+ [InlineData(typeof(TestDao3))]
+ [InlineData(typeof(TestTable4))]
+ protected virtual async Task Can_create_tables_from_model_classes_async(Type type)
+ {
+ var tableDef = DxTableFactory.GetTable(type);
+
+ using var db = await OpenConnectionAsync();
+
+ if (!string.IsNullOrWhiteSpace(tableDef.SchemaName))
+ {
+ await db.CreateSchemaIfNotExistsAsync(tableDef.SchemaName);
+ }
+
+ await db.CreateTableIfNotExistsAsync(tableDef);
+
+ var tableExists = await db.DoesTableExistAsync(tableDef.SchemaName, tableDef.TableName);
+ Assert.True(tableExists);
+
+ var dropped = await db.DropTableIfExistsAsync(tableDef.SchemaName, tableDef.TableName);
+ Assert.True(dropped);
+ }
+}
+
+[Table("TestTable1")]
+public class TestDao1
+{
+ [Key]
+ public Guid Id { get; set; }
+}
+
+[Table("TestTable2", Schema = "my_app")]
+public class TestDao2
+{
+ [Key]
+ public Guid Id { get; set; }
+}
+
+[DxTable("TestTable3")]
+public class TestDao3
+{
+ [DxPrimaryKeyConstraint]
+ public Guid Id { get; set; }
+}
+
+[DxPrimaryKeyConstraint([nameof(TestTable4.Id)])]
+public class TestTable4
+{
+ public Guid Id { get; set; }
+
+ // create column of all supported types
+ public string StringColumn { get; set; } = null!;
+ public int IntColumn { get; set; }
+ public long LongColumn { get; set; }
+ public short ShortColumn { get; set; }
+ public byte ByteColumn { get; set; }
+ public decimal DecimalColumn { get; set; }
+ public double DoubleColumn { get; set; }
+ public float FloatColumn { get; set; }
+ public bool BoolColumn { get; set; }
+ public DateTime DateTimeColumn { get; set; }
+ public DateTimeOffset DateTimeOffsetColumn { get; set; }
+ public TimeSpan TimeSpanColumn { get; set; }
+ public byte[] ByteArrayColumn { get; set; } = null!;
+ public Guid GuidColumn { get; set; }
+ public char CharColumn { get; set; }
+ public char[] CharArrayColumn { get; set; } = null!;
+ public object ObjectColumn { get; set; } = null!;
+
+ // create column of all supported nullable types
+ public string? NullableStringColumn { get; set; }
+ public int? NullableIntColumn { get; set; }
+ public long? NullableLongColumn { get; set; }
+ public short? NullableShortColumn { get; set; }
+ public byte? NullableByteColumn { get; set; }
+ public decimal? NullableDecimalColumn { get; set; }
+ public double? NullableDoubleColumn { get; set; }
+ public float? NullableFloatColumn { get; set; }
+ public bool? NullableBoolColumn { get; set; }
+ public DateTime? NullableDateTimeColumn { get; set; }
+ public DateTimeOffset? NullableDateTimeOffsetColumn { get; set; }
+ public TimeSpan? NullableTimeSpanColumn { get; set; }
+ public byte[]? NullableByteArrayColumn { get; set; }
+ public Guid? NullableGuidColumn { get; set; }
+ public char? NullableCharColumn { get; set; }
+ public char[]? NullableCharArrayColumn { get; set; }
+ public object? NullableObjectColumn { get; set; }
+
+ // create columns of all enumerable types
+ public IDictionary IDictionaryColumn { get; set; } = null!;
+ public IDictionary? NullableIDictionaryColumn { get; set; }
+ public Dictionary DictionaryColumn { get; set; } = null!;
+ public Dictionary? NullableDictionaryColumn { get; set; }
+ public IDictionary IObjectDictionaryColumn { get; set; } = null!;
+ public IDictionary? NullableIObjectDictionaryColumn { get; set; }
+ public Dictionary ObjectDictionaryColumn { get; set; } = null!;
+ public Dictionary? NullableObjectDictionaryColumn { get; set; }
+ public IList IListColumn { get; set; } = null!;
+ public IList? NullableIListColumn { get; set; }
+ public List ListColumn { get; set; } = null!;
+ public List? NullableListColumn { get; set; }
+ public ICollection ICollectionColumn { get; set; } = null!;
+ public ICollection? NullableICollectionColumn { get; set; }
+ public Collection CollectionColumn { get; set; } = null!;
+ public Collection? NullableCollectionColumn { get; set; }
+ public IEnumerable IEnumerableColumn { get; set; } = null!;
+ public IEnumerable? NullableIEnumerableColumn { get; set; }
+
+ // create columns of arrays
+ public string[] StringArrayColumn { get; set; } = null!;
+ public string[]? NullableStringArrayColumn { get; set; }
+ public int[] IntArrayColumn { get; set; } = null!;
+ public int[]? NullableIntArrayColumn { get; set; }
+ public long[] LongArrayColumn { get; set; } = null!;
+ public long[]? NullableLongArrayColumn { get; set; }
+ public Guid[] GuidArrayColumn { get; set; } = null!;
+ public Guid[]? NullableGuidArrayColumn { get; set; }
+
+ // create columns of enums, structs and classes
+ public TestEnum EnumColumn { get; set; }
+ public TestEnum? NullableEnumColumn { get; set; }
+ public TestStruct StructColumn { get; set; }
+ public TestStruct? NullableStructColumn { get; set; }
+ public TestClass ClassColumn { get; set; } = null!;
+ public TestClass? NullableClassColumn { get; set; }
+ public TestInterface InterfaceColumn { get; set; } = null!;
+ public TestInterface? NullableInterfaceColumn { get; set; }
+ public TestAbstractClass AbstractClassColumn { get; set; } = null!;
+ public TestAbstractClass? NullableAbstractClassColumn { get; set; }
+ public TestConcreteClass ConcreteClass { get; set; } = null!;
+ public TestConcreteClass? NullableConcreteClass { get; set; }
+}
+
+public enum TestEnum
+{
+ Value1,
+ Value2,
+ Value3
+}
+
+public struct TestStruct
+{
+ public int Value { get; set; }
+}
+
+public class TestClass
+{
+ public int Value { get; set; }
+}
+
+public interface TestInterface
+{
+ int Value { get; set; }
+}
+
+public abstract class TestAbstractClass
+{
+ public int Value { get; set; }
+}
+
+public class TestConcreteClass : TestAbstractClass
+{
+ public int Value2 { get; set; }
+}
diff --git a/tests/DapperMatic.Tests/DatabaseMethodsTests.Types.cs b/tests/DapperMatic.Tests/DatabaseMethodsTests.Types.cs
index e6b609a..5735d1f 100644
--- a/tests/DapperMatic.Tests/DatabaseMethodsTests.Types.cs
+++ b/tests/DapperMatic.Tests/DatabaseMethodsTests.Types.cs
@@ -3,8 +3,8 @@
namespace DapperMatic.Tests;
public abstract partial class DatabaseMethodsTests
-{
- private static Type[] GetSupportedTypes(IProviderTypeMap dbTypeMap)
+{
+ private static Type[] GetSupportedTypes(IDbProviderTypeMap dbTypeMap)
{
// Type[] supportedTypes = dbTypeMap
// .GetProviderSqlTypes()
@@ -25,24 +25,31 @@ private static Type[] GetSupportedTypes(IProviderTypeMap dbTypeMap)
// })
// .Distinct()
// .ToArray();
-
+
Type[] typesToSupport =
[
+ typeof(bool),
typeof(byte),
+ typeof(sbyte),
typeof(short),
typeof(int),
typeof(long),
- typeof(bool),
typeof(float),
typeof(double),
typeof(decimal),
+ typeof(char),
+ typeof(string),
+ typeof(char[]),
+ typeof(ReadOnlyMemory[]),
+ typeof(Stream),
+ typeof(Guid),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
+ typeof(DateOnly),
+ typeof(TimeOnly),
typeof(byte[]),
typeof(object),
- typeof(string),
- typeof(Guid),
// generic definitions
typeof(IDictionary<,>),
typeof(Dictionary<,>),
@@ -75,7 +82,7 @@ private static Type[] GetSupportedTypes(IProviderTypeMap dbTypeMap)
// custom classes
typeof(TestClassDao)
];
-
+
return typesToSupport;
}
@@ -91,9 +98,8 @@ protected virtual async Task Provider_type_map_supports_all_desired_dotnet_types
var dbTypeMap = db.GetProviderTypeMap();
foreach (var desiredType in GetSupportedTypes(dbTypeMap))
{
- var exists = dbTypeMap.TryGetRecommendedSqlTypeMatchingDotnetType(
- desiredType,
- null, null, null, null,
+ var exists = dbTypeMap.TryGetProviderSqlTypeMatchingDotnetType(
+ new DbProviderDotnetTypeDescriptor(desiredType),
out var sqlType
);
diff --git a/tests/DapperMatic.Tests/ExtensionMethodTests.cs b/tests/DapperMatic.Tests/ExtensionMethodTests.cs
new file mode 100644
index 0000000..d5eed47
--- /dev/null
+++ b/tests/DapperMatic.Tests/ExtensionMethodTests.cs
@@ -0,0 +1,44 @@
+namespace DapperMatic.Tests;
+
+public class ExtensionMethodTests
+{
+ // tes the GetFriendlyName method
+ [Theory]
+ [InlineData(typeof(bool), "Boolean")]
+ [InlineData(typeof(List), "List")]
+ [InlineData(
+ typeof(IEnumerable>>),
+ "IEnumerable>>"
+ )]
+ public void TestGetFriendlyName(Type input, string expected)
+ {
+ var actual = input.GetFriendlyName();
+ Assert.Equal(expected, actual);
+ }
+
+ // test the ToAlpha method
+ [Theory]
+ [InlineData("abc", "abc")]
+ [InlineData("abc123", "abc")]
+ [InlineData("abc123def", "abcdef")]
+ [InlineData("abc123def456", "abcdef")]
+ [InlineData("abc (&__-1234)123def456ghi", "abcdefghi")]
+ [InlineData("abc (&__-1234)123def456ghi", "abc(&__)defghi", "_&()")]
+ public void TestToAlpha(string input, string expected, string additionalAllowedCharacters = "")
+ {
+ var actual = input.ToAlpha(additionalAllowedCharacters);
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [InlineData("char", "char")]
+ [InlineData("decimal(10,2)", "decimal")]
+ [InlineData("abc(12,2) dd aa", "abc dd aa")]
+ [InlineData("abc ( 12 ,2 ) dd aa", "abc dd aa")]
+ [InlineData(" nvarchar ( 255 ) ", "nvarchar")]
+ public void TestDiscardLengthPrecisionAndScaleFromSqlTypeName(string input, string expected)
+ {
+ var actual = input.DiscardLengthPrecisionAndScaleFromSqlTypeName();
+ Assert.Equal(expected, actual);
+ }
+}