mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-19 12:28:15 +08:00
Merge branch 'master' of https://github.com/dotnetcore/FreeSql
This commit is contained in:
@ -12,43 +12,76 @@ using ClickHouse.Client.ADO;
|
||||
|
||||
namespace FreeSql.ClickHouse
|
||||
{
|
||||
|
||||
class ClickHouseCodeFirst : Internal.CommonProvider.CodeFirstProvider
|
||||
{
|
||||
public ClickHouseCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm,
|
||||
commonUtils, commonExpression)
|
||||
{
|
||||
}
|
||||
|
||||
public ClickHouseCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) { }
|
||||
|
||||
static object _dicCsToDbLock = new object();
|
||||
|
||||
static Dictionary<string, CsToDb<DbType>> _dicCsToDb = new Dictionary<string, CsToDb<DbType>>() {
|
||||
{ typeof(bool).FullName, CsToDb.New(DbType.SByte, "Int8","Int8", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(DbType.SByte, "Int8","Nullable(Int8)", null, true, null) },
|
||||
|
||||
{ typeof(sbyte).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", false, true, null) },
|
||||
{ typeof(short).FullName, CsToDb.New(DbType.Int16, "Int16","Int16", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DbType.Int16, "Int16", "Nullable(Int16)", false, true, null) },
|
||||
{ typeof(int).FullName, CsToDb.New(DbType.Int32, "Int32", "Int32", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DbType.Int32, "Int32", "Nullable(Int32)", false, true, null) },
|
||||
{ typeof(long).FullName, CsToDb.New(DbType.Int64, "Int64","Int64", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DbType.Int64, "Int64","Nullable(Int64)", false, true, null) },
|
||||
|
||||
{ typeof(byte).FullName, CsToDb.New(DbType.Byte, "UInt8","UInt8", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DbType.Byte, "UInt8","Nullable(UInt8)", true, true, null) },
|
||||
{ typeof(ushort).FullName, CsToDb.New(DbType.UInt16, "UInt16","UInt16", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, "UInt16", "Nullable(UInt16)", true, true, null) },
|
||||
{ typeof(uint).FullName, CsToDb.New(DbType.UInt32, "UInt32", "UInt32", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DbType.UInt32, "UInt32", "Nullable(UInt32)", true, true, null) },
|
||||
{ typeof(ulong).FullName, CsToDb.New(DbType.UInt64, "UInt64", "UInt64", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, "UInt64", "Nullable(UInt64)", true, true, null) },
|
||||
static Dictionary<string, CsToDb<DbType>> _dicCsToDb = new Dictionary<string, CsToDb<DbType>>()
|
||||
{
|
||||
{ typeof(bool).FullName, CsToDb.New(DbType.SByte, "Int8", "Int8", null, false, false) },
|
||||
{ typeof(bool?).FullName, CsToDb.New(DbType.SByte, "Int8", "Nullable(Int8)", null, true, null) },
|
||||
|
||||
{ typeof(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, "Decimal128(19)","Decimal128(19)", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DbType.Decimal, "Nullable(Decimal128(19))","Nullable(Decimal128(19))", false, 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(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(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(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) },
|
||||
{ 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);
|
||||
if (type.IsArray) return null;
|
||||
if (_dicCsToDb.TryGetValue(type.FullName, out var trydc))
|
||||
return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable,
|
||||
trydc.defaultValue);
|
||||
if (type.IsArray)
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -56,7 +89,7 @@ namespace FreeSql.ClickHouse
|
||||
{
|
||||
Object<DbConnection> conn = null;
|
||||
string database = null;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
conn = _orm.Ado.MasterPool.Get(TimeSpan.FromSeconds(5));
|
||||
@ -65,19 +98,25 @@ namespace FreeSql.ClickHouse
|
||||
var sb = new StringBuilder();
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
if (sb.Length > 0) sb.Append("\r\n");
|
||||
if (sb.Length > 0)
|
||||
sb.Append("\r\n");
|
||||
var tb = _commonUtils.GetTableByEntity(obj.entityType);
|
||||
if (tb == null) throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName));
|
||||
if (tb.Columns.Any() == false) throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName));
|
||||
if (tb == null)
|
||||
throw new Exception(CoreStrings.S_Type_IsNot_Migrable(obj.entityType.FullName));
|
||||
if (tb.Columns.Any() == false)
|
||||
throw new Exception(CoreStrings.S_Type_IsNot_Migrable_0Attributes(obj.entityType.FullName));
|
||||
var tbname = _commonUtils.SplitTableName(tb.DbName);
|
||||
if (tbname?.Length == 1) tbname = new[] { database, tbname[0] };
|
||||
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 (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 (tbtmpname?.Length == 1)
|
||||
tbtmpname = new[] { database, tbtmpname[0] };
|
||||
if (tbname[0] != tbtmpname[0] || tbname[1] != tbtmpname[1])
|
||||
{
|
||||
tbname = tbtmpname;
|
||||
@ -85,20 +124,32 @@ namespace FreeSql.ClickHouse
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
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 (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)
|
||||
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)
|
||||
{
|
||||
//创建表
|
||||
@ -106,33 +157,44 @@ namespace FreeSql.ClickHouse
|
||||
sb.Append("CREATE TABLE IF NOT EXISTS ").Append(createTableName).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));
|
||||
//如果这里是主键就是不为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("INDEX ")
|
||||
.Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1])));
|
||||
|
||||
sb.Append(" (");
|
||||
foreach (var tbcol in uk.Columns)
|
||||
{
|
||||
sb.Append(" ");
|
||||
sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name));
|
||||
sb.Append("TYPE set(8192) GRANULARITY 5, ");
|
||||
sb.Append(", ");
|
||||
}
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
|
||||
sb.Remove(sb.Length - 2, 2).Append(") ");
|
||||
sb.Append("TYPE set(8192) GRANULARITY 5,");
|
||||
}
|
||||
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
sb.Append("\r\n) ");
|
||||
sb.Append("\r\nENGINE = MergeTree()");
|
||||
|
||||
|
||||
if (tb.Primarys.Any())
|
||||
{
|
||||
sb.Append(" \r\nORDER BY ( ");
|
||||
var ls = new StringBuilder();
|
||||
foreach (var tbcol in tb.Primarys) ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
foreach (var tbcol in tb.Primarys)
|
||||
ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Append(ls);
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
sb.Append(" )");
|
||||
@ -140,15 +202,20 @@ namespace FreeSql.ClickHouse
|
||||
sb.Append(ls);
|
||||
sb.Remove(sb.Length - 2, 2).Append(",");
|
||||
}
|
||||
|
||||
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");
|
||||
continue;
|
||||
}
|
||||
|
||||
//如果新表,旧表在一个数据库下,直接修改表名
|
||||
if (string.Compare(tbname[0], tboldname[0], true) == 0)
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1])).Append(" RENAME TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
|
||||
sbalter.Append("RENAME TABLE ")
|
||||
.Append(_commonUtils.QuoteSqlName(tboldname[0], tboldname[1]))
|
||||
.Append(" TO ").Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1]))
|
||||
.Append(";\r\n");
|
||||
else
|
||||
{
|
||||
//如果新表,旧表不在一起,创建新表,导入数据,删除旧表
|
||||
@ -178,64 +245,162 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
{
|
||||
column = string.Concat(a[0]),
|
||||
sqlType = (string)a[1],
|
||||
is_nullable = string.Concat(a[2]) == "1",
|
||||
is_nullable = a[1]?.ToString().Contains("Nullable"),
|
||||
is_identity = false,
|
||||
comment = string.Concat(a[3]),
|
||||
is_primary= string.Concat(a[6]) == "1",
|
||||
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)
|
||||
{
|
||||
if (tbstruct.TryGetValue(tbcol.Attribute.Name, out var tbstructcol) ||
|
||||
string.IsNullOrEmpty(tbcol.Attribute.OldName) == false && tbstruct.TryGetValue(tbcol.Attribute.OldName, out tbstructcol))
|
||||
//表中有这个字段
|
||||
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 ?? "");
|
||||
if (tbcol.Attribute.DbType.StartsWith(tbstructcol.sqlType, StringComparison.CurrentCultureIgnoreCase) == false ||
|
||||
tbcol.Attribute.IsNullable != tbstructcol.is_nullable || isCommentChanged)
|
||||
sbalter.Append("ALTER TABLE ").Append(_commonUtils.QuoteSqlName($"{tbname[0]}.{tbname[1]}")).Append(" MODIFY COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(tbcol.Attribute.IsNullable ? $"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");
|
||||
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(" COLUMN ").Append(_commonUtils.QuoteSqlName(tbstructcol.column)).Append(" TO ").Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(";\r\n");
|
||||
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 ").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("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 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(" \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("(");
|
||||
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()");
|
||||
@ -244,7 +409,8 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
{
|
||||
sb.Append(" \r\nORDER BY ( ");
|
||||
var ls = new StringBuilder();
|
||||
foreach (var tbcol in tb.Primarys) ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
foreach (var tbcol in tb.Primarys)
|
||||
ls.Append(_commonUtils.QuoteSqlName(tbcol.Attribute.Name)).Append(", ");
|
||||
sb.Append(ls);
|
||||
sb.Remove(sb.Length - 2, 2);
|
||||
sb.Append(" )");
|
||||
@ -252,6 +418,7 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
sb.Append(ls);
|
||||
sb.Remove(sb.Length - 2, 2).Append(",");
|
||||
}
|
||||
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
//if (string.IsNullOrEmpty(tb.Comment) == false)
|
||||
// sb.Append(" Comment=").Append(_commonUtils.FormatSql("{0}", tb.Comment));
|
||||
@ -262,25 +429,32 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
{
|
||||
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))
|
||||
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)
|
||||
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");
|
||||
sb.Append("RENAME TABLE ").Append(tmptablename).Append(" TO ")
|
||||
.Append(_commonUtils.QuoteSqlName(tbname[0], tbname[1])).Append(";\r\n");
|
||||
}
|
||||
|
||||
return sb.Length == 0 ? null : sb.ToString();
|
||||
}
|
||||
finally
|
||||
@ -299,7 +473,8 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
|
||||
object LocalExecuteScalar(string db, string sql)
|
||||
{
|
||||
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(db);
|
||||
if (string.Compare(database, db) != 0)
|
||||
conn.Value.ChangeDatabase(db);
|
||||
try
|
||||
{
|
||||
using (var cmd = conn.Value.CreateCommand())
|
||||
@ -311,17 +486,72 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (string.Compare(database, db) != 0) conn.Value.ChangeDatabase(database);
|
||||
if (string.Compare(database, db) != 0)
|
||||
conn.Value.ChangeDatabase(database);
|
||||
}
|
||||
}
|
||||
|
||||
string CkNullablePrimaryAdapter(string dbType, bool isPrimary)
|
||||
{
|
||||
return isPrimary
|
||||
? dbType.Replace("Nullable(", "").Replace(")", "")
|
||||
: dbType.Replace(" NOT NULL", "");
|
||||
}
|
||||
string CkNullableAdapter(string dbType, bool isPrimary)
|
||||
{
|
||||
return isPrimary
|
||||
? dbType.Replace("Nullable(", "").Replace(")","").Replace(" NOT NULL", "")
|
||||
: dbType.Replace(" NOT NULL", "");
|
||||
}
|
||||
|
||||
|
||||
string CkIntAdapter(string dbType)
|
||||
{
|
||||
var result = 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 (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;
|
||||
if (scripts.Any() == false)
|
||||
return 0;
|
||||
|
||||
var affrows = 0;
|
||||
foreach (var script in scripts)
|
||||
@ -329,4 +559,16 @@ where a.database in ({0}) and a.table in ({1})", tboldname ?? tbname);
|
||||
return affrows;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClickHouseTableIndex
|
||||
{
|
||||
public string name
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string expr
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
@ -79,7 +79,7 @@ namespace FreeSql.ClickHouse
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatClickHouse(args);
|
||||
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -46,7 +46,7 @@ namespace FreeSql.ClickHouse.Curd
|
||||
//如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1
|
||||
for (var b = 1; b < tbsfrom.Length; b++)
|
||||
{
|
||||
sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias);
|
||||
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)) sb.Append(" ON 1 = 1");
|
||||
else
|
||||
@ -80,13 +80,13 @@ namespace FreeSql.ClickHouse.Curd
|
||||
case SelectTableInfoType.RawJoin:
|
||||
continue;
|
||||
case SelectTableInfoType.LeftJoin:
|
||||
sb.Append(" \r\nLEFT JOIN ");
|
||||
sb.Append(" \r\nGLOBAL LEFT JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.InnerJoin:
|
||||
sb.Append(" \r\nINNER JOIN ");
|
||||
sb.Append(" \r\nGLOBAL INNER JOIN ");
|
||||
break;
|
||||
case SelectTableInfoType.RightJoin:
|
||||
sb.Append(" \r\nRIGHT JOIN ");
|
||||
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(tb.On ?? tb.NavigateCondition);
|
||||
|
@ -19,7 +19,7 @@
|
||||
<SignAssembly>False</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -41,7 +41,7 @@ namespace FreeSql.Custom
|
||||
|
||||
static FreeSql.Custom.CustomAdo _customAdo = new FreeSql.Custom.CustomAdo();
|
||||
public override string FormatSql(string sql, params object[] args) => (_orm?.Ado as CustomAdo)?.Addslashes(sql, args) ?? _customAdo.Addslashes(sql, args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -41,7 +41,7 @@ namespace FreeSql.Custom.MySql
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomMySql(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ namespace FreeSql.Custom.Oracle
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomOracle(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -97,7 +97,7 @@ namespace FreeSql.Custom.PostgreSQL
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomPostgreSQL(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -49,7 +49,7 @@ namespace FreeSql.Custom.SqlServer
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatCustomSqlServer(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -82,7 +82,7 @@ namespace FreeSql.Dameng
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatDameng(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -52,7 +52,7 @@ namespace FreeSql.Firebird
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatFirebird(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -56,7 +56,7 @@ namespace FreeSql.GBase
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatGBase(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -92,7 +92,7 @@ namespace FreeSql.KingbaseES
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatKingbaseES(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -38,7 +38,7 @@ namespace FreeSql.MsAccess
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatAccess(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -36,6 +36,7 @@ namespace FreeSql.MySql
|
||||
});
|
||||
|
||||
Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) });
|
||||
Select0Provider._dicMethodDataReaderGetValue[typeof(DateTimeOffset)] = typeof(DbDataReader).GetMethod("GetDateTime", new Type[] { typeof(int) });
|
||||
}
|
||||
|
||||
public override ISelect<T1> CreateSelectProvider<T1>(object dywhere) => new MySqlSelect<T1>(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere);
|
||||
|
@ -73,7 +73,7 @@ namespace FreeSql.MySql
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatMySql(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -4,6 +4,11 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using FreeSql.Internal.Model;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.Internal.ObjectPool;
|
||||
using System.Linq;
|
||||
using System.Data.Common;
|
||||
#if MySqlConnector
|
||||
using MySqlConnector;
|
||||
#else
|
||||
@ -13,6 +18,55 @@ using MySql.Data.MySqlClient;
|
||||
public static class FreeSqlMySqlConnectorGlobalExtensions
|
||||
{
|
||||
#region ExecuteMySqlBulkCopy
|
||||
|
||||
/// <summary>
|
||||
/// 批量更新(更新字段数量超过 2000 时收益大)<para></para>
|
||||
/// 实现原理:使用 MySqlBulkCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="bulkCopyTimeout"></param>
|
||||
/// <returns></returns>
|
||||
public static int ExecuteMySqlBulkCopy<T>(this IUpdate<T> that, int? bulkCopyTimeout = null) where T : class
|
||||
{
|
||||
var update = that as UpdateProvider<T>;
|
||||
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0;
|
||||
var state = ExecuteMySqlBulkCopyState(update);
|
||||
return UpdateProvider.ExecuteBulkUpdate(update, state, insert => insert.ExecuteMySqlBulkCopy(bulkCopyTimeout));
|
||||
}
|
||||
static NativeTuple<string, string, string, string, string[]> ExecuteMySqlBulkCopyState<T>(UpdateProvider<T> update) where T : class
|
||||
{
|
||||
if (update._source.Any() != true) return null;
|
||||
var _table = update._table;
|
||||
var _commonUtils = update._commonUtils;
|
||||
var updateTableName = update._tableRule?.Invoke(_table.DbName) ?? _table.DbName;
|
||||
var tempTableName = $"Temp_{Guid.NewGuid().ToString("N")}";
|
||||
if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower();
|
||||
if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
|
||||
if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null)
|
||||
update.WithTransaction(update._orm.Ado.TransactionCurrentThread);
|
||||
var sb = new StringBuilder().Append("CREATE TEMPORARY TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( ");
|
||||
var setColumns = new List<string>();
|
||||
var pkColumns = new List<string>();
|
||||
foreach (var col in _table.Columns.Values)
|
||||
{
|
||||
if (update._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name);
|
||||
else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && update._ignore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name);
|
||||
else continue;
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" ").Append(col.Attribute.DbType.Replace("NOT NULL", ""));
|
||||
sb.Append(",");
|
||||
}
|
||||
var sql1 = sb.Remove(sb.Length - 1, 1).Append(" \r\n) Engine=InnoDB;").ToString();
|
||||
|
||||
sb.Clear().Append("UPDATE ").Append(_commonUtils.QuoteSqlName(updateTableName)).Append(" a ")
|
||||
.Append(" \r\nINNER JOIN ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" b ON ").Append(string.Join(" AND ", pkColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")))
|
||||
.Append(" \r\nSET \r\n ").Append(string.Join(", \r\n ", setColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")));
|
||||
var sql2 = sb.ToString();
|
||||
sb.Clear();
|
||||
var sql3 = $"DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}";
|
||||
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MySql MySqlCopyBulk 批量插入功能<para></para>
|
||||
/// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列<para></para>
|
||||
@ -93,6 +147,13 @@ public static class FreeSqlMySqlConnectorGlobalExtensions
|
||||
}
|
||||
#if net40
|
||||
#else
|
||||
public static Task<int> ExecuteMySqlBulkCopyAsync<T>(this IUpdate<T> that, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
var update = that as UpdateProvider<T>;
|
||||
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return Task.FromResult(0);
|
||||
var state = ExecuteMySqlBulkCopyState(update);
|
||||
return UpdateProvider.ExecuteBulkUpdateAsync(update, state, insert => insert.ExecuteMySqlBulkCopyAsync(bulkCopyTimeout, cancellationToken));
|
||||
}
|
||||
async public static Task ExecuteMySqlBulkCopyAsync<T>(this IInsert<T> that, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
var insert = that as FreeSql.MySql.Curd.MySqlInsert<T>;
|
||||
|
@ -91,7 +91,7 @@ namespace FreeSql.MySql
|
||||
}
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatMySql(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ namespace FreeSql.Odbc.Dameng
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcDameng(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -39,7 +39,7 @@ namespace FreeSql.Odbc.Default
|
||||
|
||||
static FreeSql.Odbc.Default.OdbcAdo _customAdo = new FreeSql.Odbc.Default.OdbcAdo();
|
||||
public override string FormatSql(string sql, params object[] args) => (_orm?.Ado as OdbcAdo)?.Addslashes(sql, args) ?? _customAdo.Addslashes(sql, args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -91,7 +91,7 @@ namespace FreeSql.Odbc.KingbaseES
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcKingbaseES(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -37,7 +37,7 @@ namespace FreeSql.Odbc.MySql
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcMySql(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ namespace FreeSql.Odbc.Oracle
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcOracle(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ namespace FreeSql.Odbc.PostgreSQL
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcPostgreSQL(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -43,7 +43,7 @@ namespace FreeSql.Odbc.SqlServer
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOdbcSqlServer(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -100,7 +100,7 @@ namespace FreeSql.Oracle
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOracle(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -104,7 +104,7 @@ namespace FreeSql.Oracle
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatOracle(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,9 +1,13 @@
|
||||
using FreeSql;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using FreeSql.Internal.Model;
|
||||
using FreeSql.PostgreSQL.Curd;
|
||||
using Npgsql;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@ -32,6 +36,54 @@ public static partial class FreeSqlPostgreSQLGlobalExtensions
|
||||
public static OnConflictDoUpdate<T1> OnConflictDoUpdate<T1>(this IInsert<T1> that, Expression<Func<T1, object>> columns = null) where T1 : class => new FreeSql.PostgreSQL.Curd.OnConflictDoUpdate<T1>(that.InsertIdentity(), columns);
|
||||
|
||||
#region ExecutePgCopy
|
||||
/// <summary>
|
||||
/// 批量更新(更新字段数量超过 2000 时收益大)<para></para>
|
||||
/// 实现原理:使用 PgCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="that"></param>
|
||||
/// <returns></returns>
|
||||
public static int ExecutePgCopy<T>(this IUpdate<T> that) where T : class
|
||||
{
|
||||
var update = that as UpdateProvider<T>;
|
||||
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0;
|
||||
var state = ExecutePgCopyState(update);
|
||||
return UpdateProvider.ExecuteBulkUpdate(update, state, insert => insert.ExecutePgCopy());
|
||||
}
|
||||
static NativeTuple<string, string, string, string, string[]> ExecutePgCopyState<T>(UpdateProvider<T> update) where T : class
|
||||
{
|
||||
if (update._source.Any() != true) return null;
|
||||
var _table = update._table;
|
||||
var _commonUtils = update._commonUtils;
|
||||
var updateTableName = update._tableRule?.Invoke(_table.DbName) ?? _table.DbName;
|
||||
var tempTableName = $"Temp_{Guid.NewGuid().ToString("N")}";
|
||||
if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower();
|
||||
if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
|
||||
if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null)
|
||||
update.WithTransaction(update._orm.Ado.TransactionCurrentThread);
|
||||
var sb = new StringBuilder().Append("CREATE TEMP TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( ");
|
||||
var setColumns = new List<string>();
|
||||
var pkColumns = new List<string>();
|
||||
foreach (var col in _table.Columns.Values)
|
||||
{
|
||||
if (update._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name);
|
||||
else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && update._ignore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name);
|
||||
else continue;
|
||||
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" ").Append(col.Attribute.DbType.Replace("NOT NULL", ""));
|
||||
sb.Append(",");
|
||||
}
|
||||
var sql1 = sb.Remove(sb.Length - 1, 1).Append("\r\n) WITH (OIDS=FALSE);").ToString();
|
||||
|
||||
sb.Clear().Append("UPDATE ").Append(_commonUtils.QuoteSqlName(updateTableName)).Append(" a ")
|
||||
.Append("\r\nSET \r\n ").Append(string.Join(", \r\n ", setColumns.Select(col => $"{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")))
|
||||
.Append("\r\nFROM ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" b ")
|
||||
.Append("\r\nWHERE ").Append(string.Join(" AND ", pkColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")));
|
||||
var sql2 = sb.ToString();
|
||||
sb.Clear();
|
||||
var sql3 = $"DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}";
|
||||
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL COPY 批量导入功能,封装了 NpgsqlConnection.BeginBinaryImport 方法<para></para>
|
||||
/// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列<para></para>
|
||||
@ -121,6 +173,13 @@ public static partial class FreeSqlPostgreSQLGlobalExtensions
|
||||
|
||||
#if net45
|
||||
#else
|
||||
public static Task<int> ExecutePgCopyAsync<T>(this IUpdate<T> that, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
var update = that as UpdateProvider<T>;
|
||||
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return Task.FromResult(0);
|
||||
var state = ExecutePgCopyState(update);
|
||||
return UpdateProvider.ExecuteBulkUpdateAsync(update, state, insert => insert.ExecutePgCopyAsync(cancellationToken));
|
||||
}
|
||||
async public static Task ExecutePgCopyAsync<T>(this IInsert<T> that, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
var insert = that as FreeSql.PostgreSQL.Curd.PostgreSQLInsert<T>;
|
||||
|
@ -132,7 +132,7 @@ namespace FreeSql.PostgreSQL
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatPostgreSQL(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
||||
<Title>$(AssemblyName)</Title>
|
||||
<IsPackable>true</IsPackable>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -105,7 +105,7 @@ namespace FreeSql.ShenTong
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatShenTong(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -5,6 +5,10 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using FreeSql.Internal.CommonProvider;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Data.Common;
|
||||
using FreeSql.Internal.ObjectPool;
|
||||
#if microsoft
|
||||
using Microsoft.Data.SqlClient;
|
||||
#else
|
||||
@ -115,6 +119,51 @@ public static partial class FreeSqlSqlServerGlobalExtensions
|
||||
internal static ConcurrentDictionary<Guid, NativeTuple<SqlServerLock, Dictionary<Type, bool>>> _dicSetGlobalSelectWithLock = new ConcurrentDictionary<Guid, NativeTuple<SqlServerLock, Dictionary<Type, bool>>>();
|
||||
|
||||
#region ExecuteSqlBulkCopy
|
||||
/// <summary>
|
||||
/// 批量更新(更新字段数量超过 2000 时收益大)<para></para>
|
||||
/// 实现原理:使用 SqlBulkCopy 插入临时表,再使用 UPDATE INNER JOIN 联表更新
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="that"></param>
|
||||
/// <param name="copyOptions"></param>
|
||||
/// <param name="batchSize"></param>
|
||||
/// <param name="bulkCopyTimeout"></param>
|
||||
/// <returns></returns>
|
||||
public static int ExecuteSqlBulkCopy<T>(this IUpdate<T> that, SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default, int? batchSize = null, int? bulkCopyTimeout = null) where T : class
|
||||
{
|
||||
var update = that as UpdateProvider<T>;
|
||||
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return 0;
|
||||
var state = ExecuteSqlBulkCopyState(update);
|
||||
return UpdateProvider.ExecuteBulkUpdate(update, state, insert => insert.ExecuteSqlBulkCopy(copyOptions, batchSize, bulkCopyTimeout));
|
||||
}
|
||||
static NativeTuple<string, string, string, string, string[]> ExecuteSqlBulkCopyState<T>(UpdateProvider<T> update) where T : class
|
||||
{
|
||||
if (update._source.Any() != true) return null;
|
||||
var _table = update._table;
|
||||
var _commonUtils = update._commonUtils;
|
||||
var updateTableName = update._tableRule?.Invoke(_table.DbName) ?? _table.DbName;
|
||||
var tempTableName = $"#Temp_{updateTableName}";
|
||||
if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower();
|
||||
if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
|
||||
if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null)
|
||||
update.WithTransaction(update._orm.Ado.TransactionCurrentThread);
|
||||
var setColumns = new List<string>();
|
||||
var pkColumns = new List<string>();
|
||||
foreach (var col in _table.Columns.Values)
|
||||
{
|
||||
if (update._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name);
|
||||
else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && update._ignore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name);
|
||||
}
|
||||
var sql1 = $"SELECT {string.Join(", ", pkColumns)}, {string.Join(", ", setColumns)} INTO {tempTableName} FROM {_commonUtils.QuoteSqlName(updateTableName)} WHERE 1=2";
|
||||
var sb = new StringBuilder().Append("UPDATE ").Append(" a SET \r\n ").Append(string.Join(", \r\n ", setColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")));
|
||||
sb.Append(" \r\nFROM ").Append(_commonUtils.QuoteSqlName(updateTableName)).Append(" a ")
|
||||
.Append(" \r\nINNER JOIN ").Append(tempTableName).Append(" b ON ").Append(string.Join(" AND ", pkColumns.Select(col => $"a.{_commonUtils.QuoteSqlName(col)} = b.{_commonUtils.QuoteSqlName(col)}")));
|
||||
var sql2 = sb.ToString();
|
||||
sb.Clear();
|
||||
var sql3 = $"DROP TABLE {tempTableName}";
|
||||
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SqlServer SqlCopyBulk 批量插入功能<para></para>
|
||||
/// 使用 IgnoreColumns/InsertColumns 设置忽略/指定导入的列<para></para>
|
||||
@ -214,6 +263,13 @@ public static partial class FreeSqlSqlServerGlobalExtensions
|
||||
}
|
||||
#if net40
|
||||
#else
|
||||
public static Task<int> ExecuteSqlBulkCopyAsync<T>(this IUpdate<T> that, SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default, int? batchSize = null, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
var update = that as UpdateProvider<T>;
|
||||
if (update._source.Any() != true || update._tempPrimarys.Any() == false) return Task.FromResult(0);
|
||||
var state = ExecuteSqlBulkCopyState(update);
|
||||
return UpdateProvider.ExecuteBulkUpdateAsync(update, state, insert => insert.ExecuteSqlBulkCopyAsync(copyOptions, batchSize, bulkCopyTimeout, cancellationToken));
|
||||
}
|
||||
async public static Task ExecuteSqlBulkCopyAsync<T>(this IInsert<T> that, SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default, int? batchSize = null, int? bulkCopyTimeout = null, CancellationToken cancellationToken = default) where T : class
|
||||
{
|
||||
var insert = that as FreeSql.SqlServer.Curd.SqlServerInsert<T>;
|
||||
|
@ -59,7 +59,7 @@ namespace FreeSql.SqlServer
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatSqlServer(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -82,7 +82,7 @@ namespace FreeSql.Sqlite
|
||||
});
|
||||
|
||||
public override string FormatSql(string sql, params object[] args) => sql?.FormatSqlite(args);
|
||||
public override string QuoteSqlName(params string[] name)
|
||||
public override string QuoteSqlNameAdapter(params string[] name)
|
||||
{
|
||||
if (name.Length == 1)
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<DelaySign>false</DelaySign>
|
||||
<Version>3.2.684-preview20221124</Version>
|
||||
<Version>3.2.686-preview20221226</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Reference in New Issue
Block a user