diff --git a/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest2.cs b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest2.cs
index 9bc76f41..b96e8e9d 100644
--- a/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest2.cs
+++ b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest2.cs
@@ -8,11 +8,14 @@ using System.Diagnostics;
using System.ComponentModel.DataAnnotations;
using FreeSql.DataAnnotations;
using Xunit;
+using Xunit.Abstractions;
namespace FreeSql.Tests.ClickHouse
{
public class ClickHouseTest2
{
+
+
private static IFreeSql fsql = new FreeSqlBuilder().UseConnectionString(DataType.ClickHouse,
"Host=127.0.0.1;Port=8123;Database=test;Compress=True;Min Pool Size=1")
.UseMonitorCommand(cmd => Console.WriteLine($"线程:{cmd.CommandText}\r\n"))
@@ -29,6 +32,7 @@ namespace FreeSql.Tests.ClickHouse
{
fsql.CodeFirst.SyncStructure(typeof(PositionInfoModel));
}
+
[Fact]
public void Issuse1587TestOnePrimary()
{
diff --git a/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest3.cs b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest3.cs
new file mode 100644
index 00000000..b3fbd72d
--- /dev/null
+++ b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest3.cs
@@ -0,0 +1,456 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using FreeSql.DataAnnotations;
+using Newtonsoft.Json;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace FreeSql.Tests.ClickHouse
+{
+ public class ClickHouseTest3
+ {
+ private static ITestOutputHelper _output;
+ private static IFreeSql _fsql;
+
+ public ClickHouseTest3(ITestOutputHelper output)
+ {
+ _output = output;
+ _fsql = new FreeSqlBuilder().UseConnectionString(DataType.ClickHouse,
+ "Host=192.168.1.123;Port=8123;Database=test;Compress=True;Min Pool Size=1")
+ .UseMonitorCommand(cmd => _output.WriteLine($"线程:{cmd.CommandText}\r\n"))
+ .UseNoneCommandParameter(true)
+ .Build();
+ }
+
+ ///
+ /// 测试bool类型映射
+ ///
+ [Fact]
+ public void TestBoolMappingSync()
+ {
+ _fsql.CodeFirst.SyncStructure(typeof(BoolMappingTest));
+ }
+
+ ///
+ /// 测试bool类型插入
+ ///
+ [Fact]
+ public void TestBoolMappingInsert()
+ {
+ _fsql.Insert(new BoolMappingTest
+ {
+ Name = "Tom",
+ Age = 20,
+ Id = Guid.NewGuid().ToString(),
+ IsDelete = true,
+ IsEnable = true
+ }).ExecuteAffrows();
+
+ _fsql.Insert(new BoolMappingTest
+ {
+ Name = "Jess",
+ Age = 21,
+ Id = Guid.NewGuid().ToString(),
+ IsDelete = true,
+ IsEnable = false
+ }).ExecuteAffrows();
+
+ _fsql.Insert(new BoolMappingTest
+ {
+ Name = "Daily",
+ Age = 22,
+ Id = Guid.NewGuid().ToString(),
+ IsDelete = false,
+ IsEnable = false
+ }).ExecuteAffrows();
+ }
+
+ ///
+ /// 测试bool类型修改
+ ///
+ [Fact]
+ public void TestBoolMappingUpdateSet()
+ {
+ _fsql.Update()
+ .Set(t => t.IsDelete, true)
+ .Where(b => b.Age > 10)
+ .ExecuteAffrows();
+ }
+
+ ///
+ /// 测试bool类型修改
+ ///
+ [Fact]
+ public void TestBoolMappingUpdate()
+ {
+ _fsql.Update()
+ .SetSource(new BoolMappingTest
+ {
+ Id = "af199304-239a-48da-9c75-1d5e36167d74",
+ IsEnable = false,
+ IsDelete = true,
+ Age = 60,
+ Name = "Update"
+ })
+ .ExecuteAffrows();
+ }
+
+
+ ///
+ /// 测试bool类型查询
+ ///
+ [Fact]
+ public void TestBoolMappingSelect()
+ {
+ var list = _fsql.Select().ToList();
+ }
+
+ ///
+ /// 测试Array类型映射
+ ///
+ [Fact]
+ public void ArrayBoolMappingSync()
+ {
+ _fsql.CodeFirst.SyncStructure(typeof(ArrayMappingTestSimple));
+ }
+
+ ///
+ /// 测试Array类型插入
+ ///
+ [Fact]
+ public void ArrayBoolMappingInsert()
+ {
+ var source = new List()
+ {
+ new()
+ {
+ Name = "daily",
+ Tags1 = Array.Empty(),
+ Tags2 = new[] { 3, 45, 100, 400 },
+ Tags3 = new[] { false, true, false }
+ }
+ };
+ var str = _fsql.Insert(source).ExecuteAffrows();
+ }
+
+ ///
+ /// 测试Array类型映射
+ ///
+ [Fact]
+ public void ArrayBoolMappingSelect()
+ {
+ var list = _fsql.Select().ToList();
+ _output.WriteLine(JsonConvert.SerializeObject(list));
+ }
+
+ ///
+ /// 测试Array常用查询函数
+ ///
+ [Fact]
+ public void ArraySelectAnySync()
+ {
+ var sql = _fsql.Select().Where(a => !a.Tags1.Any()).ToList(a => a.Name);
+ _output.WriteLine(JsonConvert.SerializeObject(sql));
+ }
+
+
+ ///
+ /// 测试Array常用查询函数
+ ///
+ [Fact]
+ public void ArraySelectLengthSync()
+ {
+ var sql = _fsql.Select().ToList(a => a.Tags1.Count());
+ _output.WriteLine(JsonConvert.SerializeObject(sql));
+
+ var sql2 = _fsql.Select().Where(a => a.Tags1.Count() > 5).ToList(a => a.Tags1);
+ _output.WriteLine(JsonConvert.SerializeObject(sql2));
+ }
+
+
+ ///
+ /// 测试Array常用查询函数
+ ///
+ [Fact]
+ public void ArraySelectContainsSync()
+ {
+ var sql = _fsql.Select().ToList(a => a.Tags1.Contains("a"));
+ _output.WriteLine(JsonConvert.SerializeObject(sql));
+
+ var sql2 = _fsql.Select().Where(a => a.Tags2.Contains(2)).ToList(a => a.Tags2);
+ _output.WriteLine(JsonConvert.SerializeObject(sql2));
+ }
+
+ ///
+ /// 测试Array常用查询函数
+ ///
+ [Fact]
+ public void ArraySelectConcatSync()
+ {
+ var list = new List() { "f" };
+ var sql = _fsql.Select().ToList(a => a.Tags1.Concat(list));
+ _output.WriteLine(JsonConvert.SerializeObject(sql));
+ }
+
+ ///
+ /// 测试Array常用查询函数
+ ///
+ [Fact]
+ public void ArraySelectConstContainsSync()
+ {
+ var list = new List() { "daily", "a" };
+ var sql = _fsql.Select().Where(a => list.Contains(a.Name)).ToList();
+ _output.WriteLine(JsonConvert.SerializeObject(sql));
+ }
+
+ ///
+ /// 测试Array常用查询函数
+ ///
+ [Fact]
+ public void ArraySelectConstLengthSync()
+ {
+ var sql = _fsql.Select().ToList(a => "aaaa".Length);
+ _output.WriteLine(JsonConvert.SerializeObject(sql));
+ }
+
+ ///
+ /// 测试ArrayFilter测试
+ ///
+ [Fact]
+ public void ArrayFilterFuncTest()
+ {
+ //var list = _fsql.Select().Where(a => a.Tags2.ArrayFilter(o => o == 1).Any())
+ // .ToSql();
+
+
+ ////SELECT a.`name`, a.`tags1`, a.`tags2`, a.`tags3`
+ ////FROM `table_test_array_simple` a
+ ////WHERE (arrayFilter(x -> x = '1', a.`tags2`) != [])
+
+ //_output.WriteLine(JsonConvert.SerializeObject(list));
+ }
+
+ ///
+ /// 测试ArrayFilter测试
+ ///
+ [Fact]
+ public void IsPrimaryTest()
+ {
+ _fsql.CodeFirst.SyncStructure();
+ }
+
+ ///
+ /// https://github.com/dotnetcore/FreeSql/issues/969
+ ///
+ [Fact]
+ public async Task UriStringIsTooLongTest()
+ {
+ _fsql.CodeFirst.SyncStructure();
+ var json =
+ "[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}][{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}[{\"date\":\"2021-12-19T02:47:53.4365075 08:00\",\"temperatureC\":6,\"temperatureF\":42,\"summary\":\"Balmy\"},{\"date\":\"2021-12-20T02:47:53.4366893 08:00\",\"temperatureC\":36,\"temperatureF\":96,\"summary\":\"Bracing\"},{\"date\":\"2021-12-21T02:47:53.4366903 08:00\",\"temperatureC\":-15,\"temperatureF\":6,\"summary\":\"Bracing\"},{\"date\":\"2021-12-22T02:47:53.4366904 08:00\",\"temperatureC\":14,\"temperatureF\":57,\"summary\":\"Cool\"},{\"date\":\"2021-12-23T02:47:53.4366905 08:00\",\"temperatureC\":29,\"temperatureF\":84,\"summary\":\"Mild\"}";
+
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+ json += json;
+
+ var t = new TestTable
+ {
+ Id = Guid.NewGuid().ToString(),
+ Content = json,
+ Content2 = json,
+ Time = DateTime.Now
+ };
+
+ //单个插入报错
+ await _fsql.Insert(t).ExecuteAffrowsAsync();
+
+ // await _fsql.Insert(t).ExecuteBulkCopyAsync();
+ }
+
+
+ ///
+ /// 测试BulkCopy单条
+ ///
+ ///
+ [Fact]
+ public async Task TestBulkCopySingle()
+ {
+ var t = new TestTable
+ {
+ Id = Guid.NewGuid().ToString(),
+ Content = "1",
+ Content2 = "2",
+ Time = DateTime.Now
+ };
+
+ //单个插入报错
+ await _fsql.Insert(t).ExecuteAffrowsAsync();
+
+ await _fsql.Insert(t).ExecuteBulkCopyAsync();
+
+ _fsql.Insert(t).ExecuteBulkCopy();
+ }
+
+ ///
+ /// 测试BulkCopy多条
+ ///
+ ///
+ [Fact]
+ public async Task TestBulkCopyMany()
+ {
+ var t = new List();
+
+ foreach (var i in Enumerable.Range(0, 10))
+ {
+ t.Add(new TestTable
+ {
+ Id = Guid.NewGuid().ToString(),
+ Content = i.ToString(),
+ Content2 = i.ToString(),
+ Time = DateTime.Now
+ });
+ }
+
+ //单个插入报错
+ await _fsql.Insert(t).ExecuteAffrowsAsync();
+
+ //BulkCopy不会报错
+ await _fsql.Insert(t).ExecuteBulkCopyAsync();
+
+ _fsql.Insert(t).ExecuteBulkCopy();
+ }
+ }
+
+
+ [Table(Name = "table_test_bool")]
+ public class BoolMappingTest
+ {
+ [Column(IsPrimary = true, Name = "id")]
+ public string Id { set; get; }
+
+ [Column(Name = "name")] public string Name { get; set; }
+
+ [Column(Name = "age")] public int Age { get; set; }
+
+ [Column(Name = "is_delete")] public bool IsDelete { get; set; }
+
+ [Column(Name = "is_enable")] public bool? IsEnable { get; set; }
+ }
+
+ [Table(Name = "table_test_array")]
+ public class ArrayMappingTest
+ {
+ [Column(Name = "name", IsPrimary = true)]
+ public string Name { get; set; }
+
+ [Column(Name = "tags1")] public IEnumerable Tags1 { get; set; }
+
+ [Column(Name = "tags2")] public IList Tags2 { get; set; }
+
+ [Column(Name = "tags3")] public List Tags3 { get; set; }
+
+ [Column(Name = "tags4")] public ArrayList Tags4 { get; set; }
+
+ [Column(Name = "tags5")] public Array Tags5 { get; set; }
+
+ [Column(Name = "tags6")] public List Tags6 { get; set; }
+
+ [Column(Name = "tags7")] public IEnumerable Tags7 { get; set; }
+ }
+
+ [Table(Name = "table_test_array_simple")]
+ public class ArrayMappingTestSimple
+
+ {
+ [Column(Name = "name", IsPrimary = true)]
+ public string Name { get; set; }
+
+ [Column(Name = "tags1")] public string[] Tags1 { get; set; }
+
+ [Column(Name = "tags2")] public int[] Tags2 { get; set; }
+
+ [Column(Name = "tags3")] public bool[] Tags3 { get; set; }
+ }
+
+ ///
+ /// Http请求信息统计
+ ///
+ [Table(Name = "http_context_record")]
+ public class HttpContextRecord
+ {
+ [Column(Name = "id", IsPrimary = true)]
+ public string Id { get; set; }
+
+ ///
+ /// 请求模板
+ ///
+ [Column(Name = "request_total_key", StringLength = 80)]
+ public string RequestTotalKey { get; set; }
+
+ ///
+ /// 请求量
+ ///
+ [Column(Name = "total")]
+ public long Total { get; set; }
+
+ ///
+ /// 记录请求类型
+ ///
+ [Column(Name = "type")]
+ public int Type { get; set; }
+
+ ///
+ /// 添加时间
+ ///
+ [Column(Name = "add_time")]
+ public DateTime AddTime { get; set; }
+ }
+
+ public class ContentRecord
+ {
+ [Column(IsPrimary = true)] public string Id { get; set; }
+
+ public string? Content1 { get; set; }
+
+ public string? Content2 { get; set; }
+
+ public string? Content3 { get; set; }
+
+ public string Content4 { get; set; }
+ }
+
+ internal class TestTable
+ {
+ [Required] [Column(IsIdentity = true)] public string Id { get; set; }
+
+ [Column(StringLength = -2)] public string Content { get; set; }
+
+ [Column(StringLength = -2)] public string Content2 { get; set; }
+
+ [Column(DbType = "DateTime64(3, 'Asia/Shanghai')")]
+ public DateTime Time { get; set; }
+
+ public override string ToString()
+ {
+ return $"Id:{Id} Content:{Content} Content2:{Content2} Time:{Time}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml
index 23fd15f0..47cc24ad 100644
--- a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml
+++ b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.xml
@@ -4,6 +4,128 @@
FreeSql.Tests
+
+
+ 测试bool类型映射
+
+
+
+
+ 测试bool类型插入
+
+
+
+
+ 测试bool类型修改
+
+
+
+
+ 测试bool类型修改
+
+
+
+
+ 测试bool类型查询
+
+
+
+
+ 测试Array类型映射
+
+
+
+
+ 测试Array类型插入
+
+
+
+
+ 测试Array类型映射
+
+
+
+
+ 测试Array常用查询函数
+
+
+
+
+ 测试Array常用查询函数
+
+
+
+
+ 测试Array常用查询函数
+
+
+
+
+ 测试Array常用查询函数
+
+
+
+
+ 测试Array常用查询函数
+
+
+
+
+ 测试Array常用查询函数
+
+
+
+
+ 测试ArrayFilter测试
+
+
+
+
+ 测试ArrayFilter测试
+
+
+
+
+ https://github.com/dotnetcore/FreeSql/issues/969
+
+
+
+
+ 测试BulkCopy单条
+
+
+
+
+
+ 测试BulkCopy多条
+
+
+
+
+
+ Http请求信息统计
+
+
+
+
+ 请求模板
+
+
+
+
+ 请求量
+
+
+
+
+ 记录请求类型
+
+
+
+
+ 添加时间
+
+
实时数据
diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderUtils.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderUtils.cs
index a5461a4d..001c2b4e 100644
--- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderUtils.cs
+++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderUtils.cs
@@ -43,7 +43,7 @@ namespace FreeSql.Internal.CommonProvider
sb.Append(AddslashesProcessParam(z, mapType, mapColumn));
}
- return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "(").Append(")").ToString();
+ return sb.Length == 0 ? "(NULL)" : sb.Remove(0, 1).Insert(0, "[").Append("]").ToString();
}
public static bool IsFromSlave(string cmdText)
diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseAdo/ClickHouseAdo.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseAdo/ClickHouseAdo.cs
index 3ba93410..d3de56e4 100644
--- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseAdo/ClickHouseAdo.cs
+++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseAdo/ClickHouseAdo.cs
@@ -48,7 +48,7 @@ namespace FreeSql.ClickHouse
param = Utils.GetDataReaderValue(mapType, param);
if (param is bool || param is bool?)
- return (bool)param ? 1 : 0;
+ return (bool)param; //不需要转0/1
else if (param is string)
return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\"), "'"); //只有 mysql 需要处理反斜杠
else if (param is char)
diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs
index e4cf8216..f56737fd 100644
--- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs
+++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs
@@ -1,6 +1,7 @@
using FreeSql.Internal;
using FreeSql.Internal.Model;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
@@ -23,8 +24,8 @@ namespace FreeSql.ClickHouse
static Dictionary> _dicCsToDb = new Dictionary>()
{
- { typeof(bool).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", null, false, false) },
- { typeof(bool?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", null, true, null) },
+ { typeof(bool).FullName, CsToDb.New(DbType.SByte, "Bool", "Bool", null, false, false) },
+ { typeof(bool?).FullName, CsToDb.New(DbType.SByte, "Bool", "Nullable(Bool)", null, true, null) },
{ typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", false, false, 0) },
{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", false, true, null) },
@@ -50,7 +51,8 @@ namespace FreeSql.ClickHouse
{ typeof(float?).FullName, CsToDb.New(DbType.Single, "Float32", "Nullable(Float32)", false, true, null) },
{
typeof(decimal).FullName,
- CsToDb.New(DbType.Decimal, "Decimal(38, 19)", "Decimal(38, 19)", false, false, 0) //Nullable(Decimal(38, 19))
+ CsToDb.New(DbType.Decimal, "Decimal(38, 19)", "Decimal(38, 19)", false, false,
+ 0) //Nullable(Decimal(38, 19))
},
{
typeof(decimal?).FullName,
@@ -75,17 +77,84 @@ namespace FreeSql.ClickHouse
{ typeof(Guid?).FullName, CsToDb.New(DbType.String, "String", "Nullable(String)", false, true, null) },
};
+
public override DbInfoResult GetDbInfo(Type type)
{
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc))
return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable,
trydc.defaultValue);
- if (type.IsArray)
- return null;
+
+ //判断是否是集合
+ var isCollection = IsArray(type);
+ if (isCollection.Item1)
+ {
+ var genericType = isCollection.Item2;
+ var genericTypeName = genericType?.FullName;
+ var tryGetValue = _dicCsToDb.TryGetValue(genericTypeName, out var value);
+ if (tryGetValue)
+ {
+ var arrayDbType = $"Array({value.dbtype})";
+ var defaultArray = new ArrayList(0);
+ return new DbInfoResult(Convert.ToInt32(DbType.Object), arrayDbType, arrayDbType, false,defaultArray);
+ }
+
+ }
return null;
}
- protected override string GetComparisonDDLStatements(params TypeSchemaAndName[] objects)
+
+ private Tuple IsArray(Type type)
+ {
+ var flag = false;
+ Type resultType = null;
+
+ if (type.IsArray)
+ {
+ flag = true;
+ resultType = type.GetElementType();
+ }
+
+ return new Tuple(flag, resultType);
+ }
+
+ private Tuple IsCollection(Type type)
+ {
+ var flag = false;
+ Type resultType = null;
+ var interfaces = type.GetInterfaces();
+
+ if (interfaces.Any(t => t.Name == "IList"))
+ flag = true;
+
+ if (interfaces.Any(t => t.Name == "ICollection"))
+ flag = true;
+
+ if (interfaces.Any(t => t.Name == "IEnumerable"))
+ flag = true;
+
+ if (type.Name == "Array")
+ {
+ flag = true;
+ resultType = typeof(string);
+ }
+
+ if (type.Name == "ArrayList")
+ {
+ flag = true;
+ resultType = typeof(string);
+ }
+
+ //是否是泛型
+ if (type.GetGenericArguments().Any())
+ {
+ var first = type.GetGenericArguments().First();
+ resultType = first;
+ }
+
+ return new Tuple(flag, resultType);
+ }
+
+ protected override string GetComparisonDDLStatements(params TypeAndName[] objects)
{
Object conn = null;
string database = null;
@@ -100,11 +169,11 @@ namespace FreeSql.ClickHouse
{
if (sb.Length > 0)
sb.Append("\r\n");
- var tb = obj.tableSchema;
+ var tb = _commonUtils.GetTableByEntity(obj.entityType);
if (tb == null)
- throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.tableSchema.Type.FullName));
+ throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName));
if (tb.Columns.Any() == false)
- throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.tableSchema.Type.FullName));
+ throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName));
var tbname = _commonUtils.SplitTableName(tb.DbName);
if (tbname?.Length == 1)
tbname = new[] { database, tbname[0] };
@@ -299,7 +368,8 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
//添加列
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1]))
- .Append(" ADD Column ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ")
+ .Append(" ADD Column ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name))
+ .Append(" ")
.Append(tbcol.Attribute.DbType);
if (tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" &&
tbcol.Attribute.IsIdentity == false)
@@ -483,7 +553,7 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
var before = new Aop.CommandBeforeEventArgs(cmd);
- this._orm?.Aop.CommandBeforeHandler?.Invoke(this._orm, before);
+ this._orm?.Aop.CommandBeforeHandler?.Invoke(this._orm, before);
Exception afterException = null;
try
{
@@ -496,7 +566,8 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
}
finally
{
- this._orm?.Aop.CommandAfterHandler?.Invoke(this._orm, new Aop.CommandAfterEventArgs(before, afterException, null));
+ this._orm?.Aop.CommandAfterHandler?.Invoke(this._orm,
+ new Aop.CommandAfterEventArgs(before, afterException, null));
}
}
}
@@ -511,12 +582,17 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
{
if (isPrimary)
{
- if (dbType.Contains("Nullable"))
- return dbType.Replace("Nullable(", "")
+ if (dbType.Contains("Nullable"))
+ {
+ var res = dbType.Replace("Nullable(", "")
.Replace(")", "")
.Replace(" NOT NULL", "");
+ return res;
+ }
+
return dbType;
}
+
return dbType.Replace(" NOT NULL", "");
}
@@ -524,6 +600,9 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
string CkIntAdapter(string dbType)
{
var result = dbType;
+ if (dbType.Contains("Array"))
+ return dbType;
+
if (dbType.ToLower().Contains("int64"))
{
if (dbType.Contains("Nullable"))
@@ -578,13 +657,7 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
internal class ClickHouseTableIndex
{
- public string name
- {
- get; set;
- }
- public string expr
- {
- get; set;
- }
+ public string name { get; set; }
+ public string expr { get; set; }
}
}
\ No newline at end of file
diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs
index cc613093..d4a7ea74 100644
--- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs
+++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs
@@ -5,6 +5,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
+using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
@@ -121,27 +122,51 @@ namespace FreeSql.ClickHouse
case "First":
case "FirstOrDefault":
return $"substr({getExp(callExp.Arguments[0])}, 1, 1)";
+
}
}
}
if (objType == null) objType = callExp.Method.DeclaringType;
if (objType != null || objType.IsArrayOrList())
{
- if (argIndex >= callExp.Arguments.Count) break;
- tsc.SetMapColumnTmp(null);
- var args1 = getExp(callExp.Arguments[argIndex]);
- var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
- var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
- tsc.isNotSetMapColumnTmp = true;
+ //if (argIndex >= callExp.Arguments.Count) break;
+ //tsc.SetMapColumnTmp(null);
+ //var args1 = getExp(callExp.Arguments[argIndex]);
+ //var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
+ //var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
+ //tsc.isNotSetMapColumnTmp = true;
var left = objExp == null ? null : getExp(objExp);
- tsc.isNotSetMapColumnTmp = false;
- tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
- if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
+ //tsc.isNotSetMapColumnTmp = false;
+ //tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
+ //if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
switch (callExp.Method.Name)
{
+ case "Count":
+ left = objExp == null ? null : getExp(objExp);
+ if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
+ return $"(case when {left} is null then 0 else length({left}) end)";
+ case "Any":
+ left = objExp == null ? null : getExp(objExp);
+ if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
+ return $"(case when {left} is null then 0 else length({left}) end > 0)";
case "Contains":
- //判断 in //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格
- return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})";
+ tsc.SetMapColumnTmp(null);
+ var args1 = getExp(callExp.Arguments[argIndex]);
+ var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
+ var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
+ tsc.isNotSetMapColumnTmp = true;
+ left = objExp == null ? null : getExp(objExp);
+ tsc.isNotSetMapColumnTmp = false;
+ tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
+ if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
+ if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
+ return $"(hasAny({left}, [{args1}]))";
+ case "Concat":
+ left = objExp == null ? null : getExp(objExp);
+ if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
+ var right2 = getExp(callExp.Arguments[argIndex]);
+ if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"array[{right2.TrimStart('(').TrimEnd(')')}]";
+ return $"(arrayConcat({left} || {right2}))";
}
}
break;
@@ -181,7 +206,6 @@ namespace FreeSql.ClickHouse
}
return null;
}
-
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc)
{
if (exp.Expression == null)
diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseExtensions.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseExtensions.cs
index bc3bc7c3..36428c07 100644
--- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseExtensions.cs
+++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseExtensions.cs
@@ -2,24 +2,30 @@
using FreeSql.ClickHouse.Curd;
using FreeSql.Internal.CommonProvider;
using System;
+using System.Collections.Generic;
using System.Linq.Expressions;
+using System.Threading;
+using System.Threading.Tasks;
+using FreeSql.DataAnnotations;
public static partial class FreeSqlClickHouseGlobalExtensions
{
-
///
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
///
///
///
///
- public static string FormatClickHouse(this string that, params object[] args) => _clickHouseAdo.Addslashes(that, args);
+ public static string FormatClickHouse(this string that, params object[] args) =>
+ _clickHouseAdo.Addslashes(that, args);
+
static FreeSql.ClickHouse.ClickHouseAdo _clickHouseAdo = new FreeSql.ClickHouse.ClickHouseAdo();
///
/// Clickhouse limit by
///
- public static ISelect LimitBy(this ISelect that, Expression> selector, int limit, int offset = 0)
+ public static ISelect LimitBy(this ISelect that, Expression> selector, int limit,
+ int offset = 0)
{
if (limit <= 0 && offset <= 0) return that;
var s0p = that as ClickHouseSelect;
@@ -34,6 +40,7 @@ public static partial class FreeSqlClickHouseGlobalExtensions
{
s0p._orderby = oldOrderBy;
}
+
return that;
}
@@ -52,4 +59,36 @@ public static partial class FreeSqlClickHouseGlobalExtensions
s0p._sample = $"SAMPLE {k}";
return that;
}
+
+ ///
+ /// 批量快速插入
+ ///
+ ///
+ ///
+ ///
+ public static async Task ExecuteBulkCopyAsync(this IInsert that) where T : class
+ {
+ try
+ {
+ var insert = that as ClickHouseInsert;
+ await insert.InternalBulkCopyAsync();
+ }
+ catch (Exception e)
+ {
+ throw e;
+ }
+
+ return 0;
+ }
+
+ ///
+ /// 批量快速插入
+ ///
+ ///
+ ///
+ ///
+ public static int ExecuteBulkCopy(this IInsert insert) where T : class
+ {
+ return ExecuteBulkCopyAsync(insert).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
}
diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs
index 126b0704..da3fb3ff 100644
--- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs
+++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Data;
+using System.Text.Json;
using ClickHouse.Client.ADO.Parameters;
using System.Text.RegularExpressions;
@@ -18,7 +19,7 @@ namespace FreeSql.ClickHouse
}
public override DbParameter AppendParamter(List _params, string parameterName, ColumnInfo col, Type type, object value)
- {
+ {
if (value is string str)
value = str?.Replace("\t", "\\t")
.Replace("\r\n", "\\r\\n")
@@ -43,8 +44,10 @@ namespace FreeSql.ClickHouse
if (col.DbScale != 0) ret.Scale = col.DbScale;
break;
}
- if (value is bool)
- ret.Value = (bool)value ? 1 : 0;
+ //if (value.GetType().IsArray)
+ //{
+ // ret.DbType = DbType.Object;
+ //}
}
_params?.Add(ret);
return ret;
@@ -145,7 +148,7 @@ namespace FreeSql.ClickHouse
}
public override string GetNoneParamaterSqlValue(List specialParams, string specialParamFlag, ColumnInfo col, Type type, object value)
- {
+ {
if (value == null) return "NULL";
if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value);
if (type == typeof(byte[])) return $"0x{CommonUtils.BytesSqlRaw(value as byte[])}";
diff --git a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
index 0645397a..9b6eb58d 100644
--- a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
+++ b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
@@ -57,18 +57,7 @@ namespace FreeSql.ClickHouse.Curd
{
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, null, _params);
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
- var data = ToDataTable();
- using (var conn = _orm.Ado.MasterPool.Get())
- {
- using (var bulkCopyInterface = new ClickHouseBulkCopy(conn.Value as ClickHouseConnection)
- {
- DestinationTableName = data.TableName,
- BatchSize = _source.Count
- })
- {
- bulkCopyInterface.WriteToServerAsync(data, default).Wait();
- }
- }
+ InternalBulkCopyAsync().ConfigureAwait(false).GetAwaiter().GetResult();
return affrows;
}
catch (Exception ex)
@@ -85,6 +74,24 @@ namespace FreeSql.ClickHouse.Curd
return base.RawExecuteAffrows();
}
+ internal async Task InternalBulkCopyAsync()
+ {
+ var data = ToDataTable();
+ using (var conn = _orm.Ado.MasterPool.Get())
+ {
+ using (var bulkCopyInterface = new ClickHouseBulkCopy(conn.Value as ClickHouseConnection)
+ {
+ DestinationTableName = data.TableName,
+ BatchSize = _source.Count
+ })
+ {
+ await bulkCopyInterface.WriteToServerAsync(data, default);
+ }
+ }
+ return 0;
+
+ }
+
private IDictionary GetValue(T u, System.Reflection.PropertyInfo[] columns)
{
try