From a576d157fb4985d516e4d347b7581e64f75b0f37 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sat, 17 Aug 2024 15:55:35 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=AE=8C=E5=96=84=20DuckDB=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ZeroDbContext.cs | 3 +++ FreeSql.DbContext/DbSet/DbSet.cs | 1 + FreeSql.DbContext/DbSet/DbSetAsync.cs | 2 ++ FreeSql.DbContext/DbSet/DbSetSync.cs | 2 ++ .../Duckdb/Curd/DuckdbSelectTest.cs | 4 +-- FreeSql/Internal/UtilsExpressionTree.cs | 25 ++++++++++++++----- 6 files changed, 29 insertions(+), 8 deletions(-) diff --git a/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs b/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs index 550a99108..2731ed830 100644 --- a/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs +++ b/Extensions/FreeSql.Extensions.ZeroEntity/ZeroDbContext.cs @@ -703,6 +703,7 @@ void LocalAdd(TableInfo table, T data, bool isCheck, ColumnInfo[] _tableReturnCo case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: case DataType.Firebird: //firebird 只支持单条插入 returning if (_tableIdentitys.Length == 1 && _tableReturnColumns.Length == 1) { @@ -758,6 +759,7 @@ void LocalAddRange(TableInfo table, IEnumerable data) case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: var rets = OrmInsert(table).AppendData(data).ExecuteInserted(); _cascadeAffrows += rets.Count; if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_orm.Ado.DataType} 的返回数据,与添加的数目不匹配"); @@ -810,6 +812,7 @@ bool LocalCanAdd(TableInfo table, T data, bool isThrow) case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: case DataType.ClickHouse: return true; default: diff --git a/FreeSql.DbContext/DbSet/DbSet.cs b/FreeSql.DbContext/DbSet/DbSet.cs index 3bc06c8a9..ade54e245 100644 --- a/FreeSql.DbContext/DbSet/DbSet.cs +++ b/FreeSql.DbContext/DbSet/DbSet.cs @@ -344,6 +344,7 @@ bool CanAdd(TEntity data, bool isThrow) case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: case DataType.ClickHouse: return true; default: diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 68a7753dc..42ad2ca8b 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -47,6 +47,7 @@ async Task AddPrivAsync(TEntity data, bool isCheck, CancellationToken cancellati case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: case DataType.Firebird: //firebird 只支持单条插入 returning if (_tableIdentitys.Length == 1 && _tableReturnColumns.Length == 1) { @@ -115,6 +116,7 @@ async public Task AddRangeAsync(IEnumerable data, CancellationToken can case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: await DbContextFlushCommandAsync(cancellationToken); var rets = await this.OrmInsert(data).ExecuteInsertedAsync(cancellationToken); if (rets.Count != data.Count()) throw new Exception(DbContextStrings.SpecialError_BatchAdditionFailed(_db.OrmOriginal.Ado.DataType)); diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 601319147..8cc04d29d 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -46,6 +46,7 @@ void AddPriv(TEntity data, bool isCheck) case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: case DataType.Firebird: //firebird 只支持单条插入 returning if (_tableIdentitys.Length == 1 && _tableReturnColumns.Length == 1) { @@ -118,6 +119,7 @@ public void AddRange(IEnumerable data) case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: + case DataType.DuckDB: DbContextFlushCommand(); var rets = this.OrmInsert(data).ExecuteInserted(); if (rets.Count != data.Count()) throw new Exception(DbContextStrings.SpecialError_BatchAdditionFailed(_db.OrmOriginal.Ado.DataType)); diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Duckdb/Duckdb/Curd/DuckdbSelectTest.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Duckdb/Duckdb/Curd/DuckdbSelectTest.cs index d8b36897c..a4a07dd65 100644 --- a/FreeSql.Tests/FreeSql.Tests.Provider.Duckdb/Duckdb/Curd/DuckdbSelectTest.cs +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Duckdb/Duckdb/Curd/DuckdbSelectTest.cs @@ -1117,14 +1117,14 @@ public void WhereIn() var subquery = select.Where(a => select.As("b").ToList(b => b.Title).Contains(a.Id.ToString())).ToSql(); Assert.Equal(@"SELECT a.""id"", a.""clicks"", a.""typeguid"", a.""title"", a.""createtime"" FROM ""tb_topic"" a -WHERE (((cast(a.""id"" as text)) in (SELECT b.""title"" +WHERE ((((a.""id"")::text) in (SELECT b.""title"" FROM ""tb_topic"" b)))", subquery); var subqueryList = select.Where(a => select.As("b").ToList(b => b.Title).Contains(a.Id.ToString())).ToList(); subquery = select.Where(a => select.As("b").Limit(10).ToList(b => b.Title).Contains(a.Id.ToString())).ToSql(); Assert.Equal(@"SELECT a.""id"", a.""clicks"", a.""typeguid"", a.""title"", a.""createtime"" FROM ""tb_topic"" a -WHERE (((cast(a.""id"" as text)) in (SELECT b.""title"" +WHERE ((((a.""id"")::text) in (SELECT b.""title"" FROM ""tb_topic"" b limit 10)))", subquery); subqueryList = select.Where(a => select.As("b").Limit(10).ToList(b => b.Title).Contains(a.Id.ToString())).ToList(); diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index a6c8bda4e..bdc002ee7 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -8,6 +8,7 @@ using System.Collections.ObjectModel; using System.Data; using System.Data.Common; +using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Numerics; @@ -394,7 +395,7 @@ public static ColumnInfo ColumnAttributeToInfo(TableInfo trytb, object entityDef case DataType.CustomPostgreSQL: case DataType.KingbaseES: case DataType.ShenTong: - if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; + if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; else colattr.DbType = Regex.Replace(colattr.DbType, charPattern, m => replaceCounter++ == 0 ? $"{m.Groups[1].Value}({strlen})" : m.Groups[0].Value); break; @@ -416,7 +417,8 @@ public static ColumnInfo ColumnAttributeToInfo(TableInfo trytb, object entityDef replaceCounter++ == 0 ? $"{m.Groups[1].Value}({strlen})" : m.Groups[0].Value); break; case DataType.Sqlite: - if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; + case DataType.DuckDB: + if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; else colattr.DbType = Regex.Replace(colattr.DbType, charPattern, m => replaceCounter++ == 0 ? $"{m.Groups[1].Value}({strlen})" : m.Groups[0].Value); break; @@ -477,7 +479,8 @@ public static ColumnInfo ColumnAttributeToInfo(TableInfo trytb, object entityDef colattr.DbType = $"BLOB{strNotNull}"; break; case DataType.Sqlite: - colattr.DbType = $"BLOB{strNotNull}"; + case DataType.DuckDB: + colattr.DbType = $"BLOB{strNotNull}"; break; case DataType.MsAccess: if (strlen < 0) colattr.DbType = $"BLOB{strNotNull}"; @@ -2137,7 +2140,12 @@ public static string ToStringConcat(object obj) { if (obj == null) return null; if (obj is bool || obj is bool?) return (bool)obj == true ? "1" : "0"; - return string.Concat(obj); + return string.Format(CultureInfo.InvariantCulture, "{0}", obj); + } + public static bool ValueIsEnumAndTargetIsNumber(object value, Type targetType) + { + if (value == null || targetType == null) return false; + return value.GetType().IsEnum && targetType.IsNumberType(); } public static byte[] GuidToBytes(Guid guid) { @@ -2207,6 +2215,7 @@ public static TElement[] ListOrArrayToArray(object listOrArray) static MethodInfo MethodDateTimeTryParse = typeof(DateTime).GetMethod("TryParse", new[] { typeof(string), typeof(DateTime).MakeByRefType() }); static MethodInfo MethodDateTimeOffsetTryParse = typeof(DateTimeOffset).GetMethod("TryParse", new[] { typeof(string), typeof(DateTimeOffset).MakeByRefType() }); static MethodInfo MethodToString = typeof(Utils).GetMethod("ToStringConcat", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(object) }, null); + static MethodInfo MethodValueIsEnumAndTargetIsNumber = typeof(Utils).GetMethod("ValueIsEnumAndTargetIsNumber", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(object), typeof(Type) }, null); static MethodInfo MethodBigIntegerParse = typeof(Utils).GetMethod("ToBigInteger", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null); static PropertyInfo PropertyDateTimeOffsetDateTime = typeof(DateTimeOffset).GetProperty("DateTime", BindingFlags.Instance | BindingFlags.Public); static PropertyInfo PropertyDateTimeTicks = typeof(DateTime).GetProperty("Ticks", BindingFlags.Instance | BindingFlags.Public); @@ -2555,8 +2564,12 @@ Expression LocalFuncGetExpression(bool ignoreArray = false) Expression.Constant(typeof(DateTime)), Expression.Constant(typeof(DateTimeOffset)) ) ); - // BigInteger/bool -> int - //defaultRetExp = Expression.Return(returnTarget, Expression.Call(MethodGetDataReaderValue, Expression.Constant(type), Expression.Convert(Expression.Call(MethodToString, valueExp), typeof(object)))); + //BigInteger/bool/Enum -> int + defaultRetExp = Expression.IfThenElse( + Expression.Call(MethodValueIsEnumAndTargetIsNumber, valueExp, Expression.Constant(type)), + defaultRetExp, + Expression.Return(returnTarget, Expression.Call(MethodGetDataReaderValue, Expression.Constant(type), Expression.Convert(Expression.Call(MethodToString, valueExp), typeof(object)))) + ); } else if (tryparseBooleanExp != null) switchExp = Expression.Switch(