mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
添加ClickHouse数据库支持
This commit is contained in:
parent
e6f05aa24e
commit
d085acc4e9
@ -180,6 +180,7 @@ new Colorful.Formatter("推荐在实体类目录创建 gen.bat,双击它重新
|
|||||||
case "dameng": ArgsDbType = DataType.Dameng; break;
|
case "dameng": ArgsDbType = DataType.Dameng; break;
|
||||||
case "kingbasees": ArgsDbType = DataType.KingbaseES; break;
|
case "kingbasees": ArgsDbType = DataType.KingbaseES; break;
|
||||||
case "shentong": ArgsDbType = DataType.ShenTong; break;
|
case "shentong": ArgsDbType = DataType.ShenTong; break;
|
||||||
|
case "clickhouse": ArgsDbType = DataType.ClickHouse; break;
|
||||||
default: throw new ArgumentException($"-DB 参数错误,不支持的类型:\"{dbargs[0]}\"");
|
default: throw new ArgumentException($"-DB 参数错误,不支持的类型:\"{dbargs[0]}\"");
|
||||||
}
|
}
|
||||||
ArgsConnectionString = dbargs[1].Trim();
|
ArgsConnectionString = dbargs[1].Trim();
|
||||||
|
@ -538,5 +538,14 @@
|
|||||||
<param name="that"></param>
|
<param name="that"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:Microsoft.Extensions.DependencyInjection.FreeSqlRepositoryDependencyInjection.AddFreeRepository(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{FreeSql.FluentDataFilter},System.Reflection.Assembly[])">
|
||||||
|
<summary>
|
||||||
|
批量注入 Repository,可以参考代码自行调整
|
||||||
|
</summary>
|
||||||
|
<param name="services"></param>
|
||||||
|
<param name="globalDataFilter"></param>
|
||||||
|
<param name="assemblies"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
</members>
|
</members>
|
||||||
</doc>
|
</doc>
|
||||||
|
95
FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest1.cs
Normal file
95
FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest1.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace FreeSql.Tests.MySql
|
||||||
|
{
|
||||||
|
public class ClickHouseTest1
|
||||||
|
{
|
||||||
|
|
||||||
|
class TestAuditValue
|
||||||
|
{
|
||||||
|
public Guid id { get; set; }
|
||||||
|
[Now]
|
||||||
|
public DateTime createtime { get; set; }
|
||||||
|
}
|
||||||
|
[FreeSql.DataAnnotations.Table(Name = "ClickHouseTest")]
|
||||||
|
public class TestClickHouse
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
class NowAttribute: Attribute { }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AuditValue()
|
||||||
|
{
|
||||||
|
var date = DateTime.Now.Date;
|
||||||
|
var item = new TestAuditValue();
|
||||||
|
|
||||||
|
EventHandler<Aop.AuditValueEventArgs> audit = (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.Property.GetCustomAttribute<NowAttribute>(false) != null)
|
||||||
|
e.Value = DateTime.Now.Date;
|
||||||
|
};
|
||||||
|
g.mysql.Aop.AuditValue += audit;
|
||||||
|
|
||||||
|
g.mysql.Insert(item).ExecuteAffrows();
|
||||||
|
|
||||||
|
g.mysql.Aop.AuditValue -= audit;
|
||||||
|
|
||||||
|
Assert.Equal(item.createtime, date);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestInsert()
|
||||||
|
{
|
||||||
|
var fsql = g.clickHouse;
|
||||||
|
List<TestClickHouse> list=new List<TestClickHouse>();
|
||||||
|
for (int i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
list.Add(new TestClickHouse()
|
||||||
|
{
|
||||||
|
Id = i,
|
||||||
|
Name = $"测试{i}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fsql.Insert(list).ExecuteAffrows();
|
||||||
|
var items = fsql.Select<TestClickHouse>().Where(o=>o.Id>900).OrderByDescending(o=>o.Id).ToList();
|
||||||
|
Assert.Equal(100, items.Count);
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void TestPage()
|
||||||
|
{
|
||||||
|
var fsql = g.clickHouse;
|
||||||
|
|
||||||
|
var list=fsql.Select<TestClickHouse>()
|
||||||
|
.Page(1,100)
|
||||||
|
.Where(o=>o.Id>200&&o.Id<500)
|
||||||
|
.Count(out var count).ToList();
|
||||||
|
Assert.Equal(100, list.Count);
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void TestDelete()
|
||||||
|
{
|
||||||
|
var fsql = g.clickHouse;
|
||||||
|
var count1=fsql.Select<TestClickHouse>().Count();
|
||||||
|
fsql.Delete<TestClickHouse>().Where(o => o.Id < 500).ExecuteAffrows();
|
||||||
|
var count2 = fsql.Select<TestClickHouse>().Count();
|
||||||
|
Assert.NotEqual(count1, count2);
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void TestUpdate()
|
||||||
|
{
|
||||||
|
var fsql = g.clickHouse;
|
||||||
|
fsql.Update<TestClickHouse>().Where(o => o.Id > 900)
|
||||||
|
.Set(o=>o.Name,"修改后的值")
|
||||||
|
.ExecuteAffrows();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@
|
|||||||
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.Linq\FreeSql.Extensions.Linq.csproj" />
|
<ProjectReference Include="..\..\Extensions\FreeSql.Extensions.Linq\FreeSql.Extensions.Linq.csproj" />
|
||||||
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
|
<ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
|
||||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Providers\FreeSql.Provider.ClickHouse\FreeSql.Provider.ClickHouse.csproj" />
|
||||||
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Dameng\FreeSql.Provider.Dameng.csproj" />
|
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Dameng\FreeSql.Provider.Dameng.csproj" />
|
||||||
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Firebird\FreeSql.Provider.Firebird.csproj" />
|
<ProjectReference Include="..\..\Providers\FreeSql.Provider.Firebird\FreeSql.Provider.Firebird.csproj" />
|
||||||
<ProjectReference Include="..\..\Providers\FreeSql.Provider.KingbaseES\FreeSql.Provider.KingbaseES.csproj" />
|
<ProjectReference Include="..\..\Providers\FreeSql.Provider.KingbaseES\FreeSql.Provider.KingbaseES.csproj" />
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
<name>FreeSql.Tests</name>
|
<name>FreeSql.Tests</name>
|
||||||
</assembly>
|
</assembly>
|
||||||
<members>
|
<members>
|
||||||
|
<member name="M:FreeSql.Tests.MySql.MySqlCodeFirstTest.Tb_alltype.Save">
|
||||||
|
<summary>
|
||||||
|
保存或添加,如果主键有值则尝试 Update,如果影响的行为 0 则尝试 Insert
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="P:FreeSql.Tests.Firebird.FirebirdCodeFirstTest.AddUniquesInfo.id">
|
<member name="P:FreeSql.Tests.Firebird.FirebirdCodeFirstTest.AddUniquesInfo.id">
|
||||||
<summary>
|
<summary>
|
||||||
编号
|
编号
|
||||||
@ -287,11 +292,6 @@
|
|||||||
修改人
|
修改人
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:FreeSql.Tests.MySql.MySqlCodeFirstTest.Tb_alltype.Save">
|
|
||||||
<summary>
|
|
||||||
保存或添加,如果主键有值则尝试 Update,如果影响的行为 0 则尝试 Insert
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:FreeSql.Tests.SqlServer.SqlServerCodeFirstTest.tbdot01">
|
<member name="T:FreeSql.Tests.SqlServer.SqlServerCodeFirstTest.tbdot01">
|
||||||
<summary>
|
<summary>
|
||||||
表中带点
|
表中带点
|
||||||
|
@ -34,6 +34,29 @@ namespace FreeSql.Tests
|
|||||||
var items = fsql.Select<TestJsonb01Cls1>().ToList();
|
var items = fsql.Select<TestJsonb01Cls1>().ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TestClickHouse()
|
||||||
|
{
|
||||||
|
var fsql = g.mysql;
|
||||||
|
fsql.Delete<TestJsonb01Cls1>().Where("1=1").ExecuteAffrows();
|
||||||
|
|
||||||
|
var item = new TestJsonb01Cls1
|
||||||
|
{
|
||||||
|
jsonb01 = new List<int> { 1, 5, 10, 20 },
|
||||||
|
jsonb02 = new List<long> { 11, 51, 101, 201 },
|
||||||
|
jsonb03 = new List<string> { "12", "52", "102", "202" },
|
||||||
|
};
|
||||||
|
fsql.Insert(item).ExecuteAffrows();
|
||||||
|
|
||||||
|
}
|
||||||
|
[FreeSql.DataAnnotations.Table(Name = "ClickHouseTest")]
|
||||||
|
public class ClickHouse
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class TestJsonb01Cls1
|
public class TestJsonb01Cls1
|
||||||
{
|
{
|
||||||
public Guid id { get; set; }
|
public Guid id { get; set; }
|
||||||
|
@ -7,6 +7,19 @@ using System.Threading;
|
|||||||
|
|
||||||
public class g
|
public class g
|
||||||
{
|
{
|
||||||
|
static Lazy<IFreeSql> clickHouseLazy = new Lazy<IFreeSql>(() => new FreeSql.FreeSqlBuilder()
|
||||||
|
.UseConnectionString(FreeSql.DataType.ClickHouse, "Compress=False;BufferSize=32768;SocketTimeout=10000;CheckCompressedHash=False;Encrypt=False;Compressor=lz4;Host=192.168.0.121;Port=8125;Database=PersonnelLocation;Username=root;Password=+riQ8V9D")
|
||||||
|
//.UseConnectionFactory(FreeSql.DataType.MySql, () => new MySql.Data.MySqlClient.MySqlConnection("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;"))
|
||||||
|
//.UseConnectionString(FreeSql.DataType.MySql, "Data Source=192.168.164.10;Port=33061;User ID=root;Password=root;Initial Catalog=cccddd_mysqlconnector;Charset=utf8;SslMode=none;Max pool size=10")
|
||||||
|
//.UseAutoSyncStructure(true)
|
||||||
|
//.UseGenerateCommandParameterWithLambda(true)
|
||||||
|
.UseMonitorCommand(
|
||||||
|
cmd => Trace.WriteLine("\r\n线程" + Thread.CurrentThread.ManagedThreadId + ": " + cmd.CommandText) //监听SQL命令对象,在执行前
|
||||||
|
//, (cmd, traceLog) => Console.WriteLine(traceLog)
|
||||||
|
)
|
||||||
|
.UseLazyLoading(true)
|
||||||
|
.Build());
|
||||||
|
public static IFreeSql clickHouse => clickHouseLazy.Value;
|
||||||
|
|
||||||
static Lazy<IFreeSql> mysqlLazy = new Lazy<IFreeSql>(() => new FreeSql.FreeSqlBuilder()
|
static Lazy<IFreeSql> mysqlLazy = new Lazy<IFreeSql>(() => new FreeSql.FreeSqlBuilder()
|
||||||
.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=5;Allow User Variables=True")
|
.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=5;Allow User Variables=True")
|
||||||
|
19
FreeSql.sln
19
FreeSql.sln
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29324.140
|
VisualStudioVersion = 17.0.31903.59
|
||||||
MinimumVisualStudioVersion = 15.0.26124.0
|
MinimumVisualStudioVersion = 15.0.26124.0
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql", "FreeSql\FreeSql.csproj", "{AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql", "FreeSql\FreeSql.csproj", "{AF9C50EC-6EB6-494B-9B3B-7EDBA6FD0EBB}"
|
||||||
EndProject
|
EndProject
|
||||||
@ -101,6 +101,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
README.zh-CN.md = README.zh-CN.md
|
README.zh-CN.md = README.zh-CN.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FreeSql.Provider.ClickHouse", "Providers\FreeSql.Provider.ClickHouse\FreeSql.Provider.ClickHouse.csproj", "{86C56235-5D37-4422-807B-B31681C7D01C}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -579,6 +581,18 @@ Global
|
|||||||
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x64.Build.0 = Release|Any CPU
|
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x86.ActiveCfg = Release|Any CPU
|
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x86.Build.0 = Release|Any CPU
|
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -612,6 +626,7 @@ Global
|
|||||||
{CDD6A896-F6DF-44CB-B430-06B383916EB0} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
|
{CDD6A896-F6DF-44CB-B430-06B383916EB0} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
|
||||||
{101B11D2-7780-4E14-9B72-77F5D69B3DF9} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
|
{101B11D2-7780-4E14-9B72-77F5D69B3DF9} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
|
||||||
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
|
{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
|
||||||
|
{86C56235-5D37-4422-807B-B31681C7D01C} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98}
|
SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98}
|
||||||
|
@ -60,6 +60,7 @@ namespace FreeSql
|
|||||||
/// 自定义适配器,访问任何数据库<para></para>
|
/// 自定义适配器,访问任何数据库<para></para>
|
||||||
/// 注意:该类型不提供 DbFirst/CodeFirst 功能
|
/// 注意:该类型不提供 DbFirst/CodeFirst 功能
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Custom
|
Custom,
|
||||||
|
ClickHouse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,10 @@ namespace FreeSql
|
|||||||
providerType = Type.GetType("FreeSql.Firebird.FirebirdProvider`1,FreeSql.Provider.Firebird")?.MakeGenericType(connType);
|
providerType = Type.GetType("FreeSql.Firebird.FirebirdProvider`1,FreeSql.Provider.Firebird")?.MakeGenericType(connType);
|
||||||
if (providerType == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.Firebird.dll,可前往 nuget 下载");
|
if (providerType == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.Firebird.dll,可前往 nuget 下载");
|
||||||
break;
|
break;
|
||||||
|
case "ClickHouseConnection":
|
||||||
|
providerType = Type.GetType("FreeSql.ClickHouse.ClickHouseProvider`1,FreeSql.Provider.ClickHouse")?.MakeGenericType(connType);
|
||||||
|
if (providerType == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.ClickHouse.dll,可前往 nuget 下载");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception("未实现");
|
throw new Exception("未实现");
|
||||||
}
|
}
|
||||||
|
@ -495,6 +495,7 @@ JOIN {select._commonUtils.QuoteSqlName(tb.DbName)} a ON cte_tbc.cte_id = a.{sele
|
|||||||
case DataType.SqlServer:
|
case DataType.SqlServer:
|
||||||
case DataType.OdbcSqlServer:
|
case DataType.OdbcSqlServer:
|
||||||
case DataType.Firebird:
|
case DataType.Firebird:
|
||||||
|
case DataType.ClickHouse:
|
||||||
sql1ctePath = select._commonExpression.ExpressionWhereLambda(select._tables, Expression.Call(typeof(Convert).GetMethod("ToString", new Type[] { typeof(string) }), pathSelector?.Body), null, null, null);
|
sql1ctePath = select._commonExpression.ExpressionWhereLambda(select._tables, Expression.Call(typeof(Convert).GetMethod("ToString", new Type[] { typeof(string) }), pathSelector?.Body), null, null, null);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -609,6 +610,7 @@ SELECT ");
|
|||||||
{
|
{
|
||||||
case DataType.MySql:
|
case DataType.MySql:
|
||||||
case DataType.OdbcMySql:
|
case DataType.OdbcMySql:
|
||||||
|
case DataType.ClickHouse:
|
||||||
return that.OrderBy("rand()");
|
return that.OrderBy("rand()");
|
||||||
case DataType.SqlServer:
|
case DataType.SqlServer:
|
||||||
case DataType.OdbcSqlServer:
|
case DataType.OdbcSqlServer:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;net45;net40</TargetFrameworks>
|
<TargetFrameworks>netstandard2.1;netstandard2.0;net45;net40</TargetFrameworks>
|
||||||
<Version>2.6.100</Version>
|
<Version>2.6.100</Version>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Authors>FreeSql;ncc;YeXiangQin</Authors>
|
<Authors>FreeSql;ncc;YeXiangQin</Authors>
|
||||||
|
@ -265,6 +265,11 @@ namespace FreeSql
|
|||||||
if (type == null) throwNotFind("FreeSql.Provider.Custom.dll", "FreeSql.Custom.CustomProvider<>");
|
if (type == null) throwNotFind("FreeSql.Provider.Custom.dll", "FreeSql.Custom.CustomProvider<>");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DataType.ClickHouse:
|
||||||
|
type = Type.GetType("FreeSql.ClickHouse.ClickHouseProvider`1,FreeSql.Provider.ClickHouse")?.MakeGenericType(typeof(TMark));
|
||||||
|
if (type == null) throwNotFind("FreeSql.Provider.ClickHouse.dll", "FreeSql.ClickHouse.ClickHouseProvider<>");
|
||||||
|
break;
|
||||||
|
|
||||||
default: throw new Exception("未指定 UseConnectionString 或者 UseConnectionFactory");
|
default: throw new Exception("未指定 UseConnectionString 或者 UseConnectionFactory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ namespace FreeSql.Internal.CommonProvider
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ToSql()
|
public virtual string ToSql()
|
||||||
{
|
{
|
||||||
if (_whereTimes <= 0) return null;
|
if (_whereTimes <= 0) return null;
|
||||||
var sb = new StringBuilder().Append("DELETE FROM ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" WHERE ").Append(_where);
|
var sb = new StringBuilder().Append("DELETE FROM ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" WHERE ").Append(_where);
|
||||||
|
@ -445,7 +445,7 @@ namespace FreeSql.Internal.CommonProvider
|
|||||||
_set.Append(", ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ");
|
_set.Append(", ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ");
|
||||||
|
|
||||||
var colsql = _noneParameter ? _commonUtils.GetNoneParamaterSqlValue(_params, "u", col, col.Attribute.MapType, val) :
|
var colsql = _noneParameter ? _commonUtils.GetNoneParamaterSqlValue(_params, "u", col, col.Attribute.MapType, val) :
|
||||||
_commonUtils.QuoteWriteParamterAdapter(col.Attribute.MapType, $"{_commonUtils.QuoteParamterName("p_")}{_params.Count}");
|
_commonUtils.QuoteWriteParamterAdapter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"p_{_params.Count}"));
|
||||||
_set.Append(_commonUtils.RewriteColumn(col, colsql));
|
_set.Append(_commonUtils.RewriteColumn(col, colsql));
|
||||||
if (_noneParameter == false)
|
if (_noneParameter == false)
|
||||||
_commonUtils.AppendParamter(_params, null, col, col.Attribute.MapType, val);
|
_commonUtils.AppendParamter(_params, null, col, col.Attribute.MapType, val);
|
||||||
|
@ -54,7 +54,7 @@ namespace FreeSql.Internal
|
|||||||
public abstract string NowUtc { get; }
|
public abstract string NowUtc { get; }
|
||||||
public abstract string QuoteWriteParamterAdapter(Type type, string paramterName);
|
public abstract string QuoteWriteParamterAdapter(Type type, string paramterName);
|
||||||
protected abstract string QuoteReadColumnAdapter(Type type, Type mapType, string columnName);
|
protected abstract string QuoteReadColumnAdapter(Type type, Type mapType, string columnName);
|
||||||
public string RewriteColumn(ColumnInfo col, string sql)
|
public virtual string RewriteColumn(ColumnInfo col, string sql)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(col?.Attribute.RewriteSql) == false)
|
if (string.IsNullOrWhiteSpace(col?.Attribute.RewriteSql) == false)
|
||||||
return string.Format(col.Attribute.RewriteSql, sql);
|
return string.Format(col.Attribute.RewriteSql, sql);
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using FreeSql.Internal.ObjectPool;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using ClickHouse.Client.ADO;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse
|
||||||
|
{
|
||||||
|
class ClickHouseAdo : FreeSql.Internal.CommonProvider.AdoProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
public ClickHouseAdo() : base(DataType.ClickHouse, null, null) { }
|
||||||
|
public ClickHouseAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func<DbConnection> connectionFactory) : base(DataType.MySql, masterConnectionString, slaveConnectionStrings)
|
||||||
|
{
|
||||||
|
base._util = util;
|
||||||
|
if (connectionFactory != null)
|
||||||
|
{
|
||||||
|
MasterPool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.ClickHouse, connectionFactory);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||||
|
MasterPool = new ClickHouseConnectionPool("主库", masterConnectionString, null, null);
|
||||||
|
if (slaveConnectionStrings != null)
|
||||||
|
{
|
||||||
|
foreach (var slaveConnectionString in slaveConnectionStrings)
|
||||||
|
{
|
||||||
|
var slavePool = new ClickHouseConnectionPool($"从库{SlavePools.Count + 1}", slaveConnectionString, () => Interlocked.Decrement(ref slaveUnavailables), () => Interlocked.Increment(ref slaveUnavailables));
|
||||||
|
SlavePools.Add(slavePool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn)
|
||||||
|
{
|
||||||
|
if (param == null) return "NULL";
|
||||||
|
if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false))
|
||||||
|
param = Utils.GetDataReaderValue(mapType, param);
|
||||||
|
|
||||||
|
if (param is bool || param is bool?)
|
||||||
|
return (bool)param ? 1 : 0;
|
||||||
|
else if (param is string)
|
||||||
|
return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\"), "'"); //只有 mysql 需要处理反斜杠
|
||||||
|
else if (param is char)
|
||||||
|
return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace('\0', ' '), "'");
|
||||||
|
else if (param is Enum)
|
||||||
|
return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\"), "'"); //((Enum)val).ToInt64();
|
||||||
|
else if (decimal.TryParse(string.Concat(param), out var trydec))
|
||||||
|
return param;
|
||||||
|
else if (param is DateTime || param is DateTime?)
|
||||||
|
return string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.fff"), "'");
|
||||||
|
else if (param is TimeSpan || param is TimeSpan?)
|
||||||
|
return ((TimeSpan)param).Ticks / 10;
|
||||||
|
else if (param is byte[])
|
||||||
|
return $"0x{CommonUtils.BytesSqlRaw(param as byte[])}";
|
||||||
|
else if (param is IEnumerable)
|
||||||
|
return AddslashesIEnumerable(param, mapType, mapColumn);
|
||||||
|
|
||||||
|
return string.Concat("'", param.ToString().Replace("'", "''").Replace("\\", "\\\\"), "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DbCommand CreateCommand()
|
||||||
|
{
|
||||||
|
System.Data.IDbCommand command = new ClickHouseCommand();
|
||||||
|
return (DbCommand)command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ReturnConnection(IObjectPool<DbConnection> pool, Object<DbConnection> conn, Exception ex)
|
||||||
|
{
|
||||||
|
var rawPool = pool as ClickHouseConnectionPool;
|
||||||
|
if (rawPool != null) rawPool.Return(conn, ex);
|
||||||
|
else pool.Return(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,257 @@
|
|||||||
|
using FreeSql.Internal.ObjectPool;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ClickHouse.Client.ADO;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClickHouseConnectionPool : ObjectPool<DbConnection>
|
||||||
|
{
|
||||||
|
|
||||||
|
internal Action availableHandler;
|
||||||
|
internal Action unavailableHandler;
|
||||||
|
|
||||||
|
public ClickHouseConnectionPool(string name, string connectionString, Action availableHandler, Action unavailableHandler) : base(null)
|
||||||
|
{
|
||||||
|
this.availableHandler = availableHandler;
|
||||||
|
this.unavailableHandler = unavailableHandler;
|
||||||
|
var policy = new ClickHouseConnectionPoolPolicy
|
||||||
|
{
|
||||||
|
_pool = this,
|
||||||
|
Name = name
|
||||||
|
};
|
||||||
|
this.Policy = policy;
|
||||||
|
policy.ConnectionString = connectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(Object<DbConnection> obj, Exception exception, bool isRecreate = false)
|
||||||
|
{
|
||||||
|
if (exception != null && exception is ClickHouseException)
|
||||||
|
{
|
||||||
|
try { if (obj.Value.Ping() == false) obj.Value.Open(); } catch { base.SetUnavailable(exception); }
|
||||||
|
}
|
||||||
|
base.Return(obj, isRecreate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class ClickHouseException : Exception
|
||||||
|
{
|
||||||
|
public int Code;
|
||||||
|
|
||||||
|
public string Name;
|
||||||
|
public string ServerStackTrace;
|
||||||
|
|
||||||
|
public ClickHouseException() { }
|
||||||
|
|
||||||
|
public ClickHouseException(string message) : base(message) { }
|
||||||
|
|
||||||
|
public ClickHouseException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
}
|
||||||
|
class ClickHouseConnectionPoolPolicy : IPolicy<DbConnection>
|
||||||
|
{
|
||||||
|
|
||||||
|
internal ClickHouseConnectionPool _pool;
|
||||||
|
public string Name { get; set; } = "ClickHouse ClickHouseConnection 对象池";
|
||||||
|
public int PoolSize { get; set; } = 100;
|
||||||
|
public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||||
|
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(20);
|
||||||
|
public int AsyncGetCapacity { get; set; } = 10000;
|
||||||
|
public bool IsThrowGetTimeoutException { get; set; } = true;
|
||||||
|
public bool IsAutoDisposeWithSystem { get; set; } = true;
|
||||||
|
public int CheckAvailableInterval { get; set; } = 5;
|
||||||
|
|
||||||
|
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
private string _connectionString;
|
||||||
|
public string ConnectionString
|
||||||
|
{
|
||||||
|
get => _connectionString;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_connectionString = value ?? "";
|
||||||
|
|
||||||
|
//var pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||||
|
//var m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||||
|
//if (m.Success == false || int.TryParse(m.Groups[1].Value, out var poolsize) == false || poolsize <= 0) poolsize = 100;
|
||||||
|
//var connStrIncr = dicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => Math.Min(5, oldval + 1));
|
||||||
|
//PoolSize = poolsize + connStrIncr;
|
||||||
|
//_connectionString = m.Success ?
|
||||||
|
// Regex.Replace(_connectionString, pattern, $"Max pool size={PoolSize}", RegexOptions.IgnoreCase) :
|
||||||
|
// $"{_connectionString};Max pool size={PoolSize}";
|
||||||
|
|
||||||
|
|
||||||
|
var pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||||
|
Match m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||||
|
if (m.Success)
|
||||||
|
{
|
||||||
|
PoolSize = int.Parse(m.Groups[1].Value);
|
||||||
|
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
|
||||||
|
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||||
|
if (m.Success)
|
||||||
|
{
|
||||||
|
IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
|
||||||
|
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
var minPoolSize = 0;
|
||||||
|
pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
|
||||||
|
m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||||
|
if (m.Success)
|
||||||
|
{
|
||||||
|
minPoolSize = int.Parse(m.Groups[1].Value);
|
||||||
|
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnCheckAvailable(Object<DbConnection> obj)
|
||||||
|
{
|
||||||
|
if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
|
||||||
|
return obj.Value.Ping(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbConnection OnCreate()
|
||||||
|
{
|
||||||
|
var conn = new ClickHouseConnection(_connectionString);
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDestroy(DbConnection obj)
|
||||||
|
{
|
||||||
|
if (obj.State != ConnectionState.Closed) obj.Close();
|
||||||
|
obj.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGet(Object<DbConnection> obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_pool.IsAvailable)
|
||||||
|
{
|
||||||
|
if (obj.Value == null)
|
||||||
|
{
|
||||||
|
if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
|
||||||
|
throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
obj.Value.Open();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (_pool.SetUnavailable(ex) == true)
|
||||||
|
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if net40
|
||||||
|
#else
|
||||||
|
async public Task OnGetAsync(Object<DbConnection> obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_pool.IsAvailable)
|
||||||
|
{
|
||||||
|
if (obj.Value == null)
|
||||||
|
{
|
||||||
|
if (_pool.SetUnavailable(new Exception("连接字符串错误")) == true)
|
||||||
|
throw new Exception($"【{this.Name}】连接字符串错误,请检查。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (await obj.Value.PingAsync()) == false)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await obj.Value.OpenAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (_pool.SetUnavailable(ex) == true)
|
||||||
|
throw new Exception($"【{this.Name}】状态不可用,等待后台检查程序恢复方可使用。{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public void OnGetTimeout()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReturn(Object<DbConnection> obj)
|
||||||
|
{
|
||||||
|
//if (obj?.Value != null && obj.Value.State != ConnectionState.Closed) try { obj.Value.Close(); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnAvailable()
|
||||||
|
{
|
||||||
|
_pool.availableHandler?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnUnavailable()
|
||||||
|
{
|
||||||
|
_pool.unavailableHandler?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DbConnectionExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
static DbCommand PingCommand(DbConnection conn)
|
||||||
|
{
|
||||||
|
var cmd = conn.CreateCommand();
|
||||||
|
cmd.CommandTimeout = 5;
|
||||||
|
cmd.CommandText = "select 1";
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
public static bool Ping(this DbConnection that, bool isThrow = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PingCommand(that).ExecuteNonQuery();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||||
|
if (isThrow) throw;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if net40
|
||||||
|
#else
|
||||||
|
async public static Task<bool> PingAsync(this DbConnection that, bool isThrow = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await PingCommand(that).ExecuteNonQueryAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
if (that.State != ConnectionState.Closed) try { that.Close(); } catch { }
|
||||||
|
if (isThrow) throw;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
371
Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs
Normal file
371
Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Data.Common;
|
||||||
|
using FreeSql.Internal.ObjectPool;
|
||||||
|
using ClickHouse.Client.ADO;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClickHouseCodeFirst : Internal.CommonProvider.CodeFirstProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
public ClickHouseCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { }
|
||||||
|
|
||||||
|
static object _dicCsToDbLock = new object();
|
||||||
|
|
||||||
|
static Dictionary<string, CsToDb<DbType>> _dicCsToDb = new Dictionary<string, CsToDb<DbType>>() {
|
||||||
|
{ 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(sbyte).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", false, true, null) },
|
||||||
|
{ typeof(short).FullName, CsToDb.New(DbType.Int16, "Int16","Int16", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DbType.Int16, "Int16", "Nullable(Int16)", false, true, null) },
|
||||||
|
{ typeof(int).FullName, CsToDb.New(DbType.Int32, "Int32", "Int32", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DbType.Int32, "Int32", "Nullable(Int32)", false, true, null) },
|
||||||
|
{ typeof(long).FullName, CsToDb.New(DbType.Int64, "Int64","Int64", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DbType.Int64, "Int64","Nullable(Int64)", false, true, null) },
|
||||||
|
|
||||||
|
{ typeof(byte).FullName, CsToDb.New(DbType.Byte, "UInt8","UInt8", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DbType.Byte, "UInt8","Nullable(UInt8)", true, true, null) },
|
||||||
|
{ typeof(ushort).FullName, CsToDb.New(DbType.UInt16, "UInt16","UInt16", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, "UInt16", "Nullable(UInt16)", true, true, null) },
|
||||||
|
{ typeof(uint).FullName, CsToDb.New(DbType.UInt32, "UInt32", "UInt32", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DbType.UInt32, "UInt32", "Nullable(UInt32)", true, true, null) },
|
||||||
|
{ typeof(ulong).FullName, CsToDb.New(DbType.UInt64, "UInt64", "UInt64", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, "UInt64", "Nullable(UInt64)", true, true, null) },
|
||||||
|
|
||||||
|
{ typeof(double).FullName, CsToDb.New(DbType.Double, "Float64", "Float64", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DbType.Double, "Float64", "Nullable(Float64)", false, true, null) },
|
||||||
|
{ typeof(float).FullName, CsToDb.New(DbType.Single, "Float32","Float32", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DbType.Single, "Float32","Nullable(Float32)", false, true, null) },
|
||||||
|
|
||||||
|
{ typeof(DateTime).FullName, CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "DateTime('Asia/Shanghai')", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "Nullable(DateTime('Asia/Shanghai'))", false, true, null) },
|
||||||
|
|
||||||
|
{ typeof(string).FullName, CsToDb.New(DbType.String, "String", "String", false, null, "") },
|
||||||
|
{ typeof(char).FullName, CsToDb.New(DbType.String, "String", "String", false, false, "") },{ typeof(char?).FullName, CsToDb.New(DbType.Single, "String","Nullable(String)", false, true, null) },
|
||||||
|
{ typeof(Guid).FullName, CsToDb.New(DbType.String, "String", "String", false, false, Guid.Empty) },{ 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;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string GetComparisonDDLStatements(params TypeAndName[] objects)
|
||||||
|
{
|
||||||
|
Object<DbConnection> conn = null;
|
||||||
|
string database = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
|
||||||
|
database = conn.Value.Database;
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var obj in objects)
|
||||||
|
{
|
||||||
|
if (sb.Length > 0) sb.Append("\r\n");
|
||||||
|
var tb = _commonUtils.GetTableByEntity(obj.entityType);
|
||||||
|
if (tb == null) throw new Exception($"类型 {obj.entityType.FullName} 不可迁移");
|
||||||
|
if (tb.Columns.Any() == false) throw new Exception($"类型 {obj.entityType.FullName} 不可迁移,可迁移属性0个");
|
||||||
|
var tbname = _commonUtils.SplitTableName(tb.DbName);
|
||||||
|
if (tbname?.Length == 1) tbname = new[] { database, tbname[0] };
|
||||||
|
|
||||||
|
var tboldname = _commonUtils.SplitTableName(tb.DbOldName); //旧表名
|
||||||
|
if (tboldname?.Length == 1) tboldname = new[] { database, tboldname[0] };
|
||||||
|
if (string.IsNullOrEmpty(obj.tableName) == false)
|
||||||
|
{
|
||||||
|
var tbtmpname = _commonUtils.SplitTableName(obj.tableName);
|
||||||
|
if (tbtmpname?.Length == 1) tbtmpname = new[] { database, tbtmpname[0] };
|
||||||
|
if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1])
|
||||||
|
{
|
||||||
|
tbname = tbtmpname;
|
||||||
|
tboldname = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Compare(tbname[0], database, true) != 0 && LocalExecuteScalar(database, _commonUtils.FormatSql(" select 1 from information_schema.schemata where schema_name={0}", tbname[0])) == null) //创建数据库
|
||||||
|
sb.Append($"CREATE DATABASE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName(tbname[0])).Append(" default charset utf8 COLLATE utf8_general_ci;\r\n");
|
||||||
|
|
||||||
|
var sbalter = new StringBuilder();
|
||||||
|
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||||
|
if (LocalExecuteScalar(tbname[0], _commonUtils.FormatSql(" SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}", tbname)) == null)
|
||||||
|
{ //表不存在
|
||||||
|
if (tboldname != null)
|
||||||
|
{
|
||||||
|
if (string.Compare(tboldname[0], tbname[0], true) != 0 && LocalExecuteScalar(database, _commonUtils.FormatSql(" select 1 from information_schema.schemata where schema_name={0}", tboldname[0])) == null ||
|
||||||
|
LocalExecuteScalar(tboldname[0], _commonUtils.FormatSql(" SELECT 1 FROM information_schema.TABLES WHERE table_schema={0} and table_name={1}", tboldname)) == null)
|
||||||
|
//数据库或表不存在
|
||||||
|
tboldname = null;
|
||||||
|
}
|
||||||
|
if (tboldname == null)
|
||||||
|
{
|
||||||
|
//创建表
|
||||||
|
var createTableName = _commonUtils.QuoteSqlName(tbname[0], tbname[1]);
|
||||||
|
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(createTableName).Append(" ( ");
|
||||||
|
foreach (var tbcol in tb.ColumnsByPosition)
|
||||||
|
{
|
||||||
|
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||||
|
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||||
|
if (string.IsNullOrEmpty(tbcol.Comment) == false) sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment));
|
||||||
|
sb.Append(",");
|
||||||
|
}
|
||||||
|
if (tb.Primarys.Any())
|
||||||
|
{
|
||||||
|
sb.Append(" \r\n PRIMARY KEY (");
|
||||||
|
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||||
|
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||||
|
}
|
||||||
|
//创建表的索引,感谢 @mafeng8,这样写可以支持自增不是主键的情况
|
||||||
|
foreach (var uk in tb.Indexes)
|
||||||
|
{
|
||||||
|
sb.Append(" \r\n ");
|
||||||
|
if (uk.IsUnique) sb.Append("UNIQUE ");
|
||||||
|
sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append("(");
|
||||||
|
foreach (var tbcol in uk.Columns)
|
||||||
|
{
|
||||||
|
sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||||
|
if (tbcol.IsDesc) sb.Append(" DESC");
|
||||||
|
sb.Append(", ");
|
||||||
|
}
|
||||||
|
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||||
|
}
|
||||||
|
sb.Remove(sb.Length - 1, 1);
|
||||||
|
sb.Append("\r\n) Engine=InnoDB");
|
||||||
|
if (string.IsNullOrEmpty(tb.Comment) == false)
|
||||||
|
sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment));
|
||||||
|
sb.Append(";\r\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//如果新表,旧表在一个数据库下,直接修改表名
|
||||||
|
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||||
|
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1])).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||||
|
istmpatler = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||||
|
|
||||||
|
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||||
|
var sql = _commonUtils.FormatSql(@"
|
||||||
|
select
|
||||||
|
a.column_name,
|
||||||
|
a.column_type,
|
||||||
|
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
|
||||||
|
case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity',
|
||||||
|
a.column_comment 'comment'
|
||||||
|
from information_schema.columns a
|
||||||
|
where a.table_schema in ({0}) and a.table_name in ({1})", tboldname ?? tbname);
|
||||||
|
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||||
|
var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a =>
|
||||||
|
{
|
||||||
|
var a1 = string.Concat(a[1]);
|
||||||
|
if (a1 == "datetime") a1 = string.Concat(a1, "(0)");
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
column = string.Concat(a[0]),
|
||||||
|
sqlType = a1,
|
||||||
|
is_nullable = string.Concat(a[2]) == "1",
|
||||||
|
is_identity = string.Concat(a[3]) == "1",
|
||||||
|
is_unsigned = string.Concat(a[1]).EndsWith(" unsigned"),
|
||||||
|
comment = string.Concat(a[4])
|
||||||
|
};
|
||||||
|
}, StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
|
||||||
|
if (istmpatler == false)
|
||||||
|
{
|
||||||
|
var existsPrimary = LocalExecuteScalar(tbname[0], _commonUtils.FormatSql(" select 1 from information_schema.key_column_usage where table_schema={0} and table_name={1} and constraint_name = 'PRIMARY' limit 1", tbname));
|
||||||
|
foreach (var tbcol in tb.ColumnsByPosition)
|
||||||
|
{
|
||||||
|
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||||
|
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
|
||||||
|
{
|
||||||
|
var isCommentChanged = tbstructcol.comment != (tbcol.Comment ?? "");
|
||||||
|
var isDbTypeChanged = tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false;
|
||||||
|
if (tbstructcol.sqlType == "datetime(0)" && Regex.IsMatch(tbcol.Attribute.DbType, @"datetime\s*\(", RegexOptions.IgnoreCase) == false)
|
||||||
|
isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("datetime", StringComparison.CurrentCultureIgnoreCase) == false;
|
||||||
|
else if (tbstructcol.sqlType.StartsWith("datetime", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("datetime", StringComparison.CurrentCultureIgnoreCase) == false ||
|
||||||
|
(int.TryParse(Regex.Match(tbcol.Attribute.DbType, @"datetime\s*\((\d*)", RegexOptions.IgnoreCase).Groups[1].Value, out var trydtrd) ? trydtrd : 3) !=
|
||||||
|
(int.TryParse(Regex.Match(tbstructcol.sqlType, @"datetime\s*\((\d*)", RegexOptions.IgnoreCase).Groups[1].Value, out var trydtrd2) ? trydtrd2 : 3);
|
||||||
|
else if (tbstructcol.sqlType.StartsWith("int", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("int", StringComparison.CurrentCultureIgnoreCase) == false;
|
||||||
|
else if (tbstructcol.sqlType.StartsWith("tinyint", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("tinyint", StringComparison.CurrentCultureIgnoreCase) == false;
|
||||||
|
else if (tbstructcol.sqlType.StartsWith("smallint", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("smallint", StringComparison.CurrentCultureIgnoreCase) == false;
|
||||||
|
else if (tbstructcol.sqlType.StartsWith("bigint", StringComparison.CurrentCultureIgnoreCase)) isDbTypeChanged = tbcol.Attribute.DbType.StartsWith("bigint", StringComparison.CurrentCultureIgnoreCase) == false;
|
||||||
|
|
||||||
|
if ((tbcol.Attribute.DbType.IndexOf(" unsigned", StringComparison.CurrentCultureIgnoreCase) != -1) != tbstructcol.is_unsigned ||
|
||||||
|
isDbTypeChanged ||
|
||||||
|
tbcol.Attribute.IsNullable != tbstructcol.is_nullable ||
|
||||||
|
tbcol.Attribute.IsIdentity != tbstructcol.is_identity ||
|
||||||
|
isCommentChanged)
|
||||||
|
{
|
||||||
|
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable && tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false)
|
||||||
|
sbalter.Append("UPDATE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" SET ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" = ").Append(tbcol.DbDefaultValue).Append(" WHERE ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" IS NULL;\r\n");
|
||||||
|
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" MODIFY ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||||
|
if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? ""));
|
||||||
|
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" AUTO_INCREMENT");
|
||||||
|
if (tbcol.Attribute.IsIdentity == true) sbalter.Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
|
||||||
|
sbalter.Append(";\r\n");
|
||||||
|
}
|
||||||
|
if (string.Compare(tbstructcol.column, tbcol.Attribute.OldName, true) == 0)
|
||||||
|
{
|
||||||
|
//修改列名
|
||||||
|
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" CHANGE COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||||
|
if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? ""));
|
||||||
|
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" AUTO_INCREMENT");
|
||||||
|
if (tbcol.Attribute.IsIdentity == true) sbalter.Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
|
||||||
|
sbalter.Append(";\r\n");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//添加列
|
||||||
|
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" ADD ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||||
|
if (tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false) sbalter.Append(" DEFAULT ").Append(tbcol.DbDefaultValue);
|
||||||
|
if (string.IsNullOrEmpty(tbcol.Comment) == false) sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? ""));
|
||||||
|
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sbalter.Append(" AUTO_INCREMENT");
|
||||||
|
if (tbcol.Attribute.IsIdentity == true) sbalter.Append(existsPrimary == null ? "" : ", DROP PRIMARY KEY").Append(", ADD PRIMARY KEY(").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(")");
|
||||||
|
sbalter.Append(";\r\n");
|
||||||
|
}
|
||||||
|
var dsuksql = _commonUtils.FormatSql(@"
|
||||||
|
select
|
||||||
|
a.column_name,
|
||||||
|
a.index_name 'index_id',
|
||||||
|
0 'IsDesc',
|
||||||
|
case when a.non_unique = 0 then 1 else 0 end 'IsUnique'
|
||||||
|
from information_schema.statistics a
|
||||||
|
where a.table_schema IN ({0}) and a.table_name IN ({1}) and a.index_name <> 'PRIMARY'", tboldname ?? tbname);
|
||||||
|
var dsuk = _orm.Ado.ExecuteArray(CommandType.Text, dsuksql).Select(a => new[] { string.Concat(a[0]), string.Concat(a[1]), string.Concat(a[2]), string.Concat(a[3]) });
|
||||||
|
foreach (var uk in tb.Indexes)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false) continue;
|
||||||
|
var ukname = ReplaceIndexName(uk.Name, tbname[1]);
|
||||||
|
var dsukfind1 = dsuk.Where(a => string.Compare(a[1], ukname, true) == 0).ToArray();
|
||||||
|
if (dsukfind1.Any() == false || dsukfind1.Length != uk.Columns.Length || dsukfind1.Where(a => (a[3] == "1") == uk.IsUnique && uk.Columns.Where(b => string.Compare(b.Column.Attribute.Name, a[0], true) == 0 && (a[2] == "1") == b.IsDesc).Any()).Count() != uk.Columns.Length)
|
||||||
|
{
|
||||||
|
if (dsukfind1.Any()) sbalter.Append("DROP INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append(" ON ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
|
||||||
|
sbalter.Append("CREATE ");
|
||||||
|
if (uk.IsUnique) sbalter.Append("UNIQUE ");
|
||||||
|
sbalter.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ukname)).Append(" ON ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append("(");
|
||||||
|
foreach (var tbcol in uk.Columns)
|
||||||
|
{
|
||||||
|
sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||||
|
if (tbcol.IsDesc) sbalter.Append(" DESC");
|
||||||
|
sbalter.Append(", ");
|
||||||
|
}
|
||||||
|
sbalter.Remove(sbalter.Length - 2, 2).Append(");\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (istmpatler == false)
|
||||||
|
{
|
||||||
|
var dbcomment = string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, _commonUtils.FormatSql(@" select table_comment from information_schema.tables where table_schema = {0} and table_name = {1}", tbname[0], tbname[1])));
|
||||||
|
if (dbcomment != (tb.Comment ?? ""))
|
||||||
|
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(" COMMENT ").Append(" ").Append(_commonUtils.FormatSql("{0}", tb.Comment ?? "")).Append(";\r\n");
|
||||||
|
|
||||||
|
sb.Append(sbalter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建临时表,数据导进临时表,然后删除原表,将临时表改名为原表名
|
||||||
|
var tablename = tboldname == null ? _commonUtils.QuoteSqlName(tbname[0], tbname[1]) : _commonUtils.QuoteSqlName(tboldname[0], tboldname[1]);
|
||||||
|
var tmptablename = _commonUtils.QuoteSqlName(tbname[0], $"FreeSqlTmp_{tbname[1]}");
|
||||||
|
//创建临时表
|
||||||
|
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(tmptablename).Append(" ( ");
|
||||||
|
foreach (var tbcol in tb.ColumnsByPosition)
|
||||||
|
{
|
||||||
|
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ").Append(tbcol.Attribute.DbType);
|
||||||
|
if (tbcol.Attribute.IsIdentity == true && tbcol.Attribute.DbType.IndexOf("AUTO_INCREMENT", StringComparison.CurrentCultureIgnoreCase) == -1) sb.Append(" AUTO_INCREMENT");
|
||||||
|
if (string.IsNullOrEmpty(tbcol.Comment) == false) sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment));
|
||||||
|
sb.Append(",");
|
||||||
|
}
|
||||||
|
if (tb.Primarys.Any())
|
||||||
|
{
|
||||||
|
sb.Append(" \r\n PRIMARY KEY (");
|
||||||
|
foreach (var tbcol in tb.Primarys) sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||||
|
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||||
|
}
|
||||||
|
sb.Remove(sb.Length - 1, 1);
|
||||||
|
sb.Append("\r\n) Engine=InnoDB");
|
||||||
|
if (string.IsNullOrEmpty(tb.Comment) == false)
|
||||||
|
sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment));
|
||||||
|
sb.Append(";\r\n");
|
||||||
|
|
||||||
|
sb.Append("INSERT INTO ").Append(tmptablename).Append(" (");
|
||||||
|
foreach (var tbcol in tb.ColumnsByPosition)
|
||||||
|
sb.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||||
|
sb.Remove(sb.Length - 2, 2).Append(")\r\nSELECT ");
|
||||||
|
foreach (var tbcol in tb.ColumnsByPosition)
|
||||||
|
{
|
||||||
|
var insertvalue = "NULL";
|
||||||
|
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||||
|
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
|
||||||
|
{
|
||||||
|
insertvalue = _commonUtils.QuoteSqlName(tbstructcol.column);
|
||||||
|
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false)
|
||||||
|
{
|
||||||
|
//insertvalue = $"cast({insertvalue} as {tbcol.Attribute.DbType.Split(' ').First()})";
|
||||||
|
}
|
||||||
|
if (tbcol.Attribute.IsNullable != tbstructcol.is_nullable)
|
||||||
|
insertvalue = $"ifnull({insertvalue},{tbcol.DbDefaultValue})";
|
||||||
|
}
|
||||||
|
else if (tbcol.Attribute.IsNullable == false)
|
||||||
|
if (tbcol.DbDefaultValue != "NULL" && tbcol.Attribute.IsIdentity == false)
|
||||||
|
insertvalue = tbcol.DbDefaultValue;
|
||||||
|
sb.Append(insertvalue).Append(", ");
|
||||||
|
}
|
||||||
|
sb.Remove(sb.Length - 2, 2).Append(" FROM ").Append(tablename).Append(";\r\n");
|
||||||
|
sb.Append("DROP TABLE ").Append(tablename).Append(";\r\n");
|
||||||
|
sb.Append("ALTER TABLE ").Append(tmptablename).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
|
||||||
|
//创建表的索引
|
||||||
|
foreach (var uk in tb.Indexes)
|
||||||
|
{
|
||||||
|
sb.Append("CREATE ");
|
||||||
|
if (uk.IsUnique) sb.Append("UNIQUE ");
|
||||||
|
sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append(" ON ").Append(tablename).Append("(");
|
||||||
|
foreach (var tbcol in uk.Columns)
|
||||||
|
{
|
||||||
|
sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||||
|
if (tbcol.IsDesc) sb.Append(" DESC");
|
||||||
|
sb.Append(", ");
|
||||||
|
}
|
||||||
|
sb.Remove(sb.Length - 2, 2).Append(");\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.Length == 0 ? null : sb.ToString();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(database) == false)
|
||||||
|
conn.Value.ChangeDatabase(database);
|
||||||
|
_orm.Ado.MasterPool.Return(conn);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_orm.Ado.MasterPool.Return(conn, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object LocalExecuteScalar(string db, string sql)
|
||||||
|
{
|
||||||
|
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var cmd = conn.Value.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = sql;
|
||||||
|
cmd.CommandType = CommandType.Text;
|
||||||
|
return cmd.ExecuteScalar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
445
Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs
Normal file
445
Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
using FreeSql.DatabaseModel;
|
||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using ClickHouse.Client.ADO;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse
|
||||||
|
{
|
||||||
|
class ClickHouseDbFirst : IDbFirst
|
||||||
|
{
|
||||||
|
IFreeSql _orm;
|
||||||
|
protected CommonUtils _commonUtils;
|
||||||
|
protected CommonExpression _commonExpression;
|
||||||
|
public ClickHouseDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||||
|
{
|
||||||
|
_orm = orm;
|
||||||
|
_commonUtils = commonUtils;
|
||||||
|
_commonExpression = commonExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetDbType(DbColumnInfo column) => (int)GetClickHouseDbType(column);
|
||||||
|
DbType GetClickHouseDbType(DbColumnInfo column)
|
||||||
|
{
|
||||||
|
var is_unsigned = column.DbTypeTextFull.ToLower().EndsWith(" unsigned");
|
||||||
|
switch (column.DbTypeText.ToLower())
|
||||||
|
{
|
||||||
|
case "bit":
|
||||||
|
case "tinyint":
|
||||||
|
case "bool":
|
||||||
|
case "sbyte":
|
||||||
|
case "Int8": return DbType.SByte;
|
||||||
|
case "byte":
|
||||||
|
case "UInt8": return DbType.Byte;
|
||||||
|
case "smallint":
|
||||||
|
case "Int16": return DbType.Int16;
|
||||||
|
case "UInt16": return DbType.UInt16;
|
||||||
|
case "Int32":
|
||||||
|
case "int": return DbType.Int32;
|
||||||
|
case "uint":
|
||||||
|
case "UInt32": return DbType.UInt32;
|
||||||
|
case "bigint":
|
||||||
|
case "Int64":
|
||||||
|
case "long": return DbType.Int64;
|
||||||
|
case "UInt64":
|
||||||
|
case "ulong": return DbType.UInt64;
|
||||||
|
case "real":
|
||||||
|
case "Float64":
|
||||||
|
case "double": return DbType.Double;
|
||||||
|
case "Float32":
|
||||||
|
case "float": return DbType.Single;
|
||||||
|
case "date": return DbType.Date;
|
||||||
|
case "datetime": return DbType.DateTime;
|
||||||
|
case "datetime64": return DbType.DateTime;
|
||||||
|
case "tinytext":
|
||||||
|
case "text":
|
||||||
|
case "mediumtext":
|
||||||
|
case "longtext":
|
||||||
|
case "char":
|
||||||
|
case "varchar": return DbType.String;
|
||||||
|
default: return DbType.String;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly Dictionary<int, DbToCs> _dicDbToCs = new Dictionary<int, DbToCs>() {
|
||||||
|
{ (int)DbType.SByte, new DbToCs("(sbyte?)", "sbyte.Parse({0})", "{0}.ToString()", "sbyte?", typeof(sbyte), typeof(sbyte?), "{0}.Value", "GetByte") },
|
||||||
|
{ (int)DbType.Int16, new DbToCs("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") },
|
||||||
|
{ (int)DbType.Int32, new DbToCs("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") },
|
||||||
|
{ (int)DbType.Int64, new DbToCs("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") },
|
||||||
|
|
||||||
|
{ (int)DbType.Byte, new DbToCs("(byte?)", "byte.Parse({0})", "{0}.ToString()", "byte?", typeof(byte), typeof(byte?), "{0}.Value", "GetByte") },
|
||||||
|
{ (int)DbType.UInt16, new DbToCs("(ushort?)", "ushort.Parse({0})", "{0}.ToString()", "ushort?", typeof(ushort), typeof(ushort?), "{0}.Value", "GetInt16") },
|
||||||
|
{ (int)DbType.UInt32, new DbToCs("(uint?)", "uint.Parse({0})", "{0}.ToString()", "uint?", typeof(uint), typeof(uint?), "{0}.Value", "GetInt32") },
|
||||||
|
{ (int)DbType.UInt64, new DbToCs("(ulong?)", "ulong.Parse({0})", "{0}.ToString()", "ulong?", typeof(ulong), typeof(ulong?), "{0}.Value", "GetInt64") },
|
||||||
|
|
||||||
|
{ (int)DbType.Double, new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") },
|
||||||
|
{ (int)DbType.Single, new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") },
|
||||||
|
|
||||||
|
{ (int)DbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||||
|
{ (int)DbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||||
|
{ (int)DbType.DateTime, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||||
|
{ (int)DbType.DateTime2, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") },
|
||||||
|
|
||||||
|
{ (int)DbType.Guid, new DbToCs("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid?", typeof(Guid), typeof(Guid?), "{0}", "GetString") },
|
||||||
|
{ (int)DbType.String, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") },
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null;
|
||||||
|
public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null;
|
||||||
|
public string GetCsStringify(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csStringify : null;
|
||||||
|
public string GetCsType(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csType : trydc.csType.Replace("?", "")) : null;
|
||||||
|
public Type GetCsTypeInfo(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeInfo : null;
|
||||||
|
public string GetCsTypeValue(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csTypeValue : null;
|
||||||
|
public string GetDataReaderMethod(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.dataReaderMethod : null;
|
||||||
|
|
||||||
|
public List<string> GetDatabases()
|
||||||
|
{
|
||||||
|
var sql = @" select name from system.databases where name not in ('system', 'default')";
|
||||||
|
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||||
|
return ds.Select(a => a.FirstOrDefault()?.ToString()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ExistsTable(string name, bool ignoreCase)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) return false;
|
||||||
|
var tbname = _commonUtils.SplitTableName(name);
|
||||||
|
if (tbname?.Length == 1)
|
||||||
|
{
|
||||||
|
var database = "";
|
||||||
|
using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)))
|
||||||
|
{
|
||||||
|
database = conn.Value.Database;
|
||||||
|
}
|
||||||
|
tbname = new[] { database, tbname[0] };
|
||||||
|
}
|
||||||
|
if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
|
||||||
|
var sql = $" SELECT 1 FROM information_schema.TABLES WHERE {(ignoreCase ? "lower(table_schema)" : "table_schema")} = {_commonUtils.FormatSql("{0}", tbname[0])} and {(ignoreCase ? "lower(table_name)" : "table_name")} = {_commonUtils.FormatSql("{0}", tbname[1])}";
|
||||||
|
return string.Concat(_orm.Ado.ExecuteScalar(CommandType.Text, sql)) == "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbTableInfo GetTableByName(string name, bool ignoreCase = true) => GetTables(null, name, ignoreCase)?.FirstOrDefault();
|
||||||
|
public List<DbTableInfo> GetTablesByDatabase(params string[] database) => GetTables(database, null, false);
|
||||||
|
|
||||||
|
public List<DbTableInfo> GetTables(string[] database, string tablename, bool ignoreCase)
|
||||||
|
{
|
||||||
|
var loc1 = new List<DbTableInfo>();
|
||||||
|
var loc2 = new Dictionary<string, DbTableInfo>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
var loc3 = new Dictionary<string, Dictionary<string, DbColumnInfo>>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
string[] tbname = null;
|
||||||
|
if (string.IsNullOrEmpty(tablename) == false)
|
||||||
|
{
|
||||||
|
tbname = _commonUtils.SplitTableName(tablename);
|
||||||
|
if (tbname?.Length == 1)
|
||||||
|
{
|
||||||
|
using (var conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5)))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conn.Value.Database)) return loc1;
|
||||||
|
tbname = new[] { conn.Value.Database, tbname[0] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ignoreCase) tbname = tbname.Select(a => a.ToLower()).ToArray();
|
||||||
|
database = new[] { tbname[0] };
|
||||||
|
}
|
||||||
|
else if (database == null || database.Any() == false)
|
||||||
|
{
|
||||||
|
using (var conn = _orm.Ado.MasterPool.Get())
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(conn.Value.Database)) return loc1;
|
||||||
|
database = new[] { conn.Value.Database };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var databaseIn = string.Join(",", database.Select(a => _commonUtils.FormatSql("{0}", a)));
|
||||||
|
var sql = $@"
|
||||||
|
select
|
||||||
|
concat(a.table_schema, '.', a.table_name) 'id',
|
||||||
|
a.table_schema 'schema',
|
||||||
|
a.table_name 'table',
|
||||||
|
a.table_comment,
|
||||||
|
a.table_type 'type'
|
||||||
|
from information_schema.tables a
|
||||||
|
where {(ignoreCase ? "lower(a.table_schema)" : "a.table_schema")} in ({databaseIn}){(tbname == null ? "" : $" and {(ignoreCase ? "lower(a.table_name)" : "a.table_name")}={_commonUtils.FormatSql("{0}", tbname[1])}")}";
|
||||||
|
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||||
|
if (ds == null) return loc1;
|
||||||
|
|
||||||
|
var loc6 = new List<string[]>();
|
||||||
|
var loc66 = new List<string[]>();
|
||||||
|
var loc6_1000 = new List<string>();
|
||||||
|
var loc66_1000 = new List<string>();
|
||||||
|
foreach (var row in ds)
|
||||||
|
{
|
||||||
|
var table_id = string.Concat(row[0]);
|
||||||
|
var schema = string.Concat(row[1]);
|
||||||
|
var table = string.Concat(row[2]);
|
||||||
|
var comment = string.Concat(row[3]);
|
||||||
|
var type = string.Concat(row[4]) == "VIEW" ? DbTableType.VIEW : DbTableType.TABLE;
|
||||||
|
if (database.Length == 1)
|
||||||
|
{
|
||||||
|
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||||
|
schema = "";
|
||||||
|
}
|
||||||
|
loc2.Add(table_id, new DbTableInfo { Id = table_id, Schema = schema, Name = table, Comment = comment, Type = type });
|
||||||
|
loc3.Add(table_id, new Dictionary<string, DbColumnInfo>(StringComparer.CurrentCultureIgnoreCase));
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case DbTableType.TABLE:
|
||||||
|
case DbTableType.VIEW:
|
||||||
|
loc6_1000.Add(table.Replace("'", "''"));
|
||||||
|
if (loc6_1000.Count >= 500)
|
||||||
|
{
|
||||||
|
loc6.Add(loc6_1000.ToArray());
|
||||||
|
loc6_1000.Clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DbTableType.StoreProcedure:
|
||||||
|
loc66_1000.Add(table.Replace("'", "''"));
|
||||||
|
if (loc66_1000.Count >= 500)
|
||||||
|
{
|
||||||
|
loc66.Add(loc66_1000.ToArray());
|
||||||
|
loc66_1000.Clear();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (loc6_1000.Count > 0) loc6.Add(loc6_1000.ToArray());
|
||||||
|
if (loc66_1000.Count > 0) loc66.Add(loc66_1000.ToArray());
|
||||||
|
|
||||||
|
if (loc6.Count == 0) return loc1;
|
||||||
|
var loc8 = new StringBuilder().Append("(");
|
||||||
|
for (var loc8idx = 0; loc8idx < loc6.Count; loc8idx++)
|
||||||
|
{
|
||||||
|
if (loc8idx > 0) loc8.Append(" OR ");
|
||||||
|
loc8.Append("a.table_name in (");
|
||||||
|
for (var loc8idx2 = 0; loc8idx2 < loc6[loc8idx].Length; loc8idx2++)
|
||||||
|
{
|
||||||
|
if (loc8idx2 > 0) loc8.Append(",");
|
||||||
|
loc8.Append($"'{loc6[loc8idx][loc8idx2]}'");
|
||||||
|
}
|
||||||
|
loc8.Append(")");
|
||||||
|
}
|
||||||
|
loc8.Append(")");
|
||||||
|
|
||||||
|
sql = $@"
|
||||||
|
select
|
||||||
|
concat(a.table_schema, '.', a.table_name),
|
||||||
|
a.column_name,
|
||||||
|
a.data_type,
|
||||||
|
ifnull(a.character_maximum_length, 0) 'len',
|
||||||
|
a.column_type,
|
||||||
|
case when a.is_nullable = 'YES' then 1 else 0 end 'is_nullable',
|
||||||
|
case when locate('auto_increment', a.extra) > 0 then 1 else 0 end 'is_identity',
|
||||||
|
a.column_comment 'comment',
|
||||||
|
a.column_default 'default_value'
|
||||||
|
from information_schema.columns a
|
||||||
|
where {(ignoreCase ? "lower(a.table_schema)" : "a.table_schema")} in ({databaseIn}) and {loc8}
|
||||||
|
";
|
||||||
|
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||||
|
if (ds == null) return loc1;
|
||||||
|
|
||||||
|
var position = 0;
|
||||||
|
foreach (var row in ds)
|
||||||
|
{
|
||||||
|
string table_id = string.Concat(row[0]);
|
||||||
|
string column = string.Concat(row[1]);
|
||||||
|
string type = string.Concat(row[2]);
|
||||||
|
//long max_length = long.Parse(string.Concat(row[3]));
|
||||||
|
string sqlType = string.Concat(row[4]);
|
||||||
|
var m_len = Regex.Match(sqlType, @"\w+\((\d+)");
|
||||||
|
int max_length = m_len.Success ? int.Parse(m_len.Groups[1].Value) : -1;
|
||||||
|
bool is_nullable = string.Concat(row[5]) == "1";
|
||||||
|
bool is_identity = string.Concat(row[6]) == "1";
|
||||||
|
string comment = string.Concat(row[7]);
|
||||||
|
string defaultValue = string.Concat(row[8]);
|
||||||
|
if (max_length == 0) max_length = -1;
|
||||||
|
if (database.Length == 1)
|
||||||
|
{
|
||||||
|
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
loc3[table_id].Add(column, new DbColumnInfo
|
||||||
|
{
|
||||||
|
Name = column,
|
||||||
|
MaxLength = max_length,
|
||||||
|
IsIdentity = is_identity,
|
||||||
|
IsNullable = is_nullable,
|
||||||
|
IsPrimary = false,
|
||||||
|
DbTypeText = type,
|
||||||
|
DbTypeTextFull = sqlType,
|
||||||
|
Table = loc2[table_id],
|
||||||
|
Coment = comment,
|
||||||
|
DefaultValue = defaultValue,
|
||||||
|
Position = ++position
|
||||||
|
});
|
||||||
|
loc3[table_id][column].DbType = this.GetDbType(loc3[table_id][column]);
|
||||||
|
loc3[table_id][column].CsType = this.GetCsTypeInfo(loc3[table_id][column]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sql = $@"
|
||||||
|
select
|
||||||
|
concat(a.table_schema, '.', a.table_name) 'table_id',
|
||||||
|
a.column_name,
|
||||||
|
a.index_name 'index_id',
|
||||||
|
case when a.non_unique = 0 then 1 else 0 end 'IsUnique',
|
||||||
|
case when a.index_name = 'PRIMARY' then 1 else 0 end 'IsPrimaryKey',
|
||||||
|
0 'IsClustered',
|
||||||
|
0 'IsDesc'
|
||||||
|
from information_schema.statistics a
|
||||||
|
where {(ignoreCase ? "lower(a.table_schema)" : "a.table_schema")} in ({databaseIn}) and {loc8}
|
||||||
|
";
|
||||||
|
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||||
|
if (ds == null) return loc1;
|
||||||
|
|
||||||
|
var indexColumns = new Dictionary<string, Dictionary<string, DbIndexInfo>>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
var uniqueColumns = new Dictionary<string, Dictionary<string, DbIndexInfo>>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
foreach (var row in ds)
|
||||||
|
{
|
||||||
|
string table_id = string.Concat(row[0]);
|
||||||
|
string column = string.Concat(row[1]);
|
||||||
|
string index_id = string.Concat(row[2]);
|
||||||
|
bool is_unique = string.Concat(row[3]) == "1";
|
||||||
|
bool is_primary_key = string.Concat(row[4]) == "1";
|
||||||
|
bool is_clustered = string.Concat(row[5]) == "1";
|
||||||
|
bool is_desc = string.Concat(row[6]) == "1";
|
||||||
|
if (database.Length == 1)
|
||||||
|
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||||
|
if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
|
||||||
|
var loc9 = loc3[table_id][column];
|
||||||
|
if (loc9.IsPrimary == false && is_primary_key) loc9.IsPrimary = is_primary_key;
|
||||||
|
|
||||||
|
Dictionary<string, DbIndexInfo> loc10 = null;
|
||||||
|
DbIndexInfo loc11 = null;
|
||||||
|
if (!indexColumns.TryGetValue(table_id, out loc10))
|
||||||
|
indexColumns.Add(table_id, loc10 = new Dictionary<string, DbIndexInfo>(StringComparer.CurrentCultureIgnoreCase));
|
||||||
|
if (!loc10.TryGetValue(index_id, out loc11))
|
||||||
|
loc10.Add(index_id, loc11 = new DbIndexInfo());
|
||||||
|
loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc });
|
||||||
|
if (is_unique && !is_primary_key)
|
||||||
|
{
|
||||||
|
if (!uniqueColumns.TryGetValue(table_id, out loc10))
|
||||||
|
uniqueColumns.Add(table_id, loc10 = new Dictionary<string, DbIndexInfo>(StringComparer.CurrentCultureIgnoreCase));
|
||||||
|
if (!loc10.TryGetValue(index_id, out loc11))
|
||||||
|
loc10.Add(index_id, loc11 = new DbIndexInfo());
|
||||||
|
loc11.Columns.Add(new DbIndexColumnInfo { Column = loc9, IsDesc = is_desc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (string table_id in indexColumns.Keys)
|
||||||
|
{
|
||||||
|
foreach (var column in indexColumns[table_id])
|
||||||
|
loc2[table_id].IndexesDict.Add(column.Key, column.Value);
|
||||||
|
}
|
||||||
|
foreach (string table_id in uniqueColumns.Keys)
|
||||||
|
{
|
||||||
|
foreach (var column in uniqueColumns[table_id])
|
||||||
|
{
|
||||||
|
column.Value.Columns.Sort((c1, c2) => c1.Column.Name.CompareTo(c2.Column.Name));
|
||||||
|
loc2[table_id].UniquesDict.Add(column.Key, column.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tbname == null)
|
||||||
|
{
|
||||||
|
sql = $@"
|
||||||
|
select
|
||||||
|
concat(a.constraint_schema, '.', a.table_name) 'table_id',
|
||||||
|
a.column_name,
|
||||||
|
a.constraint_name 'FKId',
|
||||||
|
concat(a.referenced_table_schema, '.', a.referenced_table_name) 'ref_table_id',
|
||||||
|
1 'IsForeignKey',
|
||||||
|
a.referenced_column_name 'ref_column'
|
||||||
|
from information_schema.key_column_usage a
|
||||||
|
where {(ignoreCase ? "lower(a.constraint_schema)" : "a.constraint_schema")} in ({databaseIn}) and {loc8} and not isnull(position_in_unique_constraint)
|
||||||
|
";
|
||||||
|
ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||||
|
if (ds == null) return loc1;
|
||||||
|
|
||||||
|
var fkColumns = new Dictionary<string, Dictionary<string, DbForeignInfo>>(StringComparer.CurrentCultureIgnoreCase);
|
||||||
|
foreach (var row in ds)
|
||||||
|
{
|
||||||
|
string table_id = string.Concat(row[0]);
|
||||||
|
string column = string.Concat(row[1]);
|
||||||
|
string fk_id = string.Concat(row[2]);
|
||||||
|
string ref_table_id = string.Concat(row[3]);
|
||||||
|
bool is_foreign_key = string.Concat(row[4]) == "1";
|
||||||
|
string referenced_column = string.Concat(row[5]);
|
||||||
|
if (database.Length == 1)
|
||||||
|
{
|
||||||
|
table_id = table_id.Substring(table_id.IndexOf('.') + 1);
|
||||||
|
ref_table_id = ref_table_id.Substring(ref_table_id.IndexOf('.') + 1);
|
||||||
|
}
|
||||||
|
if (loc3.ContainsKey(table_id) == false || loc3[table_id].ContainsKey(column) == false) continue;
|
||||||
|
var loc9 = loc3[table_id][column];
|
||||||
|
if (loc2.ContainsKey(ref_table_id) == false) continue;
|
||||||
|
var loc10 = loc2[ref_table_id];
|
||||||
|
var loc11 = loc3[ref_table_id][referenced_column];
|
||||||
|
|
||||||
|
Dictionary<string, DbForeignInfo> loc12 = null;
|
||||||
|
DbForeignInfo loc13 = null;
|
||||||
|
if (!fkColumns.TryGetValue(table_id, out loc12))
|
||||||
|
fkColumns.Add(table_id, loc12 = new Dictionary<string, DbForeignInfo>(StringComparer.CurrentCultureIgnoreCase));
|
||||||
|
if (!loc12.TryGetValue(fk_id, out loc13))
|
||||||
|
loc12.Add(fk_id, loc13 = new DbForeignInfo { Table = loc2[table_id], ReferencedTable = loc10 });
|
||||||
|
loc13.Columns.Add(loc9);
|
||||||
|
loc13.ReferencedColumns.Add(loc11);
|
||||||
|
}
|
||||||
|
foreach (var table_id in fkColumns.Keys)
|
||||||
|
foreach (var fk in fkColumns[table_id])
|
||||||
|
loc2[table_id].ForeignsDict.Add(fk.Key, fk.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var table_id in loc3.Keys)
|
||||||
|
{
|
||||||
|
foreach (var loc5 in loc3[table_id].Values)
|
||||||
|
{
|
||||||
|
loc2[table_id].Columns.Add(loc5);
|
||||||
|
if (loc5.IsIdentity) loc2[table_id].Identitys.Add(loc5);
|
||||||
|
if (loc5.IsPrimary) loc2[table_id].Primarys.Add(loc5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var loc4 in loc2.Values)
|
||||||
|
{
|
||||||
|
//if (loc4.Primarys.Count == 0 && loc4.UniquesDict.Count > 0)
|
||||||
|
//{
|
||||||
|
// foreach (var loc5 in loc4.UniquesDict.First().Value.Columns)
|
||||||
|
// {
|
||||||
|
// loc5.Column.IsPrimary = true;
|
||||||
|
// loc4.Primarys.Add(loc5.Column);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
loc4.Primarys.Sort((c1, c2) => c1.Name.CompareTo(c2.Name));
|
||||||
|
loc4.Columns.Sort((c1, c2) =>
|
||||||
|
{
|
||||||
|
int compare = c2.IsPrimary.CompareTo(c1.IsPrimary);
|
||||||
|
if (compare == 0)
|
||||||
|
{
|
||||||
|
bool b1 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c1.Name).Any()).Any();
|
||||||
|
bool b2 = loc4.ForeignsDict.Values.Where(fk => fk.Columns.Where(c3 => c3.Name == c2.Name).Any()).Any();
|
||||||
|
compare = b2.CompareTo(b1);
|
||||||
|
}
|
||||||
|
if (compare == 0) compare = c1.Name.CompareTo(c2.Name);
|
||||||
|
return compare;
|
||||||
|
});
|
||||||
|
loc1.Add(loc4);
|
||||||
|
}
|
||||||
|
loc1.Sort((t1, t2) =>
|
||||||
|
{
|
||||||
|
var ret = t1.Schema.CompareTo(t2.Schema);
|
||||||
|
if (ret == 0) ret = t1.Name.CompareTo(t2.Name);
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
loc2.Clear();
|
||||||
|
loc3.Clear();
|
||||||
|
return loc1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DbEnumInfo> GetEnumsByDatabase(params string[] database)
|
||||||
|
{
|
||||||
|
return new List<DbEnumInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
559
Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs
Normal file
559
Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs
Normal file
@ -0,0 +1,559 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse
|
||||||
|
{
|
||||||
|
class ClickHouseExpression : CommonExpression
|
||||||
|
{
|
||||||
|
|
||||||
|
public ClickHouseExpression(CommonUtils common) : base(common) { }
|
||||||
|
|
||||||
|
public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||||
|
switch (exp.NodeType)
|
||||||
|
{
|
||||||
|
case ExpressionType.ArrayLength:
|
||||||
|
var arrOper = (exp as UnaryExpression)?.Operand;
|
||||||
|
if (arrOper.Type == typeof(byte[])) return $"length({getExp(arrOper)})";
|
||||||
|
break;
|
||||||
|
case ExpressionType.Convert:
|
||||||
|
var operandExp = (exp as UnaryExpression)?.Operand;
|
||||||
|
var gentype = exp.Type.NullableTypeOrThis();
|
||||||
|
if (gentype != operandExp.Type.NullableTypeOrThis())
|
||||||
|
{
|
||||||
|
switch (gentype.ToString())
|
||||||
|
{
|
||||||
|
case "System.Boolean": return $"({getExp(operandExp)} not in ('0','false'))";
|
||||||
|
case "System.Byte": return $"cast({getExp(operandExp)} as Int8)";
|
||||||
|
case "System.Char": return $"substr(cast({getExp(operandExp)} as String), 1, 1)";
|
||||||
|
case "System.DateTime": return $"cast({getExp(operandExp)} as DateTime)";
|
||||||
|
case "System.Decimal": return $"cast({getExp(operandExp)} as Float64)";
|
||||||
|
case "System.Double": return $"cast({getExp(operandExp)} as Float64)";
|
||||||
|
case "System.Int16": return $"cast({getExp(operandExp)} as Int16)";
|
||||||
|
case "System.Int32": return $"cast({getExp(operandExp)} as Int32)";
|
||||||
|
case "System.Int64": return $"cast({getExp(operandExp)} as Int64)";
|
||||||
|
case "System.SByte": return $"cast({getExp(operandExp)} as UInt8)";
|
||||||
|
case "System.Single": return $"cast({getExp(operandExp)} as Float32)";
|
||||||
|
case "System.String": return $"cast({getExp(operandExp)} as String)";
|
||||||
|
case "System.UInt16": return $"cast({getExp(operandExp)} as UInt16)";
|
||||||
|
case "System.UInt32": return $"cast({getExp(operandExp)} as UInt32)";
|
||||||
|
case "System.UInt64": return $"cast({getExp(operandExp)} as UInt64)";
|
||||||
|
case "System.Guid": return $"substr(cast({getExp(operandExp)} as String), 1, 36)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ExpressionType.Call:
|
||||||
|
var callExp = exp as MethodCallExpression;
|
||||||
|
|
||||||
|
switch (callExp.Method.Name)
|
||||||
|
{
|
||||||
|
case "Parse":
|
||||||
|
case "TryParse":
|
||||||
|
switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString())
|
||||||
|
{
|
||||||
|
case "System.Boolean": return $"({getExp(callExp.Arguments[0])} not in ('0','false'))";
|
||||||
|
case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as Int8)";
|
||||||
|
case "System.Char": return $"substr(cast({getExp(callExp.Arguments[0])} as String), 1, 1)";
|
||||||
|
case "System.DateTime": return $"cast({getExp(callExp.Arguments[0])} as DateTime)";
|
||||||
|
case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as Float64)";
|
||||||
|
case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as Float64)";
|
||||||
|
case "System.Int16": return $"cast({getExp(callExp.Arguments[0])} as Int16)";
|
||||||
|
case "System.Int32": return $"cast({getExp(callExp.Arguments[0])} as Int32)";
|
||||||
|
case "System.Int64": return $"cast({getExp(callExp.Arguments[0])} as Int64)";
|
||||||
|
case "System.SByte": return $"cast({getExp(callExp.Arguments[0])} as UInt8)";
|
||||||
|
case "System.Single": return $"cast({getExp(callExp.Arguments[0])} as Float32)";
|
||||||
|
case "System.UInt16": return $"cast({getExp(callExp.Arguments[0])} as UInt16)";
|
||||||
|
case "System.UInt32": return $"cast({getExp(callExp.Arguments[0])} as UInt32)";
|
||||||
|
case "System.UInt64": return $"cast({getExp(callExp.Arguments[0])} as UInt64)";
|
||||||
|
case "System.Guid": return $"substr(cast({getExp(callExp.Arguments[0])} as String), 1, 36)";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
case "NewGuid":
|
||||||
|
return null;
|
||||||
|
case "Next":
|
||||||
|
if (callExp.Object?.Type == typeof(Random)) return "cast(rand()*1000000000 as Int64)";
|
||||||
|
return null;
|
||||||
|
case "NextDouble":
|
||||||
|
if (callExp.Object?.Type == typeof(Random)) return "rand()";
|
||||||
|
return null;
|
||||||
|
case "Random":
|
||||||
|
if (callExp.Method.DeclaringType.IsNumberType()) return "rand()";
|
||||||
|
return null;
|
||||||
|
case "ToString":
|
||||||
|
if (callExp.Object != null) return callExp.Arguments.Count == 0 ? $"cast({getExp(callExp.Object)} as String)" : null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var objExp = callExp.Object;
|
||||||
|
var objType = objExp?.Type;
|
||||||
|
if (objType?.FullName == "System.Byte[]") return null;
|
||||||
|
|
||||||
|
var argIndex = 0;
|
||||||
|
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable))
|
||||||
|
{
|
||||||
|
objExp = callExp.Arguments.FirstOrDefault();
|
||||||
|
objType = objExp?.Type;
|
||||||
|
argIndex++;
|
||||||
|
|
||||||
|
if (objType == typeof(string))
|
||||||
|
{
|
||||||
|
switch (callExp.Method.Name)
|
||||||
|
{
|
||||||
|
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 = tsc.SetDbParamsReturnOld(null);
|
||||||
|
var left = objExp == null ? null : getExp(objExp);
|
||||||
|
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
|
||||||
|
tsc.SetDbParamsReturnOld(oldDbParams);
|
||||||
|
switch (callExp.Method.Name)
|
||||||
|
{
|
||||||
|
case "Contains":
|
||||||
|
//判断 in //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格
|
||||||
|
return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ExpressionType.NewArrayInit:
|
||||||
|
var arrExp = exp as NewArrayExpression;
|
||||||
|
var arrSb = new StringBuilder();
|
||||||
|
arrSb.Append("(");
|
||||||
|
for (var a = 0; a < arrExp.Expressions.Count; a++)
|
||||||
|
{
|
||||||
|
if (a > 0) arrSb.Append(",");
|
||||||
|
if (a % 500 == 499) arrSb.Append(" \r\n \r\n"); //500元素分割, 3空格\r\n4空格
|
||||||
|
arrSb.Append(getExp(arrExp.Expressions[a]));
|
||||||
|
}
|
||||||
|
if (arrSb.Length == 1) arrSb.Append("NULL");
|
||||||
|
return arrSb.Append(")").ToString();
|
||||||
|
case ExpressionType.ListInit:
|
||||||
|
var listExp = exp as ListInitExpression;
|
||||||
|
var listSb = new StringBuilder();
|
||||||
|
listSb.Append("(");
|
||||||
|
for (var a = 0; a < listExp.Initializers.Count; a++)
|
||||||
|
{
|
||||||
|
if (listExp.Initializers[a].Arguments.Any() == false) continue;
|
||||||
|
if (a > 0) listSb.Append(",");
|
||||||
|
listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault()));
|
||||||
|
}
|
||||||
|
if (listSb.Length == 1) listSb.Append("NULL");
|
||||||
|
return listSb.Append(")").ToString();
|
||||||
|
case ExpressionType.New:
|
||||||
|
var newExp = exp as NewExpression;
|
||||||
|
if (typeof(IList).IsAssignableFrom(newExp.Type))
|
||||||
|
{
|
||||||
|
if (newExp.Arguments.Count == 0) return "(NULL)";
|
||||||
|
if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)";
|
||||||
|
return getExp(newExp.Arguments[0]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
if (exp.Expression == null)
|
||||||
|
{
|
||||||
|
switch (exp.Member.Name)
|
||||||
|
{
|
||||||
|
case "Empty": return "''";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||||
|
switch (exp.Member.Name)
|
||||||
|
{
|
||||||
|
case "Length": return $"char_length({left})";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
if (exp.Expression == null)
|
||||||
|
{
|
||||||
|
switch (exp.Member.Name)
|
||||||
|
{
|
||||||
|
case "Now": return _common.Now;
|
||||||
|
case "UtcNow": return _common.NowUtc;
|
||||||
|
case "Today": return "curdate()";
|
||||||
|
case "MinValue": return "cast('0001/1/1 0:00:00' as DateTime)";
|
||||||
|
case "MaxValue": return "cast('9999/12/31 23:59:59' as DateTime)";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||||
|
|
||||||
|
switch (exp.Member.Name)
|
||||||
|
{
|
||||||
|
case "Date": return $"toDate({left})";
|
||||||
|
case "TimeOfDay": return $"dateDiff(second, toDate({left}), toDateTime({left}))*1000000";
|
||||||
|
case "DayOfWeek": return $"(toDayOfWeek({left})-1)";
|
||||||
|
case "Day": return $"toDayOfMonth({left})";
|
||||||
|
case "DayOfYear": return $"toDayOfYear({left})";
|
||||||
|
case "Month": return $"toMonth({left})";
|
||||||
|
case "Year": return $"toYear({left})";
|
||||||
|
case "Hour": return $"toHour({left})";
|
||||||
|
case "Minute": return $"toMinute({left})";
|
||||||
|
case "Second": return $"toSecond({left})";
|
||||||
|
case "Millisecond": return $"(toSecond({left})/1000)";
|
||||||
|
case "Ticks": return $"(dateDiff(second, toDate('0001-1-1'), toDateTime({left}))*10000000+621355968000000000)";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
if (exp.Expression == null)
|
||||||
|
{
|
||||||
|
switch (exp.Member.Name)
|
||||||
|
{
|
||||||
|
case "Zero": return "0";
|
||||||
|
case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10
|
||||||
|
case "MaxValue": return "922337203685477580";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var left = ExpressionLambdaToSql(exp.Expression, tsc);
|
||||||
|
switch (exp.Member.Name)
|
||||||
|
{
|
||||||
|
case "Days": return $"intDiv(({left})/{60 * 60 * 24})";
|
||||||
|
case "Hours": return $"intDiv(({left})/{60 * 60}%24)";
|
||||||
|
case "Milliseconds": return $"(cast({left} as Int64)*1000)";
|
||||||
|
case "Minutes": return $"intDiv(({left})/60%60)";
|
||||||
|
case "Seconds": return $"(({left})%60)";
|
||||||
|
case "Ticks": return $"(intDiv({left} as Int64)*10000000)";
|
||||||
|
case "TotalDays": return $"(({left})/{60 * 60 * 24})";
|
||||||
|
case "TotalHours": return $"(({left})/{60 * 60})";
|
||||||
|
case "TotalMilliseconds": return $"(cast({left} as Int64)*1000)";
|
||||||
|
case "TotalMinutes": return $"(({left})/60)";
|
||||||
|
case "TotalSeconds": return $"({left})";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||||
|
if (exp.Object == null)
|
||||||
|
{
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "IsNullOrEmpty":
|
||||||
|
var arg1 = getExp(exp.Arguments[0]);
|
||||||
|
return $"({arg1} is null or {arg1} = '')";
|
||||||
|
case "IsNullOrWhiteSpace":
|
||||||
|
var arg2 = getExp(exp.Arguments[0]);
|
||||||
|
return $"({arg2} is null or {arg2} = '' or ltrim({arg2}) = '')";
|
||||||
|
case "Concat":
|
||||||
|
return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
|
||||||
|
case "Format":
|
||||||
|
if (exp.Arguments[0].NodeType != ExpressionType.Constant) throw new Exception($"未实现函数表达式 {exp} 解析,参数 {exp.Arguments[0]} 必须为常量");
|
||||||
|
if (exp.Arguments.Count == 1) return ExpressionLambdaToSql(exp.Arguments[0], tsc);
|
||||||
|
var expArgsHack = exp.Arguments.Count == 2 && exp.Arguments[1].NodeType == ExpressionType.NewArrayInit ?
|
||||||
|
(exp.Arguments[1] as NewArrayExpression).Expressions : exp.Arguments.Where((a, z) => z > 0);
|
||||||
|
//3个 {} 时,Arguments 解析出来是分开的
|
||||||
|
//4个 {} 时,Arguments[1] 只能解析这个出来,然后里面是 NewArray []
|
||||||
|
var expArgs = expArgsHack.Select(a => $"',{_common.IsNull(ExpressionLambdaToSql(a, tsc), "''")},'").ToArray();
|
||||||
|
return $"concat({string.Format(ExpressionLambdaToSql(exp.Arguments[0], tsc), expArgs)})";
|
||||||
|
case "Join":
|
||||||
|
if (exp.IsStringJoin(out var tolistObjectExp, out var toListMethod, out var toListArgs1))
|
||||||
|
{
|
||||||
|
var newToListArgs0 = Expression.Call(tolistObjectExp, toListMethod,
|
||||||
|
Expression.Lambda(
|
||||||
|
Expression.Call(
|
||||||
|
typeof(SqlExtExtensions).GetMethod("StringJoinClickHouseGroupConcat"),
|
||||||
|
Expression.Convert(toListArgs1.Body, typeof(object)),
|
||||||
|
Expression.Convert(exp.Arguments[0], typeof(object))),
|
||||||
|
toListArgs1.Parameters));
|
||||||
|
var newToListSql = getExp(newToListArgs0);
|
||||||
|
return newToListSql;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var left = getExp(exp.Object);
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "StartsWith":
|
||||||
|
case "EndsWith":
|
||||||
|
case "Contains":
|
||||||
|
var args0Value = getExp(exp.Arguments[0]);
|
||||||
|
if (args0Value == "NULL") return $"({left}) IS NULL";
|
||||||
|
if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"concat({args0Value}, '%')")}";
|
||||||
|
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}";
|
||||||
|
if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}";
|
||||||
|
return $"({left}) LIKE concat('%', {args0Value}, '%')";
|
||||||
|
case "ToLower": return $"lower({left})";
|
||||||
|
case "ToUpper": return $"upper({left})";
|
||||||
|
case "Substring":
|
||||||
|
var substrArgs1 = getExp(exp.Arguments[0]);
|
||||||
|
if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString();
|
||||||
|
else substrArgs1 += "+1";
|
||||||
|
if (exp.Arguments.Count == 1) return $"substr({left}, {substrArgs1})";
|
||||||
|
return $"substr({left}, {substrArgs1}, {getExp(exp.Arguments[1])})";
|
||||||
|
case "IndexOf":
|
||||||
|
var indexOfFindStr = getExp(exp.Arguments[0]);
|
||||||
|
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32")
|
||||||
|
{
|
||||||
|
var locateArgs1 = getExp(exp.Arguments[1]);
|
||||||
|
if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString();
|
||||||
|
else locateArgs1 += "+1";
|
||||||
|
return $"(locate({indexOfFindStr}, {left}, {locateArgs1})-1)";
|
||||||
|
}
|
||||||
|
return $"(locate({indexOfFindStr}, {left})-1)";
|
||||||
|
case "PadLeft":
|
||||||
|
if (exp.Arguments.Count == 1) return $"lpad({left}, {getExp(exp.Arguments[0])})";
|
||||||
|
return $"lpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||||
|
case "PadRight":
|
||||||
|
if (exp.Arguments.Count == 1) return $"rpad({left}, {getExp(exp.Arguments[0])})";
|
||||||
|
return $"rpad({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||||
|
case "Trim":
|
||||||
|
case "TrimStart":
|
||||||
|
case "TrimEnd":
|
||||||
|
if (exp.Arguments.Count == 0)
|
||||||
|
{
|
||||||
|
if (exp.Method.Name == "Trim") return $"trim({left})";
|
||||||
|
if (exp.Method.Name == "TrimStart") return $"ltrim({left})";
|
||||||
|
if (exp.Method.Name == "TrimEnd") return $"rtrim({left})";
|
||||||
|
}
|
||||||
|
foreach (var argsTrim02 in exp.Arguments)
|
||||||
|
{
|
||||||
|
var argsTrim01s = new[] { argsTrim02 };
|
||||||
|
if (argsTrim02.NodeType == ExpressionType.NewArrayInit)
|
||||||
|
{
|
||||||
|
var arritem = argsTrim02 as NewArrayExpression;
|
||||||
|
argsTrim01s = arritem.Expressions.ToArray();
|
||||||
|
}
|
||||||
|
foreach (var argsTrim01 in argsTrim01s)
|
||||||
|
{
|
||||||
|
if (exp.Method.Name == "Trim") left = $"trim({getExp(argsTrim01)} from {left})";
|
||||||
|
if (exp.Method.Name == "TrimStart") left = $"trim(leading {getExp(argsTrim01)} from {left})";
|
||||||
|
if (exp.Method.Name == "TrimEnd") left = $"trim(trailing {getExp(argsTrim01)} from {left})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||||
|
case "CompareTo": return $"strcmp({left}, {getExp(exp.Arguments[0])})";
|
||||||
|
case "Equals": return $"({left} = {getExp(exp.Arguments[0])})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "Abs": return $"abs({getExp(exp.Arguments[0])})";
|
||||||
|
case "Sign": return $"sign({getExp(exp.Arguments[0])})";
|
||||||
|
case "Floor": return $"floor({getExp(exp.Arguments[0])})";
|
||||||
|
case "Ceiling": return $"ceiling({getExp(exp.Arguments[0])})";
|
||||||
|
case "Round":
|
||||||
|
if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return $"round({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||||
|
return $"round({getExp(exp.Arguments[0])})";
|
||||||
|
case "Exp": return $"exp({getExp(exp.Arguments[0])})";
|
||||||
|
case "Log": return $"log({getExp(exp.Arguments[0])})";
|
||||||
|
case "Log10": return $"log10({getExp(exp.Arguments[0])})";
|
||||||
|
case "Pow": return $"pow({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||||
|
case "Sqrt": return $"sqrt({getExp(exp.Arguments[0])})";
|
||||||
|
case "Cos": return $"cos({getExp(exp.Arguments[0])})";
|
||||||
|
case "Sin": return $"sin({getExp(exp.Arguments[0])})";
|
||||||
|
case "Tan": return $"tan({getExp(exp.Arguments[0])})";
|
||||||
|
case "Acos": return $"acos({getExp(exp.Arguments[0])})";
|
||||||
|
case "Asin": return $"asin({getExp(exp.Arguments[0])})";
|
||||||
|
case "Atan": return $"atan({getExp(exp.Arguments[0])})";
|
||||||
|
case "Atan2": return $"atan2({getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})";
|
||||||
|
case "Truncate": return $"truncate({getExp(exp.Arguments[0])}, 0)";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||||
|
if (exp.Object == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "Compare": return $"({getExp(exp.Arguments[0])} - ({getExp(exp.Arguments[1])}))";
|
||||||
|
case "DaysInMonth": return $"toDayOfMonth(subtractDays(addMonths(toStartOfMonth(concat({getExp(exp.Arguments[0])}, '-', {getExp(exp.Arguments[1])}, '-01')), 1), 1))";
|
||||||
|
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||||
|
|
||||||
|
case "IsLeapYear":
|
||||||
|
var isLeapYearArgs1 = getExp(exp.Arguments[0]);
|
||||||
|
return $"(({isLeapYearArgs1})%4=0 AND ({isLeapYearArgs1})%100<>0 OR ({isLeapYearArgs1})%400=0)";
|
||||||
|
|
||||||
|
case "Parse": return $"cast({getExp(exp.Arguments[0])} as DateTime)";
|
||||||
|
case "ParseExact":
|
||||||
|
case "TryParse":
|
||||||
|
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as DateTime)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var left = getExp(exp.Object);
|
||||||
|
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "Add": return $"addSeconds(toDateTime({left}), {args1})";
|
||||||
|
case "AddDays": return $"addDays(toDateTime({left}), {args1})";
|
||||||
|
case "AddHours": return $"addHours(toDateTime({left}), {args1})";
|
||||||
|
case "AddMilliseconds": return $"addSeconds(toDateTime({left}), {args1}/1000)";
|
||||||
|
case "AddMinutes": return $"addMinutes(toDateTime({left}),{args1})";
|
||||||
|
case "AddMonths": return $"addMonths(toDateTime({left}),{args1})";
|
||||||
|
case "AddSeconds": return $"addSeconds(toDateTime({left}),{args1})";
|
||||||
|
case "AddTicks": return $"addSeconds(toDateTime({left}), {args1}/10000000)";
|
||||||
|
case "AddYears": return $"addYears(toDateTime({left}),{args1})";
|
||||||
|
case "Subtract":
|
||||||
|
switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName)
|
||||||
|
{
|
||||||
|
case "System.DateTime": return $"dateDiff(second, {args1}, toDateTime({left}))";
|
||||||
|
case "System.TimeSpan": return $"addSeconds(toDateTime({left}),(({args1})*-1))";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Equals": return $"({left} = {args1})";
|
||||||
|
case "CompareTo": return $"dateDiff(second,{args1},toDateTime({left}))";
|
||||||
|
case "ToString":
|
||||||
|
if (exp.Arguments.Count == 0) return $"date_format({left},'%Y-%m-%d %H:%M:%S.%f')";
|
||||||
|
switch (args1)
|
||||||
|
{
|
||||||
|
case "'yyyy-MM-dd HH:mm:ss'": return $"formatDateTime(toDateTime({left}),'%Y-%m-%d %H:%M:%S')";
|
||||||
|
case "'yyyy-MM-dd HH:mm'": return $"formatDateTime(toDateTime({left}),'%Y-%m-%d %H:%M')";
|
||||||
|
case "'yyyy-MM-dd HH'": return $"formatDateTime(toDateTime({left}),'%Y-%m-%d %H')";
|
||||||
|
case "'yyyy-MM-dd'": return $"formatDateTime(toDateTime({left}),'%Y-%m-%d')";
|
||||||
|
case "'yyyy-MM'": return $"formatDateTime(toDateTime({left}),'%Y-%m')";
|
||||||
|
case "'yyyyMMddHHmmss'": return $"formatDateTime(toDateTime({left}),'%Y%m%d%H%M%S')";
|
||||||
|
case "'yyyyMMddHHmm'": return $"formatDateTime(toDateTime({left}),'%Y%m%d%H%M')";
|
||||||
|
case "'yyyyMMddHH'": return $"formatDateTime(toDateTime({left}),'%Y%m%d%H')";
|
||||||
|
case "'yyyyMMdd'": return $"formatDateTime(toDateTime({left}),'%Y%m%d')";
|
||||||
|
case "'yyyyMM'": return $"formatDateTime(toDateTime({left}),'%Y%m')";
|
||||||
|
case "'yyyy'": return $"formatDateTime(toDateTime({left}),'%Y')";
|
||||||
|
case "'HH:mm:ss'": return $"formatDateTime(toDateTime({left}),'%H:%M:%S')";
|
||||||
|
}
|
||||||
|
args1 = Regex.Replace(args1, "(yyyy|yy|MM|M|dd|d|HH|H|hh|h|mm|ss|tt)", m =>
|
||||||
|
{
|
||||||
|
switch (m.Groups[1].Value)
|
||||||
|
{
|
||||||
|
case "yyyy": return $"%Y";
|
||||||
|
case "yy": return $"%y";
|
||||||
|
case "MM": return $"%_a1";
|
||||||
|
case "M": return $"%c";
|
||||||
|
case "dd": return $"%d";
|
||||||
|
case "d": return $"%e";
|
||||||
|
case "HH": return $"%H";
|
||||||
|
case "H": return $"%k";
|
||||||
|
case "hh": return $"%h";
|
||||||
|
case "h": return $"%l";
|
||||||
|
case "mm": return $"%i";
|
||||||
|
case "ss": return $"%_a2";
|
||||||
|
case "tt": return $"%p";
|
||||||
|
}
|
||||||
|
return m.Groups[0].Value;
|
||||||
|
});
|
||||||
|
var argsFinds = new[] { "%Y", "%y", "%_a1", "%c", "%d", "%e", "%H", "%k", "%h", "%l", "%M", "%_a2", "%p" };
|
||||||
|
var argsSpts = Regex.Split(args1, "(m|s|t)");
|
||||||
|
for (var a = 0; a < argsSpts.Length; a++)
|
||||||
|
{
|
||||||
|
switch (argsSpts[a])
|
||||||
|
{
|
||||||
|
case "m": argsSpts[a] = $"case when substr(formatDateTime(toDateTime({left}),'%M'),1,1) = '0' then substr(formatDateTime(toDateTime({left}),'%M'),2,1) else formatDateTime(toDateTime({left}),'%M') end"; break;
|
||||||
|
case "s": argsSpts[a] = $"case when substr(formatDateTime(toDateTime({left}),'%S'),1,1) = '0' then substr(formatDateTime(toDateTime({left}),'%S'),2,1) else formatDateTime(toDateTime({left}),'%S') end"; break;
|
||||||
|
case "t": argsSpts[a] = $"trim(trailing 'M' from formatDateTime(toDateTime({left}),'%p'))"; break;
|
||||||
|
default:
|
||||||
|
var argsSptsA = argsSpts[a];
|
||||||
|
if (argsSptsA.StartsWith("'")) argsSptsA = argsSptsA.Substring(1);
|
||||||
|
if (argsSptsA.EndsWith("'")) argsSptsA = argsSptsA.Remove(argsSptsA.Length - 1);
|
||||||
|
argsSpts[a] = argsFinds.Any(m => argsSptsA.Contains(m)) ? $"formatDateTime(toDateTime({left}),'{argsSptsA}')" : $"'{argsSptsA}'";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argsSpts.Length > 0) args1 = $"concat({string.Join(", ", argsSpts.Where(a => a != "''"))})";
|
||||||
|
return args1.Replace("%_a1", "%m").Replace("%_a2", "%S");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||||
|
if (exp.Object == null)
|
||||||
|
{
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))";
|
||||||
|
case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})";
|
||||||
|
case "FromDays": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60 * 24})";
|
||||||
|
case "FromHours": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60 * 60})";
|
||||||
|
case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})*1000)";
|
||||||
|
case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*{(long)1000000 * 60})";
|
||||||
|
case "FromSeconds": return $"(({getExp(exp.Arguments[0])})*1000000)";
|
||||||
|
case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10)";
|
||||||
|
case "Parse": return $"cast({getExp(exp.Arguments[0])} as Int64)";
|
||||||
|
case "ParseExact":
|
||||||
|
case "TryParse":
|
||||||
|
case "TryParseExact": return $"cast({getExp(exp.Arguments[0])} as Int64)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var left = getExp(exp.Object);
|
||||||
|
var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]);
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "Add": return $"({left}+{args1})";
|
||||||
|
case "Subtract": return $"({left}-({args1}))";
|
||||||
|
case "Equals": return $"({left} = {args1})";
|
||||||
|
case "CompareTo": return $"({left}-({args1}))";
|
||||||
|
case "ToString": return $"cast({left} as String)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc)
|
||||||
|
{
|
||||||
|
Func<Expression, string> getExp = exparg => ExpressionLambdaToSql(exparg, tsc);
|
||||||
|
if (exp.Object == null)
|
||||||
|
{
|
||||||
|
switch (exp.Method.Name)
|
||||||
|
{
|
||||||
|
case "ToBoolean": return $"({getExp(exp.Arguments[0])} not in ('0','false'))";
|
||||||
|
case "ToByte": return $"cast({getExp(exp.Arguments[0])} as Int8)";
|
||||||
|
case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as String), 1, 1)";
|
||||||
|
case "ToDateTime": return $"cast({getExp(exp.Arguments[0])} as DateTime)";
|
||||||
|
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as Float64)";
|
||||||
|
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as Float64)";
|
||||||
|
case "ToInt16":
|
||||||
|
case "ToInt32":
|
||||||
|
case "ToInt64":
|
||||||
|
case "ToSByte": return $"cast({getExp(exp.Arguments[0])} as UInt8)";
|
||||||
|
case "ToSingle": return $"cast({getExp(exp.Arguments[0])} as Float32)";
|
||||||
|
case "ToString": return $"cast({getExp(exp.Arguments[0])} as String)";
|
||||||
|
case "ToUInt16":
|
||||||
|
case "ToUInt32":
|
||||||
|
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as UInt64)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using FreeSql;
|
||||||
|
using FreeSql.ClickHouse.Curd;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public static partial class FreeSqlClickHouseGlobalExtensions
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 特殊处理类似 string.Format 的使用方法,防止注入,以及 IS NULL 转换
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="that"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string FormatClickHouse(this string that, params object[] args) => _clickHouseAdo.Addslashes(that, args);
|
||||||
|
static FreeSql.ClickHouse.ClickHouseAdo _clickHouseAdo = new FreeSql.ClickHouse.ClickHouseAdo();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
42
Providers/FreeSql.Provider.ClickHouse/ClickHouseProvider.cs
Normal file
42
Providers/FreeSql.Provider.ClickHouse/ClickHouseProvider.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.CommonProvider;
|
||||||
|
using FreeSql.ClickHouse.Curd;
|
||||||
|
using System;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse
|
||||||
|
{
|
||||||
|
|
||||||
|
public class ClickHouseProvider<TMark> : BaseDbProvider, IFreeSql<TMark>
|
||||||
|
{
|
||||||
|
public override ISelect<T1> CreateSelectProvider<T1>(object dywhere) => new ClickHouseSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||||
|
public override IInsert<T1> CreateInsertProvider<T1>() => new ClickHouseInsert<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||||
|
public override IUpdate<T1> CreateUpdateProvider<T1>(object dywhere) => new ClickHouseUpdate<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||||
|
public override IDelete<T1> CreateDeleteProvider<T1>(object dywhere) => new ClickHouseDelete<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||||
|
public override IInsertOrUpdate<T1> CreateInsertOrUpdateProvider<T1>()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
public ClickHouseProvider(string masterConnectionString, string[] slaveConnectionString, Func<DbConnection> connectionFactory = null)
|
||||||
|
{
|
||||||
|
this.InternalCommonUtils = new ClickHouseUtils(this);
|
||||||
|
this.InternalCommonExpression = new ClickHouseExpression(this.InternalCommonUtils);
|
||||||
|
|
||||||
|
this.Ado = new ClickHouseAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory);
|
||||||
|
this.Aop = new AopProvider();
|
||||||
|
|
||||||
|
this.DbFirst = new ClickHouseDbFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||||
|
this.CodeFirst = new ClickHouseCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ClickHouseProvider() => this.Dispose();
|
||||||
|
int _disposeCounter;
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
if (Interlocked.Increment(ref _disposeCounter) != 1) return;
|
||||||
|
(this.Ado as AdoProvider)?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
133
Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs
Normal file
133
Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using ClickHouse.Client.ADO;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Data;
|
||||||
|
using ClickHouse.Client.ADO.Parameters;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClickHouseUtils : CommonUtils
|
||||||
|
{
|
||||||
|
public ClickHouseUtils(IFreeSql orm) : base(orm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DbParameter AppendParamter(List<DbParameter> _params, string parameterName, ColumnInfo col, Type type, object value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||||
|
var dbtype = (DbType)_orm.CodeFirst.GetDbInfo(type)?.type;
|
||||||
|
DbParameter ret = new ClickHouseDbParameter { ParameterName = parameterName, DbType= dbtype, Value = value };//QuoteParamterName(parameterName)
|
||||||
|
if (col != null)
|
||||||
|
{
|
||||||
|
var dbtype2 = (DbType)_orm.DbFirst.GetDbType(new DatabaseModel.DbColumnInfo { DbTypeText = col.DbTypeText, DbTypeTextFull = col.Attribute.DbType, MaxLength = col.DbSize });
|
||||||
|
switch (dbtype2)
|
||||||
|
{
|
||||||
|
case DbType.Binary:
|
||||||
|
default:
|
||||||
|
dbtype = dbtype2;
|
||||||
|
//if (col.DbSize != 0) ret.Size = col.DbSize;
|
||||||
|
if (col.DbPrecision != 0) ret.Precision = col.DbPrecision;
|
||||||
|
if (col.DbScale != 0) ret.Scale = col.DbScale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.DbType = dbtype;
|
||||||
|
_params?.Add(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||||
|
Utils.GetDbParamtersByObject<DbParameter>(sql, obj, "?", (name, type, value) =>
|
||||||
|
{
|
||||||
|
DbParameter ret = new ClickHouseDbParameter { ParameterName = $"?{name}", Value = value };
|
||||||
|
var tp = _orm.CodeFirst.GetDbInfo(type)?.type;
|
||||||
|
if (tp != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
ret.DbType = (DbType)tp.Value;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
public override string RewriteColumn(ColumnInfo col, string sql)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(col?.Attribute.RewriteSql) == false)
|
||||||
|
return string.Format(col.Attribute.RewriteSql, sql);
|
||||||
|
return string.Format(sql, col.CsType.Name);
|
||||||
|
}
|
||||||
|
public override string FormatSql(string sql, params object[] args) => sql?.FormatClickHouse(args);
|
||||||
|
public override string QuoteSqlName(params string[] name)
|
||||||
|
{
|
||||||
|
if (name.Length == 1)
|
||||||
|
{
|
||||||
|
var nametrim = name[0].Trim();
|
||||||
|
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||||
|
return nametrim; //原生SQL
|
||||||
|
if (nametrim.StartsWith("`") && nametrim.EndsWith("`"))
|
||||||
|
return nametrim;
|
||||||
|
return $"`{nametrim.Replace(".", "`.`")}`";
|
||||||
|
}
|
||||||
|
return $"`{string.Join("`.`", name)}`";
|
||||||
|
}
|
||||||
|
public override string TrimQuoteSqlName(string name)
|
||||||
|
{
|
||||||
|
var nametrim = name.Trim();
|
||||||
|
if (nametrim.StartsWith("(") && nametrim.EndsWith(")"))
|
||||||
|
return nametrim; //原生SQL
|
||||||
|
return $"{nametrim.Trim('`').Replace("`.`", ".").Replace(".`", ".")}";
|
||||||
|
}
|
||||||
|
public override string[] SplitTableName(string name) => GetSplitTableNames(name, '`', '`', 2);
|
||||||
|
public override string QuoteParamterName(string name) => $"{{{{{name}:{{0}}}}}}";
|
||||||
|
public override string IsNull(string sql, object value) => $"ifnull({sql}, {value})";
|
||||||
|
public override string StringConcat(string[] objs, Type[] types) => $"concat({string.Join(", ", objs)})";
|
||||||
|
public override string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}";
|
||||||
|
public override string Div(string left, string right, Type leftType, Type rightType) => $"{left} div {right}";
|
||||||
|
public override string Now => "now()";
|
||||||
|
public override string NowUtc => "now('UTC')";
|
||||||
|
|
||||||
|
public override string QuoteWriteParamterAdapter(Type type, string paramterName)
|
||||||
|
{
|
||||||
|
switch (type.FullName)
|
||||||
|
{
|
||||||
|
case "MygisPoint":
|
||||||
|
case "MygisLineString":
|
||||||
|
case "MygisPolygon":
|
||||||
|
case "MygisMultiPoint":
|
||||||
|
case "MygisMultiLineString":
|
||||||
|
case "MygisMultiPolygon": return $"ST_GeomFromText({paramterName})";
|
||||||
|
}
|
||||||
|
return paramterName;
|
||||||
|
}
|
||||||
|
protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName)
|
||||||
|
{
|
||||||
|
switch (mapType.FullName)
|
||||||
|
{
|
||||||
|
case "MygisPoint":
|
||||||
|
case "MygisLineString":
|
||||||
|
case "MygisPolygon":
|
||||||
|
case "MygisMultiPoint":
|
||||||
|
case "MygisMultiLineString":
|
||||||
|
case "MygisMultiPolygon": return $"ST_AsText({columnName})";
|
||||||
|
}
|
||||||
|
return columnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetNoneParamaterSqlValue(List<DbParameter> 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[])}";
|
||||||
|
if (type == typeof(TimeSpan) || type == typeof(TimeSpan?))
|
||||||
|
{
|
||||||
|
var ts = (TimeSpan)value;
|
||||||
|
value = $"{Math.Floor(ts.TotalHours)}:{ts.Minutes}:{ts.Seconds}";
|
||||||
|
}
|
||||||
|
return FormatSql("{0}", value, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse.Curd
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClickHouseDelete<T1> : Internal.CommonProvider.DeleteProvider<T1>
|
||||||
|
{
|
||||||
|
public ClickHouseDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||||
|
: base(orm, commonUtils, commonExpression, dywhere)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<T1> ExecuteDeleted()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("FreeSql.ClickHouse.Curd 暂未实现");
|
||||||
|
}
|
||||||
|
public override string ToSql()
|
||||||
|
{
|
||||||
|
return base.ToSql().Replace("DELETE FROM", "ALTER TABLE").Replace("WHERE", "DELETE WHERE");
|
||||||
|
}
|
||||||
|
#if net40
|
||||||
|
#else
|
||||||
|
public override Task<List<T1>> ExecuteDeletedAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("FreeSql.ClickHouse.Curd 暂未实现");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
178
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
Normal file
178
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse.Curd
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClickHouseInsert<T1> : Internal.CommonProvider.InsertProvider<T1> where T1 : class
|
||||||
|
{
|
||||||
|
public ClickHouseInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression)
|
||||||
|
: base(orm, commonUtils, commonExpression)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool InternalIsIgnoreInto = false;
|
||||||
|
internal IFreeSql InternalOrm => _orm;
|
||||||
|
internal TableInfo InternalTable => _table;
|
||||||
|
internal DbParameter[] InternalParams => _params;
|
||||||
|
internal DbConnection InternalConnection => _connection;
|
||||||
|
internal DbTransaction InternalTransaction => _transaction;
|
||||||
|
internal CommonUtils InternalCommonUtils => _commonUtils;
|
||||||
|
internal CommonExpression InternalCommonExpression => _commonExpression;
|
||||||
|
internal List<T1> InternalSource => _source;
|
||||||
|
internal Dictionary<string, bool> InternalIgnore => _ignore;
|
||||||
|
internal void InternalClearData() => ClearData();
|
||||||
|
|
||||||
|
public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||||
|
public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||||
|
public override List<T1> ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToSql()
|
||||||
|
{
|
||||||
|
if (InternalIsIgnoreInto == false) return base.ToSqlValuesOrSelectUnionAll();
|
||||||
|
var sql = base.ToSqlValuesOrSelectUnionAll();
|
||||||
|
return $"INSERT IGNORE INTO {sql.Substring(12)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override long RawExecuteIdentity()
|
||||||
|
{
|
||||||
|
var sql = this.ToSql();
|
||||||
|
if (string.IsNullOrEmpty(sql)) return 0;
|
||||||
|
|
||||||
|
sql = string.Concat(sql, "; SELECT LAST_INSERT_ID();");
|
||||||
|
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||||
|
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||||
|
long ret = 0;
|
||||||
|
Exception exception = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params)), out var trylng) ? trylng : 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||||
|
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
protected override List<T1> RawExecuteInserted()
|
||||||
|
{
|
||||||
|
var sql = this.ToSql();
|
||||||
|
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append(sql).Append(" RETURNING ");
|
||||||
|
|
||||||
|
var colidx = 0;
|
||||||
|
foreach (var col in _table.Columns.Values)
|
||||||
|
{
|
||||||
|
if (colidx > 0) sb.Append(", ");
|
||||||
|
sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||||
|
++colidx;
|
||||||
|
}
|
||||||
|
sql = sb.ToString();
|
||||||
|
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||||
|
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||||
|
var ret = new List<T1>();
|
||||||
|
Exception exception = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = _orm.Ado.Query<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||||
|
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if net40
|
||||||
|
#else
|
||||||
|
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||||
|
public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||||
|
public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : 5000, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||||
|
|
||||||
|
async protected override Task<long> RawExecuteIdentityAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var sql = this.ToSql();
|
||||||
|
if (string.IsNullOrEmpty(sql)) return 0;
|
||||||
|
|
||||||
|
sql = string.Concat(sql, "; SELECT LAST_INSERT_ID();");
|
||||||
|
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||||
|
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||||
|
long ret = 0;
|
||||||
|
Exception exception = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken)), out var trylng) ? trylng : 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||||
|
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
async protected override Task<List<T1>> RawExecuteInsertedAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var sql = this.ToSql();
|
||||||
|
if (string.IsNullOrEmpty(sql)) return new List<T1>();
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.Append(sql).Append(" RETURNING ");
|
||||||
|
|
||||||
|
var colidx = 0;
|
||||||
|
foreach (var col in _table.Columns.Values)
|
||||||
|
{
|
||||||
|
if (colidx > 0) sb.Append(", ");
|
||||||
|
sb.Append(_commonUtils.RereadColumn(col, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName));
|
||||||
|
++colidx;
|
||||||
|
}
|
||||||
|
sql = sb.ToString();
|
||||||
|
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, sql, _params);
|
||||||
|
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||||
|
var ret = new List<T1>();
|
||||||
|
Exception exception = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = await _orm.Ado.QueryAsync<T1>(_table.TypeLazy ?? _table.Type, _connection, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var after = new Aop.CurdAfterEventArgs(before, exception, ret);
|
||||||
|
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
210
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs
Normal file
210
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse.Curd
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClickHouseSelect<T1> : FreeSql.Internal.CommonProvider.Select1Provider<T1>
|
||||||
|
{
|
||||||
|
|
||||||
|
internal static string ToSqlStatic(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List<SelectTableInfo> _tables, List<Dictionary<Type, string>> tbUnions, Func<Type, string, string> _aliasRule, string _tosqlAppendContent, List<GlobalFilter.Item> _whereGlobalFilter, IFreeSql _orm)
|
||||||
|
{
|
||||||
|
if (_orm.CodeFirst.IsAutoSyncStructure)
|
||||||
|
_orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray());
|
||||||
|
|
||||||
|
if (_whereGlobalFilter.Any())
|
||||||
|
foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent))
|
||||||
|
tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter, true);
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var tbUnionsGt0 = tbUnions.Count > 1;
|
||||||
|
for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++)
|
||||||
|
{
|
||||||
|
if (tbUnionsIdx > 0) sb.Append("\r\n \r\nUNION ALL\r\n \r\n");
|
||||||
|
if (tbUnionsGt0) sb.Append(_select).Append(" * from (");
|
||||||
|
var tbUnion = tbUnions[tbUnionsIdx];
|
||||||
|
|
||||||
|
var sbnav = new StringBuilder();
|
||||||
|
sb.Append(_select);
|
||||||
|
if (_distinct) sb.Append("DISTINCT ");
|
||||||
|
sb.Append(field).Append(" \r\nFROM ");
|
||||||
|
var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray();
|
||||||
|
var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray();
|
||||||
|
for (var a = 0; a < tbsfrom.Length; a++)
|
||||||
|
{
|
||||||
|
sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias);
|
||||||
|
if (tbsjoin.Length > 0)
|
||||||
|
{
|
||||||
|
//如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
|
||||||
|
for (var b = 1; b < tbsfrom.Length; b++)
|
||||||
|
{
|
||||||
|
sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On;
|
||||||
|
sb.Append(" ON ").Append(onSql);
|
||||||
|
if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade);
|
||||||
|
else sb.Append(" AND ").Append(tbsfrom[b].Cascade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")");
|
||||||
|
if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")");
|
||||||
|
if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND ").Append(tbsfrom[a].Cascade);
|
||||||
|
}
|
||||||
|
if (a < tbsfrom.Length - 1) sb.Append(", ");
|
||||||
|
}
|
||||||
|
foreach (var tb in tbsjoin)
|
||||||
|
{
|
||||||
|
if (tb.Type == SelectTableInfoType.Parent) continue;
|
||||||
|
switch (tb.Type)
|
||||||
|
{
|
||||||
|
case SelectTableInfoType.LeftJoin:
|
||||||
|
sb.Append(" \r\nLEFT JOIN ");
|
||||||
|
break;
|
||||||
|
case SelectTableInfoType.InnerJoin:
|
||||||
|
sb.Append(" \r\nINNER JOIN ");
|
||||||
|
break;
|
||||||
|
case SelectTableInfoType.RightJoin:
|
||||||
|
sb.Append(" \r\nRIGHT JOIN ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition);
|
||||||
|
if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND ").Append(tb.Cascade);
|
||||||
|
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||||
|
}
|
||||||
|
if (_join.Length > 0) sb.Append(_join);
|
||||||
|
|
||||||
|
sbnav.Append(_where);
|
||||||
|
if (!string.IsNullOrEmpty(_tables[0].Cascade))
|
||||||
|
sbnav.Append(" AND ").Append(_tables[0].Cascade);
|
||||||
|
|
||||||
|
if (sbnav.Length > 0)
|
||||||
|
{
|
||||||
|
sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5));
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(_groupby) == false)
|
||||||
|
{
|
||||||
|
sb.Append(_groupby);
|
||||||
|
if (string.IsNullOrEmpty(_having) == false)
|
||||||
|
sb.Append(" \r\nHAVING ").Append(_having.Substring(5));
|
||||||
|
}
|
||||||
|
sb.Append(_orderby);
|
||||||
|
if (_skip > 0 || _limit > 0)
|
||||||
|
sb.Append(" \r\nlimit ").Append(Math.Max(0, _skip)).Append(",").Append(_limit > 0 ? _limit : -1);
|
||||||
|
|
||||||
|
sbnav.Clear();
|
||||||
|
if (tbUnionsGt0) sb.Append(") ftb");
|
||||||
|
}
|
||||||
|
return sb.Append(_tosqlAppendContent).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClickHouseSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override ISelect<T1, T2> From<T2>(Expression<Func<ISelectFromExpression<T1>, T2, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3> From<T2, T3>(Expression<Func<ISelectFromExpression<T1>, T2, T3, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4> From<T2, T3, T4>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5> From<T2, T3, T4, T5>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6> From<T2, T3, T4, T5, T6>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7> From<T2, T3, T4, T5, T6, T7>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8> From<T2, T3, T4, T5, T6, T7, T8>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> From<T2, T3, T4, T5, T6, T7, T8, T9>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> From<T2, T3, T4, T5, T6, T7, T8, T9, T10>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override ISelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> From<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(Expression<Func<ISelectFromExpression<T1>, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ISelectFromExpression<T1>>> exp) { this.InternalFrom(exp); var ret = new MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(_orm, _commonUtils, _commonExpression, null); ClickHouseSelect<T1>.CopyData(this, ret, exp?.Parameters); return ret; }
|
||||||
|
public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T2 : class where T3 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T2 : class where T3 : class where T4 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5> : FreeSql.Internal.CommonProvider.Select5Provider<T1, T2, T3, T4, T5> where T2 : class where T3 : class where T4 : class where T5 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6> : FreeSql.Internal.CommonProvider.Select6Provider<T1, T2, T3, T4, T5, T6> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7> : FreeSql.Internal.CommonProvider.Select7Provider<T1, T2, T3, T4, T5, T6, T7> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8> : FreeSql.Internal.CommonProvider.Select8Provider<T1, T2, T3, T4, T5, T6, T7, T8> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9> : FreeSql.Internal.CommonProvider.Select9Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : FreeSql.Internal.CommonProvider.Select10Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> : FreeSql.Internal.CommonProvider.Select11Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> : FreeSql.Internal.CommonProvider.Select12Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> : FreeSql.Internal.CommonProvider.Select13Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> : FreeSql.Internal.CommonProvider.Select14Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> : FreeSql.Internal.CommonProvider.Select15Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
class MySqlSelect<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : FreeSql.Internal.CommonProvider.Select16Provider<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class where T16 : class
|
||||||
|
{
|
||||||
|
public MySqlSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { }
|
||||||
|
public override string ToSql(string field = null) => ClickHouseSelect<T1>.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
using FreeSql.Internal;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FreeSql.ClickHouse.Curd
|
||||||
|
{
|
||||||
|
|
||||||
|
class ClickHouseUpdate<T1> : Internal.CommonProvider.UpdateProvider<T1>
|
||||||
|
{
|
||||||
|
|
||||||
|
public ClickHouseUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere)
|
||||||
|
: base(orm, commonUtils, commonExpression, dywhere)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal StringBuilder InternalSbSet => _set;
|
||||||
|
internal StringBuilder InternalSbSetIncr => _setIncr;
|
||||||
|
internal Dictionary<string, bool> InternalIgnore => _ignore;
|
||||||
|
public override string ToSql()
|
||||||
|
{
|
||||||
|
return base.ToSql().Replace("UPDATE", "ALTER TABLE").Replace("SET", "UPDATE");
|
||||||
|
}
|
||||||
|
internal void InternalResetSource(List<T1> source) => _source = source;
|
||||||
|
internal string InternalWhereCaseSource(string CsName, Func<string, string> thenValue) => WhereCaseSource(CsName, thenValue);
|
||||||
|
internal void InternalToSqlCaseWhenEnd(StringBuilder sb, ColumnInfo col) => ToSqlCaseWhenEnd(sb, col);
|
||||||
|
|
||||||
|
public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||||
|
public override List<T1> ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||||
|
protected override List<T1> RawExecuteUpdated() => throw new NotImplementedException("FreeSql.ClickHouse.Custom 未实现该功能 未实现该功能");
|
||||||
|
protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys)
|
||||||
|
{
|
||||||
|
if (primarys.Length == 1)
|
||||||
|
{
|
||||||
|
var pk = primarys.First();
|
||||||
|
caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
caseWhen.Append("CONCAT(");
|
||||||
|
var pkidx = 0;
|
||||||
|
foreach (var pk in primarys)
|
||||||
|
{
|
||||||
|
if (pkidx > 0) caseWhen.Append(", '+', ");
|
||||||
|
caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)));
|
||||||
|
++pkidx;
|
||||||
|
}
|
||||||
|
caseWhen.Append(")");
|
||||||
|
}
|
||||||
|
protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d)
|
||||||
|
{
|
||||||
|
if (primarys.Length == 1)
|
||||||
|
{
|
||||||
|
sb.Append(_commonUtils.FormatSql("{0}", primarys[0].GetDbValue(d)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sb.Append("concat(");
|
||||||
|
var pkidx = 0;
|
||||||
|
foreach (var pk in primarys)
|
||||||
|
{
|
||||||
|
if (pkidx > 0) sb.Append(", '+', ");
|
||||||
|
sb.Append(_commonUtils.FormatSql("{0}", pk.GetDbValue(d)));
|
||||||
|
++pkidx;
|
||||||
|
}
|
||||||
|
sb.Append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if net40
|
||||||
|
#else
|
||||||
|
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||||
|
public override Task<List<T1>> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||||
|
protected override Task<List<T1>> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException("FreeSql.ClickHouse.Custom 未实现该功能 未实现该功能");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>netstandard2.1</TargetFrameworks>
|
||||||
|
<Version>2.6.100</Version>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<Authors>FreeSql;ncc;YeXiangQin</Authors>
|
||||||
|
<Description>FreeSql 数据库自定义适配,访问所有数据库</Description>
|
||||||
|
<PackageProjectUrl>https://github.com/2881099/FreeSql</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/2881099/FreeSql</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<PackageTags>FreeSql;ORM</PackageTags>
|
||||||
|
<PackageId>$(AssemblyName)</PackageId>
|
||||||
|
<PackageIcon>logo.png</PackageIcon>
|
||||||
|
<Title>$(AssemblyName)</Title>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
|
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||||
|
<SignAssembly>False</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<DelaySign>false</DelaySign>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="../../logo.png" Pack="true" PackagePath="\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ClickHouse.Client">
|
||||||
|
<Version>4.0.1.462</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user