mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-08-02 09:55:57 +08:00
initial commit
This commit is contained in:
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.DataAnnotations
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class ClickHousePartitionAttribute : Attribute
|
||||
{
|
||||
public ClickHousePartitionAttribute(string format = "toYYYYMM({0})")
|
||||
{
|
||||
Format = format;
|
||||
}
|
||||
|
||||
public string Format { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
using ClickHouse.Client.ADO;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.Internal.Model;
|
||||
using FreeSql.Internal.ObjectPool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
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.ClickHouse, masterConnectionString, slaveConnectionStrings)
|
||||
{
|
||||
base._util = util;
|
||||
if (connectionFactory != null)
|
||||
{
|
||||
var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.ClickHouse, connectionFactory);
|
||||
ConnectionString = pool.TestConnection?.ConnectionString;
|
||||
MasterPool = pool;
|
||||
return;
|
||||
}
|
||||
|
||||
var isAdoPool = masterConnectionString?.StartsWith("AdoConnectionPool,") ?? false;
|
||||
if (isAdoPool) masterConnectionString = masterConnectionString.Substring("AdoConnectionPool,".Length);
|
||||
if (!string.IsNullOrEmpty(masterConnectionString))
|
||||
MasterPool = isAdoPool ?
|
||||
new DbConnectionStringPool(base.DataType, CoreStrings.S_MasterDatabase, () => new ClickHouseConnection(masterConnectionString)) as IObjectPool<DbConnection> :
|
||||
new ClickHouseConnectionPool(CoreStrings.S_MasterDatabase, masterConnectionString, null, null);
|
||||
|
||||
slaveConnectionStrings?.ToList().ForEach(slaveConnectionString =>
|
||||
{
|
||||
var slavePool = isAdoPool ?
|
||||
new DbConnectionStringPool(base.DataType, $"{CoreStrings.S_SlaveDatabase}{SlavePools.Count + 1}", () => new ClickHouseConnection(slaveConnectionString)) as IObjectPool<DbConnection> :
|
||||
new ClickHouseConnectionPool($"{CoreStrings.S_SlaveDatabase}{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 ? "true" : "false"; //不需要转0/1
|
||||
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 AddslashesTypeHandler(param.GetType(), param) ?? 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)
|
||||
return AddslashesTypeHandler(typeof(DateTime), param) ?? string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss"), "'");
|
||||
else if (param is DateTime?)
|
||||
return AddslashesTypeHandler(typeof(DateTime?), param) ?? string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss"), "'");
|
||||
|
||||
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,250 @@
|
||||
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)
|
||||
{
|
||||
if (obj.Value.Ping() == false)
|
||||
base.SetUnavailable(exception, obj.LastGetTimeCopy);
|
||||
}
|
||||
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 {CoreStrings.S_ObjectPool}";
|
||||
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; } = 2;
|
||||
public int Weight { get; set; } = 1;
|
||||
|
||||
static ConcurrentDictionary<string, int> dicConnStrIncr = new ConcurrentDictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
|
||||
private string _connectionString;
|
||||
public string ConnectionString
|
||||
{
|
||||
get => _connectionString;
|
||||
set
|
||||
{
|
||||
_connectionString = value ?? "";
|
||||
|
||||
var minPoolSize = 0;
|
||||
var pattern = @"Min\s*pool\s*size\s*=\s*(\d+)";
|
||||
var m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
{
|
||||
minPoolSize = int.Parse(m.Groups[1].Value);
|
||||
_connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
pattern = @"Max\s*pool\s*size\s*=\s*(\d+)";
|
||||
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);
|
||||
}
|
||||
|
||||
FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(_pool, minPoolSize);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnCheckAvailable(Object<DbConnection> obj)
|
||||
{
|
||||
if (obj.Value == null) return false;
|
||||
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)
|
||||
{
|
||||
_pool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy);
|
||||
throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name));
|
||||
}
|
||||
|
||||
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, obj.LastGetTimeCopy) == true)
|
||||
throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
async public Task OnGetAsync(Object<DbConnection> obj)
|
||||
{
|
||||
|
||||
if (_pool.IsAvailable)
|
||||
{
|
||||
if (obj.Value == null)
|
||||
{
|
||||
_pool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy);
|
||||
throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name));
|
||||
}
|
||||
|
||||
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, obj.LastGetTimeCopy) == true)
|
||||
throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#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
|
||||
}
|
||||
}
|
681
Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs
Normal file
681
Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs
Normal file
@ -0,0 +1,681 @@
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Data.Common;
|
||||
using System.Reflection;
|
||||
using FreeSql.Internal.ObjectPool;
|
||||
using ClickHouse.Client.ADO;
|
||||
using FreeSql.DataAnnotations;
|
||||
|
||||
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, "Bool", "Bool", null, false, false) },
|
||||
{ typeof(bool?).FullName, CsToDb.New(DbType.SByte, "Bool", "Nullable(Bool)", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", false, false, 0) },
|
||||
{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", false, true, null) },
|
||||
{ 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(decimal).FullName,
|
||||
CsToDb.New(DbType.Decimal, "Decimal(38, 19)", "Decimal(38, 19)", false, false,
|
||||
0) //Nullable(Decimal(38, 19))
|
||||
},
|
||||
{
|
||||
typeof(decimal?).FullName,
|
||||
CsToDb.New(DbType.Decimal, "Nullable(Decimal(38, 19))", "Nullable(Decimal(38, 19))", 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);
|
||||
|
||||
//判断是否是集合
|
||||
var isCollection = IsArray(type);
|
||||
if (isCollection.Item1)
|
||||
{
|
||||
var genericType = isCollection.Item2;
|
||||
var genericTypeName = genericType?.FullName;
|
||||
var tryGetValue = _dicCsToDb.TryGetValue(genericTypeName, out var value);
|
||||
if (tryGetValue)
|
||||
{
|
||||
var arrayDbType = $"Array({value.dbtype})";
|
||||
var defaultArray = new ArrayList(0);
|
||||
return new DbInfoResult(Convert.ToInt32(DbType.Object), arrayDbType, arrayDbType, false, defaultArray);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Tuple<bool, Type> IsArray(Type type)
|
||||
{
|
||||
var flag = false;
|
||||
Type resultType = null;
|
||||
|
||||
if (type.IsArray)
|
||||
{
|
||||
flag = true;
|
||||
resultType = type.GetElementType();
|
||||
}
|
||||
|
||||
return new Tuple<bool, Type>(flag, resultType);
|
||||
}
|
||||
|
||||
private Tuple<bool, Type> IsCollection(Type type)
|
||||
{
|
||||
var flag = false;
|
||||
Type resultType = null;
|
||||
var interfaces = type.GetInterfaces();
|
||||
|
||||
if (interfaces.Any(t => t.Name == "IList"))
|
||||
flag = true;
|
||||
|
||||
if (interfaces.Any(t => t.Name == "ICollection"))
|
||||
flag = true;
|
||||
|
||||
if (interfaces.Any(t => t.Name == "IEnumerable"))
|
||||
flag = true;
|
||||
|
||||
if (type.Name == "Array")
|
||||
{
|
||||
flag = true;
|
||||
resultType = typeof(string);
|
||||
}
|
||||
|
||||
if (type.Name == "ArrayList")
|
||||
{
|
||||
flag = true;
|
||||
resultType = typeof(string);
|
||||
}
|
||||
|
||||
//是否是泛型
|
||||
if (type.GetGenericArguments().Any())
|
||||
{
|
||||
var first = type.GetGenericArguments().First();
|
||||
resultType = first;
|
||||
}
|
||||
|
||||
return new Tuple<bool, Type>(flag, resultType);
|
||||
}
|
||||
|
||||
protected override string GetComparisonDDLStatements(params TypeSchemaAndName[] 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 = obj.tableSchema;
|
||||
if (tb == null)
|
||||
throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.tableSchema.Type.FullName));
|
||||
if (tb.Columns.Any() == false)
|
||||
throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.tableSchema.Type.FullName));
|
||||
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 system.databases d where name={0}", tbname[0])) ==
|
||||
null) //创建数据库
|
||||
sb.Append($"CREATE DATABASE IF NOT EXISTS ").Append(_commonUtils.QuoteSqlName(tbname[0]))
|
||||
.Append(" ENGINE=Ordinary;\r\n");
|
||||
|
||||
var sbalter = new StringBuilder();
|
||||
var istmpatler = false; //创建临时表,导入数据,删除旧表,修改
|
||||
if (LocalExecuteScalar(tbname[0],
|
||||
_commonUtils.FormatSql(" SELECT 1 FROM system.tables t WHERE database ={0} and name ={1}",
|
||||
tbname)) == null)
|
||||
{
|
||||
//表不存在
|
||||
if (tboldname != null)
|
||||
{
|
||||
if (string.Compare(tboldname[0], tbname[0], true) != 0 && LocalExecuteScalar(database,
|
||||
_commonUtils.FormatSql(" select 1 from system.databases where name={0}",
|
||||
tboldname[0])) == null ||
|
||||
LocalExecuteScalar(tboldname[0],
|
||||
_commonUtils.FormatSql(
|
||||
" SELECT 1 FROM system.tables WHERE database={0} and 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)
|
||||
{
|
||||
//如果这里是主键就是不为Nullable
|
||||
tbcol.Attribute.DbType =
|
||||
CkNullableAdapter(tbcol.Attribute.DbType, tbcol.Attribute.IsPrimary);
|
||||
tbcol.Attribute.DbType = CkIntAdapter(tbcol.Attribute.DbType);
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ")
|
||||
.Append(tbcol.Attribute.DbType);
|
||||
if (string.IsNullOrEmpty(tbcol.Comment) == false)
|
||||
sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment));
|
||||
sb.Append(",");
|
||||
}
|
||||
|
||||
foreach (var uk in tb.Indexes)
|
||||
{
|
||||
sb.Append(" \r\n ");
|
||||
sb.Append("INDEX ")
|
||||
.Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1])));
|
||||
|
||||
sb.Append(" (");
|
||||
foreach (var tbcol in uk.Columns)
|
||||
{
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||
sb.Append(", ");
|
||||
}
|
||||
|
||||
sb.Remove(sb.Length - 2, 2).Append(") ");
|
||||
sb.Append("TYPE set(8192) GRANULARITY 5,");
|
||||
}
|
||||
|
||||
//sb.Remove(sb.Length - 1, 1);
|
||||
|
||||
if (tb.Primarys.Any())
|
||||
{
|
||||
var primaryKeys = string.Join(",",
|
||||
tb.Primarys.Select(p => _commonUtils.QuoteSqlName(p.Attribute.Name)));
|
||||
sb.Append(" \r\n PRIMARY KEY ");
|
||||
sb.Append($"( {primaryKeys} ) ");
|
||||
}
|
||||
|
||||
sb.Append("\r\n) ");
|
||||
sb.Append("\r\nENGINE = MergeTree()");
|
||||
|
||||
if (tb.Primarys.Any())
|
||||
{
|
||||
var primaryKeys = string.Join(",",
|
||||
tb.Primarys.Select(p => _commonUtils.QuoteSqlName(p.Attribute.Name)));
|
||||
sb.Append(" \r\nORDER BY ");
|
||||
sb.Append($"( {primaryKeys} ) ");
|
||||
}
|
||||
|
||||
//查找属性是否标记了分区特性
|
||||
var partitionColumnInfos = tb.Properties.Where(
|
||||
c => c.Value.GetCustomAttribute<ClickHousePartitionAttribute>() != null).ToList();
|
||||
|
||||
if (partitionColumnInfos != null && partitionColumnInfos.Any())
|
||||
{
|
||||
var partitionProperty = partitionColumnInfos.First();
|
||||
|
||||
var partitionColumnInfo = tb.Columns.FirstOrDefault(pair =>
|
||||
pair.Value.CsName == partitionProperty.Value.Name);
|
||||
var partitionName = _commonUtils.QuoteSqlName(partitionColumnInfo.Value.Attribute.Name);
|
||||
var partitionAttribute = partitionProperty.Value
|
||||
.GetCustomAttribute<ClickHousePartitionAttribute>();
|
||||
sb.Append($" \r\nPARTITION BY {string.Format(partitionAttribute.Format, partitionName)}");
|
||||
}
|
||||
|
||||
|
||||
//if (string.IsNullOrEmpty(tb.Comment) == false)
|
||||
// sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment));
|
||||
sb.Append(" \r\nSETTINGS index_granularity = 8192;\r\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果新表,旧表在一个数据库下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||
sbalter.Append("RENAME TABLE ")
|
||||
.Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1]))
|
||||
.Append(" TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1]))
|
||||
.Append(";\r\n");
|
||||
else
|
||||
{
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
istmpatler = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
tboldname = null; //如果新表已经存在,不走改表名逻辑
|
||||
|
||||
//对比字段,只可以修改类型、增加字段、有限的修改字段名;保证安全不删除字段
|
||||
var sql = _commonUtils.FormatSql(@"
|
||||
select
|
||||
a.name,
|
||||
a.type,
|
||||
if(ilike(a.`type`, 'Nullable(%S%)'),'is_nullable','0'),
|
||||
a.comment as comment,
|
||||
a.is_in_partition_key,
|
||||
a.is_in_sorting_key,
|
||||
a.is_in_primary_key,
|
||||
a.is_in_sampling_key
|
||||
from system.columns a
|
||||
where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
var ds = _orm.Ado.ExecuteArray(CommandType.Text, sql);
|
||||
var tbstruct = ds.ToDictionary(a => string.Concat(a[0]), a =>
|
||||
{
|
||||
return new
|
||||
{
|
||||
column = string.Concat(a[0]),
|
||||
sqlType = (string)a[1],
|
||||
is_nullable = a[1]?.ToString().Contains("Nullable"),
|
||||
is_identity = false,
|
||||
comment = string.Concat(a[3]),
|
||||
is_primary = string.Concat(a[6]) == "1",
|
||||
};
|
||||
}, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
|
||||
if (istmpatler == false)
|
||||
{
|
||||
var existsPrimary = tbstruct.Any(o => o.Value.is_primary);
|
||||
//对比列
|
||||
foreach (var tbcol in tb.ColumnsByPosition)
|
||||
{
|
||||
//表中有这个字段
|
||||
var condition1 = tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol);
|
||||
var condition2 = string.IsNullOrEmpty(tbcol.Attribute.OldName) == false;
|
||||
if (condition1 ||
|
||||
condition2 && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
|
||||
{
|
||||
var isCommentChanged = tbstructcol.comment != (tbcol.Comment ?? "");
|
||||
var ckDbType = CkNullableAdapter(tbcol.Attribute.DbType, tbcol.Attribute.IsPrimary);
|
||||
ckDbType = CkIntAdapter(ckDbType);
|
||||
var typeCondition1 = RemoveSpaceComparison(tbstructcol.sqlType, ckDbType) == false;
|
||||
var typeCondition2 = tbcol.Attribute.IsNullable != tbstructcol.is_nullable;
|
||||
if (typeCondition1 || typeCondition1 || isCommentChanged)
|
||||
{
|
||||
sbalter.Append("ALTER TABLE ")
|
||||
.Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}"))
|
||||
.Append(" MODIFY COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column))
|
||||
.Append(tbcol.Attribute.IsNullable &&
|
||||
tbcol.Attribute.DbType.Contains("Nullable") == false
|
||||
? $"Nullable({tbcol.Attribute.DbType.Split(' ').First()})"
|
||||
: tbcol.Attribute.DbType.Split(' ').First())
|
||||
.Append(";\r\n");
|
||||
}
|
||||
|
||||
if (isCommentChanged)
|
||||
sbalter.Append("ALTER TABLE ")
|
||||
.Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}"))
|
||||
.Append(" COMMENT COLUMN ")
|
||||
.Append(_commonUtils.QuoteSqlName(tbstructcol.column))
|
||||
.Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? "")).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(" RENAME COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column))
|
||||
.Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name))
|
||||
.Append(";\r\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
//添加列
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1]))
|
||||
.Append(" ADD Column ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name))
|
||||
.Append(" ")
|
||||
.Append(tbcol.Attribute.DbType);
|
||||
if (tbcol.Attribute.IsNullable == false && tbcol.DbDefaultValue != "NULL" &&
|
||||
tbcol.Attribute.IsIdentity == false)
|
||||
sbalter.Append(" DEFAULT ").Append(tbcol.DbDefaultValue);
|
||||
if (string.IsNullOrEmpty(tbcol.Comment) == false)
|
||||
sbalter.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment ?? ""));
|
||||
sbalter.Append(";\r\n");
|
||||
}
|
||||
|
||||
var indexSelectSql = _commonUtils.FormatSql(
|
||||
@"SELECT name,expr FROM system.data_skipping_indices WHERE database={0}",
|
||||
tboldname ?? tbname);
|
||||
var indexCollect = _orm.Ado.Query<ClickHouseTableIndex>(CommandType.Text, indexSelectSql);
|
||||
//对比索引
|
||||
foreach (var uk in tb.Indexes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(uk.Name) || uk.Columns.Any() == false)
|
||||
continue;
|
||||
var ukname = ReplaceIndexName(uk.Name, tbname[1]);
|
||||
//先判断表中有没此字段的索引
|
||||
if (indexCollect.Any(c =>
|
||||
RemoveSpaceComparison(c.expr,
|
||||
string.Join(",", uk.Columns.Select(i => i.Column.CsName)))))
|
||||
{
|
||||
//有这个字段的索引,但是名称不一样 修改名 , ClickHouse不支持修改列
|
||||
//if (!indexCollect.Where(c => c.name == uk.Name).Any())
|
||||
//{
|
||||
//
|
||||
// sbalter.Append("ALTER TABLE ")
|
||||
// .Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1]))
|
||||
// .Append(" DROP INDEX ").Append(ukname).Append(" ").Append(";\r\n");
|
||||
|
||||
// //添加
|
||||
// sbalter.Append("ALTER TABLE ")
|
||||
// .Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1]))
|
||||
// .Append(" ADD INDEX ").Append(ukname).Append(" (");
|
||||
// foreach (var tbcol in uk.Columns)
|
||||
// {
|
||||
// sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||
// sbalter.Append(", ");
|
||||
// }
|
||||
|
||||
// sbalter.Remove(sbalter.Length - 2, 2).Append(") TYPE set(8192) GRANULARITY 5")
|
||||
// .Append(";\r\n");
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
//创建索引
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1]))
|
||||
.Append(" ADD INDEX ").Append(ukname).Append(" (");
|
||||
foreach (var tbcol in uk.Columns)
|
||||
{
|
||||
sbalter.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||
sbalter.Append(", ");
|
||||
}
|
||||
|
||||
sbalter.Remove(sbalter.Length - 2, 2).Append(") TYPE set(8192) GRANULARITY 5")
|
||||
.Append(";\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (istmpatler == false)
|
||||
{
|
||||
sb.Append(sbalter);
|
||||
Console.WriteLine(sb.ToString());
|
||||
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)
|
||||
{
|
||||
tbcol.Attribute.DbType = tbcol.Attribute.DbType.Replace(" NOT NULL", "");
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(" ")
|
||||
.Append(tbcol.Attribute.DbType);
|
||||
if (string.IsNullOrEmpty(tbcol.Comment) == false)
|
||||
sb.Append(" COMMENT ").Append(_commonUtils.FormatSql("{0}", tbcol.Comment));
|
||||
sb.Append(",");
|
||||
}
|
||||
|
||||
foreach (var uk in tb.Indexes)
|
||||
{
|
||||
sb.Append(" \r\n ");
|
||||
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));
|
||||
sb.Append("TYPE set(8192) GRANULARITY 5, ");
|
||||
}
|
||||
|
||||
sb.Remove(sb.Length - 2, 2).Append("),");
|
||||
}
|
||||
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) ");
|
||||
sb.Append("\r\nENGINE = MergeTree()");
|
||||
|
||||
if (tb.Primarys.Any())
|
||||
{
|
||||
var primaryKeys = string.Join(",",
|
||||
tb.Primarys.Select(p => _commonUtils.QuoteSqlName(p.Attribute.Name)));
|
||||
sb.Append(" \r\nORDER BY ( ");
|
||||
sb.Append(primaryKeys);
|
||||
sb.Append(" )");
|
||||
sb.Append(" \r\nPRIMARY KEY ");
|
||||
sb.Append($"({primaryKeys}) ");
|
||||
}
|
||||
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
//if (string.IsNullOrEmpty(tb.Comment) == false)
|
||||
// sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment));
|
||||
sb.Append(" SETTINGS index_granularity = 8192;\r\n");
|
||||
|
||||
sb.Append("INSERT INTO ").Append(tmptablename).Append(" SELECT ");
|
||||
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")
|
||||
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("RENAME TABLE ").Append(tmptablename).Append(" TO ")
|
||||
.Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
|
||||
}
|
||||
|
||||
var res = sb.Length == 0 ? null : sb.ToString();
|
||||
return res;
|
||||
}
|
||||
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;
|
||||
var before = new Aop.CommandBeforeEventArgs(cmd);
|
||||
this._orm?.Aop.CommandBeforeHandler?.Invoke(this._orm, before);
|
||||
Exception afterException = null;
|
||||
try
|
||||
{
|
||||
return cmd.ExecuteScalar();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
afterException = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._orm?.Aop.CommandAfterHandler?.Invoke(this._orm,
|
||||
new Aop.CommandAfterEventArgs(before, afterException, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (string.Compare(database, db) != 0)
|
||||
conn.Value.ChangeDatabase(database);
|
||||
}
|
||||
}
|
||||
|
||||
string CkNullableAdapter(string dbType, bool isPrimary)
|
||||
{
|
||||
if (isPrimary)
|
||||
{
|
||||
if (dbType.Contains("Nullable"))
|
||||
{
|
||||
var res = dbType.Replace("Nullable(", "")
|
||||
.Replace(")", "")
|
||||
.Replace(" NOT NULL", "");
|
||||
return res;
|
||||
}
|
||||
|
||||
return dbType;
|
||||
}
|
||||
|
||||
return dbType.Replace(" NOT NULL", "");
|
||||
}
|
||||
|
||||
|
||||
string CkIntAdapter(string dbType)
|
||||
{
|
||||
var result = dbType;
|
||||
if (dbType.Contains("Array"))
|
||||
return dbType;
|
||||
|
||||
if (dbType.ToLower().Contains("int64"))
|
||||
{
|
||||
if (dbType.Contains("Nullable"))
|
||||
{
|
||||
result = "Nullable(Int64)";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "Int64";
|
||||
}
|
||||
}
|
||||
else if (dbType.ToLower().Contains("int"))
|
||||
{
|
||||
if (dbType.Contains("Nullable"))
|
||||
{
|
||||
result = "Nullable(Int32)";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "Int32";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//去除空格后比较
|
||||
bool RemoveSpaceComparison(string a, string b)
|
||||
{
|
||||
a = Regex.Replace(a, @"\s", "").ToLower();
|
||||
b = Regex.Replace(b, @"\s", "").ToLower();
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
|
||||
public override int ExecuteDDLStatements(string ddl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ddl))
|
||||
return 0;
|
||||
var scripts = ddl.Split(new string[] { ";\r\n" }, StringSplitOptions.None)
|
||||
.Where(a => string.IsNullOrEmpty(a.Trim()) == false).ToArray();
|
||||
|
||||
if (scripts.Any() == false)
|
||||
return 0;
|
||||
|
||||
var affrows = 0;
|
||||
foreach (var script in scripts)
|
||||
affrows += base.ExecuteDDLStatements(script);
|
||||
return affrows;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClickHouseTableIndex
|
||||
{
|
||||
public string name { get; set; }
|
||||
public string expr { get; set; }
|
||||
}
|
||||
}
|
473
Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs
Normal file
473
Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs
Normal file
@ -0,0 +1,473 @@
|
||||
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)
|
||||
{
|
||||
if (column.DbTypeText == "Nullable")
|
||||
{
|
||||
column.DbTypeText = column.DbTypeTextFull;
|
||||
//(?<=\()(\S +)(?=\))
|
||||
}
|
||||
switch (column.DbTypeText?.ToLower())
|
||||
{
|
||||
case "bit":
|
||||
case "tinyint":
|
||||
case "bool":
|
||||
case "sbyte":
|
||||
case "int8":
|
||||
case "nullable(int8)": return DbType.SByte;
|
||||
case "byte":
|
||||
case "uint8":
|
||||
case "nullable(uint8)": return DbType.Byte;
|
||||
case "smallint":
|
||||
case "int16":
|
||||
case "nullable(int16)": return DbType.Int16;
|
||||
case "uint16":
|
||||
case "nullable(uint16)": return DbType.UInt16;
|
||||
case "int32":
|
||||
case "int":
|
||||
case "nullable(int32)": return DbType.Int32;
|
||||
case "uint":
|
||||
case "uint32":
|
||||
case "nullable(uint32)": return DbType.UInt32;
|
||||
case "bigint":
|
||||
case "int64":
|
||||
case "long":
|
||||
case "nullable(int64)": return DbType.Int64;
|
||||
case "uint64":
|
||||
case "ulong":
|
||||
case "nullable(uint64)": return DbType.UInt64;
|
||||
case "real":
|
||||
case "Float64":
|
||||
case "double":
|
||||
case "nullable(float64)": return DbType.Double;
|
||||
case "Float32":
|
||||
case "float":
|
||||
case "nullable(float32)": return DbType.Single;
|
||||
case "decimal":
|
||||
case "decimal128":
|
||||
case "nullable(decimal128)": return DbType.Decimal;
|
||||
case "date":
|
||||
case "nullable(date)": return DbType.Date;
|
||||
case "datetime":
|
||||
case "nullable(datetime)": return DbType.DateTime;
|
||||
case "datetime64":
|
||||
case "nullable(datetime64)": return DbType.DateTime;
|
||||
case "tinytext":
|
||||
case "text":
|
||||
case "mediumtext":
|
||||
case "longtext":
|
||||
case "char":
|
||||
case "string":
|
||||
case "nullable(string)":
|
||||
case "varchar": return DbType.String;
|
||||
default:
|
||||
{
|
||||
if (column.DbTypeText?.ToLower().Contains("datetime") == true)
|
||||
return DbType.DateTime;
|
||||
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.Decimal, new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") },
|
||||
|
||||
{ (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],
|
||||
Comment = 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>();
|
||||
}
|
||||
}
|
||||
}
|
621
Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs
Normal file
621
Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs
Normal file
@ -0,0 +1,621 @@
|
||||
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.Reflection;
|
||||
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 ExpressionConstDateTime(operandExp) ?? $"cast({getExp(operandExp)} as DateTime)";
|
||||
case "System.Decimal": return $"cast({getExp(operandExp)} as Decimal128(19))";
|
||||
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 ExpressionConstDateTime(callExp.Arguments[0]) ?? $"cast({getExp(callExp.Arguments[0])} as DateTime)";
|
||||
case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as Decimal128(19))";
|
||||
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)
|
||||
{
|
||||
if (callExp.Object.Type.NullableTypeOrThis().IsEnum)
|
||||
{
|
||||
tsc.SetMapColumnTmp(null);
|
||||
var oldMapType = tsc.SetMapTypeReturnOld(typeof(string));
|
||||
var enumStr = ExpressionLambdaToSql(callExp.Object, tsc);
|
||||
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
|
||||
return enumStr;
|
||||
}
|
||||
var value = ExpressionGetValue(callExp.Object, out var success);
|
||||
if (success) return formatSql(value, typeof(string), null, 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 = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
|
||||
//tsc.isNotSetMapColumnTmp = true;
|
||||
var left = objExp == null ? null : getExp(objExp);
|
||||
//tsc.isNotSetMapColumnTmp = false;
|
||||
//tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
|
||||
//if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
|
||||
switch (callExp.Method.Name)
|
||||
{
|
||||
case "Count":
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"(case when {left} is null then 0 else length({left}) end)";
|
||||
case "Any":
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"(case when {left} is null then 0 else length({left}) end > 0)";
|
||||
case "Contains":
|
||||
tsc.SetMapColumnTmp(null);
|
||||
var args1 = getExp(callExp.Arguments[argIndex]);
|
||||
var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp);
|
||||
var oldDbParams = objExp?.NodeType == ExpressionType.MemberAccess ? tsc.SetDbParamsReturnOld(null) : null; //#900 UseGenerateCommandParameterWithLambda(true) 子查询 bug、以及 #1173 参数化 bug
|
||||
tsc.isNotSetMapColumnTmp = true;
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
tsc.isNotSetMapColumnTmp = false;
|
||||
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
|
||||
if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams);
|
||||
//判断 in 或 hasAny
|
||||
if (left.StartsWith("array[") && left.EndsWith("]"))
|
||||
return $"({args1}) in ({left.Substring(6, left.Length - 7)})";
|
||||
if (left.StartsWith("(") && left.EndsWith(")")) //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格
|
||||
return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})";
|
||||
if (args1.StartsWith("(") && args1.EndsWith(")")) args1 = $"[{args1.TrimStart('(').TrimEnd(')')}]";
|
||||
else args1 = $"[{args1}]";
|
||||
return $"(hasAny({left}, {args1}))";
|
||||
case "Concat":
|
||||
left = objExp == null ? null : getExp(objExp);
|
||||
if (left.StartsWith("(") || left.EndsWith(")")) left = $"array[{left.TrimStart('(').TrimEnd(')')}]";
|
||||
var right2 = getExp(callExp.Arguments[argIndex]);
|
||||
if (right2.StartsWith("(") || right2.EndsWith(")")) right2 = $"array[{right2.TrimStart('(').TrimEnd(')')}]";
|
||||
return $"(arrayConcat({left} || {right2}))";
|
||||
}
|
||||
}
|
||||
break;
|
||||
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);
|
||||
if ((exp.Expression as MemberExpression)?.Expression.NodeType == ExpressionType.Constant)
|
||||
left = $"toDateTime({left})";
|
||||
|
||||
//IsDate(left);
|
||||
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 bool IsInt(string _string)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_string))
|
||||
return false;
|
||||
int i = 0;
|
||||
return int.TryParse(_string, out i);
|
||||
}
|
||||
public bool IsDate(string date)
|
||||
{
|
||||
if (string.IsNullOrEmpty(date))
|
||||
return true;
|
||||
return DateTime.TryParse(date, out var time);
|
||||
}
|
||||
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":
|
||||
if (exp.Arguments.Count == 1 && exp.Arguments[0].NodeType == ExpressionType.NewArrayInit && exp.Arguments[0] is NewArrayExpression concatNewArrExp)
|
||||
return _common.StringConcat(concatNewArrExp.Expressions.Select(a => getExp(a)).ToArray(), null);
|
||||
return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null);
|
||||
case "Format":
|
||||
if (exp.Arguments[0].NodeType != ExpressionType.Constant) throw new Exception(CoreStrings.Not_Implemented_Expression_ParameterUseConstant(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 $"positionCaseInsensitive({left}, {args0Value}) = 1";
|
||||
if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"concat('%', {args0Value})")}";
|
||||
return $"positionCaseInsensitive({left}, {args0Value}) > 0";
|
||||
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 ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as DateTime)";
|
||||
case "ParseExact":
|
||||
case "TryParse":
|
||||
case "TryParseExact": return ExpressionConstDateTime(exp.Arguments[0]) ?? $"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 ExpressionConstDateTime(exp.Arguments[0]) ?? $"cast({getExp(exp.Arguments[0])} as DateTime)";
|
||||
case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as Decimal128(19))";
|
||||
case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as Float64)";
|
||||
case "ToInt16": return $"cast({getExp(exp.Arguments[0])} as Int16)";
|
||||
case "ToInt32": return $"cast({getExp(exp.Arguments[0])} as Int32)";
|
||||
case "ToInt64": return $"cast({getExp(exp.Arguments[0])} as Int64)";
|
||||
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": return $"cast({getExp(exp.Arguments[0])} as UInt16)";
|
||||
case "ToUInt32": return $"cast({getExp(exp.Arguments[0])} as UInt32)";
|
||||
case "ToUInt64": return $"cast({getExp(exp.Arguments[0])} as UInt64)";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
using FreeSql;
|
||||
using FreeSql.ClickHouse.Curd;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FreeSql.DataAnnotations;
|
||||
|
||||
public static partial class FreeSqlClickHouseGlobalExtensions
|
||||
{
|
||||
/// <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();
|
||||
|
||||
/// <summary>
|
||||
/// Clickhouse limit by
|
||||
/// </summary>
|
||||
public static ISelect<T> LimitBy<T>(this ISelect<T> that, Expression<Func<T, object>> selector, int limit,
|
||||
int offset = 0)
|
||||
{
|
||||
if (limit <= 0 && offset <= 0) return that;
|
||||
var s0p = that as ClickHouseSelect<T>;
|
||||
var oldOrderBy = s0p._orderby;
|
||||
s0p._orderby = "";
|
||||
try
|
||||
{
|
||||
s0p.InternalOrderBy(selector);
|
||||
s0p._limitBy = $"LIMIT {(offset > 0 ? $"{offset}, " : "")}{limit} BY {s0p._orderby}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
s0p._orderby = oldOrderBy;
|
||||
}
|
||||
|
||||
return that;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clickhouse 采样子句
|
||||
/// </summary>
|
||||
public static ISelect<T> Sample<T>(this ISelect<T> that, decimal k, int n, decimal m)
|
||||
{
|
||||
var s0p = that as ClickHouseSelect<T>;
|
||||
if (k > 0)
|
||||
if (m > 0)
|
||||
s0p._sample = $"SAMPLE {k} OFFSET {m}";
|
||||
else
|
||||
s0p._sample = $"SAMPLE {k}";
|
||||
else if (n > 0)
|
||||
s0p._sample = $"SAMPLE {k}";
|
||||
return that;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量快速插入
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="that"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<int> ExecuteClickHouseBulkCopyAsync<T>(this IInsert<T> that) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
var insert = that as ClickHouseInsert<T>;
|
||||
await insert.InternalBulkCopyAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量快速插入
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="insert"></param>
|
||||
/// <returns></returns>
|
||||
public static int ExecuteClickHouseBulkCopy<T>(this IInsert<T> insert) where T : class
|
||||
{
|
||||
return ExecuteClickHouseBulkCopyAsync(insert).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
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();
|
||||
}
|
||||
}
|
||||
}
|
179
Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs
Normal file
179
Providers/FreeSql.Provider.ClickHouse/ClickHouseUtils.cs
Normal file
@ -0,0 +1,179 @@
|
||||
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 System.Text.Json;
|
||||
using ClickHouse.Client.ADO.Parameters;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
|
||||
namespace FreeSql.ClickHouse
|
||||
{
|
||||
internal 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 (value is string str)
|
||||
value = str?.Replace("\t", "\\t")
|
||||
.Replace("\r\n", "\\r\\n")
|
||||
.Replace("\n", "\\n")
|
||||
.Replace("\r", "\\r")
|
||||
.Replace("/", "\\/");
|
||||
if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}";
|
||||
var dbtype = (DbType?)_orm.CodeFirst.GetDbInfo(type)?.type;
|
||||
DbParameter ret = new ClickHouseDbParameter { ParameterName = parameterName };//QuoteParamterName(parameterName)
|
||||
if (dbtype != null) ret.DbType = dbtype.Value;
|
||||
ret.Value = value;
|
||||
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;
|
||||
}
|
||||
//if (value.GetType().IsArray)
|
||||
//{
|
||||
// ret.DbType = DbType.Object;
|
||||
//}
|
||||
}
|
||||
_params?.Add(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override DbParameter[] GetDbParamtersByObject(string sql, object obj) =>
|
||||
Utils.GetDbParamtersByObject<DbParameter>(sql, obj, "@", (name, type, value) =>
|
||||
{
|
||||
if (value is string str)
|
||||
value = str?.Replace("\t", "\\t")
|
||||
.Replace("\r\n", "\\r\\n")
|
||||
.Replace("\n", "\\n")
|
||||
.Replace("\r", "\\r")
|
||||
.Replace("/", "\\/");
|
||||
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?.DbType)) return sql;
|
||||
col.Attribute.DbType = col.Attribute.DbType.Replace(" NOT NULL", "");
|
||||
if (string.IsNullOrWhiteSpace(col?.Attribute.RewriteSql) == false)
|
||||
return string.Format(col.Attribute.RewriteSql, sql);
|
||||
if (Regex.IsMatch(sql, @"\{\{[\w\d]+_+\d:\{\d\}\}\}"))
|
||||
return string.Format(sql, col.Attribute.DbType);
|
||||
return sql;
|
||||
}
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatClickHouse(args);
|
||||
|
||||
public override string QuoteSqlNameAdapter(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}";
|
||||
}
|
||||
else if (value is Array)
|
||||
{
|
||||
var valueArr = value as Array;
|
||||
var eleType = type.GetElementType();
|
||||
var len = valueArr.GetLength(0);
|
||||
var sb = new StringBuilder().Append("[");
|
||||
for (var a = 0; a < len; a++)
|
||||
{
|
||||
var item = valueArr.GetValue(a);
|
||||
if (a > 0) sb.Append(",");
|
||||
sb.Append(GetNoneParamaterSqlValue(specialParams, specialParamFlag, col, eleType, item));
|
||||
}
|
||||
sb.Append("]");
|
||||
return sb.ToString();
|
||||
}
|
||||
return FormatSql("{0}", value, 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
using FreeSql.Internal;
|
||||
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 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.Provider.ClickHouse {CoreStrings.S_Not_Implemented_Feature}");
|
||||
|
||||
public override string ToSql()
|
||||
{
|
||||
return ReplaceDeleteSql(base.ToSql());
|
||||
}
|
||||
|
||||
string ReplaceDeleteSql(string sql) => sql?.Replace("DELETE FROM ", "ALTER TABLE ").Replace(" WHERE ", " DELETE WHERE ");
|
||||
|
||||
public override int ExecuteAffrows()
|
||||
{
|
||||
var affrows = 0;
|
||||
DbParameter[] dbParms = null;
|
||||
ToSqlFetch(sb =>
|
||||
{
|
||||
if (dbParms == null) dbParms = _params.ToArray();
|
||||
var sql = ReplaceDeleteSql(sb.ToString());
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
affrows += _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, affrows);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
});
|
||||
if (dbParms != null) this.ClearData();
|
||||
return affrows;
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
public override Task<List<T1>> ExecuteDeletedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException($"FreeSql.Provider.ClickHouse {CoreStrings.S_Not_Implemented_Feature}");
|
||||
|
||||
async public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var affrows = 0;
|
||||
DbParameter[] dbParms = null;
|
||||
await ToSqlFetchAsync(async sb =>
|
||||
{
|
||||
if (dbParms == null) dbParms = _params.ToArray();
|
||||
var sql = ReplaceDeleteSql(sb.ToString());
|
||||
var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Delete, sql, dbParms);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
affrows += await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, affrows);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
});
|
||||
if (dbParms != null) this.ClearData();
|
||||
return affrows;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
448
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
Normal file
448
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs
Normal file
@ -0,0 +1,448 @@
|
||||
using ClickHouse.Client.ADO;
|
||||
using ClickHouse.Client.Copy;
|
||||
using FreeSql.Internal;
|
||||
using FreeSql.Internal.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
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() => SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchParameterLimit > 0 ? _batchParameterLimit : int.MaxValue);
|
||||
public override long ExecuteIdentity() => SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchParameterLimit > 0 ? _batchParameterLimit : int.MaxValue);
|
||||
public override List<T1> ExecuteInserted() => SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchParameterLimit > 0 ? _batchParameterLimit : int.MaxValue);
|
||||
|
||||
|
||||
public override string ToSql()
|
||||
{
|
||||
if (InternalIsIgnoreInto == false) return base.ToSqlValuesOrSelectUnionAll();
|
||||
var sql = base.ToSqlValuesOrSelectUnionAll();
|
||||
return $"INSERT IGNORE INTO {sql.Substring(12)}";
|
||||
}
|
||||
|
||||
protected override int RawExecuteAffrows()
|
||||
{
|
||||
var affrows = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
if (_source.Count > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, null, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
InternalBulkCopyAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
return affrows;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, affrows);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
}
|
||||
return base.RawExecuteAffrows();
|
||||
}
|
||||
|
||||
internal async Task<int> InternalBulkCopyAsync()
|
||||
{
|
||||
var data = ToDataTable();
|
||||
using (var conn = _orm.Ado.MasterPool.Get())
|
||||
{
|
||||
using (var bulkCopyInterface = new ClickHouseBulkCopy(conn.Value as ClickHouseConnection)
|
||||
{
|
||||
DestinationTableName = data.TableName,
|
||||
BatchSize = _source.Count
|
||||
})
|
||||
{
|
||||
await bulkCopyInterface.InitAsync();
|
||||
await bulkCopyInterface.WriteToServerAsync(data, default);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
private IDictionary<string, object> GetValue<T>(T u, System.Reflection.PropertyInfo[] columns)
|
||||
{
|
||||
try
|
||||
{
|
||||
Dictionary<string, object> dic = new Dictionary<string, object>();
|
||||
foreach (var item in columns)
|
||||
{
|
||||
object v = null;
|
||||
if (u != null)
|
||||
{
|
||||
v = item.GetValue(u);
|
||||
}
|
||||
if (dic.ContainsKey(item.Name) == false)
|
||||
dic.Add(item.Name, v);
|
||||
}
|
||||
return dic;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected override long RawExecuteIdentity()
|
||||
{
|
||||
long ret = 0;
|
||||
var identCols = _table.Columns.Where(a => a.Value.Attribute.IsIdentity == true);
|
||||
if (identCols.Any()&&_source.Count==1)
|
||||
ret = (long)identCols.First().Value.GetValue(_source.First());
|
||||
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, null, 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) => SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken);
|
||||
public override Task<long> ExecuteIdentityAsync(CancellationToken cancellationToken = default) => SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken);
|
||||
public override Task<List<T1>> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken);
|
||||
|
||||
async protected override Task<int> RawExecuteAffrowsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var affrows = 0;
|
||||
Exception exception = null;
|
||||
Aop.CurdBeforeEventArgs before = null;
|
||||
if (_source.Count > 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, null, _params);
|
||||
_orm.Aop.CurdBeforeHandler?.Invoke(this, before);
|
||||
var data = ToDataTable();
|
||||
using (var conn = await _orm.Ado.MasterPool.GetAsync())
|
||||
{
|
||||
using (var bulkCopyInterface = new ClickHouseBulkCopy(conn.Value as ClickHouseConnection)
|
||||
{
|
||||
DestinationTableName = data.TableName,
|
||||
BatchSize = _source.Count
|
||||
})
|
||||
{
|
||||
await bulkCopyInterface.WriteToServerAsync(data, default);
|
||||
}
|
||||
}
|
||||
return affrows;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.CurdAfterEventArgs(before, exception, affrows);
|
||||
_orm.Aop.CurdAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
}
|
||||
return await base.RawExecuteAffrowsAsync(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, null, 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 await Task.FromResult(ret);
|
||||
}
|
||||
|
||||
|
||||
protected override int SplitExecuteAffrows(int valuesLimit, int parameterLimit)
|
||||
{
|
||||
var ss = SplitSource(valuesLimit, parameterLimit);
|
||||
var ret = 0;
|
||||
if (ss.Any() == false)
|
||||
{
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
if (ss.Length == 1)
|
||||
{
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, 1, 1));
|
||||
ret = this.RawExecuteAffrows();
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
var before = new Aop.TraceBeforeEventArgs("SplitExecuteAffrows", null);
|
||||
_orm.Aop.TraceBeforeHandler?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
for (var a = 0; a < ss.Length; a++)
|
||||
{
|
||||
_source = ss[a];
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, a + 1, ss.Length));
|
||||
ret += this.RawExecuteAffrows();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.TraceAfterEventArgs(before, null, exception);
|
||||
_orm.Aop.TraceAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
async protected override Task<int> SplitExecuteAffrowsAsync(int valuesLimit, int parameterLimit, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var ss = SplitSource(valuesLimit, parameterLimit);
|
||||
var ret = 0;
|
||||
if (ss.Any() == false)
|
||||
{
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
if (ss.Length == 1)
|
||||
{
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, 1, 1));
|
||||
await this.RawExecuteAffrowsAsync(cancellationToken);
|
||||
ret = _source.Count;
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
var before = new Aop.TraceBeforeEventArgs("SplitExecuteAffrowsAsync", null);
|
||||
_orm.Aop.TraceBeforeHandler?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
for (var a = 0; a < ss.Length; a++)
|
||||
{
|
||||
_source = ss[a];
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, a + 1, ss.Length));
|
||||
await this.RawExecuteAffrowsAsync(cancellationToken);
|
||||
ret += _source.Count;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.TraceAfterEventArgs(before, null, exception);
|
||||
_orm.Aop.TraceAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
async protected override Task<long> SplitExecuteIdentityAsync(int valuesLimit, int parameterLimit, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var ss = SplitSource(valuesLimit, parameterLimit);
|
||||
long ret = 0;
|
||||
if (ss.Any() == false)
|
||||
{
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
if (ss.Length == 1)
|
||||
{
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, 1, 1));
|
||||
ret = await this.RawExecuteIdentityAsync(cancellationToken);
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
var before = new Aop.TraceBeforeEventArgs("SplitExecuteIdentityAsync", null);
|
||||
_orm.Aop.TraceBeforeHandler?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
for (var a = 0; a < ss.Length; a++)
|
||||
{
|
||||
_source = ss[a];
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, a + 1, ss.Length));
|
||||
if (a < ss.Length - 1) await this.RawExecuteAffrowsAsync(cancellationToken);
|
||||
else ret = await this.RawExecuteIdentityAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.TraceAfterEventArgs(before, null, exception);
|
||||
_orm.Aop.TraceAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
async protected override Task<List<T1>> SplitExecuteInsertedAsync(int valuesLimit, int parameterLimit, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var ss = SplitSource(valuesLimit, parameterLimit);
|
||||
var ret = new List<T1>();
|
||||
if (ss.Any() == false)
|
||||
{
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
if (ss.Length == 1)
|
||||
{
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, 1, 1));
|
||||
ret = await this.RawExecuteInsertedAsync(cancellationToken);
|
||||
ClearData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
var before = new Aop.TraceBeforeEventArgs("SplitExecuteInsertedAsync", null);
|
||||
_orm.Aop.TraceBeforeHandler?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
for (var a = 0; a < ss.Length; a++)
|
||||
{
|
||||
_source = ss[a];
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, a + 1, ss.Length));
|
||||
ret.AddRange(await this.RawExecuteInsertedAsync(cancellationToken));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.TraceAfterEventArgs(before, null, exception);
|
||||
_orm.Aop.TraceAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
ClearData();
|
||||
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, null, 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
|
||||
}
|
||||
}
|
227
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs
Normal file
227
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseSelect.cs
Normal file
@ -0,0 +1,227 @@
|
||||
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>
|
||||
{
|
||||
public string _limitBy = "";
|
||||
public string _sample = "";
|
||||
|
||||
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,
|
||||
string _limitBy = "", string _sample = "")
|
||||
{
|
||||
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.Where(a => a.Before == false), true);
|
||||
tb.CascadeBefore = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter.Where(a => a.Before == true), 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\nGLOBAL LEFT 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) &&
|
||||
string.IsNullOrEmpty(tbsfrom[b].CascadeBefore)) sb.Append(" ON 1 = 1");
|
||||
else sb.Append(" ON ").Append(string.Join(" AND ", new[]
|
||||
{
|
||||
tbsfrom[b].CascadeBefore,
|
||||
tbsfrom[b].NavigateCondition ?? tbsfrom[b].On,
|
||||
tbsfrom[b].Cascade
|
||||
}.Where(sql => string.IsNullOrEmpty(sql) == false)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].CascadeBefore)) sbnav.Append(" AND ").Append(tbsfrom[a].CascadeBefore);
|
||||
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(", ");
|
||||
}
|
||||
if (string.IsNullOrEmpty(_sample) == false)
|
||||
sb.Append(" \r\n").Append(_sample);
|
||||
foreach (var tb in tbsjoin)
|
||||
{
|
||||
switch (tb.Type)
|
||||
{
|
||||
case SelectTableInfoType.Parent:
|
||||
case SelectTableInfoType.RawJoin:
|
||||
continue;
|
||||
case SelectTableInfoType.LeftJoin:
|
||||
sb.Append(" \r\nGLOBAL LEFT JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.InnerJoin:
|
||||
sb.Append(" \r\nGLOBAL INNER JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.RightJoin:
|
||||
sb.Append(" \r\nGLOBAL RIGHT JOIN ");
|
||||
break;
|
||||
}
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias)
|
||||
.Append(" ON ").Append(string.Join(" AND ", new[]
|
||||
{
|
||||
tb.CascadeBefore,
|
||||
tb.On ?? tb.NavigateCondition,
|
||||
tb.Cascade
|
||||
}.Where(sql => string.IsNullOrEmpty(sql) == false)));
|
||||
if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")");
|
||||
}
|
||||
if (_join.Length > 0) sb.Append(_join);
|
||||
|
||||
if (!string.IsNullOrEmpty(_tables[0].CascadeBefore)) sbnav.Append(" AND ").Append(_tables[0].CascadeBefore);
|
||||
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 (string.IsNullOrEmpty(_limitBy) == false)
|
||||
sb.Append(" \r\n").Append(_limitBy);
|
||||
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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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 ClickHouseSelect<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, _limitBy);
|
||||
}
|
||||
class ClickHouseSelect<T1, T2> : FreeSql.Internal.CommonProvider.Select2Provider<T1, T2> where T2 : class
|
||||
{
|
||||
public ClickHouseSelect(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 ClickHouseSelect<T1, T2, T3> : FreeSql.Internal.CommonProvider.Select3Provider<T1, T2, T3> where T2 : class where T3 : class
|
||||
{
|
||||
public ClickHouseSelect(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 ClickHouseSelect<T1, T2, T3, T4> : FreeSql.Internal.CommonProvider.Select4Provider<T1, T2, T3, T4> where T2 : class where T3 : class where T4 : class
|
||||
{
|
||||
public ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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 ClickHouseSelect<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 ClickHouseSelect(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);
|
||||
}
|
||||
}
|
322
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseUpdate.cs
Normal file
322
Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseUpdate.cs
Normal file
@ -0,0 +1,322 @@
|
||||
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;
|
||||
using DateTime = System.DateTime;
|
||||
|
||||
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;
|
||||
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() => SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000);
|
||||
protected override List<TReturn> ExecuteUpdated<TReturn>(IEnumerable<ColumnInfo> columns) => base.SplitExecuteUpdated<TReturn>(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, columns);
|
||||
|
||||
protected override List<TReturn> RawExecuteUpdated<TReturn>(IEnumerable<ColumnInfo> columns) => throw new NotImplementedException($"FreeSql.Provider.ClickHouse {CoreStrings.S_Not_Implemented_Feature}");
|
||||
|
||||
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(")");
|
||||
}
|
||||
|
||||
public override void ToSqlExtension110(StringBuilder sb, bool isAsTableSplited)
|
||||
{
|
||||
if (_where.Length == 0 && _source.Any() == false) return;
|
||||
if (_source.Any() == false && _set.Length == 0 && _setIncr.Length == 0) return;
|
||||
|
||||
if (_table.AsTableImpl != null && isAsTableSplited == false && _source == _sourceOld && _source.Any())
|
||||
{
|
||||
var atarr = _source.Select(a => new
|
||||
{
|
||||
item = a,
|
||||
splitKey = _table.AsTableImpl.GetTableNameByColumnValue(_table.AsTableColumn.GetValue(a))
|
||||
}).GroupBy(a => a.splitKey, a => a.item).ToArray();
|
||||
if (atarr.Length > 1)
|
||||
{
|
||||
var oldSource = _source;
|
||||
var arrret = new List<List<T1>>();
|
||||
foreach (var item in atarr)
|
||||
{
|
||||
_source = item.ToList();
|
||||
ToSqlExtension110(sb, true);
|
||||
sb.Append("\r\n\r\n;\r\n\r\n");
|
||||
}
|
||||
_source = oldSource;
|
||||
if (sb.Length > 0) sb.Remove(sb.Length - 9, 9);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sb.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(TableRuleInvoke())).Append(" UPDATE ");
|
||||
|
||||
if (_set.Length > 0)
|
||||
{ //指定 set 更新
|
||||
sb.Append(_set.ToString().Substring(2));
|
||||
|
||||
}
|
||||
else if (_source.Count == 1)
|
||||
{ //保存 Source
|
||||
_paramsSource.Clear();
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values)
|
||||
{
|
||||
if (col.Attribute.IsPrimary) continue;
|
||||
if (_tempPrimarys.Any(a => a.CsName == col.CsName)) continue;
|
||||
if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && _ignore.ContainsKey(col.Attribute.Name) == false)
|
||||
{
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ");
|
||||
|
||||
if (col.Attribute.CanUpdate && string.IsNullOrEmpty(col.DbUpdateValue) == false)
|
||||
sb.Append(col.DbUpdateValue);
|
||||
else
|
||||
{
|
||||
var val = col.GetDbValue(_source.First());
|
||||
|
||||
var colsql = _noneParameter ? _commonUtils.GetNoneParamaterSqlValue(_paramsSource, "u", col, col.Attribute.MapType, val) :
|
||||
_commonUtils.QuoteWriteParamterAdapter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}"));
|
||||
sb.Append(_commonUtils.RewriteColumn(col, colsql));
|
||||
if (_noneParameter == false)
|
||||
_commonUtils.AppendParamter(_paramsSource, null, col, col.Attribute.MapType, val);
|
||||
}
|
||||
++colidx;
|
||||
}
|
||||
}
|
||||
if (colidx == 0) return;
|
||||
|
||||
}
|
||||
else if (_source.Count > 1)
|
||||
{ //批量保存 Source
|
||||
if (_tempPrimarys.Any() == false) return;
|
||||
|
||||
var caseWhen = new StringBuilder();
|
||||
ToSqlCase(caseWhen, _tempPrimarys);
|
||||
var cw = $"{caseWhen.ToString()}=";
|
||||
_paramsSource.Clear();
|
||||
var colidx = 0;
|
||||
foreach (var col in _table.Columns.Values)
|
||||
{
|
||||
if (col.Attribute.IsPrimary) continue;
|
||||
if (_tempPrimarys.Any(a => a.CsName == col.CsName)) continue;
|
||||
if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && _ignore.ContainsKey(col.Attribute.Name) == false)
|
||||
{
|
||||
if (colidx > 0) sb.Append(", ");
|
||||
var columnName = _commonUtils.QuoteSqlName(col.Attribute.Name);
|
||||
sb.Append(columnName).Append(" = ");
|
||||
|
||||
if (col.Attribute.CanUpdate && string.IsNullOrEmpty(col.DbUpdateValue) == false)
|
||||
sb.Append(col.DbUpdateValue);
|
||||
else
|
||||
{
|
||||
var nulls = 0;
|
||||
var cwsb = new StringBuilder().Append(" multiIf( ");
|
||||
foreach (var d in _source)
|
||||
{
|
||||
cwsb.Append(cw);
|
||||
ToSqlWhen(cwsb, _tempPrimarys, d);
|
||||
cwsb.Append(",");
|
||||
var val = col.GetDbValue(d);
|
||||
|
||||
var colsql = _noneParameter ? _commonUtils.GetNoneParamaterSqlValue(_paramsSource, "u", col, col.Attribute.MapType, val) :
|
||||
_commonUtils.QuoteWriteParamterAdapter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}"));
|
||||
|
||||
//判断是否是DateTime类型,如果是DateTime类型,需要转换成ClickHouse支持的时间格式 #1813
|
||||
if (col.Attribute.MapType == typeof(DateTime) || col.Attribute.MapType == typeof(DateTime?) )
|
||||
{
|
||||
//获取当前实时区
|
||||
colsql = $"toDateTime({colsql},'Asia/Shanghai')";
|
||||
}
|
||||
|
||||
cwsb.Append(_commonUtils.RewriteColumn(col, colsql));
|
||||
if (_noneParameter == false)
|
||||
_commonUtils.AppendParamter(_paramsSource, null, col, col.Attribute.MapType, val);
|
||||
if (val == null || val == DBNull.Value) nulls++;
|
||||
cwsb.Append(", ");
|
||||
}
|
||||
if (nulls == _source.Count) sb.Append("NULL");
|
||||
else
|
||||
{
|
||||
cwsb.Append(columnName).Append(" )");
|
||||
ToSqlCaseWhenEnd(cwsb, col);
|
||||
sb.Append(cwsb);
|
||||
}
|
||||
cwsb.Clear();
|
||||
}
|
||||
++colidx;
|
||||
}
|
||||
}
|
||||
if (colidx == 0) return;
|
||||
}
|
||||
else if (_setIncr.Length == 0)
|
||||
return;
|
||||
|
||||
if (_setIncr.Length > 0)
|
||||
sb.Append(_set.Length > 0 || _source.Any() ? _setIncr.ToString() : _setIncr.ToString().Substring(2));
|
||||
|
||||
if (_source.Any() == false)
|
||||
{
|
||||
var sbString = "";
|
||||
foreach (var col in _table.Columns.Values)
|
||||
if (col.Attribute.CanUpdate && string.IsNullOrEmpty(col.DbUpdateValue) == false)
|
||||
{
|
||||
if (sbString == "") sbString = sb.ToString();
|
||||
var loc3 = _commonUtils.QuoteSqlName(col.Attribute.Name);
|
||||
if (sbString.Contains(loc3)) continue;
|
||||
sb.Append(", ").Append(loc3).Append(" = ").Append(col.DbUpdateValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (_table.VersionColumn != null)
|
||||
{
|
||||
var vcname = _commonUtils.QuoteSqlName(_table.VersionColumn.Attribute.Name);
|
||||
if (_table.VersionColumn.Attribute.MapType == typeof(byte[]))
|
||||
{
|
||||
_updateVersionValue = Utils.GuidToBytes(Guid.NewGuid());
|
||||
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _table.VersionColumn, _table.VersionColumn.Attribute.MapType, _updateVersionValue));
|
||||
}
|
||||
else if (_versionColumn.Attribute.MapType == typeof(string))
|
||||
{
|
||||
_updateVersionValue = Guid.NewGuid().ToString();
|
||||
sb.Append(", ").Append(vcname).Append(" = ").Append(_noneParameter ? _commonUtils.GetNoneParamaterSqlValue(_paramsSource, "uv", _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue) :
|
||||
_commonUtils.QuoteWriteParamterAdapter(_versionColumn.Attribute.MapType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}")));
|
||||
if (_noneParameter == false)
|
||||
_commonUtils.AppendParamter(_paramsSource, null, _versionColumn, _versionColumn.Attribute.MapType, _updateVersionValue);
|
||||
}
|
||||
else
|
||||
sb.Append(", ").Append(vcname).Append(" = ").Append(_commonUtils.IsNull(vcname, 0)).Append(" + 1");
|
||||
}
|
||||
ToSqlWhere(sb);
|
||||
_interceptSql?.Invoke(sb);
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void SplitExecute(int valuesLimit, int parameterLimit, string traceName, Action execute)
|
||||
{
|
||||
var ss = SplitSource(valuesLimit, parameterLimit);
|
||||
if (ss.Length <= 1)
|
||||
{
|
||||
if (_source?.Any() == true) _batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, 1, 1));
|
||||
execute();
|
||||
ClearData();
|
||||
return;
|
||||
}
|
||||
|
||||
var before = new Aop.TraceBeforeEventArgs(traceName, null);
|
||||
_orm.Aop.TraceBeforeHandler?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
for (var a = 0; a < ss.Length; a++)
|
||||
{
|
||||
_source = ss[a];
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, a + 1, ss.Length));
|
||||
execute();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.TraceAfterEventArgs(before, null, exception);
|
||||
_orm.Aop.TraceAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
ClearData();
|
||||
}
|
||||
|
||||
#if net40
|
||||
#else
|
||||
public override Task<int> ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken);
|
||||
protected override Task<List<TReturn>> ExecuteUpdatedAsync<TReturn>(IEnumerable<ColumnInfo> columns, CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync<TReturn>(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, columns, cancellationToken);
|
||||
|
||||
protected override Task<List<TReturn>> RawExecuteUpdatedAsync<TReturn>(IEnumerable<ColumnInfo> columns, CancellationToken cancellationToken = default) => throw new NotImplementedException($"FreeSql.Provider.ClickHouse {CoreStrings.S_Not_Implemented_Feature}");
|
||||
|
||||
async protected override Task SplitExecuteAsync(int valuesLimit, int parameterLimit, string traceName, Func<Task> executeAsync, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var ss = SplitSource(valuesLimit, parameterLimit);
|
||||
if (ss.Length <= 1)
|
||||
{
|
||||
if (_source?.Any() == true) _batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, 1, 1));
|
||||
await executeAsync();
|
||||
ClearData();
|
||||
return;
|
||||
}
|
||||
|
||||
var before = new Aop.TraceBeforeEventArgs(traceName, null);
|
||||
_orm.Aop.TraceBeforeHandler?.Invoke(this, before);
|
||||
Exception exception = null;
|
||||
try
|
||||
{
|
||||
for (var a = 0; a < ss.Length; a++)
|
||||
{
|
||||
_source = ss[a];
|
||||
_batchProgress?.Invoke(new BatchProgressStatus<T1>(_source, a + 1, ss.Length));
|
||||
await executeAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
var after = new Aop.TraceAfterEventArgs(before, null, exception);
|
||||
_orm.Aop.TraceAfterHandler?.Invoke(this, after);
|
||||
}
|
||||
ClearData();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<LangVersion>9</LangVersion>
|
||||
<TargetFrameworks>netstandard2.1</TargetFrameworks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>FreeSql;ncc;YeXiangQin;ChenBo</Authors>
|
||||
<Description>FreeSql 数据库实现,基于 ClickHouse.Client Ado.net</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>
|
||||
<Version>3.2.833</Version>
|
||||
<PackageReadmeFile>readme.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../../readme.md" Pack="true" PackagePath="\" />
|
||||
<None Include="../../logo.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ClickHouse.Client" Version="6.8.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
Reference in New Issue
Block a user