From 3897da4f4f8ce829ffad2a476a05c5fe16c9571b Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Thu, 5 Jan 2023 16:53:25 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E4=BF=AE=E5=A4=8D=20pgsql=20OnConflictDoUp?= =?UTF-8?q?date=20=E4=B8=B4=E6=97=B6=E4=B8=BB=E9=94=AE=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=9B#1393?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PostgreSQL/Curd/OnConflictDoUpdateTest.cs | 122 +++++++++++- FreeSql/FreeSql.xml | 183 ------------------ .../CustomPostgreSQLOnConflictDoUpdate.cs | 3 +- .../Curd/KingbaseESOnConflictDoUpdate.cs | 1 + .../Curd/OdbcKingbaseESOnConflictDoUpdate.cs | 1 + .../Curd/OdbcPostgreSQLOnConflictDoUpdate.cs | 1 + .../Curd/OnConflictDoUpdate.cs | 1 + 7 files changed, 127 insertions(+), 185 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/OnConflictDoUpdateTest.cs b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/OnConflictDoUpdateTest.cs index 1f54becc..098d29b8 100644 --- a/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/OnConflictDoUpdateTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/PostgreSQL/Curd/OnConflictDoUpdateTest.cs @@ -1,7 +1,8 @@ -using FreeSql.DataAnnotations; +using FreeSql.DataAnnotations; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Xunit; namespace FreeSql.Tests.PostgreSQL @@ -16,6 +17,125 @@ namespace FreeSql.Tests.PostgreSQL public DateTime? time { get; set; } } + [Table(Name = "demo_class1"), Index("uk_demo1", "name", true)] + public class DemoClass1 + { + [Column(Name = "id")] + public int Id { get; set; } + + [Column(Name = "name", IsNullable = false)] + public string Name { get; set; } + + [Column(Name = "desc")] + public string Description { get; set; } + + #region 系统非业务基础字段 + //更新操作忽略此字段 只在OnConflictDoUpdate的插入操作时生效 + [Column(Name = "created_id", CanUpdate = false, InsertValueSql = "1")] + public virtual int CreatedId { get; set; } + + //插入操作忽略此字段 只在OnConflictDoUpdate的更新操作时生效 + [Column(Name = "modified_id", CanInsert = false, InsertValueSql = "1")] + [UpdateValueSql("1")] + public virtual int? ModifiedId { get; set; } + + //更新操作忽略此字段 只在OnConflictDoUpdate的插入操作时生效 + [Column(Name = "created_time", CanUpdate = false, ServerTime = DateTimeKind.Local)] + public virtual DateTime CreatedTime { get; set; } + + //插入操作忽略此字段 只在OnConflictDoUpdate的更新操作时生效 + [Column(Name = "modified_time", CanInsert = false, ServerTime = DateTimeKind.Local)] + public virtual DateTime? ModifiedTime { get; set; } + #endregion + } + [Table(Name = "demo_class2")] + public class DemoClass2 + { + [Column(Name = "name", IsNullable = false)] + public string Name { get; set; } + + [Column(Name = "desc")] + public string Description { get; set; } + + #region 系统非业务基础字段 + //更新操作忽略此字段 只在OnConflictDoUpdate的插入操作时生效 + [Column(Name = "created_id", CanUpdate = false, InsertValueSql = "1")] + public virtual int CreatedId { get; set; } + + //插入操作忽略此字段 只在OnConflictDoUpdate的更新操作时生效 + [Column(Name = "modified_id", CanInsert = false, InsertValueSql = "1")] + [UpdateValueSql("1")] + public virtual int? ModifiedId { get; set; } + + //更新操作忽略此字段 只在OnConflictDoUpdate的插入操作时生效 + [Column(Name = "created_time", CanUpdate = false, ServerTime = DateTimeKind.Local)] + public virtual DateTime CreatedTime { get; set; } + + //插入操作忽略此字段 只在OnConflictDoUpdate的更新操作时生效 + [Column(Name = "modified_time", CanInsert = false, ServerTime = DateTimeKind.Local)] + public virtual DateTime? ModifiedTime { get; set; } + #endregion + } + class UpdateValueSqlAttribute : Attribute + { + public string Value { get; set; } + public UpdateValueSqlAttribute(string value) => Value = value; + } + [Fact] + public void Issues1393() + { + var fsql = g.pgsql; + //跟随 FreeSqlBuilder Build 之后初始化,批量设置实体类: + foreach (var entity in new[] { typeof(DemoClass1) }) + { + var table = fsql.CodeFirst.GetTableByEntity(entity); + table.Properties.Values + .Select(a => new { Property = a, UpdateValueSql = a.GetCustomAttribute()?.Value }) + .Where(a => a.UpdateValueSql != null) + .ToList() + .ForEach(a => + { + var col = table.ColumnsByCs[a.Property.Name]; + col.GetType().GetProperty("DbUpdateValue").SetValue(col, a.UpdateValueSql); + }); + } + + var sql = fsql.Insert(Enumerable.Range(1, 5).Select(i => new DemoClass1 { Id = i, Name = $"Name{i}", Description = $"Description{i}" })) + .NoneParameter() + .OnConflictDoUpdate(a => new { a.Name }) + .ToSql(); + Assert.Equal(@"INSERT INTO ""demo_class1""(""id"", ""name"", ""desc"", ""created_id"", ""created_time"") VALUES(1, 'Name1', 'Description1', 1, current_timestamp), (2, 'Name2', 'Description2', 1, current_timestamp), (3, 'Name3', 'Description3', 1, current_timestamp), (4, 'Name4', 'Description4', 1, current_timestamp), (5, 'Name5', 'Description5', 1, current_timestamp) +ON CONFLICT(""name"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""desc"" = EXCLUDED.""desc"", +""modified_id"" = 1, +""modified_time"" = current_timestamp", sql); + + + sql = fsql.Insert(Enumerable.Range(1, 5).Select(i => new DemoClass2 { Name = $"Name{i}", Description = $"Description{i}", ModifiedId = 1 })) + .NoneParameter() + .OnConflictDoUpdate(a => new { a.Name }) + .ToSql(); + Assert.Equal(@"INSERT INTO ""demo_class2""(""name"", ""desc"", ""created_id"", ""created_time"") VALUES('Name1', 'Description1', 1, current_timestamp), ('Name2', 'Description2', 1, current_timestamp), ('Name3', 'Description3', 1, current_timestamp), ('Name4', 'Description4', 1, current_timestamp), ('Name5', 'Description5', 1, current_timestamp) +ON CONFLICT(""name"") DO UPDATE SET +""name"" = EXCLUDED.""name"", +""desc"" = EXCLUDED.""desc"", +""modified_id"" = CASE EXCLUDED.""name"" +WHEN 'Name1' THEN 1 +WHEN 'Name2' THEN 1 +WHEN 'Name3' THEN 1 +WHEN 'Name4' THEN 1 +WHEN 'Name5' THEN 1 END::int4, +""modified_time"" = current_timestamp", sql); + + //sql = g.pgsql.Insert(data) + // .NoneParameter() + // .OnConflictDoUpdate(a => new { a.Name }) + // .UpdateColumns() + // .ToSql(); + } + + [Fact] public void ExecuteAffrows() { diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 21395735..ec33a9bf 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3335,177 +3335,6 @@ - - - 测试数据库是否连接正确,本方法执行如下命令: - MySql/SqlServer/PostgreSQL/达梦/人大金仓/神通: SELECT 1 - Oracle: SELECT 1 FROM dual - - 命令超时设置(秒) - - true: 成功, false: 失败 - - - - 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 - - - - - - - - - - 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteArrayAsync("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteDataSetAsync("select * from user where age > @age; select 2", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteDataTableAsync("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 在【主库】执行 - - - - - - - - - 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 在【主库】执行 - - - - - - - - - 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new SqlParameter { ParameterName = "age", Value = 25 }) - - - - - - - - - - - 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - - 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) - - - - - - - - - - - - 执行SQL返回对象集合,Query<User, Address>("select * from user where age > @age; select * from address", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - 可自定义解析表达式 @@ -4496,12 +4325,6 @@ 超时 - - - 获取资源 - - - 使用完毕后,归还资源 @@ -4577,12 +4400,6 @@ 资源对象 - - - 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 - - 资源对象 - 归还对象给对象池的时候触发 diff --git a/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLOnConflictDoUpdate.cs b/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLOnConflictDoUpdate.cs index b7717283..c81d5b51 100644 --- a/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLOnConflictDoUpdate.cs +++ b/Providers/FreeSql.Provider.Custom/PostgreSQL/Curd/CustomPostgreSQLOnConflictDoUpdate.cs @@ -24,7 +24,7 @@ namespace FreeSql.Custom.PostgreSQL public CustomPostgreSQLOnConflictDoUpdate(IInsert insert, Expression> columns = null) { _pgsqlInsert = insert as CustomPostgreSQLInsert; - if (_pgsqlInsert == null) throw new Exception(CoreStrings.S_Features_Unique("OnConflictDoUpdate", "Odbc/PostgreSQL")); + if (_pgsqlInsert == null) throw new Exception(CoreStrings.S_Features_Unique("OnConflictDoUpdate", "Custom/PostgreSQL")); if (_pgsqlInsert._noneParameterFlag == "c") _pgsqlInsert._noneParameterFlag = "cu"; if (columns != null) @@ -109,6 +109,7 @@ namespace FreeSql.Custom.PostgreSQL { sb.Append(") DO UPDATE SET\r\n"); + if (_pgsqlUpdate._tempPrimarys.Any() == false) _pgsqlUpdate._tempPrimarys = _tempPrimarys; var sbSetEmpty = _pgsqlUpdate.InternalSbSet.Length == 0; var sbSetIncrEmpty = _pgsqlUpdate.InternalSbSetIncr.Length == 0; if (sbSetEmpty == false || sbSetIncrEmpty == false) diff --git a/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESOnConflictDoUpdate.cs b/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESOnConflictDoUpdate.cs index 91036fb5..ebe991fa 100644 --- a/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESOnConflictDoUpdate.cs +++ b/Providers/FreeSql.Provider.KingbaseES/Curd/KingbaseESOnConflictDoUpdate.cs @@ -109,6 +109,7 @@ namespace FreeSql.KingbaseES { sb.Append(") DO UPDATE SET\r\n"); + if (_update._tempPrimarys.Any() == false) _update._tempPrimarys = _tempPrimarys; var sbSetEmpty = _update.InternalSbSet.Length == 0; var sbSetIncrEmpty = _update.InternalSbSetIncr.Length == 0; if (sbSetEmpty == false || sbSetIncrEmpty == false) diff --git a/Providers/FreeSql.Provider.Odbc/KingbaseES/Curd/OdbcKingbaseESOnConflictDoUpdate.cs b/Providers/FreeSql.Provider.Odbc/KingbaseES/Curd/OdbcKingbaseESOnConflictDoUpdate.cs index e061da85..c676190d 100644 --- a/Providers/FreeSql.Provider.Odbc/KingbaseES/Curd/OdbcKingbaseESOnConflictDoUpdate.cs +++ b/Providers/FreeSql.Provider.Odbc/KingbaseES/Curd/OdbcKingbaseESOnConflictDoUpdate.cs @@ -109,6 +109,7 @@ namespace FreeSql.Odbc.KingbaseES { sb.Append(") DO UPDATE SET\r\n"); + if (_pgsqlUpdate._tempPrimarys.Any() == false) _pgsqlUpdate._tempPrimarys = _tempPrimarys; var sbSetEmpty = _pgsqlUpdate.InternalSbSet.Length == 0; var sbSetIncrEmpty = _pgsqlUpdate.InternalSbSetIncr.Length == 0; if (sbSetEmpty == false || sbSetIncrEmpty == false) diff --git a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs index 474fd73e..23562c66 100644 --- a/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs +++ b/Providers/FreeSql.Provider.Odbc/PostgreSQL/Curd/OdbcPostgreSQLOnConflictDoUpdate.cs @@ -109,6 +109,7 @@ namespace FreeSql.Odbc.PostgreSQL { sb.Append(") DO UPDATE SET\r\n"); + if (_pgsqlUpdate._tempPrimarys.Any() == false) _pgsqlUpdate._tempPrimarys = _tempPrimarys; var sbSetEmpty = _pgsqlUpdate.InternalSbSet.Length == 0; var sbSetIncrEmpty = _pgsqlUpdate.InternalSbSetIncr.Length == 0; if (sbSetEmpty == false || sbSetIncrEmpty == false) diff --git a/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs b/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs index 7727ea78..03113e00 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs +++ b/Providers/FreeSql.Provider.PostgreSQL/Curd/OnConflictDoUpdate.cs @@ -109,6 +109,7 @@ namespace FreeSql.PostgreSQL.Curd { sb.Append(") DO UPDATE SET\r\n"); + if (_pgsqlUpdate._tempPrimarys.Any() == false) _pgsqlUpdate._tempPrimarys = _tempPrimarys; var sbSetEmpty = _pgsqlUpdate.InternalSbSet.Length == 0; var sbSetIncrEmpty = _pgsqlUpdate.InternalSbSetIncr.Length == 0; if (sbSetEmpty == false || sbSetIncrEmpty == false)